puppet-lint 0.2.0.pre1 → 0.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.
Files changed (42) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +1 -14
  3. data/Gemfile +2 -0
  4. data/Rakefile +5 -0
  5. data/bin/puppet-lint +1 -99
  6. data/lib/puppet-lint.rb +4 -12
  7. data/lib/puppet-lint/bin.rb +115 -0
  8. data/lib/puppet-lint/configuration.rb +6 -2
  9. data/lib/puppet-lint/lexer.rb +135 -83
  10. data/lib/puppet-lint/lexer/token.rb +62 -0
  11. data/lib/puppet-lint/plugin.rb +57 -51
  12. data/lib/puppet-lint/plugins.rb +2 -0
  13. data/lib/puppet-lint/plugins/check_classes.rb +161 -45
  14. data/lib/puppet-lint/plugins/check_comments.rb +33 -0
  15. data/lib/puppet-lint/plugins/check_conditionals.rb +8 -10
  16. data/lib/puppet-lint/plugins/check_documentation.rb +41 -0
  17. data/lib/puppet-lint/plugins/check_resources.rb +28 -2
  18. data/lib/puppet-lint/plugins/check_strings.rb +6 -4
  19. data/lib/puppet-lint/plugins/check_variables.rb +1 -1
  20. data/lib/puppet-lint/plugins/check_whitespace.rb +26 -49
  21. data/lib/puppet-lint/tasks/puppet-lint.rb +2 -1
  22. data/lib/puppet-lint/version.rb +1 -1
  23. data/puppet-lint.gemspec +1 -0
  24. data/spec/fixtures/test/manifests/fail.pp +2 -0
  25. data/spec/fixtures/test/manifests/init.pp +3 -0
  26. data/spec/fixtures/test/manifests/warning.pp +2 -0
  27. data/spec/puppet-lint/bin_spec.rb +266 -0
  28. data/spec/puppet-lint/configuration_spec.rb +51 -0
  29. data/spec/puppet-lint/lexer/token_spec.rb +18 -0
  30. data/spec/puppet-lint/lexer_spec.rb +738 -0
  31. data/spec/puppet-lint/{check_classes_spec.rb → plugins/check_classes_spec.rb} +74 -7
  32. data/spec/puppet-lint/plugins/check_comments_spec.rb +40 -0
  33. data/spec/puppet-lint/{check_conditionals_spec.rb → plugins/check_conditionals_spec.rb} +19 -0
  34. data/spec/puppet-lint/plugins/check_documentation_spec.rb +55 -0
  35. data/spec/puppet-lint/{check_resources_spec.rb → plugins/check_resources_spec.rb} +65 -0
  36. data/spec/puppet-lint/{check_strings_spec.rb → plugins/check_strings_spec.rb} +18 -1
  37. data/spec/puppet-lint/{check_variables_spec.rb → plugins/check_variables_spec.rb} +0 -0
  38. data/spec/puppet-lint/plugins/check_whitespace_spec.rb +291 -0
  39. data/spec/puppet-lint_spec.rb +10 -0
  40. data/spec/spec_helper.rb +5 -0
  41. metadata +58 -24
  42. data/spec/puppet-lint/check_whitespace_spec.rb +0 -120
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  .rbenv-version
4
4
  Gemfile.lock
5
5
  vendor/gems
6
+ coverage/
data/.travis.yml CHANGED
@@ -5,20 +5,7 @@ rvm:
5
5
  branches:
6
6
  only:
7
7
  - master
8
+ - dust_bunny
8
9
  notifications:
9
10
  email:
10
11
  - tim@github.com
11
- env:
12
- - PUPPET_VERSION=2.6.13
13
- - PUPPET_VERSION=2.7.9
14
- - PUPPET_VERSION=0.25.4
15
- matrix:
16
- exclude:
17
- - rvm: 1.9.2
18
- env: PUPPET_VERSION=2.6.13
19
- - rvm: 1.9.3
20
- env: PUPPET_VERSION=2.6.13
21
- - rvm: 1.9.2
22
- env: PUPPET_VERSION=0.25.4
23
- - rvm: 1.9.3
24
- env: PUPPET_VERSION=0.25.4
data/Gemfile CHANGED
@@ -5,4 +5,6 @@ puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>=
5
5
  gem 'rake'
