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
@@ -0,0 +1,62 @@
1
+ class PuppetLint
2
+ class Lexer
3
+ class Token
4
+ # Internal: Returns the Symbol type of the Token.
5
+ attr_reader :type
6
+
7
+ # Internal: Returns the String value of the Token.
8
+ attr_reader :value
9
+
10
+ # Internal: Returns the Integer line number of the manifest text where
11
+ # the Token can be found.
12
+ attr_reader :line
13
+
14
+ # Internal: Returns the Integer column number of the line of the manifest
15
+ # text where the Token can be found.
16
+ attr_reader :column
17
+
18
+ # Internal: Gets/sets the next token in the manifest.
19
+ attr_accessor :next_token
20
+
21
+ # Internal: Gets/sets the previous token in the manifest.
22
+ attr_accessor :prev_token
23
+
24
+ # Internal: Gets/sets the next code token (skips whitespace, comments,
25
+ # etc) in the manifest.
26
+ attr_accessor :next_code_token
27
+
28
+ # Internal: Gets/sets the previous code tokne (skips whitespace,
29
+ # comments, etc) in the manifest.
30
+ attr_accessor :prev_code_token
31
+
32
+ # Internal: Initialise a new Token object.
33
+ #
34
+ # type - An upper case Symbol describing the type of Token.
35
+ # value - The String value of the Token.
36
+ # line - The Integer line number where the Token can be found in the
37
+ # manifest.
38
+ # column - The Integer number of characters from the start of the line to
39
+ # the start of the Token.
40
+ #
41
+ # Returns the instantiated Token.
42
+ def initialize(type, value, line, column)
43
+ @value = value
44
+ @type = type
45
+ @line = line
46
+ @column = column
47
+ @next_token = nil
48
+ @prev_token = nil
49
+ @next_code_token = nil
50
+ @prev_code_token = nil
51
+ end
52
+
53
+ # Internal: Produce a human friendly description of the Token when
54
+ # inspected.
55
+ #
56
+ # Returns a String describing the Token.
57
+ def inspect
58
+ "<Token #{@type.inspect} (#{@value}) @#{@line}:#{@column}>"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -19,18 +19,13 @@ end
19
19
 
20
20
  class PuppetLint::CheckPlugin
21
21
  include PuppetLint::Plugin
22
- attr_reader :problems, :checks
22
+ attr_reader :problems
23
23
 
24
24
  def initialize
25
25
  @problems = []
26
- @checks = []
27
26
  @default_info = {:check => 'unknown', :linenumber => 0}
28
27
  end
29
28
 
30
- def register_check(check)
31
- @checks << check
32
- end
33
-
34
29
  # notify(kind, message_hash) #=> nil
35
30
  #
36
31
  # Adds the message to the problems array.
@@ -59,33 +54,34 @@ class PuppetLint::CheckPlugin
59
54
  @fileinfo = fileinfo
60
55
  @data = data
61
56
 
62
- self.public_methods.select { |method|
63
- method.to_s.start_with? 'lint_check_'
64
- }.each { |method|
65
- name = method.to_s[11..-1]
66
- @default_info[:check] = name
67
- self.send(method) if PuppetLint.configuration.send("#{name}_enabled?")
68
- }
57
+ enabled_checks.each do |check|
58
+ @default_info[:check] = check
59
+ self.send("lint_check_#{check}")
60
+ end
69
61
 
70
62
  @problems
71
63
  end
72
64
 
73
- def tokens
74
- @tokens
65
+ def enabled_checks
66
+ @enabled_checks ||= Proc.new do
67
+ self.public_methods.select { |method|
68
+ method.to_s.start_with? 'lint_check_'
69
+ }.map { |method|
70
+ method.to_s[11..-1]
71
+ }.select { |name|
72
+ PuppetLint.configuration.send("#{name}_enabled?")
73
+ }
74
+ end.call
75
75
  end
76
76
 
77
- def path
78
- @fileinfo[:path]
77
+ def tokens
78
+ @tokens
79
79
  end
80
80
 
81
81
  def fullpath
82
82
  @fileinfo[:fullpath]
83
83
  end
