puppet-lint 2.3.0 → 2.3.1

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 (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