puppet-lint 0.2.0.pre1 → 0.2.0

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