84
84
 
85
- def data
86
- @data
87
- end
88
-
89
85
  def title_tokens
90
86
  @title_tokens ||= Proc.new do
91
87
  result = []
@@ -98,10 +94,11 @@ class PuppetLint::CheckPlugin
98
94
  }
99
95
  title_array_tokens = tokens[(array_start_idx + 1)..(token_idx - 2)]
100
96
  result += title_array_tokens.select { |token|
101
- [:STRING, :NAME].include? token.type
97
+ {:STRING => true, :NAME => true}.include? token.type
102
98
  }
103
99
  else
104
- if tokens[token_idx + 1].type != :LBRACE
100
+ next_token = tokens[token_idx].next_code_token
101
+ if next_token.type != :LBRACE
105
102
  result << tokens[token_idx - 1]
106
103
  end
107
104
  end
@@ -126,15 +123,23 @@ class PuppetLint::CheckPlugin
126
123
  result = []
127
124
  tokens.each_index do |token_idx|
128
125
  if tokens[token_idx].type == :COLON
129
- next_tokens = tokens[(token_idx + 1)..-1].reject { |r|
130
- formatting_tokens.include? r.type
131
- }
132
- if next_tokens.first.type != :LBRACE
133
- end_idx = tokens[(token_idx + 1)..-1].index { |r|
134
- [:SEMIC, :RBRACE].include? r.type
135
- } + token_idx
136
-
137
- result << {:start => token_idx + 1, :end => end_idx}
126
+ next_token = tokens[token_idx].next_code_token
127
+ depth = 1
128
+ if next_token.type != :LBRACE
129
+ tokens[(token_idx + 1)..-1].each_index do |idx|
130
+ real_idx = token_idx + idx + 1
131
+ if tokens[real_idx].type == :LBRACE
132
+ depth += 1
133
+ elsif {:SEMIC => true, :RBRACE => true}.include? tokens[real_idx].type
134
+ unless tokens[real_idx].type == :SEMIC && depth > 1
135
+ depth -= 1
136
+ if depth == 0
137
+ result << {:start => token_idx + 1, :end => real_idx}
138
+ break
139
+ end
140
+ end
141
+ end
142
+ end
138
143
  end
139
144
  end
140
145
  end
@@ -156,16 +161,19 @@ class PuppetLint::CheckPlugin
156
161
  tokens.each_index do |token_idx|
157
162
  if tokens[token_idx].type == :CLASS
158
163
  depth = 0
164
+ in_params = false
159
165
  tokens[token_idx+1..-1].each_index do |class_token_idx|
160
166
  idx = class_token_idx + token_idx + 1
161
- if tokens[idx].type == :LBRACE
162
- depth += 1
167
+ if tokens[idx].type == :LPAREN
168
+ in_params = true
169
+ elsif tokens[idx].type == :RPAREN
170
+ in_params = false
171
+ elsif tokens[idx].type == :LBRACE
172
+ depth += 1 unless in_params
163
173
  elsif tokens[idx].type == :RBRACE
164
- depth -= 1
165
- if depth == 0
166
- if tokens[token_idx..-1].reject { |r|
167
- r.type == :WHITESPACE
168
- }[1].type != :LBRACE
174
+ depth -= 1 unless in_params
175
+ if depth == 0 && ! in_params
176
+ if tokens[token_idx].next_code_token.type != :LBRACE
169
177
  result << {:start => token_idx, :end => idx}
170
178
  end
171
179
  break
@@ -192,13 +200,18 @@ class PuppetLint::CheckPlugin
192
200
  tokens.each_index do |token_idx|
193
201
  if tokens[token_idx].type == :DEFINE
194
202
  depth = 0
203
+ in_params = false
195
204
  tokens[token_idx+1..-1].each_index do |define_token_idx|
196
205
  idx = define_token_idx + token_idx + 1
197
- if tokens[idx].type == :LBRACE
198
- depth += 1
206
+ if tokens[idx].type == :LPAREN
207
+ in_params = true
208
+ elsif tokens[idx].type == :RPAREN
209
+ in_params = false
210
+ elsif tokens[idx].type == :LBRACE
211
+ depth += 1 unless in_params
199
212
  elsif tokens[idx].type == :RBRACE
