puppet-lint 2.3.0 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +74 -0
  3. data/.rubocop_todo.yml +89 -0
  4. data/.travis.yml +11 -9
  5. data/CHANGELOG.md +54 -0
  6. data/Gemfile +3 -0
  7. data/Rakefile +14 -3
  8. data/appveyor.yml +1 -4
  9. data/bin/puppet-lint +1 -1
  10. data/lib/puppet-lint.rb +25 -21
  11. data/lib/puppet-lint/bin.rb +15 -19
  12. data/lib/puppet-lint/checkplugin.rb +24 -17
  13. data/lib/puppet-lint/checks.rb +42 -31
  14. data/lib/puppet-lint/configuration.rb +11 -8
  15. data/lib/puppet-lint/data.rb +236 -164
  16. data/lib/puppet-lint/lexer.rb +225 -203
  17. data/lib/puppet-lint/lexer/token.rb +34 -18
  18. data/lib/puppet-lint/optparser.rb +33 -30
  19. data/lib/puppet-lint/plugins.rb +42 -38
  20. data/lib/puppet-lint/plugins/check_classes/arrow_on_right_operand_line.rb +26 -28
  21. data/lib/puppet-lint/plugins/check_classes/autoloader_layout.rb +21 -20
  22. data/lib/puppet-lint/plugins/check_classes/class_inherits_from_params_class.rb +8 -9
  23. data/lib/puppet-lint/plugins/check_classes/code_on_top_scope.rb +9 -8
  24. data/lib/puppet-lint/plugins/check_classes/inherits_across_namespaces.rb +12 -11
  25. data/lib/puppet-lint/plugins/check_classes/names_containing_dash.rb +13 -12
  26. data/lib/puppet-lint/plugins/check_classes/names_containing_uppercase.rb +14 -13
  27. data/lib/puppet-lint/plugins/check_classes/nested_classes_or_defines.rb +9 -10
  28. data/lib/puppet-lint/plugins/check_classes/parameter_order.rb +40 -31
  29. data/lib/puppet-lint/plugins/check_classes/right_to_left_relationship.rb +3 -2
  30. data/lib/puppet-lint/plugins/check_classes/variable_scope.rb +21 -24
  31. data/lib/puppet-lint/plugins/check_comments/slash_comments.rb +3 -2
  32. data/lib/puppet-lint/plugins/check_comments/star_comments.rb +6 -5
  33. data/lib/puppet-lint/plugins/check_conditionals/case_without_default.rb +21 -20
  34. data/lib/puppet-lint/plugins/check_conditionals/selector_inside_resource.rb +10 -13
  35. data/lib/puppet-lint/plugins/check_documentation/documentation.rb +26 -17
  36. data/lib/puppet-lint/plugins/check_nodes/unquoted_node_name.rb +11 -11
  37. data/lib/puppet-lint/plugins/check_resources/duplicate_params.rb +4 -3
  38. data/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb +17 -18
  39. data/lib/puppet-lint/plugins/check_resources/ensure_not_symlink_target.rb +17 -16
  40. data/lib/puppet-lint/plugins/check_resources/file_mode.rb +22 -23
  41. data/lib/puppet-lint/plugins/check_resources/unquoted_file_mode.rb +14 -13
  42. data/lib/puppet-lint/plugins/check_resources/unquoted_resource_title.rb +9 -8
  43. data/lib/puppet-lint/plugins/check_strings/double_quoted_strings.rb +8 -8
  44. data/lib/puppet-lint/plugins/check_strings/only_variable_string.rb +29 -42
  45. data/lib/puppet-lint/plugins/check_strings/puppet_url_without_modules.rb +5 -4
  46. data/lib/puppet-lint/plugins/check_strings/quoted_booleans.rb +3 -2
  47. data/lib/puppet-lint/plugins/check_strings/single_quote_string_with_variables.rb +4 -3
  48. data/lib/puppet-lint/plugins/check_strings/variables_not_enclosed.rb +3 -2
  49. data/lib/puppet-lint/plugins/check_variables/variable_contains_dash.rb +9 -8
  50. data/lib/puppet-lint/plugins/check_variables/variable_is_lowercase.rb +9 -8
  51. data/lib/puppet-lint/plugins/check_whitespace/140chars.rb +9 -9
  52. data/lib/puppet-lint/plugins/check_whitespace/2sp_soft_tabs.rb +4 -3
  53. data/lib/puppet-lint/plugins/check_whitespace/80chars.rb +10 -10
  54. data/lib/puppet-lint/plugins/check_whitespace/arrow_alignment.rb +23 -22
  55. data/lib/puppet-lint/plugins/check_whitespace/hard_tabs.rb +3 -2
  56. data/lib/puppet-lint/plugins/check_whitespace/trailing_whitespace.rb +3 -2
  57. data/lib/puppet-lint/tasks/puppet-lint.rb +3 -3
  58. data/lib/puppet-lint/version.rb +1 -1
  59. data/puppet-lint.gemspec +4 -4
  60. data/spec/puppet-lint/bin_spec.rb +268 -140
  61. data/spec/puppet-lint/checks_spec.rb +229 -0
  62. data/spec/puppet-lint/configuration_spec.rb +10 -9
  63. data/spec/puppet-lint/data_spec.rb +84 -0
  64. data/spec/puppet-lint/ignore_overrides_spec.rb +71 -40
  65. data/spec/puppet-lint/lexer/token_spec.rb +1 -1
  66. data/spec/puppet-lint/lexer_spec.rb +306 -73
  67. data/spec/puppet-lint/plugins/check_classes/arrow_on_right_operand_line_spec.rb +12 -6
  68. data/spec/puppet-lint/plugins/check_classes/autoloader_layout_spec.rb +10 -10
  69. data/spec/puppet-lint/plugins/check_classes/class_inherits_from_params_class_spec.rb +15 -11
  70. data/spec/puppet-lint/plugins/check_classes/code_on_top_scope_spec.rb +26 -21
  71. data/spec/puppet-lint/plugins/check_classes/inherits_across_namespaces_spec.rb +3 -3
  72. data/spec/puppet-lint/plugins/check_classes/name_contains_uppercase_spec.rb +4 -5
  73. data/spec/puppet-lint/plugins/check_classes/names_containing_dash_spec.rb +13 -0
  74. data/spec/puppet-lint/plugins/check_classes/nested_classes_or_defines_spec.rb +33 -25
  75. data/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb +80 -55
  76. data/spec/puppet-lint/plugins/check_classes/right_to_left_relationship_spec.rb +2 -2
  77. data/spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb +165 -130
  78. data/spec/puppet-lint/plugins/check_comments/slash_comments_spec.rb +2 -2
  79. data/spec/puppet-lint/plugins/check_comments/star_comments_spec.rb +53 -35
  80. data/spec/puppet-lint/plugins/check_conditionals/case_without_default_spec.rb +59 -49
  81. data/spec/puppet-lint/plugins/check_conditionals/selector_inside_resource_spec.rb +18 -14
  82. data/spec/puppet-lint/plugins/check_documentation/documentation_spec.rb +14 -10
  83. data/spec/puppet-lint/plugins/check_nodes/unquoted_node_name_spec.rb +7 -7
  84. data/spec/puppet-lint/plugins/check_resources/duplicate_params_spec.rb +54 -44
  85. data/spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb +114 -89
  86. data/spec/puppet-lint/plugins/check_resources/ensure_not_symlink_target_spec.rb +41 -30
  87. data/spec/puppet-lint/plugins/check_resources/file_mode_spec.rb +46 -40
  88. data/spec/puppet-lint/plugins/check_resources/unquoted_file_mode_spec.rb +46 -40
  89. data/spec/puppet-lint/plugins/check_resources/unquoted_resource_title_spec.rb +95 -71
  90. data/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb +28 -24
  91. data/spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb +11 -9
  92. data/spec/puppet-lint/plugins/check_strings/puppet_url_without_modules_spec.rb +1 -2
  93. data/spec/puppet-lint/plugins/check_strings/single_quote_string_with_variables_spec.rb +18 -14
  94. data/spec/puppet-lint/plugins/check_variables/variable_contains_dash_spec.rb +1 -1
  95. data/spec/puppet-lint/plugins/check_variables/variable_is_lowercase_spec.rb +1 -1
  96. data/spec/puppet-lint/plugins/check_whitespace/140chars_spec.rb +22 -15
  97. data/spec/puppet-lint/plugins/check_whitespace/2sp_soft_tabs_spec.rb +8 -6
  98. data/spec/puppet-lint/plugins/check_whitespace/80chars_spec.rb +23 -29
  99. data/spec/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb +588 -494
  100. data/spec/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb +1 -1
  101. data/spec/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb +27 -19
  102. data/spec/puppet-lint_spec.rb +2 -12
  103. data/spec/spec_helper.rb +35 -30
  104. metadata +6 -5
  105. data/lib/puppet-lint/monkeypatches.rb +0 -2
  106. data/lib/puppet-lint/monkeypatches/string_percent.rb +0 -52
  107. data/lib/puppet-lint/monkeypatches/string_prepend.rb +0 -13
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'pp'
2
4
  require 'strscan'
