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
@@ -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