puppet-lint 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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