puppet-lint 2.1.1 → 2.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 09c3216991dd5d40c797bb951adeef212f612456
4
- data.tar.gz: ac37d47099b67bf5693ce27b8312fcff4648419e
3
+ metadata.gz: 3d618e442c08c4d3e79853fc370348c9ee8e8970
4
+ data.tar.gz: 82099093c21a374e137a39bd0323443e16015e95
5
5
  SHA512:
6
- metadata.gz: f66775062fc637446250708ff5fb9fb5106a38bc758a6276bf325a7db6b97c4a50f528b964cc0bda44e1c550c8cc2b38ac0d83489dc1f97e9508c2a43c350337
7
- data.tar.gz: 41b45bc46f3be42f046319ffdc588cdc70df842da61cdd098e032b44ba752d805128a88d95296b66b91d35313608230dff491de010e5e10f195ec9bb184e4498
6
+ metadata.gz: 179cd2b6f7ada144261bf630064a4ca26a212d3693fba9244a9a4fbd2b8c75ec78f6935f50175845bf2d743e53671f19c683e9e5ccb14b950e7d6e9e86c269d4
7
+ data.tar.gz: 8d5f9a973d53c47e8ce6a7caac88c4aa8ab7637e95221e95676b7adf40b8cc8c2ed9b615d09f19857aeadbc36d7629d86518ba644fbaf08845050ae2c2018201
data/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # Change Log
2
2
 