200
- depth -= 1
201
- if depth == 0
213
+ depth -= 1 unless in_params
214
+ if depth == 0 && ! in_params
202
215
  result << {:start => token_idx, :end => idx}
203
216
  break
204
217
  end
@@ -211,14 +224,7 @@ class PuppetLint::CheckPlugin
211
224
  end
212
225
 
213
226
  def formatting_tokens
214
- [
215
- :COMMENT,
216
- :MLCOMMENT,
217
- :SLASH_COMENT,
218
- :INDENT,
219
- :WHITESPACE,
220
- :NEWLINE,
221
- ]
227
+ @formatting_tokens ||= PuppetLint::Lexer::FORMATTING_TOKENS
222
228
  end
223
229
 
224
230
  def manifest_lines
@@ -4,7 +4,9 @@ class PuppetLint
4
4
  end
5
5
 
6
6
  require 'puppet-lint/plugins/check_classes'
7
+ require 'puppet-lint/plugins/check_comments'
7
8
  require 'puppet-lint/plugins/check_conditionals'
9
+ require 'puppet-lint/plugins/check_documentation'
8
10
  require 'puppet-lint/plugins/check_strings'
9
11
  require 'puppet-lint/plugins/check_variables'
10
12
  require 'puppet-lint/plugins/check_whitespace'
@@ -42,40 +42,153 @@ class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
42
42
  end
43
43
  end
44
44
 
45
+ # Public: Check the manifest tokens for any classes or defined types that
46
+ # have a dash in their name and record a warning for each instance found.
47
+ #
48
+ # Returns nothing.
49
+ check 'names_containing_dash' do
50
+ (class_indexes + defined_type_indexes).each do |class_idx|
51
+ class_tokens = tokens[class_idx[:start]..class_idx[:end]]
52
+ title_token = class_tokens[class_tokens.index { |r| r.type == :NAME }]
53
+
54
+ if title_token.value.include? '-'
55
+ if class_tokens.first.type == :CLASS
56
+ obj_type = 'class'
57
+ else
58
+ obj_type = 'defined type'
59
+ end
60
+
61
+ notify :warning, {
62
+ :message => "#{obj_type} name containing a dash",
63
+ :linenumber => title_token.line,
64
+ :column => title_token.column,
65
+ }
66
+ end
67
+ end
68
+ end
69
+
70
+ check 'parameterised_classes' do
71
+ class_indexes.each do |class_idx|
72
+ token_idx = class_idx[:start]
73
+ depth = 0
74
+ lparen_idx = nil
75
+ rparen_idx = nil
76
+ tokens[token_idx..-1].each_index do |t|
77
+ idx = token_idx + t
78
+ if tokens[idx].type == :LPAREN
79
+ depth += 1
80
+ lparen_idx = idx if depth == 1
81
+ elsif tokens[idx].type == :RPAREN
82
+ depth -= 1
83
+ if depth == 0
84
+ rparen_idx = idx
85
+ break
86
+ end
87
+ end
88
+ end
89
+
90
+ class_tokens = tokens[class_idx[:start]..class_idx[:end]].reject { |r|
91
+ formatting_tokens.include? r.type
92
+ }
93
+ inherits_idx = class_tokens.index { |r| r.type == :INHERITS }
94
+ unless inherits_idx.nil?
95
+ inherited_class_token = class_tokens[inherits_idx + 1]
96
+ if inherited_class_token.value.end_with? '::params'
97
+ notify :warning, {
98
+ :message => 'class inheriting from params class',
99
+ :linenumber => inherited_class_token.line,
100
+ :column => inherited_class_token.column,
101
+ }
102
+ end
103
+ end
104
+
105
+ unless lparen_idx.nil? or rparen_idx.nil?
106
+ param_tokens = tokens[lparen_idx+1..rparen_idx-1].reject { |r|
107
+ formatting_tokens.include? r.type
108
+ }
109
+
110
+ paren_stack = []
111
+ param_tokens.each_index do |param_tokens_idx|
112
+ this_token = param_tokens[param_tokens_idx]
113
+ next_token = param_tokens[param_tokens_idx+1]
114
+ prev_token = param_tokens[param_tokens_idx-1]
115
+
116
+ if this_token.type == :LPAREN
117
+ paren_stack.push(true)
118
+ elsif this_token.type == :RPAREN
119
+ paren_stack.pop
120
+ end
121
+ next unless paren_stack.empty?
122
+
123
+ if this_token.type == :VARIABLE
124
+ if !next_token.nil? && next_token.type != :EQUALS
125
+ if !prev_token.nil? && prev_token.type != :EQUALS
126
+ notify :warning, {
127
+ :message => 'parameterised class parameter without a default value',
128
+ :linenumber => this_token.line,
129
+ :column => this_token.column,
130
+ }
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end end
137
+
45
138
  # Public: Test the manifest tokens for any parameterised classes or defined
