puppet-lint 0.4.0.pre1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/.travis.yml +3 -4
  2. data/Gemfile +2 -5
  3. data/README.md +2 -149
  4. data/Rakefile +0 -5
  5. data/lib/puppet-lint.rb +74 -20
  6. data/lib/puppet-lint/bin.rb +20 -85
  7. data/lib/puppet-lint/checkplugin.rb +158 -12
  8. data/lib/puppet-lint/checks.rb +39 -222
  9. data/lib/puppet-lint/configuration.rb +12 -31
  10. data/lib/puppet-lint/data.rb +329 -0
  11. data/lib/puppet-lint/lexer.rb +37 -30
  12. data/lib/puppet-lint/lexer/token.rb +14 -16
  13. data/lib/puppet-lint/monkeypatches/string_prepend.rb +6 -0
  14. data/lib/puppet-lint/optparser.rb +105 -0
  15. data/lib/puppet-lint/plugins.rb +28 -9
  16. data/lib/puppet-lint/plugins/check_classes.rb +162 -238
  17. data/lib/puppet-lint/plugins/check_comments.rb +40 -25
  18. data/lib/puppet-lint/plugins/check_conditionals.rb +16 -20
  19. data/lib/puppet-lint/plugins/check_documentation.rb +14 -20
  20. data/lib/puppet-lint/plugins/check_nodes.rb +23 -0
  21. data/lib/puppet-lint/plugins/check_resources.rb +127 -141
  22. data/lib/puppet-lint/plugins/check_strings.rb +133 -107
  23. data/lib/puppet-lint/plugins/check_variables.rb +11 -11
  24. data/lib/puppet-lint/plugins/check_whitespace.rb +86 -92
  25. data/lib/puppet-lint/tasks/puppet-lint.rb +17 -1
  26. data/lib/puppet-lint/version.rb +1 -1
  27. data/puppet-lint.gemspec +4 -2
  28. data/spec/fixtures/test/manifests/ignore.pp +1 -0
  29. data/spec/fixtures/test/manifests/ignore_reason.pp +1 -0
  30. data/spec/puppet-lint/bin_spec.rb +104 -84
  31. data/spec/puppet-lint/configuration_spec.rb +19 -19
  32. data/spec/puppet-lint/ignore_overrides_spec.rb +97 -0
  33. data/spec/puppet-lint/lexer/token_spec.rb +9 -9
  34. data/spec/puppet-lint/lexer_spec.rb +352 -325
  35. data/spec/puppet-lint/plugins/check_classes/autoloader_layout_spec.rb +77 -23
  36. data/spec/puppet-lint/plugins/check_classes/class_inherits_from_params_class_spec.rb +14 -12
  37. data/spec/puppet-lint/plugins/check_classes/inherits_across_namespaces_spec.rb +18 -14
  38. data/spec/puppet-lint/plugins/check_classes/names_containing_dash_spec.rb +30 -30
  39. data/spec/puppet-lint/plugins/check_classes/nested_classes_or_defines_spec.rb +31 -26
  40. data/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb +34 -28
  41. data/spec/puppet-lint/plugins/check_classes/right_to_left_relationship_spec.rb +14 -12
  42. data/spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb +74 -30
  43. data/spec/puppet-lint/plugins/check_comments/slash_comments_spec.rb +27 -20
  44. data/spec/puppet-lint/plugins/check_comments/star_comments_spec.rb +78 -13
  45. data/spec/puppet-lint/plugins/check_conditionals/case_without_default_spec.rb +17 -12
  46. data/spec/puppet-lint/plugins/check_conditionals/selector_inside_resource_spec.rb +13 -10
  47. data/spec/puppet-lint/plugins/check_documentation/documentation_spec.rb +21 -16
  48. data/spec/puppet-lint/plugins/check_nodes/unquoted_node_name_spec.rb +69 -0
  49. data/spec/puppet-lint/plugins/check_resources/duplicate_params_spec.rb +42 -38
  50. data/spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb +22 -10
  51. data/spec/puppet-lint/plugins/check_resources/ensure_not_symlink_target_spec.rb +81 -18
  52. data/spec/puppet-lint/plugins/check_resources/file_mode_spec.rb +69 -112
  53. data/spec/puppet-lint/plugins/check_resources/unquoted_file_mode_spec.rb +27 -20
  54. data/spec/puppet-lint/plugins/check_resources/unquoted_resource_title_spec.rb +177 -171
  55. data/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb +165 -88
  56. data/spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb +97 -22
  57. data/spec/puppet-lint/plugins/check_strings/puppet_url_without_modules_spec.rb +25 -0
  58. data/spec/puppet-lint/plugins/check_strings/quoted_booleans_spec.rb +97 -111
  59. data/spec/puppet-lint/plugins/check_strings/single_quote_string_with_variables_spec.rb +10 -9
  60. data/spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb +53 -53
  61. data/spec/puppet-lint/plugins/check_variables/variable_contains_dash_spec.rb +26 -14
  62. data/spec/puppet-lint/plugins/check_whitespace/2sp_soft_tabs_spec.rb +10 -9
  63. data/spec/puppet-lint/plugins/check_whitespace/80chars_spec.rb +31 -15
  64. data/spec/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb +340 -322
  65. data/spec/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb +30 -23
  66. data/spec/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb +42 -41
  67. data/spec/puppet-lint_spec.rb +3 -3
  68. data/spec/spec_helper.rb +109 -116
  69. metadata +109 -50
  70. data/spec/puppet-lint/plugins/check_classes/class_parameter_defaults_spec.rb +0 -60