3
+ ## [2.2.0](https://github.com/rodjek/puppet-lint/tree/2.2.0) (2017-03-29)
4
+ [Full Changelog](https://github.com/rodjek/puppet-lint/compare/2.1.1...2.2.0)
5
+
6
+ **Closed issues:**
7
+
8
+ - Missed bad file modes in file with multiple resource bodies [\#663](https://github.com/rodjek/puppet-lint/issues/663)
9
+ - Provide helper methods to search for specific tokens [\#660](https://github.com/rodjek/puppet-lint/issues/660)
10
+ - ensure\_first\_param fix can create invalid syntax [\#659](https://github.com/rodjek/puppet-lint/issues/659)
11
+ - puppet-lint dies with inline\_template syntax [\#656](https://github.com/rodjek/puppet-lint/issues/656)
12
+ - Variable use like "${$a}" isn't reported as an error and is changed with no message when run with --fix [\#655](https://github.com/rodjek/puppet-lint/issues/655)
13
+ - Linter gets confused with arrays of hashes [\#654](https://github.com/rodjek/puppet-lint/issues/654)
14
+ - 2.1.1 git tag [\#652](https://github.com/rodjek/puppet-lint/issues/652)
15
+ - heredoc throws unhandled exception [\#649](https://github.com/rodjek/puppet-lint/issues/649)
16
+ - Quoted boolean triggers on the command 'true' [\#646](https://github.com/rodjek/puppet-lint/issues/646)
17
+ - Match function breaks puppet-lint [\#645](https://github.com/rodjek/puppet-lint/issues/645)
18
+ - top-scope variable being used without an explicit namespace in a string with a lookup [\#635](https://github.com/rodjek/puppet-lint/issues/635)
19
+ - unquoted file mode & mode should be represented as a 4 digit when file mode is done by a lookup [\#634](https://github.com/rodjek/puppet-lint/issues/634)
20
+ - Namevars detected as optional parameters [\#633](https://github.com/rodjek/puppet-lint/issues/633)
21
+ - 'Duplicate Parameter' warning on dynamic class parameter [\#627](https://github.com/rodjek/puppet-lint/issues/627)
22
+ - double\_quoted\_strings-check issue with escaped character in the string [\#625](https://github.com/rodjek/puppet-lint/issues/625)
23
+ - unable to disable 140chars check with control comments "block" [\#622](https://github.com/rodjek/puppet-lint/issues/622)
24
+ - arrow\_alignment should only check the alignment of the first arrow on each line [\#609](https://github.com/rodjek/puppet-lint/issues/609)
25
+ - unquoted\_node\_name crash when curly braces missing [\#582](https://github.com/rodjek/puppet-lint/issues/582)
26
+ - Heredoc triggers exception in arrow\_alignment check [\#578](https://github.com/rodjek/puppet-lint/issues/578)
27
+ - Each + With = Fake positive top-scope variable detection [\#576](https://github.com/rodjek/puppet-lint/issues/576)
28
+ - Top-scope variable warning on inline lambda [\#549](https://github.com/rodjek/puppet-lint/issues/549)
29
+ - Top-scope variable warning on nested each loops [\#548](https://github.com/rodjek/puppet-lint/issues/548)
30
+ - Puppet-lint crash with new puppet 4 syntax [\#516](https://github.com/rodjek/puppet-lint/issues/516)
31
+ - `arrow\_alignment --fix` doesn't indent keys when introducing line breaks; erroneously reports success [\#506](https://github.com/rodjek/puppet-lint/issues/506)
32
+ - Top-scope warning when looping though an array of hashes [\#464](https://github.com/rodjek/puppet-lint/issues/464)
33
+ - Array of hashes one-liner throws a "Indentation of =\> is not properly aligned" [\#446](https://github.com/rodjek/puppet-lint/issues/446)
34
+ - heredoc escape gives syntax error [\#430](https://github.com/rodjek/puppet-lint/issues/430)
35
+ - NoMethodError when multiple heredocs are used [\#395](https://github.com/rodjek/puppet-lint/issues/395)
36
+
37
+ **Merged pull requests:**
38
+
39
+ - Ignore selectors when finding resource type [\#678](https://github.com/rodjek/puppet-lint/pull/678) ([rodjek](https://github.com/rodjek))
40
+ - Fix for arrow\_alignment bugs in \#506 [\#677](https://github.com/rodjek/puppet-lint/pull/677) ([rodjek](https://github.com/rodjek))
41
+ - Support double quoted strings inside interpolated values in double quoted strings [\#676](https://github.com/rodjek/puppet-lint/pull/676) ([rodjek](https://github.com/rodjek))
42
+ - Don't silently remove unnecessary $ from enclosed variables [\#674](https://github.com/rodjek/puppet-lint/pull/674) ([rodjek](https://github.com/rodjek))
43
+ - Fix ensure\_first\_param fix method to retrieve the full value of the ensure parameter [\#673](https://github.com/rodjek/puppet-lint/pull/673) ([rodjek](https://github.com/rodjek))
44
+ - Check that arrow is on the line of right operand [\#672](https://github.com/rodjek/puppet-lint/pull/672) ([Darhazer](https://github.com/Darhazer))
45
+ - Restrict appveyor testing to Ruby versions that appveyor supports [\#670](https://github.com/rodjek/puppet-lint/pull/670) ([james-stocks](https://github.com/james-stocks))
46
+ - Clear expected parameter column after processing each block when checking arrow alignment [\#669](https://github.com/rodjek/puppet-lint/pull/669) ([rodjek](https://github.com/rodjek))
47
+ - Allow regexps to used as function arguments [\#665](https://github.com/rodjek/puppet-lint/pull/665) ([rodjek](https://github.com/rodjek))
48
+ - Catch unhandled exception and provide debug info for issue [\#664](https://github.com/rodjek/puppet-lint/pull/664) ([rodjek](https://github.com/rodjek))
49
+ - Fix showing of a failure [\#662](https://github.com/rodjek/puppet-lint/pull/662) ([Darhazer](https://github.com/Darhazer))
50
+ - \(SDK-115\) Enable appveyor testing [\#653](https://github.com/rodjek/puppet-lint/pull/653) ([james-stocks](https://github.com/james-stocks))
51
+ - Heredoc support [\#650](https://github.com/rodjek/puppet-lint/pull/650) ([rodjek](https://github.com/rodjek))
52
+ - Warn when control comment blocks are not properly terminated [\#648](https://github.com/rodjek/puppet-lint/pull/648) ([rodjek](https://github.com/rodjek))
53
+ - Prevent incomplete node blocks from crashing puppet-lint [\#642](https://github.com/rodjek/puppet-lint/pull/642) ([rodjek](https://github.com/rodjek))
54
+ - Update the tokeniser to differentiate between unquoted strings and function names [\#640](https://github.com/rodjek/puppet-lint/pull/640) ([rodjek](https://github.com/rodjek))
55
+ - Check for escaped backslashes too [\#639](https://github.com/rodjek/puppet-lint/pull/639) ([binford2k](https://github.com/binford2k))
56
+ - Correctly handle function calls inside string interpolation [\#638](https://github.com/rodjek/puppet-lint/pull/638) ([hanazuki](https://github.com/hanazuki))
57
+ - Correctly handle nested lambdas [\#637](https://github.com/rodjek/puppet-lint/pull/637) ([hanazuki](https://github.com/hanazuki))
58
+ - Support for nested multiple-variable assignments [\#636](https://github.com/rodjek/puppet-lint/pull/636) ([hanazuki](https://github.com/hanazuki))
59
+ - Add LoadError to fix broken tests [\#631](https://github.com/rodjek/puppet-lint/pull/631) ([davidmogar](https://github.com/davidmogar))
60
+ - Deal with ruby 1.8.7 gem issues [\#630](https://github.com/rodjek/puppet-lint/pull/630) ([mterzo](https://github.com/mterzo))
61
+ - Plugin review; disable unnecessary plugins [\#567](https://github.com/rodjek/puppet-lint/pull/567) ([rnelson0](https://github.com/rnelson0))
62
+
3
63
  ## [2.1.1](https://github.com/rodjek/puppet-lint/tree/2.1.1) (2017-02-13)
4
64
  [Full Changelog](https://github.com/rodjek/puppet-lint/compare/2.1.0...2.1.1)
5
65
 
@@ -15,6 +75,7 @@
15
75
 
16
76
  **Merged pull requests:**
17
77
 
78
+ - puppet-lint 2.1.1 release [\#628](https://github.com/rodjek/puppet-lint/pull/628) ([rnelson0](https://github.com/rnelson0))
18
79
  - Correctly handle strings-with-variables as hash keys in arrow\_alignment check [\#621](https://github.com/rodjek/puppet-lint/pull/621) ([rodjek](https://github.com/rodjek))
19
80
  - Support array of variables on left side of an assign operation [\#617](https://github.com/rodjek/puppet-lint/pull/617) ([rodjek](https://github.com/rodjek))
20
81
  - Test against Ruby 2.4.0 [\#616](https://github.com/rodjek/puppet-lint/pull/616) ([rodjek](https://github.com/rodjek))
@@ -181,25 +242,6 @@
181
242
  ## [2.0.0](https://github.com/rodjek/puppet-lint/tree/2.0.0) (2016-06-22)
182
243
  [Full Changelog](https://github.com/rodjek/puppet-lint/compare/1.1.0...2.0.0)
183
244
 
184
- puppet-lint 2.0.0 is a breaking change. Specifically, the renaming of the line length test was changed from `80chars` to `140chars`. You may need to adjust your configuration and lint checks. For example:
185
- ```ruby
186
- # Line length test is 80 chars in puppet-lint 1.1.0
187
- PuppetLint.configuration.send('disable_80chars')
188
- # Line length test is 140 chars in puppet-lint 2.x
189
- PuppetLint.configuration.send('disable_140chars')
190
- ```
191
-
192
- You may also need to adjust your Gemfile if you are pointing directly at git:
193
- ```ruby
194
- # old
195
- gem 'puppet-lint', :require => false, :git => 'https://github.com/rodjek/puppet-lint.git'
196
-
197
- # new
198
- gem 'puppet-lint', '~> 2.0'
199
- ```
200
-
201
- If the additional gems you use for checks are pinned to 1.x, you should pin puppet-lint to `'~> 1.0'` or `'>= 1.0', '< 3.0'` until updated check gems are released.
202
-
203
245
  **Closed issues:**
204
246
 
205
247
  - Current package [\#471](https://github.com/rodjek/puppet-lint/issues/471)
data/Gemfile CHANGED
@@ -4,7 +4,6 @@ gemspec
4
4
 
5
5
  group :test do
6
6
  gem 'rake', '~> 10.0'
7
- gem 'rspec', '~> 3.0'
8
7
  gem 'rspec-its', '~> 1.0'
9
8
  gem 'rspec-collection_matchers', '~> 1.0'
10
9
 
@@ -15,10 +14,13 @@ group :test do
15
14
  gem 'json_pure', '= 1.8.3'
16
15
  # addressable 2.4.0 requires ruby 1.9.0. Lock to 2.3.8.
17
16
  gem 'addressable', '= 2.3.8'
17
+ gem 'diff-lcs', '< 1.3'
18
+ gem 'rspec', '<= 3.4'
18
19
  else
20
+ gem 'rspec', '~> 3.0'
19
21
  gem 'json'
20
22
  end
21
-
23
+
22
24
  if RUBY_VERSION > '1.8'
23
25
  # requires ruby 1.9+, on 1.8 we'll fall back to the old regex parsing
24
26
  gem 'rspec-json_expectations', '~> 1.4'
@@ -27,7 +29,9 @@ end
27
29
 
28
30
  group :development do
29
31
  # For Changelog generation
30
- gem 'github_changelog_generator', :require => false if RUBY_VERSION >= '2.2.2'
31
- gem 'github_changelog_generator', '~> 1.13.0', :require => false if RUBY_VERSION < '2.2.2'
32
- gem 'rack', '~> 1.0', :require => false if RUBY_VERSION < '2.2.2'
32
+ if RUBY_VERSION > '1.9'
33
+ gem 'github_changelog_generator', :require => false if RUBY_VERSION >= '2.2.2'
34
+ gem 'github_changelog_generator', '~> 1.13.0', :require => false if RUBY_VERSION < '2.2.2'
35
+ gem 'rack', '~> 1.0', :require => false if RUBY_VERSION < '2.2.2'
36
+ end
33
37
  end
data/appveyor.yml ADDED
@@ -0,0 +1,32 @@
1
+ build: off
2
+
3
+ branches:
4
+ only:
5
+ - master
6
+
7
+ # ruby versions under test
8
+ environment:
9
+ matrix:
10
+ - RUBY_VERSION: 193
11
+ - RUBY_VERSION: 200
12
+ - RUBY_VERSION: 21
13
+ - RUBY_VERSION: 22
14
+ - RUBY_VERSION: 23-x64
15
+
16
+ install:
17
+ - SET PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH%
18
+ - SET LOG_SPEC_ORDER=true
19
+ - bundle install --jobs 4 --retry 2 --without development
20
+
21
+ before_test:
22
+ - type Gemfile.lock
23
+ - ruby -v
24
+ - gem -v
25
+ - bundle -v
26
+
27
+ test_script:
28
+ - bundle exec rake test
29
+
30
+ notifications:
31
+ email:
32
+ - tim@bombasticmonkey.com
@@ -21,7 +21,7 @@ class PuppetLint::CheckPlugin
21
21
  check
22
22
 
23
23
  @problems.each do |problem|
24
- if PuppetLint::Data.ignore_overrides[problem[:check]].has_key?(problem[:line])
24
+ if problem[:check] != :syntax && PuppetLint::Data.ignore_overrides[problem[:check]].has_key?(problem[:line])
25
25
  problem[:kind] = :ignored
26
26
  problem[:reason] = PuppetLint::Data.ignore_overrides[problem[:check]][problem[:line]]
27
27
  next
@@ -35,7 +35,7 @@ class PuppetLint::CheckPlugin
35
35
  #
36
36
  # Returns an Array of problem Hashes.
37
37
  def fix_problems
38
- @problems.reject { |problem| problem[:kind] == :ignored }.each do |problem|
38
+ @problems.reject { |problem| problem[:kind] == :ignored || problem[:check] == :syntax }.each do |problem|
39
39
  if self.respond_to?(:fix)
40
40
  begin
41
41
  fix(problem)
@@ -65,6 +65,27 @@ class PuppetLint::Checks
65
65
  end
66
66
 
67
67
  @problems
68
+ rescue => e
69
+ puts <<-END
70
+ Whoops! It looks like puppet-lint has encountered an error that it doesn't
71
+ know how to handle. Please open an issue at https://github.com/rodjek/puppet-lint
72
+ and paste the following output into the issue description.
73
+ ---
74
+ puppet-lint version: #{PuppetLint::VERSION}
75
+ ruby version: #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}
76
+ platform: #{RUBY_PLATFORM}
77
+ file path: #{fileinfo}
78
+ file contents:
79
+ ```
80
+ #{File.read(fileinfo) if File.readable?(fileinfo)}
81
+ ```
82
+ error:
83
+ ```
84
+ #{e.class}: #{e.message}
85
+ #{e.backtrace.join("\n")}
86
+ ```
87
+ END
88
+ exit 1
68
89
  end
69
90
 
70
91
  # Internal: Get a list of checks that have not been disabled.
@@ -105,38 +105,28 @@ class PuppetLint::Data
105
105
  # :end - An Integer position in the `tokens` Array pointing to the last
106
106
  # Token of a resource declaration.
107
107
  def resource_indexes
108
- @resource_indexes ||= Proc.new do
108
+ @resource_indexes ||= begin
109
+ marker = 0
109
110
  result = []
110
- tokens.each_index do |token_idx|
111
- if tokens[token_idx].type == :COLON
112
- next_token = tokens[token_idx].next_code_token
113
- depth = 1
114
- if next_token && next_token.type != :LBRACE
115
- tokens[(token_idx + 1)..-1].each_index do |idx|
116
- real_idx = token_idx + idx + 1
117
- if tokens[real_idx].type == :LBRACE
118
- depth += 1
119
- elsif {:SEMIC => true, :RBRACE => true}.include? tokens[real_idx].type
120
- unless tokens[real_idx].type == :SEMIC && depth > 1
121
- depth -= 1
122
- if depth == 0
123
- result << {
124
- :start => token_idx + 1,
125
- :end => real_idx,
126
- :tokens => tokens[(token_idx + 1)..real_idx],
127
- :type => find_resource_type_token(token_idx),
128
- :param_tokens => find_resource_param_tokens(tokens[(token_idx + 1)..real_idx]),
129
- }
130
- break
131
- end
132
- end
133
- end
134
- end
135
- end
111
+ tokens.select { |t| t.type == :COLON }.each do |colon_token|
112
+ if colon_token.next_code_token && colon_token.next_code_token != :LBRACE
113
+ start_idx = tokens.index(colon_token)
114
+ next if start_idx < marker
115
+ end_token = colon_token.next_token_of([:SEMIC, :RBRACE])
116
+ end_idx = tokens.index(end_token)
117
+
118
+ result << {
119
+ :start => start_idx + 1,
120
+ :end => end_idx,
121
+ :tokens => tokens[start_idx..end_idx],
122
+ :type => find_resource_type_token(start_idx),
123
+ :param_tokens => find_resource_param_tokens(tokens[start_idx..end_idx]),
124
+ }
125
+ marker = end_idx
136
126
  end
137
127
  end
138
128
  result
139
- end.call
129
+ end
140
130
  end
141
131
 
142
132
  # Internal: Find the Token representing the type of a resource definition.
@@ -146,7 +136,10 @@ class PuppetLint::Data
146
136
  #
147
137
  # Returns a Token object.
148
138
  def find_resource_type_token(index)
149
- tokens[tokens[0..index].rindex { |token| token.type == :LBRACE }].prev_code_token
139
+ lbrace_idx = tokens[0..index].rindex { |token|
140
+ token.type == :LBRACE && token.prev_code_token.type != :QMARK
141
+ }
142
+ tokens[lbrace_idx].prev_code_token
150
143
  end
151
144
 
152
145
  # Internal: Find all the Token objects representing the parameter names in
@@ -497,7 +490,6 @@ class PuppetLint::Data
497
490
 
498
491
  if command == 'ignore'
499
492
  check = split_control[2].to_sym
500
-
501
493
  if token.prev_token && !Set[:NEWLINE, :INDENT].include?(token.prev_token.type)
502
494
  # control comment at the end of the line, override applies to
503
495
  # a single line only
@@ -524,6 +516,10 @@ class PuppetLint::Data
524
516
  end
525
517
  stack << stack_add unless stack_add.empty?
526
518
  end
519
+
520
+ stack.each do |control|
521
+ puts "WARNING: lint:ignore:#{control[0][2]} comment on line #{control[0][0]} with no closing lint:endignore comment"
522
+ end
527
523
  end
528
524
  end
529
525
  end
@@ -29,6 +29,7 @@ class PuppetLint
29
29
  def initialize
30
30
  @line_no = 1
31
31
  @column = 1
32
+ @@heredoc_queue ||= []
32
33
  end
33
34
 
34
35
  # Internal: A Hash whose keys are Strings representing reserved keywords in
@@ -83,17 +84,20 @@ class PuppetLint
83
84
  :LBRACK => true,
84
85
  :IF => true,
85
86
  :ELSIF => true,
87
+ :LPAREN => true,
86
88
  }
87
89
 
88
90
  # Internal: An Array of Arrays containing tokens that can be described by
89
91
  # a single regular expression. Each sub-Array contains 2 elements, the
90
92
  # name of the token as a Symbol and a regular expression describing the
91
93
  # value of the token.
94
+ NAME_RE = /\A(((::)?[_a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*)/
92
95
  KNOWN_TOKENS = [
93
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/],
94
97
  [:CLASSREF, /\A(((::){0,1}[A-Z][-\w]*)+)/],
95
98
  [:NUMBER, /\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b/],
96
- [:NAME, /\A(((::)?[_a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*)/],
99
+ [:FUNCTION_NAME, /#{NAME_RE}\(/],
100
+ [:NAME, NAME_RE],
97
101
  [:LBRACK, /\A(\[)/],
98
102
  [:RBRACK, /\A(\])/],
99
103
  [:LBRACE, /\A(\{)/],
@@ -128,7 +132,6 @@ class PuppetLint
128
132
  [:COMMA, /\A(,)/],
129
133
  [:DOT, /\A(\.)/],
130
134
  [:COLON, /\A(:)/],
131
- [:AT, /\A(@)/],
132
135
  [:SEMIC, /\A(;)/],
133
136
  [:QMARK, /\A(\?)/],
134
137
  [:BACKSLASH, /\A(\\)/],
@@ -176,12 +179,12 @@ class PuppetLint
176
179
  length = value.size
177
180
  if type == :NAME
178
181
  if KEYWORDS.include? value
179
- tokens << new_token(value.upcase.to_sym, value, length)
182
+ tokens << new_token(value.upcase.to_sym, value)
180
183
  else
181
- tokens << new_token(type, value, length)
184
+ tokens << new_token(type, value)
182
185
  end
183
186
  else
184
- tokens << new_token(type, value, length)
187
+ tokens << new_token(type, value)
185
188
  end
186
189
  i += length
187
190
  found = true
@@ -192,28 +195,33 @@ class PuppetLint
192
195
  unless found
193
196
  if var_name = chunk[/\A\$((::)?(\w+(-\w+)*::)*\w+(-\w+)*(\[.+?\])*)/, 1]
194
197
  length = var_name.size + 1
195
- tokens << new_token(:VARIABLE, var_name, length)
198
+ tokens << new_token(:VARIABLE, var_name)
196
199
 
197
200
  elsif chunk.match(/\A'(.*?)'/m)
198
201
  str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*'/m)
199
202
  length = str_content.size + 1
200
- tokens << new_token(:SSTRING, str_content[0..-2], length)
203
+ tokens << new_token(:SSTRING, str_content[0..-2])
201
204
 
202
205
  elsif chunk.match(/\A"/)
203
- str_contents = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*"/m)
206
+ str_contents = slurp_string(code[i+1..-1])
204
207
  _ = code[0..i].split("\n")
205
208
  interpolate_string(str_contents, _.count, _.last.length)
206
209
  length = str_contents.size + 1
207
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
+
208
216
  elsif comment = chunk[/\A(#.*)/, 1]
209
217
  length = comment.size
210
218
  comment.sub!(/#/, '')
211
- tokens << new_token(:COMMENT, comment, length)
219
+ tokens << new_token(:COMMENT, comment)
212
220
 
213
221
  elsif slash_comment = chunk[/\A(\/\/.*)/, 1]
214
222
  length = slash_comment.size
215
223
  slash_comment.sub!(/\/\//, '')
216
- tokens << new_token(:SLASH_COMMENT, slash_comment, length)
224
+ tokens << new_token(:SLASH_COMMENT, slash_comment)
217
225
 
218
226
  elsif mlcomment = chunk[/\A(\/\*.*?\*\/)/m, 1]
219
227
  length = mlcomment.size
@@ -221,32 +229,54 @@ class PuppetLint
221
229
  mlcomment.sub!(/\A\/\* ?/, '')
222
230
  mlcomment.sub!(/ ?\*\/\Z/, '')
223
231
  mlcomment.gsub!(/^ *\*/, '')
224
- tokens << new_token(:MLCOMMENT, mlcomment, length)
225
- tokens.last.raw = mlcomment_raw
232
+ tokens << new_token(:MLCOMMENT, mlcomment, :raw => mlcomment_raw)
226
233
 
227
234
  elsif chunk.match(/\A\/.*?\//) && possible_regex?
228
235
  str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*\//m)
229
236
  length = str_content.size + 1
230
- tokens << new_token(:REGEX, str_content[0..-2], length)
237
+ tokens << new_token(:REGEX, str_content[0..-2])
231
238
 
232
239
  elsif eolindent = chunk[/\A((\r\n|\r|\n)[ \t]+)/m, 1]
233
240
  eol = eolindent[/\A([\r\n]+)/m, 1]
234
- indent = eolindent[/\A[\r\n]+([ \t]+)/m, 1]
235
- tokens << new_token(:NEWLINE, eol, eol.size)
236
- tokens << new_token(:INDENT, indent, indent.size)
237
- length = indent.size + eol.size
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
238
255
 
239
256
  elsif whitespace = chunk[/\A([ \t]+)/, 1]
240
257
  length = whitespace.size
241
- tokens << new_token(:WHITESPACE, whitespace, length)
258
+ tokens << new_token(:WHITESPACE, whitespace)
242
259
 
243
260
  elsif eol = chunk[/\A(\r\n|\r|\n)/, 1]
244
261
  length = eol.size
245
- tokens << new_token(:NEWLINE, eol, length)
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
246
272
 
247
273
  elsif chunk.match(/\A\//)
248
274
  length = 1
249
- tokens << new_token(:DIV, '/', length)
275
+ tokens << new_token(:DIV, '/')
276
+
277
+ elsif chunk.match(/\A@/)
278
+ length = 1
279
+ tokens << new_token(:AT, '@')
250
280
 
251
281
  else
252
282
  raise PuppetLint::LexerError.new(@line_no, @column)
@@ -259,6 +289,18 @@ class PuppetLint
259
289
  tokens
260
290
  end
261
291
 
292
+
293
+ def slurp_string(string)
294
+ dq_str_regexp = /(\$\{|(\A|[^\\])(\\\\)*")/m
295
+ scanner = StringScanner.new(string)
296
+ contents = scanner.scan_until(dq_str_regexp)
297
+ until scanner.matched.end_with?('"')
298
+ contents += scanner.scan_until(/\}/m)
299
+ contents += scanner.scan_until(dq_str_regexp)
300
+ end
301
+ contents
302
+ end
303
+
262
304
  # Internal: Given the tokens already processed, determine if the next token
263
305
  # could be a regular expression.
264
306
  #
@@ -282,14 +324,19 @@ class PuppetLint
282
324
  #
283
325
  # type - The Symbol token type.
284
326
  # value - The token value.
285
- # length - The Integer length of the token's value.
286
327
  # opts - A Hash of additional values required to determine line number and
287
328
  # column:
288
329
  # :line - The Integer line number if calculated externally.
289
330
  # :column - The Integer column number if calculated externally.
331
+ # :raw - The String raw value of the token (if necessary).
290
332
  #
291
333
  # Returns the instantiated PuppetLint::Lexer::Token object.
292
- def new_token(type, value, length, opts = {})
334
+ def new_token(type, value, *args)
335
+ # This bit of magic is used instead of an "opts = {}" argument so that we
336
+ # can safely deprecate the old "length" parameter that might still be
337
+ # passed by 3rd party plugins that haven't updated yet.
338
+ opts = args.last.is_a?(Hash) ? args.last : {}
339
+
293
340
  column = opts[:column] || @column
294
341
  line_no = opts[:line] || @line_no
295
342
 
@@ -308,25 +355,24 @@ class PuppetLint
308
355
  end
309
356
  end
310
357
 
311
- @column += length
312
-
313
- # If creating a :VARIABLE token inside a double quoted string, add 3 to
314
- # the column state in order to account for the ${} characters when
315
- # rendering out to manifest.
316
- if token.type == :VARIABLE
317
- if !token.prev_code_token.nil? && [:DQPRE, :DQMID].include?(token.prev_code_token.type)
318
- @column += 3
319
- end
358
+ if opts[:raw]
359
+ token.raw = opts[:raw]
320
360
  end
321
361
 
322
362
  if type == :NEWLINE
323
363
  @line_no += 1
324
364
  @column = 1
325
- end
326
- if [:MLCOMMENT, :SSTRING, :STRING].include? type and /(?:\r\n|\r|\n)/.match(value)
327
- lines = value.split(/(?:\r\n|\r|\n)/, -1)
328
- @line_no += lines.length-1
329
- @column = lines.last.length
365
+ else
366
+ lines = token.to_manifest.split(/(?:\r\n|\r|\n)/, -1)
367
+ @line_no += lines.length - 1
368
+ if lines.length > 1
369
+ # if the token renders to multiple lines, set the column state to the
370
+ # length of the last line plus 1 (because column numbers are
371
+ # 1 indexed)
372
+ @column = lines.last.size + 1
373
+ else
374
+ @column += (lines.last || "").size
375
+ end
330
376
  end
331
377
 
332
378
  token
@@ -364,34 +410,37 @@ class PuppetLint
364
410
  until value.nil?
365
411
  if terminator == "\""
366
412
  if first
367
- tokens << new_token(:STRING, value, value.size + 2, :line => line, :column => column)
413
+ tokens << new_token(:STRING, value, :line => line, :column => column)
368
414
  first = false
369
415
  else
370
416
  line += value.scan(/(\r\n|\r|\n)/).size
371
417
  token_column = column + (ss.pos - value.size)
372
- tokens << new_token(:DQPOST, value, value.size + 1, :line => line, :column => token_column)
418
+ tokens << new_token(:DQPOST, value, :line => line, :column => token_column)
419
+ @column = token_column + 1
420
+ @line_no = line
373
421
  end
374
422
  else
375
423
  if first
376
- tokens << new_token(:DQPRE, value, value.size + 1, :line => line, :column => column)
424
+ tokens << new_token(:DQPRE, value, :line => line, :column => column)
377
425
  first = false
378
426
  else
379
427
  line += value.scan(/(\r\n|\r|\n)/).size
380
428
  token_column = column + (ss.pos - value.size)
381
- tokens << new_token(:DQMID, value, value.size, :line => line, :column => token_column)
429
+ tokens << new_token(:DQMID, value, :line => line, :column => token_column)
382
430
  end
383
431
  if ss.scan(/\{/).nil?
384
432
  var_name = ss.scan(/(::)?(\w+(-\w+)*::)*\w+(-\w+)*/)
385
433
  if var_name.nil?
386
434
  token_column = column + ss.pos - 1
387
- tokens << new_token(:DQMID, "$", 1, :line => line, :column => token_column)
435
+ tokens << new_token(:DQMID, "$", :line => line, :column => token_column)
388
436
  else
389
437
  token_column = column + (ss.pos - var_name.size)
390
- tokens << new_token(:UNENC_VARIABLE, var_name, var_name.size, :line => line, :column => token_column)
438
+ tokens << new_token(:UNENC_VARIABLE, var_name, :line => line, :column => token_column)
391
439
  end
392
440
  else
393
441
  contents = ss.scan_until(/\}/)[0..-2]
394
- if contents.match(/\A(::)?([\w-]+::)*[\w-]+(\[.+?\])*/)
442
+ raw = contents.dup
443
+ if contents.match(/\A(::)?([\w-]+::)*[\w-]+(\[.+?\])*/) && !contents.match(/\A\w+\(/)
395
444
  contents = "$#{contents}"
396
445
  end
397
446
  lexer = PuppetLint::Lexer.new
@@ -399,12 +448,98 @@ class PuppetLint
399
448
  lexer.tokens.each do |token|
400
449
  tok_col = column + token.column + (ss.pos - contents.size - 1)
401
450
  tok_line = token.line + line - 1
402
- tokens << new_token(token.type, token.value, token.value.size, :line => tok_line, :column => tok_col)
451
+ tokens << new_token(token.type, token.value, :line => tok_line, :column => tok_col)
452
+ end
453
+ if lexer.tokens.length == 1 && lexer.tokens[0].type == :VARIABLE
454
+ tokens.last.raw = raw
403
455
  end
404
456
  end
405
457
  end
406
458
  value, terminator = get_string_segment(ss, '"$')
407
459
  end
408
460
  end
461
+
462
+ # Internal: Tokenise the contents of a heredoc.
463
+ #
464
+ # string - The String to be tokenised.
465
+ # name - The String name/endtext of the heredoc.
466
+ #
467
+ # Returns nothing.
468
+ def interpolate_heredoc(string, name)
469
+ ss = StringScanner.new(string)
470
+ eos_text = name[/\A"?(.+?)"?(:.+?)?(\/.*)?\Z/, 1]
471
+ first = true
472
+ interpolate = name.start_with?('"')
473
+ value, terminator = get_heredoc_segment(ss, eos_text, interpolate)
474
+ until value.nil?
475
+ if terminator =~ /\A\|?\s*-?\s*#{Regexp.escape(eos_text)}/
476
+ if first
477
+ tokens << new_token(:HEREDOC, value, :raw => "#{value}#{terminator}")
478
+ first = false
479
+ else
480
+ tokens << new_token(:HEREDOC_POST, value, :raw => "#{value}#{terminator}")
481
+ end
482
+ else
483
+ if first
484
+ tokens << new_token(:HEREDOC_PRE, value)
485
+ first = false
486
+ else
487
+ tokens << new_token(:HEREDOC_MID, value)
488
+ 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
496
+ else
497
+ contents = ss.scan_until(/\}/)[0..-2]
498
+ raw = contents.dup
499
+ if contents.match(/\A(::)?([\w-]+::)*[\w-]|(\[.+?\])*/) && !contents.match(/\A\w+\(/)
500
+ contents = "$#{contents}" unless contents.start_with?("$")
501
+ end
502
+
503
+ lexer = PuppetLint::Lexer.new
504
+ lexer.tokenise(contents)
505
+ lexer.tokens.each do |token|
506
+ tokens << new_token(token.type, token.value)
507
+ end
508
+ if lexer.tokens.length == 1 && lexer.tokens[0].type == :VARIABLE
509
+ tokens.last.raw = raw
510
+ end
511
+ end
512
+ end
513
+ value, terminator = get_heredoc_segment(ss, eos_text, interpolate)
514
+ end
515
+ end
516
+
517
+ # Internal: Splits a heredoc String into segments if it is to be
518
+ # interpolated.
519
+ #
520
+ # string - The String heredoc.
521
+ # eos_text - The String endtext for the heredoc.
522
+ # interpolate - A Boolean that specifies whether this heredoc can contain
523
+ # interpolated values (defaults to True).
524
+ #
525
+ # Returns an Array consisting of two Strings, the String up to the first
526
+ # 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
533
+
534
+ str = string.scan_until(regexp)
535
+ begin
536
+ str =~ /\A(.*?)([$]+|\|?\s*-?#{Regexp.escape(eos_text)})\Z/m
537
+ value = $1
538
+ terminator = $2
539
+ [value, terminator]
540
+ rescue
541
+ [nil, nil]
542
+ end
543
+ end
409
544
  end
410
545
  end