3
5
  require 'puppet-lint/lexer/token'
@@ -13,23 +15,35 @@ class PuppetLint
13
15
  # Internal: Get the Integer column number of the location of the error.
14
16
  attr_reader :column
15
17
 
18
+ # Internal: Get the String reason for the error (if known).
19
+ attr_reader :reason
20
+
16
21
  # Internal: Initialise a new PuppetLint::LexerError object.
17
22
  #
18
23
  # line_no - The Integer line number of the location of the error.
19
24
  # column - The Integer column number of the location of the error.
20
- def initialize(line_no, column)
25
+ # reason - A String describing the cause of the error (if known).
26
+ def initialize(line_no, column, reason = nil)
21
27
  @line_no = line_no
22
28
  @column = column
29
+ @reason = reason
23
30
  end
24
31
  end
25
32
 
26
33
  # Internal: The puppet-lint lexer. Converts your manifest into its tokenised
27
34
  # form.
28
- class Lexer
35
+ class Lexer # rubocop:disable Metrics/ClassLength
29
36
  def initialize
30
37
  @line_no = 1
31
38
  @column = 1
32
- @@heredoc_queue ||= []
39
+ end
40
+
41
+ def self.heredoc_queue
42
+ @heredoc_queue ||= []
43
+ end
44
+
45
+ def heredoc_queue
46
+ self.class.heredoc_queue
33
47
  end