6
6
  gem 'rspec'
7
7
  gem 'rdoc'
8
+ gem 'ruby-prof'
8
9
  gem 'puppet', puppetversion
10
+ gem 'rcov', :platform => :ruby_18
data/Rakefile CHANGED
@@ -6,6 +6,11 @@ task :default => :test
6
6
 
7
7
  RSpec::Core::RakeTask.new(:test)
8
8
 
9
+ RSpec::Core::RakeTask.new(:cov) do |t|
10
+ t.rcov = true
11
+ t.rcov_opts = '--exclude "spec" --xrefs'
12
+ end
13
+
9
14
  ### RDOC Tasks ###
10
15
  require 'rdoc'
11
16
  if (RDoc::VERSION.split('.') <=> ['2','4','2']) >= 0
data/bin/puppet-lint CHANGED
@@ -2,104 +2,6 @@
2
2
 
3
3
  $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
4
 
5
- help = <<HELP
6
- Puppet-lint
7
-
8
- Basic Command Line Usage:
9
- puppet-lint [OPTIONS] [PATH]
10
-
11
- PATH The path to the Puppet manifest.
12
-
13
- Options:
14
- HELP
15
-
16
- require 'optparse'
17
- require 'rubygems'
18
5
  require 'puppet-lint'
19
6
 
20
- opts = OptionParser.new do |opts|
21
- opts.banner = help
22
-
23
- opts.on("--version", "Display current version.") do
24
- puts "Puppet-lint " + PuppetLint::VERSION
25
- exit 0
26
- end
27
-
28
- opts.on("--with-filename", "Display the filename before the warning") do
29
- PuppetLint.configuration.with_filename = true
30
- end
31
-
32
- opts.on("--error-level LEVEL", [:all, :warning, :error], "The level of error to return.", "(warning, error, all)") do |el|
33
- PuppetLint.configuration.error_level = el
34
- end
35
-
36
- opts.on("--fail-on-warnings", "Return a non-zero exit status for warnings.") do
37
- PuppetLint.configuration.fail_on_warnings = true
38
- end
39
-
40
- opts.on("--log-format FORMAT",
41
- "Change the log format.", "Overrides --with-filename.",
42
- "The following placeholders can be used:",
43
- "%{filename} - Filename without path.",
44
- "%{path} - Path as provided.",
45
- "%{fullpath} - Full path.",
46
- "%{linenumber} - Line number.",
47
- "%{kind} - The kind of message.",
48
- " - (warning, error)",
49
- "%{KIND} - Uppercase version of %{kind}",
50
- "%{check} - Name of the check.",
51
- "%{message} - The message."
52
- ) do |format|
53
- PuppetLint.configuration.log_format = format
54
- end
55
-
56
- opts.separator ""
57
- opts.separator " Disable checks:"
58
-
59
- PuppetLint.configuration.checks.each do |check|
60
- opts.on("--no-#{check}-check", "Skip the #{check} check") do
61
- PuppetLint.configuration.send("disable_#{check}")
62
- end
63
- end
64
-
65
- opts.load(File.expand_path("~/.puppet-lintrc")) unless opts.load(".puppet-lintrc")
66
- end
67
-
68
- begin
69
- opts.parse!
70
- rescue OptionParser::InvalidOption
71
- puts "puppet-lint: #{$!.message}"
72
- puts "puppet-lint: try 'puppet-lint --help' for more information"
73
- exit
74
- end
75
-
76
- if ARGV[0].nil?
77
- puts "puppet-lint: no file specified"
78
- puts "puppet-lint: try 'puppet-lint --help' for more information"
79
- exit
80
- end
81
-
82
- begin
83
- path = ARGV[0]
84
- if File.directory?(path)
85
- Dir.chdir(path)
86
- path = Dir.glob('**/*.pp')
87
- else
88
- path = [path]
89
- end
90
-
91
- path.each do |f|
92
- l = PuppetLint.new
93
- l.file = f
94
- l.run
95
- if l.errors? or (l.warnings? and PuppetLint.configuration.fail_on_warnings)
96
- exit 1
97
- end
98
- end
99
-
100
- rescue PuppetLint::NoCodeError
101
- puts "puppet-lint: no file specified or specified file does not exist"
102
- puts "puppet-lint: try 'puppet-lint --help' for more information"
103
- exit
104
- end
105
-
7
+ PuppetLint::Bin.new(ARGV).run
data/lib/puppet-lint.rb CHANGED
@@ -2,6 +2,7 @@ require 'puppet-lint/version'
2
2
  require 'puppet-lint/lexer'
