puppet-lint-halyard 1.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +10 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +20 -0
  6. data/README.md +210 -0
  7. data/Rakefile +14 -0
  8. data/bin/puppet-lint +7 -0
  9. data/lib/puppet-lint.rb +214 -0
  10. data/lib/puppet-lint/bin.rb +79 -0
  11. data/lib/puppet-lint/checkplugin.rb +176 -0
  12. data/lib/puppet-lint/checks.rb +91 -0
  13. data/lib/puppet-lint/configuration.rb +153 -0
  14. data/lib/puppet-lint/data.rb +521 -0
  15. data/lib/puppet-lint/lexer.rb +373 -0
  16. data/lib/puppet-lint/lexer/token.rb +101 -0
  17. data/lib/puppet-lint/monkeypatches.rb +2 -0
  18. data/lib/puppet-lint/monkeypatches/string_percent.rb +52 -0
  19. data/lib/puppet-lint/monkeypatches/string_prepend.rb +13 -0
  20. data/lib/puppet-lint/optparser.rb +118 -0
  21. data/lib/puppet-lint/plugins.rb +74 -0
  22. data/lib/puppet-lint/plugins/check_classes.rb +285 -0
  23. data/lib/puppet-lint/plugins/check_comments.rb +55 -0
  24. data/lib/puppet-lint/plugins/check_conditionals.rb +65 -0
  25. data/lib/puppet-lint/plugins/check_documentation.rb +31 -0
  26. data/lib/puppet-lint/plugins/check_nodes.rb +29 -0
  27. data/lib/puppet-lint/plugins/check_resources.rb +194 -0
  28. data/lib/puppet-lint/plugins/check_strings.rb +174 -0
  29. data/lib/puppet-lint/plugins/check_variables.rb +19 -0
  30. data/lib/puppet-lint/plugins/check_whitespace.rb +170 -0
  31. data/lib/puppet-lint/tasks/puppet-lint.rb +91 -0
  32. data/lib/puppet-lint/version.rb +3 -0
  33. data/puppet-lint.gemspec +24 -0
  34. data/spec/fixtures/test/manifests/fail.pp +2 -0
  35. data/spec/fixtures/test/manifests/ignore.pp +1 -0
  36. data/spec/fixtures/test/manifests/ignore_multiple_block.pp +6 -0
  37. data/spec/fixtures/test/manifests/ignore_multiple_line.pp +2 -0
  38. data/spec/fixtures/test/manifests/ignore_reason.pp +1 -0
  39. data/spec/fixtures/test/manifests/init.pp +3 -0
  40. data/spec/fixtures/test/manifests/malformed.pp +1 -0
  41. data/spec/fixtures/test/manifests/url_interpolation.pp +12 -0
  42. data/spec/fixtures/test/manifests/warning.pp +2 -0
  43. data/spec/puppet-lint/bin_spec.rb +326 -0
  44. data/spec/puppet-lint/configuration_spec.rb +56 -0
  45. data/spec/puppet-lint/ignore_overrides_spec.rb +109 -0
  46. data/spec/puppet-lint/lexer/token_spec.rb +18 -0
  47. data/spec/puppet-lint/lexer_spec.rb +783 -0
  48. data/spec/puppet-lint/plugins/check_classes/autoloader_layout_spec.rb +105 -0
  49. data/spec/puppet-lint/plugins/check_classes/class_inherits_from_params_class_spec.rb +35 -0
  50. data/spec/puppet-lint/plugins/check_classes/inherits_across_namespaces_spec.rb +33 -0
  51. data/spec/puppet-lint/plugins/check_classes/names_containing_dash_spec.rb +45 -0
  52. data/spec/puppet-lint/plugins/check_classes/nested_classes_or_defines_spec.rb +76 -0
  53. data/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb +73 -0
  54. data/spec/puppet-lint/plugins/check_classes/right_to_left_relationship_spec.rb +25 -0
  55. data/spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb +196 -0
  56. data/spec/puppet-lint/plugins/check_comments/slash_comments_spec.rb +45 -0
  57. data/spec/puppet-lint/plugins/check_comments/star_comments_spec.rb +84 -0
  58. data/spec/puppet-lint/plugins/check_conditionals/case_without_default_spec.rb +98 -0
  59. data/spec/puppet-lint/plugins/check_conditionals/selector_inside_resource_spec.rb +36 -0
  60. data/spec/puppet-lint/plugins/check_documentation/documentation_spec.rb +52 -0
  61. data/spec/puppet-lint/plugins/check_nodes/unquoted_node_name_spec.rb +146 -0
  62. data/spec/puppet-lint/plugins/check_resources/duplicate_params_spec.rb +100 -0
  63. data/spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb +55 -0
  64. data/spec/puppet-lint/plugins/check_resources/ensure_not_symlink_target_spec.rb +89 -0
  65. data/spec/puppet-lint/plugins/check_resources/file_mode_spec.rb +113 -0
  66. data/spec/puppet-lint/plugins/check_resources/unquoted_file_mode_spec.rb +45 -0
  67. data/spec/puppet-lint/plugins/check_resources/unquoted_resource_title_spec.rb +216 -0
  68. data/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb +199 -0
  69. data/spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb +114 -0
  70. data/spec/puppet-lint/plugins/check_strings/puppet_url_without_modules_spec.rb +62 -0
  71. data/spec/puppet-lint/plugins/check_strings/quoted_booleans_spec.rb +129 -0
  72. data/spec/puppet-lint/plugins/check_strings/single_quote_string_with_variables_spec.rb +17 -0
  73. data/spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb +73 -0
  74. data/spec/puppet-lint/plugins/check_variables/variable_contains_dash_spec.rb +37 -0
  75. data/spec/puppet-lint/plugins/check_whitespace/2sp_soft_tabs_spec.rb +21 -0
  76. data/spec/puppet-lint/plugins/check_whitespace/80chars_spec.rb +54 -0
  77. data/spec/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb +524 -0
  78. data/spec/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb +45 -0
  79. data/spec/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb +101 -0
  80. data/spec/puppet-lint_spec.rb +20 -0
  81. data/spec/spec_helper.rb +129 -0
  82. metadata +229 -0