@@ -1,4 +1,6 @@
1
1
  class PuppetLint
2
+ # Public: A singleton class to store the running configuration of
3
+ # puppet-lint.
2
4
  class Configuration
3
5
  # Internal: Add helper methods for a new check to the
4
6
  # PuppetLint::Configuration object.
@@ -38,7 +40,9 @@ class PuppetLint
38
40
  # Public: Catch situations where options are being set for the first time
39
41
  # and create the necessary methods to get & set the option in the future.
40
42
  #
41
- # args[0] - The value to set the option to.
43
+ # args - An Array of values to set the option to.
44
+ # method - The String name of the option.
45
+ # block - Unused.
42
46
  #
43
47
  # Returns nothing.
44
48
  #
@@ -102,22 +106,12 @@ class PuppetLint
102
106
  # Internal: Register a new check.
103
107
  #
104
108
  # check - The String name of the check
105
- # b - The Block containing the logic of the check
109
+ # klass - The Class containing the check logic.
106
110
  #
107
111
  # Returns nothing.
108
- def add_check(check, &b)
112
+ def add_check(check, klass)
109
113
  self.class.add_check(check)
110
- check_method[check] = b
111
- end
112
-
113
- # Internal: Register a new check helper method.
114
- #
115
- # name - The String name of the method.
116
- # b - The Block containing the logic of the helper.
117
- #
118
- # Returns nothing.
119
- def add_helper(name, &b)
120
- helper_method[name] = b
114
+ check_object[check] = klass
121
115
  end
122
116
 
123
117
  # Internal: Access the internal storage for settings.
@@ -130,29 +124,15 @@ class PuppetLint
130
124
  # Internal: Access the internal storage for check method blocks.
131
125
  #
132
126
  # Returns a Hash containing all the check blocks.
133
- def check_method
134
- @check_method ||= {}
127
+ def check_object
128
+ @check_object ||= {}
135
129
  end
136
130
 
137
131
  # Public: Get a list of all the defined checks.
138
132
  #
139
133
  # Returns an Array of String check names.
140
134
  def checks
141
- check_method.keys
142
- end
143
-
144
- # Internal: Access the internal storage for helper method blocks.
145
- #
146
- # Returns a Hash containing all the helper blocks.
147
- def helper_method
148
- @helper_method ||= {}
149
- end
150
-
151
- # Public: Get a list of all the helper methods.
152
- #
153
- # Returns an Array of String method names.
154
- def helpers
155
- helper_method.keys
135
+ check_object.keys
156
136
  end