3
3
  require 'puppet-lint/configuration'
4
4
  require 'puppet-lint/plugin'
5
+ require 'puppet-lint/bin'
5
6
 
6
7
  unless String.respond_to?('prepend')
7
8
  class String
@@ -67,7 +68,7 @@ end
67
68
  class PuppetLint::NoCodeError < StandardError; end
68
69
 
69
70
  class PuppetLint
70
- attr_reader :code, :file
71
+ attr_reader :data
71
72
 
72
73
  def initialize
73
74
  @data = nil
@@ -125,7 +126,7 @@ class PuppetLint
125
126
  end
126
127
  end
127
128
  end
128
-
129
+
129
130
  def errors?
130
131
  @statistics[:error] != 0
131
132
  end
@@ -134,12 +135,6 @@ class PuppetLint
134
135
  @statistics[:warning] != 0
135
136
  end
136
137
 
137
- def checks
138
- PuppetLint::CheckPlugin.repository.map do |plugin|
139
- plugin.new.checks
140
- end.flatten
141
- end
142
-
143
138
  def run
144
139
  if @data.nil?
145
140
  raise PuppetLint::NoCodeError
@@ -152,9 +147,6 @@ class PuppetLint
152
147
  end
153
148
 
154
149
  # Default configuration options
155
- PuppetLint.configuration.fail_on_warnings = false
156
- PuppetLint.configuration.error_level = :all
157
- PuppetLint.configuration.with_filename = false
158
- PuppetLint.configuration.log_format = ''
150
+ PuppetLint.configuration.defaults
159
151
 
160
152
  require 'puppet-lint/plugins'