46
139
  # types that take parameters and record a warning if there are any optional
47
140
  # parameters listed before required parameters.
48
141
  #
49
142
  # Returns nothing.
50
143
  check 'parameter_order' do
51
- (class_indexes + defined_type_indexes).each do |class_idx|
144
+ defined_type_indexes.each do |class_idx|
52
145
  token_idx = class_idx[:start]
53
- header_end_idx = tokens[token_idx..-1].index { |r| r.type == :LBRACE }
54
- header_tokens = tokens[token_idx..header_end_idx].reject { |r|
55
- formatting_tokens.include?(r.type)
56
- }
57
- lparen_idx = header_tokens.index { |r| r.type == :LPAREN }
58
- rparen_idx = header_tokens.rindex { |r| r.type == :RPAREN }
146
+ depth = 0
147
+ lparen_idx = nil
148
+ rparen_idx = nil
149
+ tokens[token_idx..-1].each_index do |t|
150
+ idx = token_idx + t
151
+ if tokens[idx].type == :LPAREN
152
+ depth += 1
153
+ lparen_idx = idx if depth == 1
154
+ elsif tokens[idx].type == :RPAREN
155
+ depth -= 1
156
+ if depth == 0
157
+ rparen_idx = idx
158
+ break
159
+ end
160
+ end
161
+ end
59
162
 
60
163
  unless lparen_idx.nil? or rparen_idx.nil?
61
- param_tokens = header_tokens[lparen_idx..rparen_idx]
164
+ param_tokens = tokens[lparen_idx+1..rparen_idx-1].reject { |r|
165
+ formatting_tokens.include? r.type
166
+ }
167
+
168
+ paren_stack = []
62
169
  param_tokens.each_index do |param_tokens_idx|
63
170
  this_token = param_tokens[param_tokens_idx]
64
171
  next_token = param_tokens[param_tokens_idx+1]
65
172
  prev_token = param_tokens[param_tokens_idx-1]
173
+
174
+ if this_token.type == :LPAREN
175
+ paren_stack.push(true)
176
+ elsif this_token.type == :RPAREN
177
+ paren_stack.pop
178
+ end
179
+ next unless paren_stack.empty?
180
+
66
181
  if this_token.type == :VARIABLE
67
- unless next_token.nil?
68
- if next_token.type == :COMMA or next_token.type == :RPAREN
69
- prev_tokens = param_tokens[0..param_tokens_idx]
70
- unless prev_tokens.rindex { |r| r.type == :EQUALS }.nil?
71
- unless prev_token.nil? or prev_token.type == :EQUALS
72
- msg = 'optional parameter listed before required parameter'
73
- notify :warning, {
74
- :message => msg,
75
- :linenumber => this_token.line,
76
- :column => this_token.column,
77
- }
78
- end
182
+ if next_token.nil? || next_token.type == :COMMA
183
+ prev_tokens = param_tokens[0..param_tokens_idx]
184
+ unless prev_tokens.rindex { |r| r.type == :EQUALS }.nil?
185
+ unless prev_token.nil? or prev_token.type == :EQUALS
186
+ msg = 'optional parameter listed before required parameter'
187
+ notify :warning, {
188
+ :message => msg,
189
+ :linenumber => this_token.line,
190
+ :column => this_token.column,
191
+ }
79
192
  end