157
137
 
158
138
  # Public: Clear the PuppetLint::Configuration storage and set some sane
@@ -167,6 +147,7 @@ class PuppetLint
167
147
  self.log_format = ''
168
148
  self.with_context = false
169
149
  self.fix = false
150
+ self.show_ignored = false
170
151
  end
171
152
  end
172
153
  end
@@ -0,0 +1,329 @@
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
+ end
29
+
30
+ # Public: Get the tokenised manifest.
31
+ #
32
+ # Returns an Array of PuppetLint::Lexer::Token objects.
33
+ def tokens
34
+ if caller[0][/`.*'/][1..-2] == 'check'
35
+ @tokens.dup
36
+ else
37
+ @tokens
38
+ end
39
+ end
40
+
41
+ # Internal: Store the path to the manifest file and populate fullpath and
42
+ # filename.
43
+ #
44
+ # val - The path to the file as a String.
45
+ #
46
+ # Returns nothing.
47
+ def path=(val)
48
+ @path = val
49
+ if val.nil?
50
+ @fullpath = nil
51
+ @filename = nil
52
+ else
53
+ @fullpath = File.expand_path(val, ENV['PWD'])
54
+ @filename = File.basename(val)
55
+ end
56
+ end
57
+
58
+ # Internal: Retrieve a list of tokens that represent resource titles
59
+ #
60
+ # Returns an Array of PuppetLint::Lexer::Token objects.
61
+ def title_tokens
62
+ @title_tokens ||= Proc.new do
63
+ result = []
64
+ tokens.each_index do |token_idx|
65
+ if tokens[token_idx].type == :COLON
66
+ # gather a list of tokens that are resource titles
67
+ if tokens[token_idx-1].type == :RBRACK
68
+ array_start_idx = tokens.rindex { |r|
69
+ r.type == :LBRACK
70
+ }
71
+ title_array_tokens = tokens[(array_start_idx + 1)..(token_idx - 2)]
72
+ result += title_array_tokens.select { |token|
73
+ {:STRING => true, :NAME => true}.include? token.type
74
+ }
75
+ else
76
+ next_token = tokens[token_idx].next_code_token
77
+ if next_token.type != :LBRACE
78
+ result << tokens[token_idx - 1]
79
+ end
80
+ end
81
+ end
82
+ end
83
+ result
84
+ end.call
85
+ end
86
+
87
+ # Internal: Calculate the positions of all resource declarations within the
88
+ # tokenised manifest. These positions only point to the content of the
89
+ # resource declarations, they do not include resource types or titles.
90
+ #
91
+ # Returns an Array of Hashes, each containing:
92
+ # :start - An Integer position in the `tokens` Array pointing to the
93
+ # first Token of a resource declaration.
94
+ # :end - An Integer position in the `tokens` Array pointing to the last
95
+ # Token of a resource declaration.
96
+ def resource_indexes
97
+ @resource_indexes ||= Proc.new do
98
+ result = []
99
+ tokens.each_index do |token_idx|
100
+ if tokens[token_idx].type == :COLON
101
+ next_token = tokens[token_idx].next_code_token
102
+ depth = 1
103
+ if next_token.type != :LBRACE
104
+ tokens[(token_idx + 1)..-1].each_index do |idx|
105
+ real_idx = token_idx + idx + 1
106
+ if tokens[real_idx].type == :LBRACE
107
+ depth += 1
108
+ elsif {:SEMIC => true, :RBRACE => true}.include? tokens[real_idx].type
109
+ unless tokens[real_idx].type == :SEMIC && depth > 1
110
+ depth -= 1
111
+ if depth == 0
112
+ result << {
113
+ :start => token_idx + 1,
114
+ :end => real_idx,
115
+ :tokens => tokens[(token_idx + 1)..real_idx],
116
+ :type => find_resource_type_token(token_idx),
117
+ :param_tokens => find_resource_param_tokens(tokens[(token_idx + 1)..real_idx]),
118
+ }
119
+ break
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ result
128
+ end.call
129
+ end
130
+
131
+ # Internal: Find the Token representing the type of a resource definition.
132
+ #
133
+ # index - The Integer pointing to the start of the resource in the `tokens`
134
+ # array.
135
+ #
136
+ # Returns a Token object.
137
+ def find_resource_type_token(index)
138
+ tokens[tokens[0..index].rindex { |token| token.type == :LBRACE }].prev_code_token
139
+ end
140
+
141
+ # Internal: Find all the Token objects representing the parameter names in
142
+ # a resource definition.
143
+ #
144
+ # resource_tokens - An Array of Token objects that comprise the resource
145
+ # definition.
146
+ #
147
+ # Returns an Array of Token objects.
148
+ def find_resource_param_tokens(resource_tokens)
149
+ resource_tokens.select { |token|
150
+ token.type == :NAME && token.next_code_token.type == :FARROW
151
+ }
152
+ end
153
+
154
+ # Internal: Calculate the positions of all class definitions within the
155
+ # `tokens` Array.
156
+ #
157
+ # Returns an Array of Hashes, each containing:
158
+ # :start - An Integer position in the `tokens` Array pointing to the
159
+ # first Token of a class definition.
160
+ # :end - An Integer position in the `tokens` Array pointing to the last
161
+ # Token of a class definition.
162
+ # :tokens - An Array consisting of all the Token objects that make up the
163
+ # class definition.
164
+ def class_indexes
165
+ @class_indexes ||= definition_indexes(:CLASS)
166
+ end
167
+
168
+ # Internal: Calculate the positions of all defined type definitions within
169
+ # the `tokens` Array.
170
+ #
171
+ # Returns an Array of Hashes, each containing:
172
+ # :start - An Integer position in the `tokens` Array pointing to the
173
+ # first Token of a defined type definition.
174
+ # :end - An Integer position in the `tokens` Array pointing to the last
175
+ # Token of a defined type definition.
176
+ # :tokens - An Array consisting of all the Token objects that make up the
177
+ # defined type.
178
+ def defined_type_indexes
179
+ @defined_type_indexes ||= definition_indexes(:DEFINE)
180
+ end
181
+
182
+ # Internal: Calculate the positions of all the specified defintion types
183
+ # within the `tokens` Array.
184
+ #
185
+ # Returns an Array of Hashes, each containing:
186
+ # :start - An Integer position in the `tokens` Array pointing to the
187
+ # first Token of a definition.
188
+ # :end - An Integer position in the `tokens` Array pointing to the last
189
+ # Token of a definition.
190
+ # :tokens - An Array consisting of all the Token objects that make up the
191
+ # definition.
192
+ def definition_indexes(type)
193
+ result = []
194
+ tokens.each_with_index do |token, i|
195
+ if token.type == type
196
+ brace_depth = 0
197
+ paren_depth = 0
198
+ in_params = false
199
+ inherited_class = nil
200
+ tokens[i+1..-1].each_with_index do |definition_token, j|
201
+ case definition_token.type
202
+ when :INHERITS
203
+ inherited_class = definition_token.next_code_token
204
+ when :LPAREN
205
+ in_params = true if paren_depth == 0
206
+ paren_depth += 1
207
+ when :RPAREN
208
+ in_params = false if paren_depth == 1
209
+ paren_depth -= 1
210
+ when :LBRACE
211
+ brace_depth += 1
212
+ when :RBRACE
213
+ brace_depth -= 1
214
+ if brace_depth == 0 && !in_params
215
+ if token.next_code_token.type != :LBRACE
216
+ result << {
217
+ :start => i,
218
+ :end => i + j + 1,
219
+ :tokens => tokens[i..(i + j + 1)],
220
+ :param_tokens => param_tokens(tokens[i..(i + j + 1)]),
221
+ :type => type,
222
+ :name_token => token.next_code_token,
223
+ :inherited_token => inherited_class,
224
+ }
225
+ break
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+ result
233
+ end
234
+
235
+ # Internal: Finds all the tokens that make up the defined type or class
236
+ # definition parameters.
237
+ #
238
+ # these_tokens - An Array of PuppetLint::Lexer::Token objects that make up
239
+ # the defined type or class definition.
240
+ #
241
+ # Returns an Array of PuppetLint::Lexer::Token objects or nil if it takes
242
+ # no parameters.
243
+ def param_tokens(these_tokens)
244
+ depth = 0
245
+ lparen_idx = nil
246
+ rparen_idx = nil
247
+
248
+ these_tokens.each_with_index do |token, i|
249
+ if token.type == :LPAREN
250
+ depth += 1
251
+ lparen_idx = i if depth == 1
252
+ elsif token.type == :RPAREN
253
+ depth -= 1
254
+ if depth == 0
255
+ rparen_idx = i
256
+ break
257
+ end
258
+ end
259
+ end
260
+
261
+ if lparen_idx.nil? or rparen_idx.nil?
262
+ nil
263
+ else
264
+ these_tokens[(lparen_idx + 1)..(rparen_idx - 1)]
265
+ end
266
+ end
267
+
268
+ # Internal: Retrieves a list of token types that are considered to be
269
+ # formatting tokens (whitespace, newlines, etc).
270
+ #
271
+ # Returns an Array of Symbols.
272
+ def formatting_tokens
273
+ @formatting_tokens ||= PuppetLint::Lexer::FORMATTING_TOKENS
274
+ end
275
+
276
+ # Internal: Retrieves a Hash of Sets. Each key is a check name Symbol and
277
+ # the Set of Integers returned lists all the lines that the check results
278
+ # should be ignored on.
279
+ #
280
+ # Returns a Hash of Sets of Integers.
281
+ def ignore_overrides
282
+ @ignore_overrides ||= {}
283
+ end
284
+
285
+ # Internal: Parses all COMMENT, MLCOMMENT and SLASH_COMMENT tokens looking
286
+ # for control comments (comments that enable or disable checks). Builds the
287
+ # contents of the `ignore_overrides` hash.
288
+ #
289
+ # Returns nothing.
290
+ def parse_control_comments
291
+ @ignore_overrides.each_key { |check| @ignore_overrides[check].clear }
292
+
293
+ comment_token_types = Set[:COMMENT, :MLCOMMENT, :SLASH_COMMENT]
294
+
295
+ comment_tokens = tokens.select { |token|
296
+ comment_token_types.include?(token.type)
297
+ }
298
+ control_comment_tokens = comment_tokens.select { |token|
299
+ token.value.strip =~ /\Alint:(ignore:[\w\d]+|endignore)/
300
+ }
301
+
302
+ stack = []
303
+ control_comment_tokens.each do |token|
304
+ control, reason = token.value.strip.split(' ', 2)
305
+ split_control = control.split(':')
306
+ command = split_control[1]
307
+
308
+ if command == 'ignore'
309
+ check = split_control[2].to_sym
310
+
311
+ if token.prev_token && !Set[:NEWLINE, :INDENT].include?(token.prev_token.type)
312
+ # control comment at the end of the line, override applies to
313
+ # a single line only
314
+ (ignore_overrides[check] ||= {})[token.line] = reason
315
+ else
316
+ stack << [token.line, reason, check]
317
+ end
318
+ else
319
+ start = stack.pop
320
+ unless start.nil?
321
+ (start[0]..token.line).each do |i|
322
+ (ignore_overrides[start[2]] ||= {})[i] = start[1]
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
@@ -4,6 +4,8 @@ require 'puppet-lint/lexer/token'
4
4
  require 'set'
5
5
 
6
6
  class PuppetLint
7
+ # Internal: A generic error thrown by the lexer when it encounters something
8
+ # it can't handle.
7
9
  class LexerError < StandardError
8
10
  # Internal: Get the Integer line number of the location of the error.
9
11
  attr_reader :line_no
@@ -18,16 +20,18 @@ class PuppetLint
18
20
  # at when it encountered the error.
19
21
  def initialize(code, offset)
20
22
  chunk = code[0..offset]
21
- @line_no = chunk.count("\n") + 1
23
+ @line_no = chunk.scan(/(\r\n|\r|\n)/).size + 1
22
24
  if @line_no == 1
23
25
  @column = chunk.length
24
26
  else
25
- @column = chunk.length - chunk.rindex("\n") - 1
27
+ @column = chunk.length - chunk.rindex(/(\r\n|\r|\n)/) - 1
26
28
  end
27
29
  @column = 1 if @column == 0
28
30
  end
29
31
  end
30
32
 
33
+ # Internal: The puppet-lint lexer. Converts your manifest into its tokenised
34
+ # form.
31
35
  class Lexer
32
36
  # Internal: A Hash whose keys are Strings representing reserved keywords in
33
37
  # the Puppet DSL.
@@ -109,6 +113,8 @@ class PuppetLint
109
113
  [:QMARK, /\A(\?)/],
110
114
  [:BACKSLASH, /\A(\\)/],
111
115
  [:TIMES, /\A(\*)/],
116
+ [:MODULO, /\A(%)/],
117
+ [:PIPE, /\A(\|)/],
112
118
  ]
113
119
 
114
120
  # Internal: A Hash whose keys are Symbols representing token types which
@@ -138,8 +144,6 @@ class PuppetLint
138
144
  # Raises PuppetLint::LexerError if it encounters unexpected characters
139
145
  # (usually the result of syntax errors).
140
146
  def tokenise(code)
141
- code.chomp!
142
-
143
147
  i = 0
144
148
 
145
149
  while i < code.size
@@ -165,7 +169,7 @@ class PuppetLint
165
169
  end
166
170
 
167
171
  unless found
168
- if var_name = chunk[/\A\$((::)?([\w-]+::)*[\w-]+)/, 1]
172
+ if var_name = chunk[/\A\$((::)?([\w-]+::)*[\w-]+(\[.+?\])*)/, 1]
169
173
  tokens << new_token(:VARIABLE, var_name, :chunk => code[0..i])
170
174
  i += var_name.size + 1
171
175
 
@@ -182,13 +186,13 @@ class PuppetLint
182
186
 
183
187
  elsif comment = chunk[/\A(#.*)/, 1]
184
188
  comment_size = comment.size
185
- comment.sub!(/# ?/, '')
189
+ comment.sub!(/#/, '')
186
190
  tokens << new_token(:COMMENT, comment, :chunk => code[0..i])
187
191
  i += comment_size
188
192
 
189
193
  elsif slash_comment = chunk[/\A(\/\/.*)/, 1]
190
194
  slash_comment_size = slash_comment.size
191
- slash_comment.sub!(/\/\/ ?/, '')
195
+ slash_comment.sub!(/\/\//, '')
192
196
  tokens << new_token(:SLASH_COMMENT, slash_comment, :chunk => code[0..i])
193
197
  i += slash_comment_size
194
198
 
@@ -206,18 +210,20 @@ class PuppetLint
206
210
  tokens << new_token(:REGEX, str_content[0..-2], :chunk => code[0..i])
207
211
  i += str_content.size + 1
208
212
 
209
- elsif indent = chunk[/\A\r?\n([ \t]+)/m, 1]
210
- tokens << new_token(:NEWLINE, '\n', :chunk => code[0..i])
211
- tokens << new_token(:INDENT, indent, :chunk => code[0..i+1])
212
- i += indent.size + 1
213
+ elsif eolindent = chunk[/\A((\r\n|\r|\n)[ \t]+)/m, 1]
214
+ eol = eolindent[/\A([\r\n]+)/m, 1]
215
+ indent = eolindent[/\A[\r\n]+([ \t]+)/m, 1]
216
+ tokens << new_token(:NEWLINE, eol, :chunk => code[0..i])
217
+ tokens << new_token(:INDENT, indent, :chunk => code[0..i+eol.size])
218
+ i += indent.size + eol.size
213
219
 
214
220
  elsif whitespace = chunk[/\A([ \t]+)/, 1]
215
221
  tokens << new_token(:WHITESPACE, whitespace, :chunk => code[0..i])
216
222
  i += whitespace.size
217
223
 
218
- elsif chunk.match(/\A\r?\n/)
219
- tokens << new_token(:NEWLINE, '\n', :chunk => code[0..i])
220
- i += 1
224
+ elsif eol = chunk[/\A(\r\n|\r|\n)/, 1]
225
+ tokens << new_token(:NEWLINE, eol, :chunk => code[0..i])
226
+ i += eol.size
221
227
 
222
228
  elsif chunk.match(/\A\//)
223
229
  tokens << new_token(:DIV, '/', :chunk => code[0..i])
@@ -265,11 +271,11 @@ class PuppetLint
265
271
  # Returns the instantiated PuppetLint::Lexer::Token object.
266
272
  def new_token(type, value, opts = {})
267
273
  if opts[:chunk]
268
- line_no = opts[:chunk].count("\n") + 1
274
+ line_no = opts[:chunk].scan(/(\r\n|\r|\n)/).size + 1
269
275
  if line_no == 1
270
276
  column = opts[:chunk].length
271
277
  else
272
- column = opts[:chunk].length - opts[:chunk].rindex("\n") - 1
278
+ column = opts[:chunk].length - opts[:chunk].rindex(/(\r\n|\r|\n)/) - 1
273
279
  end
274
280
  column += 1 if column == 0
275
281
  else
@@ -330,7 +336,7 @@ class PuppetLint
330
336
  tokens << new_token(:STRING, value, :line => line, :column => column)
331
337
  first = false
332
338
  else
333
- line += value.count("\n")
339
+ line += value.scan(/(\r\n|\r|\n)/).size
334
340
  token_column = column + (ss.pos - value.size)
335
341
  tokens << new_token(:DQPOST, value, :line => line, :column => token_column)
336
342
  end
@@ -339,29 +345,30 @@ class PuppetLint
339
345
  tokens << new_token(:DQPRE, value, :line => line, :column => column)
340
346
  first = false
341
347
  else
342
- line += value.count("\n")
348
+ line += value.scan(/(\r\n|\r|\n)/).size
343
349
  token_column = column + (ss.pos - value.size)
344
350
  tokens << new_token(:DQMID, value, :line => line, :column => token_column)
345
351
  end
346
352
  if ss.scan(/\{/).nil?
347
353
  var_name = ss.scan(/(::)?([\w-]+::)*[\w-]+/)
348
- unless var_name.nil?
354
+ if var_name.nil?
355
+ token_column = column + ss.pos - 1
356
+ tokens << new_token(:DQMID, "$", :line => line, :column => token_column)
357
+ else
349
358
  token_column = column + (ss.pos - var_name.size)
350
359
  tokens << new_token(:UNENC_VARIABLE, var_name, :line => line, :column => token_column)
351
360
  end
352
361
  else
353
362
  contents = ss.scan_until(/\}/)[0..-2]
354
- if contents.match(/\A(::)?([\w-]+::)*[\w-]+\Z/)
355
- token_column = column + (ss.pos - contents.size - 1)
356
- tokens << new_token(:VARIABLE, contents, :line => line, :column => token_column)
357
- else
358
- lexer = PuppetLint::Lexer.new
359
- lexer.tokenise(contents)
360
- lexer.tokens.each do |token|
361
- tok_col = column + token.column + (ss.pos - contents.size - 1)
362
- tok_line = token.line + line - 1
363
- tokens << new_token(token.type, token.value, :line => tok_line, :column => tok_col)
364
- end
363
+ if contents.match(/\A(::)?([\w-]+::)*[\w-]+(\[.+?\])*/)
364
+ contents = "$#{contents}"
365
+ end
366
+ lexer = PuppetLint::Lexer.new
367
+ lexer.tokenise(contents)
368
+ lexer.tokens.each do |token|
369
+ tok_col = column + token.column + (ss.pos - contents.size - 1)
370
+ tok_line = token.line + line - 1
371
+ tokens << new_token(token.type, token.value, :line => tok_line, :column => tok_col)
365
372
  end
366
373
  end
367
374
  end