@@ -0,0 +1,115 @@
1
+ require 'optparse'
2
+
3
+ class PuppetLint::Bin
4
+ def initialize(args)
5
+ @args = args
6
+ end
7
+
8
+ def run
9
+ help = <<-EOHELP
10
+ Puppet-lint
11
+
12
+ Basic Command Line Usage:
13
+ puppet-lint [OPTIONS] [PATH]
14
+
15
+ PATH The path to the Puppet manifest.
16
+
17
+ Options:
18
+ EOHELP
19
+
20
+ opts = OptionParser.new do |opts|
21
+ opts.banner = help
22
+
23
+ opts.on("--version", "Display current version.") do
24
+ puts "Puppet-lint " + PuppetLint::VERSION
25
+ return 0
26
+ end
27
+
28
+ opts.on("--with-filename", "Display the filename before the warning") do
29
+ PuppetLint.configuration.with_filename = true
30
+ end
31
+
32
+ opts.on("--fail-on-warnings", "Return a non-zero exit status for warnings.") do
33
+ PuppetLint.configuration.fail_on_warnings = true
34
+ end
35
+
36
+ opts.on("--error-level LEVEL", [:all, :warning, :error], "The level of error to return.", "(warning, error, all)") do |el|
37
+ PuppetLint.configuration.error_level = el
38
+ end
39
+
40
+ opts.on("--log-format FORMAT",
41
+ "Change the log format.", "Overrides --with-filename.",
42
+ "The following placeholders can be used:",
43
+ "%{filename} - Filename without path.",
44
+ "%{path} - Path as provided.",
45
+ "%{fullpath} - Full path.",
46
+ "%{linenumber} - Line number.",
47
+ "%{kind} - The kind of message.",
48
+ " - (warning, error)",
49
+ "%{KIND} - Uppercase version of %{kind}",
50
+ "%{check} - Name of the check.",
51
+ "%{message} - The message."
52
+ ) do |format|
53
+ PuppetLint.configuration.log_format = format
54
+ end
55
+
56
+ opts.separator ""
57
+ opts.separator " Disable checks:"
58
+
59
+ PuppetLint.configuration.checks.each do |check|
60
+ opts.on("--no-#{check}-check", "Skip the #{check} check") do
61
+ PuppetLint.configuration.send("disable_#{check}")
62
+ end
63
+ end
64
+
65
+ opts.load('/etc/puppet-lint.rc')
66
+ opts.load(File.expand_path('~/.puppet-lint.rc'))
67
+ if opts.load(File.expand_path('~/.puppet-lintrc'))
68
+ $stderr.puts 'Depreciated: Found ~/.puppet-lintrc instead of ~/.puppet-lint.rc'
69
+ end
70
+ opts.load('.puppet-lint.rc')
71
+ if opts.load('.puppet-lintrc')
72
+ $stderr.puts 'Depreciated: Read .puppet-lintrc instead of .puppet-lint.rc'
73
+ end
74
+ end
75
+
76
+ begin
77
+ opts.parse!(@args)
78
+ rescue OptionParser::InvalidOption
79
+ puts "puppet-lint: #{$!.message}"
80
+ puts "puppet-lint: try 'puppet-lint --help' for more information"
81
+ return 1
82
+ end
83
+
84
+ if @args[0].nil?
85
+ puts "puppet-lint: no file specified"
86
+ puts "puppet-lint: try 'puppet-lint --help' for more information"
87
+ return 1
88
+ end
89
+
90
+ begin
91
+ path = @args[0]
92
+ if File.directory?(path)
93
+ path = Dir.glob("#{path}/**/*.pp")
94
+ else
95
+ path = @args
96
+ end
97
+
98
+ return_val = 0
99
+ path.each do |f|
100
+ l = PuppetLint.new
101
+ l.file = f
102
+ l.run
103
+ if l.errors? or (l.warnings? and PuppetLint.configuration.fail_on_warnings)
104
+ return_val = 1
105
+ end
106
+ end
107
+ return return_val
108
+
109
+ rescue PuppetLint::NoCodeError
110
+ puts "puppet-lint: no file specified or specified file does not exist"
111
+ puts "puppet-lint: try 'puppet-lint --help' for more information"
112
+ return 1
113
+ end
114
+ end
115
+ end
@@ -54,8 +54,12 @@ class PuppetLint
54
54
  }
55
55
  end
56
56
 
57
- def self.ignore_paths
58
- settings[:ignore_paths] ||= []
57
+ def defaults
58
+ settings.clear
59
+ self.with_filename = false
60
+ self.fail_on_warnings = false
61
+ self.error_level = :all
62
+ self.log_format = ''
59
63
  end
60
64
  end
61
65
  end
@@ -1,31 +1,43 @@
1
1
  require 'pp'
2
2
  require 'strscan'
3
+ require 'puppet-lint/lexer/token'
4
+ require 'set'
3
5
 
4
6
  class PuppetLint
7
+ class LexerError < RuntimeError; end
5
8
  class Lexer
6
- KEYWORDS = [
7
- 'class',
8
- 'case',
9
- 'default',
10
- 'define',
11
- 'import',
12
- 'if',
13
- 'else',
14
- 'elsif',
15
- 'inherits',
16
- 'node',
17
- 'and',
18
- 'or',
19
- 'undef',
20
- 'true',
21
- 'false',
22
- 'in',
23
- 'unless',
24
- ]
9
+ KEYWORDS = {
10
+ 'class' => true,
11
+ 'case' => true,
12
+ 'default' => true,
13
+ 'define' => true,
14
+ 'import' => true,
15
+ 'if' => true,
16
+ 'else' => true,
17
+ 'elsif' => true,
18
+ 'inherits' => true,
19
+ 'node' => true,
20
+ 'and' => true,
21
+ 'or' => true,
22
+ 'undef' => true,
23
+ 'true' => true,
24
+ 'false' => true,
25
+ 'in' => true,
26
+ 'unless' => true,
27
+ }
28
+
29
+ REGEX_PREV_TOKENS = {
30
+ :NODE => true,
31
+ :LBRACE => true,
32
+ :RBRACE => true,
33
+ :MATCH => true,
34
+ :NOMATCH => true,
35
+ :COMMA => true,
36
+ }
25
37
 