@@ -0,0 +1,521 @@
1
+ require 'singleton'
2
+ require 'set'
3
+
4
+ # Public: A singleton class storing all the information about the manifest
5
+ # being analysed.
6
+ class PuppetLint::Data
7
+ include Singleton
8
+
9
+ class << self
10
+ # Internal: Get/Set the full expanded path to the manifest file being
11
+ # checked.
12
+ attr_reader :path, :fullpath, :filename
13
+
14
+ # Internal: Get/Set the raw manifest data, split by \n.
15
+ attr_accessor :manifest_lines
16
+
17
+ # Internal: Store the tokenised manifest.
18
+ #
19
+ # tokens - The Array of PuppetLint::Lexer::Token objects to store.
20
+ #
21
+ # Returns nothing.
22
+ def tokens=(tokens)
23
+ @tokens = tokens
24
+ @title_tokens = nil
25
+ @resource_indexes = nil
26
+ @class_indexes = nil
27
+ @defined_type_indexes = nil
28
+ @function_indexes = nil
29
+ @array_indexes = nil
30
+ @hash_indexes = nil
31
+ @defaults_indexes = nil
32
+ end
33
+
34
+ # Public: Get the tokenised manifest.
35
+ #
36
+ # Returns an Array of PuppetLint::Lexer::Token objects.
37
+ def tokens
38
+ calling_method = begin
39
+ caller[0][/`.*'/][1..-2]
40
+ rescue NoMethodError
41
+ caller[1][/`.*'/][1..-2]
42
+ end
43
+
44
+ if calling_method == 'check'
45
+ @tokens.dup
46
+ else
47
+ @tokens
48
+ end
49
+ end
50
+
51
+ # Internal: Store the path to the manifest file and populate fullpath and
52
+ # filename.
53
+ #
54
+ # val - The path to the file as a String.
55
+ #
56
+ # Returns nothing.
57
+ def path=(val)
58
+ @path = val
59
+ if val.nil?
60
+ @fullpath = nil
61
+ @filename = nil
62
+ else
63
+ @fullpath = File.expand_path(val, ENV['PWD'])
64
+ @filename = File.basename(val)
65
+ end
66
+ end
67
+
68
+ # Internal: Retrieve a list of tokens that represent resource titles
69
+ #
70
+ # Returns an Array of PuppetLint::Lexer::Token objects.
71
+ def title_tokens
72
+ @title_tokens ||= Proc.new do
73
+ result = []
74
+ tokens.each_index do |token_idx|
75
+ if tokens[token_idx].type == :COLON
76
+ # gather a list of tokens that are resource titles
77
+ if tokens[token_idx-1].type == :RBRACK
78
+ array_start_idx = tokens.rindex { |r|
79
+ r.type == :LBRACK
80
+ }
81
+ title_array_tokens = tokens[(array_start_idx + 1)..(token_idx - 2)]
82
+ result += title_array_tokens.select { |token|
83
+ {:STRING => true, :NAME => true}.include? token.type
84
+ }
85
+ else
86
+ next_token = tokens[token_idx].next_code_token
87
+ if next_token.type != :LBRACE
88
+ result << tokens[token_idx - 1]
89
+ end
90
+ end
91
+ end
92
+ end
93
+ result
94
+ end.call
95
+ end
96
+
97
+ # Internal: Calculate the positions of all resource declarations within the
98
+ # tokenised manifest. These positions only point to the content of the
99
+ # resource declarations, they do not include resource types or titles.
100
+ #
101
+ # Returns an Array of Hashes, each containing:
102
+ # :start - An Integer position in the `tokens` Array pointing to the
103
+ # first Token of a resource declaration.
104
+ # :end - An Integer position in the `tokens` Array pointing to the last
105
+ # Token of a resource declaration.
106
+ def resource_indexes
107
+ @resource_indexes ||= Proc.new do
108
+ result = []
109
+ tokens.each_index do |token_idx|
110
+ if tokens[token_idx].type == :COLON
111
+ next_token = tokens[token_idx].next_code_token
112
+ depth = 1
113
+ if next_token && next_token.type != :LBRACE
114
+ tokens[(token_idx + 1)..-1].each_index do |idx|
115
+ real_idx = token_idx + idx + 1
116
+ if tokens[real_idx].type == :LBRACE
117
+ depth += 1
118
+ elsif {:SEMIC => true, :RBRACE => true}.include? tokens[real_idx].type
119
+ unless tokens[real_idx].type == :SEMIC && depth > 1
120
+ depth -= 1
121
+ if depth == 0
122
+ result << {
123
+ :start => token_idx + 1,
124
+ :end => real_idx,
125
+ :tokens => tokens[(token_idx + 1)..real_idx],
126
+ :type => find_resource_type_token(token_idx),
127
+ :param_tokens => find_resource_param_tokens(tokens[(token_idx + 1)..real_idx]),
128
+ }
129
+ break
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ result
138
+ end.call
139
+ end
140
+
141
+ # Internal: Find the Token representing the type of a resource definition.
142
+ #
143
+ # index - The Integer pointing to the start of the resource in the `tokens`
144
+ # array.
145
+ #
146
+ # Returns a Token object.
147
+ def find_resource_type_token(index)
148
+ tokens[tokens[0..index].rindex { |token| token.type == :LBRACE }].prev_code_token
149
+ end
150
+
151
+ # Internal: Find all the Token objects representing the parameter names in
152
+ # a resource definition.
153
+ #
154
+ # resource_tokens - An Array of Token objects that comprise the resource
155
+ # definition.
156
+ #
157
+ # Returns an Array of Token objects.
158
+ def find_resource_param_tokens(resource_tokens)
159
+ resource_tokens.select { |token|
160
+ token.type == :NAME && token.next_code_token.type == :FARROW
161
+ }
162
+ end
163
+
164
+ # Internal: Calculate the positions of all class definitions within the
165
+ # `tokens` Array.
166
+ #
167
+ # Returns an Array of Hashes, each containing:
168
+ # :start - An Integer position in the `tokens` Array pointing to the
169
+ # first Token of a class definition.
170
+ # :end - An Integer position in the `tokens` Array pointing to the last
171
+ # Token of a class definition.
172
+ # :tokens - An Array consisting of all the Token objects that make up the
173
+ # class definition.
174
+ def class_indexes
175
+ @class_indexes ||= definition_indexes(:CLASS)
176
+ end
177
+
178
+ # Internal: Calculate the positions of all defined type definitions within
179
+ # the `tokens` Array.
180
+ #
181
+ # Returns an Array of Hashes, each containing:
182
+ # :start - An Integer position in the `tokens` Array pointing to the
183
+ # first Token of a defined type definition.
184
+ # :end - An Integer position in the `tokens` Array pointing to the last
185
+ # Token of a defined type definition.
186
+ # :tokens - An Array consisting of all the Token objects that make up the
187
+ # defined type.
188
+ def defined_type_indexes
189
+ @defined_type_indexes ||= definition_indexes(:DEFINE)
190
+ end
191
+
192
+ # Internal: Calculate the positions of all node definitions within the
193
+ # `tokens` Array.
194
+ #
195
+ # Returns an Array of Hashes, each containing:
196
+ # :start - An Integer position in the `tokens` Array pointing to the
197
+ # first Token of a defined type definition.
198
+ # :end - An Integer position in the `tokens` Array pointing to the last
199
+ # Token of a defined type definition.
200
+ # :tokens - An Array consisting of all the Token objects that make up the
201
+ # defined type.
202
+ def node_indexes
203
+ @node_indexes ||= definition_indexes(:NODE)
204
+ end
205
+
206
+ # Internal: Calculate the positions of all the specified defintion types
207
+ # within the `tokens` Array.
208
+ #
209
+ # Returns an Array of Hashes, each containing:
210
+ # :start - An Integer position in the `tokens` Array pointing to the
211
+ # first Token of a definition.
212
+ # :end - An Integer position in the `tokens` Array pointing to the last
213
+ # Token of a definition.
214
+ # :tokens - An Array consisting of all the Token objects that make up the
215
+ # definition.
216
+ def definition_indexes(type)
217
+ result = []
218
+ tokens.each_with_index do |token, i|
219
+ if token.type == type
220
+ brace_depth = 0
221
+ paren_depth = 0
222
+ in_params = false
223
+ inherited_class = nil
224
+ tokens[i+1..-1].each_with_index do |definition_token, j|
225
+ case definition_token.type
226
+ when :INHERITS
227
+ inherited_class = definition_token.next_code_token
228
+ when :LPAREN
229
+ in_params = true if paren_depth == 0
230
+ paren_depth += 1
231
+ when :RPAREN
232
+ in_params = false if paren_depth == 1
233
+ paren_depth -= 1
234
+ when :LBRACE
235
+ brace_depth += 1
236
+ when :RBRACE
237
+ brace_depth -= 1
238
+ if brace_depth == 0 && !in_params
239
+ if token.next_code_token.type != :LBRACE
240
+ result << {
241
+ :start => i,
242
+ :end => i + j + 1,
243
+ :tokens => tokens[i..(i + j + 1)],
244
+ :param_tokens => param_tokens(tokens[i..(i + j + 1)]),
245
+ :type => type,
246
+ :name_token => token.next_code_token,
247
+ :inherited_token => inherited_class,
248
+ }
249
+ break
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+ result
257
+ end
258
+
259
+ # Internal: Calculate the positions of all function calls within
260
+ # `tokens` Array.
261
+ #
262
+ # Returns an Array of Hashes, each containing:
263
+ # :start - An Integer position in the `tokens` Array pointing to the
264
+ # first Token of a function call
265
+ # :end - An Integer position in the `tokens` Array pointing to the last
266
+ # Token of a function call
267
+ # :tokens - An Array consisting of all the Token objects that make up the
268
+ # function call.
269
+ def function_indexes
270
+ @function_indexes ||= Proc.new do
271
+ functions = []
272
+ tokens.each_with_index do |token, token_idx|
273
+ if token.type == :NAME && \
274
+ (token_idx == 0 || (token_idx == 1 && tokens[0].type == :WHITESPACE) || token.prev_token.type == :NEWLINE || token.prev_token.type == :INDENT || \
275
+ # function in a function
276
+ (token.prev_code_token && token.prev_code_token.type == :LPAREN))
277
+
278
+ # Hash key
279
+ next if token.next_code_token && token.next_code_token.type == :FARROW
280
+
281
+ level = 0
282
+ real_idx = 0
283
+ in_paren = false
284
+ tokens[token_idx+1..-1].each_with_index do |cur_token, cur_token_idx|
285
+ break if level == 0 && in_paren
286
+ break if level == 0 && cur_token.type == :NEWLINE
287
+
288
+ if cur_token.type == :LPAREN
289
+ level += 1
290
+ in_paren = true
291
+ end
292
+ level -= 1 if cur_token.type == :RPAREN
293
+ real_idx = token_idx + 1 + cur_token_idx
294
+ end
295
+
296
+ functions << {
297
+ :start => token_idx,
298
+ :end => real_idx,
299
+ :tokens => tokens[token_idx..real_idx],
300
+ }
301
+ end
302
+ end
303
+ functions
304
+ end.call
305
+ end
306
+
307
+ # Internal: Calculate the positions of all array values within
308
+ # `tokens` Array.
309
+ #
310
+ # Returns an Array of Hashes, each containing:
311
+ # :start - An Integer position in the `tokens` Array pointing to the
312
+ # first Token of an array value
313
+ # :end - An Integer position in the `tokens` Array pointing to the last
314
+ # Token of an array value
315
+ # :tokens - An Array consisting of all the Token objects that make up the
316
+ # array value.
317
+ def array_indexes
318
+ @array_indexes ||= Proc.new do
319
+ arrays = []
320
+ tokens.each_with_index do |token, token_idx|
321
+ if token.type == :LBRACK
322
+ real_idx = 0
323
+ tokens[token_idx+1..-1].each_with_index do |cur_token, cur_token_idx|
324
+ real_idx = token_idx + 1 + cur_token_idx
325
+ break if cur_token.type == :RBRACK
326
+ end
327
+
328
+ # Ignore resource references
329
+ next if token.prev_code_token && \
330
+ token.prev_code_token.type == :CLASSREF
331
+ arrays << {
332
+ :start => token_idx,
333
+ :end => real_idx,
334
+ :tokens => tokens[token_idx..real_idx],
335
+ }
336
+ end
337
+ end
338
+ arrays
339
+ end.call
340
+ end
341
+
342
+ # Internal: Calculate the positions of all hash values within
343
+ # `tokens` Array.
344
+ #
345
+ # Returns an Array of Hashes, each containing:
346
+ # :start - An Integer position in the `tokens` Array pointing to the
347
+ # first Token of an hash value
348
+ # :end - An Integer position in the `tokens` Array pointing to the last
349
+ # Token of an hash value
350
+ # :tokens - An Array consisting of all the Token objects that make up the
351
+ # hash value.
352
+ def hash_indexes
353
+ @hash_indexes ||= Proc.new do
354
+ hashes = []
355
+ tokens.each_with_index do |token, token_idx|
356
+ next unless token.prev_code_token
357
+ next unless [:EQUALS, :ISEQUAL, :FARROW, :LPAREN].include? token.prev_code_token.type
358
+ if token.type == :LBRACE
359
+ level = 0
360
+ real_idx = 0
361
+ tokens[token_idx+1..-1].each_with_index do |cur_token, cur_token_idx|
362
+ real_idx = token_idx + 1 + cur_token_idx
363
+
364
+ level += 1 if cur_token.type == :LBRACE
365
+ level -= 1 if cur_token.type == :RBRACE
366
+ break if level < 0
367
+ end
368
+
369
+ hashes << {
370
+ :start => token_idx,
371
+ :end => real_idx,
372
+ :tokens => tokens[token_idx..real_idx],
373
+ }
374
+ end
375
+ end
376
+ hashes
377
+ end.call
378
+ end
379
+
380
+ # Internal: Calculate the positions of all defaults declarations within
381
+ # `tokens` Array.
382
+ #
383
+ # Returns an Array of Hashes, each containing:
384
+ # :start - An Integer position in the `tokens` Array pointing to the
385
+ # first Token of the defaults declaration
386
+ # :end - An Integer position in the `tokens` Array pointing to the last
387
+ # Token of the defaults declaration
388
+ # :tokens - An Array consisting of all the Token objects that make up the
389
+ # defaults declaration.
390
+ def defaults_indexes
391
+ @defaults_indexes ||= Proc.new do
392
+ defaults = []
393
+ tokens.each_with_index do |token, token_idx|
394
+ if token.type == :CLASSREF && token.next_code_token && \
395
+ token.next_code_token.type == :LBRACE
396
+ real_idx = 0
397
+
398
+ tokens[token_idx+1..-1].each_with_index do |cur_token, cur_token_idx|
399
+ real_idx = token_idx + 1 + cur_token_idx
400
+ break if cur_token.type == :RBRACE
401
+ end
402
+
403
+ defaults << {
404
+ :start => token_idx,
405
+ :end => real_idx,
406
+ :tokens => tokens[token_idx..real_idx],
407
+ }
408
+ end
409
+ end
410
+ defaults
411
+ end.call
412
+ end
413
+
414
+ # Internal: Finds all the tokens that make up the defined type or class
415
+ # definition parameters.
416
+ #
417
+ # these_tokens - An Array of PuppetLint::Lexer::Token objects that make up
418
+ # the defined type or class definition.
419
+ #
420
+ # Returns an Array of PuppetLint::Lexer::Token objects or nil if it takes
421
+ # no parameters.
422
+ def param_tokens(these_tokens)
423
+ depth = 0
424
+ lparen_idx = nil
425
+ rparen_idx = nil
426
+
427
+ these_tokens.each_with_index do |token, i|
428
+ if token.type == :LPAREN
429
+ depth += 1
430
+ lparen_idx = i if depth == 1
431
+ elsif token.type == :RPAREN
432
+ depth -= 1
433
+ if depth == 0
434
+ rparen_idx = i
435
+ break
436
+ end
437
+ elsif token.type == :LBRACE && depth == 0
438
+ # no parameters
439
+ break
440
+ end
441
+ end
442
+
443
+ if lparen_idx.nil? or rparen_idx.nil?
444
+ nil
445
+ else
446
+ these_tokens[(lparen_idx + 1)..(rparen_idx - 1)]
447
+ end
448
+ end
449
+
450
+ # Internal: Retrieves a list of token types that are considered to be
451
+ # formatting tokens (whitespace, newlines, etc).
452
+ #
453
+ # Returns an Array of Symbols.
454
+ def formatting_tokens
455
+ @formatting_tokens ||= PuppetLint::Lexer::FORMATTING_TOKENS
456
+ end
457
+
458
+ # Internal: Retrieves a Hash of Sets. Each key is a check name Symbol and
459
+ # the Set of Integers returned lists all the lines that the check results
460
+ # should be ignored on.
461
+ #
462
+ # Returns a Hash of Sets of Integers.
463
+ def ignore_overrides
464
+ @ignore_overrides ||= {}
465
+ end
466
+
467
+ # Internal: Parses all COMMENT, MLCOMMENT and SLASH_COMMENT tokens looking
468
+ # for control comments (comments that enable or disable checks). Builds the
469
+ # contents of the `ignore_overrides` hash.
470
+ #
471
+ # Returns nothing.
472
+ def parse_control_comments
473
+ @ignore_overrides.each_key { |check| @ignore_overrides[check].clear }
474
+ control_re = /\A(lint:\S+)(\s+lint:\S+)*(.*)/
475
+
476
+ comment_token_types = Set[:COMMENT, :MLCOMMENT, :SLASH_COMMENT]
477
+
478
+ comment_tokens = tokens.select { |token|
479
+ comment_token_types.include?(token.type)
480
+ }
481
+ control_comment_tokens = comment_tokens.select { |token|
482
+ token.value.strip =~ /\Alint:(ignore:[\w\d]+|endignore)/
483
+ }
484
+
485
+ stack = []
486
+ control_comment_tokens.each do |token|
487
+ comment_data = control_re.match(token.value.strip).to_a[1..-1].compact.map(&:strip)
488
+ if comment_data.last =~ /\Alint:(ignore|endignore)/
489
+ comment_data << ''
490
+ end
491
+ reason = comment_data.pop
492
+ stack_add = []
493
+ comment_data.each do |control|
494
+ split_control = control.split(':')
495
+ command = split_control[1]
496
+
497
+ if command == 'ignore'
498
+ check = split_control[2].to_sym
499
+
500
+ if token.prev_token && !Set[:NEWLINE, :INDENT].include?(token.prev_token.type)
501
+ # control comment at the end of the line, override applies to
502
+ # a single line only
503
+ (ignore_overrides[check] ||= {})[token.line] = reason
504
+ else
505
+ stack_add << [token.line, reason, check]
506
+ end
507
+ else
508
+ stack.pop.each do |start|
509
+ unless start.nil?
510
+ (start[0]..token.line).each do |i|
511
+ (ignore_overrides[start[2]] ||= {})[i] = start[1]
512
+ end
513
+ end
514
+ end
515
+ end
516
+ end
517
+ stack << stack_add unless stack_add.empty?
518
+ end
519
+ end
520
+ end
521
+ end