80
193
  end
81
194
  end
@@ -91,19 +204,19 @@ class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
91
204
  # Returns nothing.
92
205
  check 'inherits_across_namespaces' do
93
206
  class_indexes.each do |class_idx|
94
- class_tokens = tokens[class_idx[:start]..class_idx[:end]].reject { |r|
95
- formatting_tokens.include?(r.type)
96
- }
207
+ class_token = tokens[class_idx[:start]]
208
+ class_name_token = class_token.next_code_token
209
+ inherits_token = class_name_token.next_code_token
210
+ next if inherits_token.nil?
97
211
 
98
- if class_tokens[2].type == :INHERITS
99
- class_name = class_tokens[1].value
100
- inherited_class = class_tokens[3].value
212
+ if inherits_token.type == :INHERITS
213
+ inherited_class_token = inherits_token.next_code_token
101
214
 
102
- unless class_name =~ /^#{inherited_class}::/
215
+ unless class_name_token.value =~ /^#{inherited_class_token.value}::/
103
216
  notify :warning, {
104
217
  :message => "class inherits across namespaces",
105
- :linenumber => class_tokens[3].line,
106
- :column => class_tokens[3].column,
218
+ :linenumber => inherited_class_token.line,
219
+ :column => inherited_class_token.column,
107
220
  }
108
221
  end
109
222
  end
@@ -117,16 +230,11 @@ class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
117
230
  check 'nested_classes_or_defines' do
118
231
  class_indexes.each do |class_idx|
119
232
  # Skip the first token so that we don't pick up the first :CLASS
120
- class_tokens = tokens[class_idx[:start]+1..class_idx[:end]].reject { |r|
121
- formatting_tokens.include?(r.type)
122
- }
123
-
124
- class_tokens.each_index do |token_idx|
125
- token = class_tokens[token_idx]
126
- next_token = class_tokens[token_idx + 1]
233
+ class_tokens = tokens[class_idx[:start]+1..class_idx[:end]]
127
234
 
235
+ class_tokens.each do |token|
128
236
  if token.type == :CLASS
129
- if next_token.type != :LBRACE
237
+ if token.next_code_token.type != :LBRACE
130
238
  notify :warning, {
131
239
  :message => "class defined inside a class",
132
240
  :linenumber => token.line,
@@ -169,14 +277,22 @@ class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
169
277
  (class_indexes + defined_type_indexes).each do |idx|
170
278
  object_tokens = tokens[idx[:start]..idx[:end]]
171
279
  object_tokens.reject! { |r| formatting_tokens.include?(r.type) }
280
+ depth = 0
281
+ lparen_idx = nil
282
+ rparen_idx = nil
283
+ object_tokens.each_index do |t|
284
+ if object_tokens[t].type == :LPAREN
285
+ depth += 1
286
+ lparen_idx = t if depth == 1
287
+ elsif object_tokens[t].type == :RPAREN
288
+ depth -= 1
289
+ if depth == 0
290
+ rparen_idx = t
291
+ break
292
+ end
293
+ end
294
+ end
172
295
  referenced_variables = []
173
- header_end_idx = object_tokens.index { |r| r.type == :LBRACE }
174
- lparen_idx = object_tokens[0..header_end_idx].index { |r|
175
- r.type == :LPAREN
176
- }
177
- rparen_idx = object_tokens[0..header_end_idx].rindex { |r|
178
- r.type == :RPAREN
179
- }
180
296
 
181
297
  unless lparen_idx.nil? or rparen_idx.nil?
182
298
  param_tokens = object_tokens[lparen_idx..rparen_idx]
@@ -184,7 +300,7 @@ class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
184
300
  this_token = param_tokens[param_tokens_idx]
185
301
  next_token = param_tokens[param_tokens_idx+1]
186
302
  if this_token.type == :VARIABLE
187
- if [:COMMA, :EQUALS, :RPAREN].include? next_token.type
303
+ if {:COMMA => true, :EQUALS => true, :RPAREN => true}.include? next_token.type
188
304
  variables_in_scope << this_token.value
189
305
  end
190
306
  end