26
38
  KNOWN_TOKENS = [
27
39
  [:CLASSREF, /\A(((::){0,1}[A-Z][-\w]*)+)/],
28
- [:NUMBER, /\A(?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?)\b/],
40
+ [:NUMBER, /\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b/],
29
41
  [:NAME, /\A(((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*)/],
30
42
  [:LBRACK, /\A(\[)/],
31
43
  [:RBRACK, /\A(\])/],
@@ -65,10 +77,18 @@ class PuppetLint
65
77
  [:SEMIC, /\A(;)/],
66
78
  [:QMARK, /\A(\?)/],
67
79
  [:BACKSLASH, /\A(\\)/],
68
- [:DIV, /\A(\/)/],
69
80
  [:TIMES, /\A(\*)/],
70
81
  ]
71
82
 
83
+ FORMATTING_TOKENS = {
84
+ :WHITESPACE => true,
85
+ :NEWLINE => true,
86
+ :COMMENT => true,
87
+ :MLCOMMENT => true,
88
+ :SLASH_COMMENT => true,
89
+ :INDENT => true,
90
+ }
91
+
72
92
  def tokens
73
93
  @tokens ||= []
74
94
  end
@@ -87,12 +107,12 @@ class PuppetLint
87
107
  if value = chunk[regex, 1]
88
108
  if type == :NAME
89
109
  if KEYWORDS.include? value
90
- tokens << new_token(value.upcase.to_sym, value, code[0..i])
110
+ tokens << new_token(value.upcase.to_sym, value, :chunk => code[0..i])
91
111
  else
92
- tokens << new_token(type, value, code[0..i])
112
+ tokens << new_token(type, value, :chunk => code[0..i])
93
113
  end
94
114
  else
95
- tokens << new_token(type, value, code[0..i])
115
+ tokens << new_token(type, value, :chunk => code[0..i])
96
116
  end
97
117
  i += value.size
98
118
  found = true
@@ -101,65 +121,67 @@ class PuppetLint
101
121
  end
102
122
 
103
123
  unless found
104
- if identifier = chunk[/\A([a-z]\w*)/, 1]
105
- tokens << new_token(:IDENTIFIER, identifier, code[0..i])
106
- i += identifier.size
107
-
108
- elsif var_name = chunk[/\A\$((::)?([\w-]+::)*[\w-]+)/, 1]
109
- tokens << new_token(:VARIABLE, var_name, code[0..i])
124
+ if var_name = chunk[/\A\$((::)?([\w-]+::)*[\w-]+)/, 1]
125
+ tokens << new_token(:VARIABLE, var_name, :chunk => code[0..i])
110
126
  i += var_name.size + 1
111
127
 
112
- elsif sstring = chunk[/\A'(.*?)'/, 1]
113
- tokens << new_token(:SSTRING, sstring, code[0..i])
114
- i += sstring.size + 2
128
+ elsif chunk.match(/\A'(.*?)'/m)
129
+ str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])'/m)
130
+ tokens << new_token(:SSTRING, str_content[0..-2], :chunk => code[0..i])
131
+ i += str_content.size + 1
115
132
 
116
133
  elsif chunk.match(/\A"/)
117
- str_contents = StringScanner.new(code[i+1..-1]).scan_until(/[^\\]"/m)
134
+ str_contents = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])"/m)
118
135
  _ = code[0..i].split("\n")
119
136
  interpolate_string(str_contents, _.count, _.last.length)
120
137
  i += str_contents.size + 1
121
138
 