34
48
 
35
49
  # Internal: A Hash whose keys are Strings representing reserved keywords in
@@ -58,8 +72,8 @@ class PuppetLint
58
72
  'type' => true,
59
73
  'attr' => true,
60
74
  'private' => true,
61
- }
62
-
75
+ }.freeze
76
+
63
77
  # Internal: A Hash whose keys are Strings representing reserved keywords in
64
78
  # the Puppet DSL when Application Management is enabled
65
79
  # From https://github.com/puppetlabs/puppet/blob/master/lib/puppet/pops/parser/lexer2.rb#L142-L159
@@ -70,7 +84,7 @@ class PuppetLint
70
84
  'consumes' => true,
71
85
  'produces' => true,
72
86
  'site' => true,
73
- }
87
+ }.freeze
74
88
 
75
89
  # Internal: A Hash whose keys are Symbols representing token types which
76
90
  # a regular expression can follow.
@@ -85,60 +99,60 @@ class PuppetLint
85
99
  :IF => true,
86
100
  :ELSIF => true,
87
101
  :LPAREN => true,
88
- }
102
+ }.freeze
89
103
 
90
104
  # Internal: An Array of Arrays containing tokens that can be described by
91
105
  # a single regular expression. Each sub-Array contains 2 elements, the
92
106
  # name of the token as a Symbol and a regular expression describing the
93
107
  # value of the token.