122
- elsif chunk.match(/\A\//)
123
- str_content = StringScanner.new(code[i+1..-1]).scan_until(/[^\\]\//m)
124
- tokens << new_token(:REGEX, str_content, code[0..i])
125
- i += str_content.size + 1
126
-
127
139
  elsif comment = chunk[/\A(#.*)/, 1]
128
140
  comment_size = comment.size
129
141
  comment.sub!(/# ?/, '')
130
- tokens << new_token(:COMMENT, comment, code[0..i])
142
+ tokens << new_token(:COMMENT, comment, :chunk => code[0..i])
131
143
  i += comment_size
132
144
 
133
145
  elsif slash_comment = chunk[/\A(\/\/.*)/, 1]
134
146
  slash_comment_size = slash_comment.size
135
147
  slash_comment.sub!(/\/\/ ?/, '')
136
- tokens << new_token(:SLASH_COMMENT, slash_comment, code[0..i])
148
+ tokens << new_token(:SLASH_COMMENT, slash_comment, :chunk => code[0..i])
137
149
  i += slash_comment_size
138
150
 
139
151
  elsif mlcomment = chunk[/\A(\/\*.*?\*\/)/m, 1]
140
- mlcomment_size = mlcomment_size
141
- mlcomment.sub!(/^\/\* ?/, '')
142
- mlcomment.sub!(/ ?\*\/$/, '')
143
- tokens << new_token(:MLCOMMENT, mlcomment, code[0..i])
152
+ mlcomment_size = mlcomment.size
153
+ mlcomment.sub!(/\A\/\* ?/, '')
154
+ mlcomment.sub!(/ ?\*\/\Z/, '')
155
+ mlcomment.gsub!(/^ ?\* ?/, '')
156
+ mlcomment.gsub!(/\n/, ' ')
157
+ mlcomment.strip!
158
+ tokens << new_token(:MLCOMMENT, mlcomment, :chunk => code[0..i])
144
159
  i += mlcomment_size
145
160
 
161
+ elsif chunk.match(/\A\/.*?\//) && possible_regex?
162
+ str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])\//m)
163
+ tokens << new_token(:REGEX, str_content[0..-2], :chunk => code[0..i])
164
+ i += str_content.size + 1
165
+
146
166
  elsif indent = chunk[/\A\n([ \t]+)/m, 1]
147
- tokens << new_token(:NEWLINE, '\n', code[0..i])
148
- tokens << new_token(:INDENT, indent, code[0..i+1])
167
+ tokens << new_token(:NEWLINE, '\n', :chunk => code[0..i])
168
+ tokens << new_token(:INDENT, indent, :chunk => code[0..i+1])
149
169
  i += indent.size + 1
150
170
 
151
171
  elsif whitespace = chunk[/\A([ \t]+)/, 1]
152
- tokens << new_token(:WHITESPACE, whitespace, code[0..i])
172
+ tokens << new_token(:WHITESPACE, whitespace, :chunk => code[0..i])
153
173
  i += whitespace.size
154
174
 
155
175
  elsif chunk.match(/\A\n/)
156
- tokens << new_token(:NEWLINE, '\n', code[0..i])
176
+ tokens << new_token(:NEWLINE, '\n', :chunk => code[0..i])
157
177
  i += 1
158
178
 
159
- else
160
- value = chunk[0,1]
161
- tokens << new_token(value, value, code[0..i])
179
+ elsif chunk.match(/\A\//)
180
+ tokens << new_token(:DIV, '/', :chunk => code[0..i])
162
181
  i += 1
182
+
183
+ else
184
+ raise PuppetLint::LexerError, chunk
163
185
  end
164
186
  end
165
187
  end
@@ -167,16 +189,54 @@ class PuppetLint
167
189
  tokens
168
190
  end
169
191
 
170
- def new_token(type, value, chunk)
171
- lines = chunk.split("\n")
172
- line_no = lines.empty? ? 1 : lines.count
173
- column = lines.empty? ? 1 : lines.last.length
192
+ def possible_regex?
193
+ prev_token = tokens.reject { |r|
194
+ FORMATTING_TOKENS.include? r.type
195
+ }.last
196
+
197
+ return true if prev_token.nil?
198
+
199
+ if REGEX_PREV_TOKENS.include? prev_token.type
200
+ true
201
+ else
202
+ false
203
+ end
204
+ end
205
+
206
+ def new_token(type, value, opts = {})
207
+ if opts[:chunk]
208
+ line_no = opts[:chunk].count("\n") + 1
209
+ if line_no == 1
210
+ column = opts[:chunk].length
211
+ else
212
+ column = opts[:chunk].length - opts[:chunk].rindex("\n") - 1
213
+ end
214
+ column += 1 if column == 0
215
+ else
216
+ column = opts[:column]
217
+ line_no = opts[:line]
218
+ end
219
+
220
+ token = Token.new(type, value, line_no, column)
221
+ unless tokens.last.nil?
222
+ token.prev_token = tokens.last
223
+ tokens.last.next_token = token
224
+
225
+ unless FORMATTING_TOKENS.include?(token.type)
226
+ prev_nf_idx = tokens.rindex { |r| ! FORMATTING_TOKENS.include? r.type }
227
+ unless prev_nf_idx.nil?
228
+ prev_nf_token = tokens[prev_nf_idx]
229
+ prev_nf_token.next_code_token = token
230
+ token.prev_code_token = prev_nf_token
231
+ end
232
+ end
233
+ end
174
234
 
175
- PuppetLint::Token.new(type, value, line_no, column)
235
+ token
176
236
  end
177
237
 
178
238
  def get_string_segment(string, terminators)
179
- str = string.scan_until(/([^\\]|^|[^\\])([\\]{2})*[#{terminators}]/)
239
+ str = string.scan_until(/([^\\]|^|[^\\])([\\]{2})*[#{terminators}]+/)
180
240
  begin
181
241
  [str[0..-2], str[-1,1]]
182
242
  rescue
@@ -191,34 +251,41 @@ class PuppetLint
191
251
  until value.nil?
192
252
  if terminator == "\""
193
253
  if first
194
- tokens << PuppetLint::Token.new(:STRING, value, line, column)
254
+ tokens << new_token(:STRING, value, :line => line, :column => column)
195
255
  first = false
196
256
  else
197
257
  line += value.count("\n")
198
258
  token_column = column + (ss.pos - value.size)
199
- tokens << PuppetLint::Token.new(:DQPOST, value, line, token_column)
259
+ tokens << new_token(:DQPOST, value, :line => line, :column => token_column)
200
260
  end
201
261
  else
202
262
  if first
203
- tokens << PuppetLint::Token.new(:DQPRE, value, line, column)
263
+ tokens << new_token(:DQPRE, value, :line => line, :column => column)
204
264
  first = false
205
265
  else
206
266
  line += value.count("\n")
207
267
  token_column = column + (ss.pos - value.size)
208
- tokens << PuppetLint::Token.new(:DQMID, value, line, token_column)
268
+ tokens << new_token(:DQMID, value, :line => line, :column => token_column)
209
269
  end
210
270
  if ss.scan(/\{/).nil?
211
271
  var_name = ss.scan(/(::)?([\w-]+::)*[\w-]+/)
212
272
  unless var_name.nil?
213
273
  token_column = column + (ss.pos - var_name.size)
214
- tokens << PuppetLint::Token.new(:UNENC_VARIABLE, var_name, line, token_column)
274
+ tokens << new_token(:UNENC_VARIABLE, var_name, :line => line, :column => token_column)
215
275
  end
216
276
  else
217
- var_name = ss.scan(/(::)?([\w-]+::)*[\w-]+/)
218
- unless var_name.nil?
219
- token_column = column + (ss.pos - var_name.size)
220
- tokens << PuppetLint::Token.new(:VARIABLE, var_name, line, token_column)
221
- ss.scan(/\}/)
277
+ contents = ss.scan_until(/\}/)[0..-2]
278
+ if contents.match(/\A(::)?([\w-]+::)*[\w-]+\Z/)
279
+ token_column = column + (ss.pos - contents.size - 1)
280
+ tokens << new_token(:VARIABLE, contents, :line => line, :column => token_column)
281
+ else
282
+ lexer = PuppetLint::Lexer.new
283
+ lexer.tokenise(contents)
284
+ lexer.tokens.each do |token|
285
+ tok_col = column + token.column + (ss.pos - contents.size - 1)
286
+ tok_line = token.line + line - 1
287
+ tokens << new_token(token.type, token.value, :line => tok_line, :column => tok_col)
288
+ end
222
289
  end
223
290
  end
224
291
  end
@@ -226,19 +293,4 @@ class PuppetLint
226
293
  end
227
294
  end
228
295
  end
229
-
230
- class Token
231
- attr_reader :type, :value, :line, :column
232
-
233
- def initialize(type, value, line, column)
234
- @value = value
235
- @type = type
236
- @line = line
237
- @column = column
238
- end
239
-
240
- def inspect
241
- "<Token #{@type.inspect} (#{@value}) @#{@line}:#{@column}>"
242
- end
243
- end
244
296
  end