94
- NAME_RE = /\A(((::)?[_a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*)/
108
+ NAME_RE = %r{\A(((::)?[_a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*)}
95
109
  KNOWN_TOKENS = [
96
- [:TYPE, /\A(Integer|Float|Boolean|Regexp|String|Array|Hash|Resource|Class|Collection|Scalar|Numeric|CatalogEntry|Data|Tuple|Struct|Optional|NotUndef|Variant|Enum|Pattern|Any|Callable|Type|Runtime|Undef|Default)\b/],
97
- [:CLASSREF, /\A(((::){0,1}[A-Z][-\w]*)+)/],
98
- [:NUMBER, /\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b/],
99
- [:FUNCTION_NAME, /#{NAME_RE}\(/],
110
+ [:TYPE, %r{\A(Integer|Float|Boolean|Regexp|String|Array|Hash|Resource|Class|Collection|Scalar|Numeric|CatalogEntry|Data|Tuple|Struct|Optional|NotUndef|Variant|Enum|Pattern|Any|Callable|Type|Runtime|Undef|Default)\b}],
111
+ [:CLASSREF, %r{\A(((::){0,1}[A-Z][-\w]*)+)}],
112
+ [:NUMBER, %r{\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b}],
113
+ [:FUNCTION_NAME, %r{#{NAME_RE}\(}],
100
114
  [:NAME, NAME_RE],
101
- [:LBRACK, /\A(\[)/],
102
- [:RBRACK, /\A(\])/],
103
- [:LBRACE, /\A(\{)/],
104
- [:RBRACE, /\A(\})/],
105
- [:LPAREN, /\A(\()/],
106
- [:RPAREN, /\A(\))/],
107
- [:ISEQUAL, /\A(==)/],
108
- [:MATCH, /\A(=~)/],
109
- [:FARROW, /\A(=>)/],
110
- [:EQUALS, /\A(=)/],
111
- [:APPENDS, /\A(\+=)/],
112
- [:PARROW, /\A(\+>)/],
113
- [:PLUS, /\A(\+)/],
114
- [:GREATEREQUAL, /\A(>=)/],
115
- [:RSHIFT, /\A(>>)/],
116
- [:GREATERTHAN, /\A(>)/],
117
- [:LESSEQUAL, /\A(<=)/],
118
- [:LLCOLLECT, /\A(<<\|)/],
119
- [:OUT_EDGE, /\A(<-)/],
120
- [:OUT_EDGE_SUB, /\A(<~)/],
121
- [:LCOLLECT, /\A(<\|)/],
122
- [:LSHIFT, /\A(<<)/],
123
- [:LESSTHAN, /\A(<)/],
124
- [:NOMATCH, /\A(!~)/],
125
- [:NOTEQUAL, /\A(!=)/],
126
- [:NOT, /\A(!)/],
127
- [:RRCOLLECT, /\A(\|>>)/],
128
- [:RCOLLECT, /\A(\|>)/],
129
- [:IN_EDGE, /\A(->)/],
130
- [:IN_EDGE_SUB, /\A(~>)/],
131
- [:MINUS, /\A(-)/],
132
- [:COMMA, /\A(,)/],
133
- [:DOT, /\A(\.)/],
134
- [:COLON, /\A(:)/],
135
- [:SEMIC, /\A(;)/],
136
- [:QMARK, /\A(\?)/],
137
- [:BACKSLASH, /\A(\\)/],
138
- [:TIMES, /\A(\*)/],
139
- [:MODULO, /\A(%)/],
140
- [:PIPE, /\A(\|)/],
141
- ]
115
+ [:LBRACK, %r{\A(\[)}],
116
+ [:RBRACK, %r{\A(\])}],
117
+ [:LBRACE, %r{\A(\{)}],
118
+ [:RBRACE, %r{\A(\})}],
119
+ [:LPAREN, %r{\A(\()}],
120
+ [:RPAREN, %r{\A(\))}],
121
+ [:ISEQUAL, %r{\A(==)}],
122
+ [:MATCH, %r{\A(=~)}],
123
+ [:FARROW, %r{\A(=>)}],
124
+ [:EQUALS, %r{\A(=)}],
125
+ [:APPENDS, %r{\A(\+=)}],
126
+ [:PARROW, %r{\A(\+>)}],
127
+ [:PLUS, %r{\A(\+)}],
128
+ [:GREATEREQUAL, %r{\A(>=)}],
129
+ [:RSHIFT, %r{\A(>>)}],
130
+ [:GREATERTHAN, %r{\A(>)}],
131
+ [:LESSEQUAL, %r{\A(<=)}],
132
+ [:LLCOLLECT, %r{\A(<<\|)}],
133
+ [:OUT_EDGE, %r{\A(<-)}],
134
+ [:OUT_EDGE_SUB, %r{\A(<~)}],
135
+ [:LCOLLECT, %r{\A(<\|)}],
136
+ [:LSHIFT, %r{\A(<<)}],
137
+ [:LESSTHAN, %r{\A(<)}],
138
+ [:NOMATCH, %r{\A(!~)}],
139
+ [:NOTEQUAL, %r{\A(!=)}],
140
+ [:NOT, %r{\A(!)}],
141
+ [:RRCOLLECT, %r{\A(\|>>)}],
142
+ [:RCOLLECT, %r{\A(\|>)}],
143
+ [:IN_EDGE, %r{\A(->)}],
144
+ [:IN_EDGE_SUB, %r{\A(~>)}],
145
+ [:MINUS, %r{\A(-)}],
146
+ [:COMMA, %r{\A(,)}],
147
+ [:DOT, %r{\A(\.)}],
148
+ [:COLON, %r{\A(:)}],
149
+ [:SEMIC, %r{\A(;)}],
150
+ [:QMARK, %r{\A(\?)}],
151
+ [:BACKSLASH, %r{\A(\\)}],
152
+ [:TIMES, %r{\A(\*)}],
153
+ [:MODULO, %r{\A(%)}],
154
+ [:PIPE, %r{\A(\|)}],
155
+ ].freeze
142
156
 
143
157
  # Internal: A Hash whose keys are Symbols representing token types which
144
158
  # are considered to be formatting tokens (i.e. tokens that don't contain
@@ -150,7 +164,13 @@ class PuppetLint
150
164
  :MLCOMMENT => true,
151
165
  :SLASH_COMMENT => true,
152
166
  :INDENT => true,
153
- }
167
+ }.freeze
168
+
169
+ # \t == tab
170
+ # \v == vertical tab
171
+ # \f == form feed
172
+ # \p{Zs} == ASCII + Unicode non-linebreaking whitespace
173
+ WHITESPACE_RE = %r{[\t\v\f\p{Zs}]}
154
174
 
155
175
  # Internal: Access the internal token storage.
156
176
  #
@@ -175,127 +195,128 @@ class PuppetLint
175
195
  found = false
176
196
 
177
197
  KNOWN_TOKENS.each do |type, regex|
178
- if value = chunk[regex, 1]
179
- length = value.size
180
- if type == :NAME
181
- if KEYWORDS.include? value
182
- tokens << new_token(value.upcase.to_sym, value)
183
- else
184
- tokens << new_token(type, value)
185
- end
186
- else
187
- tokens << new_token(type, value)
188
- end
189
- i += length
190
- found = true
191
- break
192
- end
198
+ value = chunk[regex, 1]
199
+ next if value.nil?
200
+
201
+ length = value.size
202
+ tokens << if type == :NAME && KEYWORDS.include?(value)
203
+ new_token(value.upcase.to_sym, value)
204
+ else
205
+ new_token(type, value)
206
+ end
207
+ i += length
208
+ found = true
209
+ break
193
210
  end
194
211
 
195
- unless found
196
- if var_name = chunk[/\A\$((::)?(\w+(-\w+)*::)*\w+(-\w+)*(\[.+?\])*)/, 1]
197
- length = var_name.size + 1
198
- tokens << new_token(:VARIABLE, var_name)
199
-
200
- elsif chunk.match(/\A'(.*?)'/m)
201
- str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*'/m)
202
- length = str_content.size + 1
203
- tokens << new_token(:SSTRING, str_content[0..-2])
204
-
205
- elsif chunk.match(/\A"/)
206
- str_contents = slurp_string(code[i+1..-1])
207
- _ = code[0..i].split("\n")
208
- interpolate_string(str_contents, _.count, _.last.length)
209
- length = str_contents.size + 1
210
-
211
- elsif heredoc_name = chunk[/\A@\(("?.+?"?(:.+?)?(\/.*?)?)\)/, 1]
212
- @@heredoc_queue << heredoc_name
213
- tokens << new_token(:HEREDOC_OPEN, heredoc_name)
214
- length = heredoc_name.size + 3
215
-
216
- elsif comment = chunk[/\A(#.*)/, 1]
217
- length = comment.size
218
- comment.sub!(/#/, '')
219
- tokens << new_token(:COMMENT, comment)
220
-
221
- elsif slash_comment = chunk[/\A(\/\/.*)/, 1]
222
- length = slash_comment.size
223
- slash_comment.sub!(/\/\//, '')
224
- tokens << new_token(:SLASH_COMMENT, slash_comment)
225
-
226
- elsif mlcomment = chunk[/\A(\/\*.*?\*\/)/m, 1]
227
- length = mlcomment.size
228
- mlcomment_raw = mlcomment.dup
229
- mlcomment.sub!(/\A\/\* ?/, '')
230
- mlcomment.sub!(/ ?\*\/\Z/, '')
231
- mlcomment.gsub!(/^ *\*/, '')
232
- tokens << new_token(:MLCOMMENT, mlcomment, :raw => mlcomment_raw)
233
-
234
- elsif chunk.match(/\A\/.*?\//) && possible_regex?
235
- str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*\//m)
236
- length = str_content.size + 1
237
- tokens << new_token(:REGEX, str_content[0..-2])
238
-
239
- elsif eolindent = chunk[/\A((\r\n|\r|\n)[ \t]+)/m, 1]
240
- eol = eolindent[/\A([\r\n]+)/m, 1]
241
- tokens << new_token(:NEWLINE, eol)
242
- length = eol.size
243
-
244
- if @@heredoc_queue.empty?
245
- indent = eolindent[/\A[\r\n]+([ \t]+)/m, 1]
246
- tokens << new_token(:INDENT, indent)
247
- length += indent.size
248
- else
249
- heredoc_tag = @@heredoc_queue.shift
250
- heredoc_name = heredoc_tag[/\A"?(.+?)"?(:.+?)?(\/.*)?\Z/, 1]
251
- str_contents = StringScanner.new(code[i+length..-1]).scan_until(/\|?\s*-?\s*#{heredoc_name}/)
252
- interpolate_heredoc(str_contents, heredoc_tag)
253
- length += str_contents.size
254
- end
212
+ next if found
213
+
214
+ if var_name = chunk[%r{\A\$((::)?(\w+(-\w+)*::)*\w+(-\w+)*(\[.+?\])*)}, 1]
215
+ length = var_name.size + 1
216
+ tokens << new_token(:VARIABLE, var_name)
217
+
218
+ elsif chunk =~ %r{\A'.*?'}m
219
+ str_content = StringScanner.new(code[i + 1..-1]).scan_until(%r{(\A|[^\\])(\\\\)*'}m)
220
+ length = str_content.size + 1
221
+ tokens << new_token(:SSTRING, str_content[0..-2])
222
+
223
+ elsif chunk.start_with?('"')
224
+ str_contents = slurp_string(code[i + 1..-1])
225
+ lines_parsed = code[0..i].split("\n")
226
+ interpolate_string(str_contents, lines_parsed.count, lines_parsed.last.length)
227
+ length = str_contents.size + 1
228
+
229
+ elsif heredoc_name = chunk[%r{\A@\(("?.+?"?(:.+?)?(/.*?)?)\)}, 1]
230
+ heredoc_queue << heredoc_name
231
+ tokens << new_token(:HEREDOC_OPEN, heredoc_name)
232
+ length = heredoc_name.size + 3
233
+
234
+ elsif comment = chunk[%r{\A(#.*)}, 1]
235
+ length = comment.size
236
+ comment.sub!(%r{#}, '')
237
+ tokens << new_token(:COMMENT, comment)
238
+
239
+ elsif slash_comment = chunk[%r{\A(//.*)}, 1]
240
+ length = slash_comment.size
241
+ slash_comment.sub!(%r{//}, '')
242
+ tokens << new_token(:SLASH_COMMENT, slash_comment)
243
+
244
+ elsif mlcomment = chunk[%r{\A(/\*.*?\*/)}m, 1]
245
+ length = mlcomment.size
246
+ mlcomment_raw = mlcomment.dup
247
+ mlcomment.sub!(%r{\A/\* ?}, '')
248
+ mlcomment.sub!(%r{ ?\*/\Z}, '')
249
+ mlcomment.gsub!(%r{^ *\*}, '')
250
+ tokens << new_token(:MLCOMMENT, mlcomment, :raw => mlcomment_raw)
251
+
252
+ elsif chunk.match(%r{\A/.*?/}) && possible_regex?
253
+ str_content = StringScanner.new(code[i + 1..-1]).scan_until(%r{(\A|[^\\])(\\\\)*/}m)
254
+ length = str_content.size + 1
255
+ tokens << new_token(:REGEX, str_content[0..-2])
256
+
257
+ elsif eolindent = chunk[%r{\A((\r\n|\r|\n)#{WHITESPACE_RE}+)}m, 1]
258
+ eol = eolindent[%r{\A([\r\n]+)}m, 1]
259
+ tokens << new_token(:NEWLINE, eol)
260
+ length = eol.size
261
+
262
+ if heredoc_queue.empty?
263
+ indent = eolindent[%r{\A[\r\n]+(#{WHITESPACE_RE}+)}m, 1]
264
+ tokens << new_token(:INDENT, indent)
265
+ length += indent.size
266
+ else
267
+ heredoc_tag = heredoc_queue.shift
268
+ heredoc_name = heredoc_tag[%r{\A"?(.+?)"?(:.+?)?(/.*)?\Z}, 1]
269
+ str_contents = StringScanner.new(code[i + length..-1]).scan_until(%r{\|?\s*-?\s*#{heredoc_name}})
270
+ interpolate_heredoc(str_contents, heredoc_tag)
271
+ length += str_contents.size
272
+ end
255
273
 
256
- elsif whitespace = chunk[/\A([ \t]+)/, 1]
257
- length = whitespace.size
258
- tokens << new_token(:WHITESPACE, whitespace)
259
-
260
- elsif eol = chunk[/\A(\r\n|\r|\n)/, 1]
261
- length = eol.size
262
- tokens << new_token(:NEWLINE, eol)
263
-
264
- unless @@heredoc_queue.empty?
265
- heredoc_tag = @@heredoc_queue.shift
266
- heredoc_name = heredoc_tag[/\A"?(.+?)"?(:.+?)?(\/.*)?\Z/, 1]
267
- str_contents = StringScanner.new(code[i+length..-1]).scan_until(/\|?\s*-?\s*#{heredoc_name}/)
268
- _ = code[0..i+length].split("\n")
269
- interpolate_heredoc(str_contents, heredoc_tag)
270
- length += str_contents.size
271
- end
274
+ elsif whitespace = chunk[%r{\A(#{WHITESPACE_RE}+)}, 1]
275
+ length = whitespace.size
276
+ tokens << new_token(:WHITESPACE, whitespace)
277
+
278
+ elsif eol = chunk[%r{\A(\r\n|\r|\n)}, 1]
279
+ length = eol.size
280
+ tokens << new_token(:NEWLINE, eol)
281
+
282
+ unless heredoc_queue.empty?
283
+ heredoc_tag = heredoc_queue.shift
284
+ heredoc_name = heredoc_tag[%r{\A"?(.+?)"?(:.+?)?(/.*)?\Z}, 1]
285
+ str_contents = StringScanner.new(code[i + length..-1]).scan_until(%r{\|?\s*-?\s*#{heredoc_name}})
286
+ _ = code[0..i + length].split("\n")
287
+ interpolate_heredoc(str_contents, heredoc_tag)
288
+ length += str_contents.size
289
+ end
272
290
 
273
- elsif chunk.match(/\A\//)
274
- length = 1
275
- tokens << new_token(:DIV, '/')
291
+ elsif chunk.start_with?('/')
292
+ length = 1
293
+ tokens << new_token(:DIV, '/')
276
294
 
277
- elsif chunk.match(/\A@/)
278
- length = 1
279
- tokens << new_token(:AT, '@')
295
+ elsif chunk.start_with?('@')
296
+ length = 1
297
+ tokens << new_token(:AT, '@')
280
298
 
281
- else
282
- raise PuppetLint::LexerError.new(@line_no, @column)
283
- end
284
-
285
- i += length
299
+ else
300
+ raise PuppetLint::LexerError.new(@line_no, @column)
286
301
  end
302
+
303
+ i += length
287
304
  end
288
305
 
289
306
  tokens
290
307
  end
291
308
 
292
-
293
309
  def slurp_string(string)
294
- dq_str_regexp = /(\$\{|(\A|[^\\])(\\\\)*")/m
310
+ dq_str_regexp = %r{(\$\{|(\A|[^\\])(\\\\)*")}m
295
311
  scanner = StringScanner.new(string)
296
312
  contents = scanner.scan_until(dq_str_regexp)
313
+
314
+ if scanner.matched.nil?
315
+ raise LexerError.new(@line_no, @column, 'Double quoted string missing closing quote')
316
+ end
317
+
297
318
  until scanner.matched.end_with?('"')
298
- contents += scanner.scan_until(/\}/m)
319
+ contents += scanner.scan_until(%r{\}}m)
299
320
  contents += scanner.scan_until(dq_str_regexp)
300
321
  end
301
322
  contents
@@ -307,16 +328,12 @@ class PuppetLint
307
328
  # Returns true if the next token could be a regex, otherwise return false.
308
329
  def possible_regex?
309
330
  prev_token = tokens.reject { |r|
310
- FORMATTING_TOKENS.include? r.type
331
+ FORMATTING_TOKENS.include?(r.type)
311
332
  }.last
312
333
 
313
334
  return true if prev_token.nil?
314
335
 
315
- if REGEX_PREV_TOKENS.include? prev_token.type
316
- true
317
- else
318
- false
319
- end
336
+ REGEX_PREV_TOKENS.include?(prev_token.type)
320
337
  end
321
338
 
322
339
  # Internal: Create a new PuppetLint::Lexer::Token object, calculate its
@@ -337,6 +354,14 @@ class PuppetLint
337
354
  # passed by 3rd party plugins that haven't updated yet.
338
355
  opts = args.last.is_a?(Hash) ? args.last : {}
339
356
 
357
+ # column number is calculated at the end of this method by calling
358
+ # to_manifest on the token. Because the string tokens (DQPRE, DQMID etc)
359
+ # are parsed before the variable token, they default to assuming that
360
+ # they are followed by an enclosed variable and we need to remove 2 from
361
+ # the column number if we encounter an unenclosed variable because of the
362
+ # missing ${ at the end of the token value.
363
+ @column -= 2 if type == :UNENC_VARIABLE
364
+
340
365
  column = opts[:column] || @column
341
366
  line_no = opts[:line] || @line_no
342
367
 
@@ -346,7 +371,7 @@ class PuppetLint
346
371
  tokens.last.next_token = token
347
372
 
348
373
  unless FORMATTING_TOKENS.include?(token.type)
349
- prev_nf_idx = tokens.rindex { |r| ! FORMATTING_TOKENS.include? r.type }
374
+ prev_nf_idx = tokens.rindex { |r| !FORMATTING_TOKENS.include?(r.type) }
350
375
  unless prev_nf_idx.nil?
351
376
  prev_nf_token = tokens[prev_nf_idx]
352
377
  prev_nf_token.next_code_token = token
@@ -355,15 +380,13 @@ class PuppetLint
355
380
  end
356
381
  end
357
382
 
358
- if opts[:raw]
359
- token.raw = opts[:raw]
360
- end
383
+ token.raw = opts[:raw] if opts[:raw]
361
384
 
362
385
  if type == :NEWLINE
363
386
  @line_no += 1
364
387
  @column = 1
365
388
  else
366
- lines = token.to_manifest.split(/(?:\r\n|\r|\n)/, -1)
389
+ lines = token.to_manifest.split(%r{(?:\r\n|\r|\n)}, -1)
367
390
  @line_no += lines.length - 1
368
391
  if lines.length > 1
369
392
  # if the token renders to multiple lines, set the column state to the
@@ -371,7 +394,7 @@ class PuppetLint
371
394
  # 1 indexed)
372
395
  @column = lines.last.size + 1
373
396
  else
374
- @column += (lines.last || "").size
397
+ @column += (lines.last || '').size
375
398
  end
376
399
  end
377
400
 
@@ -388,9 +411,9 @@ class PuppetLint
388
411
  # Returns an Array consisting of two Strings, the String up to the first
389
412
  # terminator and the terminator that was found.
390
413
  def get_string_segment(string, terminators)
391
- str = string.scan_until(/([^\\]|^|[^\\])([\\]{2})*[#{terminators}]+/)
414
+ str = string.scan_until(%r{([^\\]|^|[^\\])([\\]{2})*[#{terminators}]+})
392
415
  begin
393
- [str[0..-2], str[-1,1]]
416
+ [str[0..-2], str[-1, 1]]
394
417
  rescue
395
418
  [nil, nil]
396
419
  end
@@ -408,14 +431,14 @@ class PuppetLint
408
431
  first = true
409
432
  value, terminator = get_string_segment(ss, '"$')
410
433
  until value.nil?
411
- if terminator == "\""
434
+ if terminator == '"'
412
435
  if first
413
436
  tokens << new_token(:STRING, value, :line => line, :column => column)
414
437
  first = false
415
438
  else
416
- line += value.scan(/(\r\n|\r|\n)/).size
417
439
  token_column = column + (ss.pos - value.size)
418
440
  tokens << new_token(:DQPOST, value, :line => line, :column => token_column)
441
+ line += value.scan(%r{(\r\n|\r|\n)}).size
419
442
  @column = column + ss.pos + 1
420
443
  @line_no = line
421
444
  end
@@ -424,23 +447,24 @@ class PuppetLint
424
447
  tokens << new_token(:DQPRE, value, :line => line, :column => column)
425
448
  first = false
426
449
  else
427
- line += value.scan(/(\r\n|\r|\n)/).size
428
450
  token_column = column + (ss.pos - value.size)
429
451
  tokens << new_token(:DQMID, value, :line => line, :column => token_column)
452
+ line += value.scan(%r{(\r\n|\r|\n)}).size
430
453
  end
431
- if ss.scan(/\{/).nil?
432
- var_name = ss.scan(/(::)?(\w+(-\w+)*::)*\w+(-\w+)*/)
454
+ if ss.scan(%r{\{}).nil?
455
+ var_name = ss.scan(%r{(::)?(\w+(-\w+)*::)*\w+(-\w+)*})
433
456
  if var_name.nil?
434
457
  token_column = column + ss.pos - 1
435
- tokens << new_token(:DQMID, "$", :line => line, :column => token_column)
458
+ tokens << new_token(:DQMID, '$', :line => line, :column => token_column)
436
459
  else
437
460
  token_column = column + (ss.pos - var_name.size)
438
461
  tokens << new_token(:UNENC_VARIABLE, var_name, :line => line, :column => token_column)
439
462
  end
440
463
  else
441
- contents = ss.scan_until(/\}/)[0..-2]
464
+ line += value.scan(%r{(\r\n|\r|\n)}).size
465
+ contents = ss.scan_until(%r{\}})[0..-2]
442
466
  raw = contents.dup
443
- if contents.match(/\A(::)?([\w-]+::)*[\w-]+(\[.+?\])*/) && !contents.match(/\A\w+\(/)
467
+ if contents.match(%r{\A(::)?([\w-]+::)*[\w-]+(\[.+?\])*}) && !contents.match(%r{\A\w+\(})
444
468
  contents = "$#{contents}"
445
469
  end
446
470
  lexer = PuppetLint::Lexer.new
@@ -467,12 +491,12 @@ class PuppetLint
467
491
  # Returns nothing.
468
492
  def interpolate_heredoc(string, name)
469
493
  ss = StringScanner.new(string)
470
- eos_text = name[/\A"?(.+?)"?(:.+?)?(\/.*)?\Z/, 1]
494
+ eos_text = name[%r{\A"?(.+?)"?(:.+?)?(/.*)?\Z}, 1]
471
495
  first = true
472
496
  interpolate = name.start_with?('"')
473
497
  value, terminator = get_heredoc_segment(ss, eos_text, interpolate)
474
498
  until value.nil?
475
- if terminator =~ /\A\|?\s*-?\s*#{Regexp.escape(eos_text)}/
499
+ if terminator =~ %r{\A\|?\s*-?\s*#{Regexp.escape(eos_text)}}
476
500
  if first
477
501
  tokens << new_token(:HEREDOC, value, :raw => "#{value}#{terminator}")
478
502
  first = false
@@ -486,18 +510,18 @@ class PuppetLint
486
510
  else
487
511
  tokens << new_token(:HEREDOC_MID, value)
488
512
  end
489
- if ss.scan(/\{/).nil?
490
- var_name = ss.scan(/(::)?(\w+(-\w+)*::)*\w+(-\w+)*/)
491
- if var_name.nil?
492
- tokens << new_token(:HEREDOC_MID, "$")
493
- else
494
- tokens << new_token(:UNENC_VARIABLE, var_name)
495
- end
513
+ if ss.scan(%r{\{}).nil?
514
+ var_name = ss.scan(%r{(::)?(\w+(-\w+)*::)*\w+(-\w+)*})
515
+ tokens << if var_name.nil?
516
+ new_token(:HEREDOC_MID, '$')
517
+ else
518
+ new_token(:UNENC_VARIABLE, var_name)
519
+ end
496
520
  else
497
- contents = ss.scan_until(/\}/)[0..-2]
521
+ contents = ss.scan_until(%r{\}})[0..-2]
498
522
  raw = contents.dup
499
- if contents.match(/\A(::)?([\w-]+::)*[\w-]|(\[.+?\])*/) && !contents.match(/\A\w+\(/)
500
- contents = "$#{contents}" unless contents.start_with?("$")
523
+ if contents.match(%r{\A(::)?([\w-]+::)*[\w-]|(\[.+?\])*}) && !contents.match(%r{\A\w+\(})
524
+ contents = "$#{contents}" unless contents.start_with?('$')
501
525
  end
502
526
 
503
527
  lexer = PuppetLint::Lexer.new
@@ -524,19 +548,17 @@ class PuppetLint
524
548
  #
525
549
  # Returns an Array consisting of two Strings, the String up to the first
526
550
  # terminator and the terminator that was found.
527
- def get_heredoc_segment(string, eos_text, interpolate=true)
528
- if interpolate
529
- regexp = /(([^\\]|^|[^\\])([\\]{2})*[$]+|\|?\s*-?#{Regexp.escape(eos_text)})/
530
- else
531
- regexp = /\|?\s*-?#{Regexp.escape(eos_text)}/
532
- end
551
+ def get_heredoc_segment(string, eos_text, interpolate = true)
552
+ regexp = if interpolate
553
+ %r{(([^\\]|^|[^\\])([\\]{2})*[$]+|\|?\s*-?#{Regexp.escape(eos_text)})}
554
+ else
555
+ %r{\|?\s*-?#{Regexp.escape(eos_text)}}
556
+ end
533
557
 
534
558
  str = string.scan_until(regexp)
535
559
  begin
536
- str =~ /\A(.*?)([$]+|\|?\s*-?#{Regexp.escape(eos_text)})\Z/m
537
- value = $1
538
- terminator = $2
539
- [value, terminator]
560
+ str =~ %r{\A(?<value>.*?)(?<terminator>[$]+|\|?\s*-?#{Regexp.escape(eos_text)})\Z}m
561
+ [Regexp.last_match(:value), Regexp.last_match(:terminator)]
540
562
  rescue
541
563
  [nil, nil]
542
564
  end