rubycop 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/README.md +3 -0
  4. data/Rakefile +1 -0
  5. data/lib/rubycop.rb +6 -0
  6. data/lib/rubycop/analyzer.rb +6 -0
  7. data/lib/rubycop/analyzer/gray_list.rb +28 -0
  8. data/lib/rubycop/analyzer/node_builder.rb +523 -0
  9. data/lib/rubycop/analyzer/policy.rb +354 -0
  10. data/lib/rubycop/analyzer/ruby.rb +24 -0
  11. data/lib/rubycop/analyzer/ruby/args.rb +28 -0
  12. data/lib/rubycop/analyzer/ruby/array.rb +11 -0
  13. data/lib/rubycop/analyzer/ruby/assignment.rb +45 -0
  14. data/lib/rubycop/analyzer/ruby/assoc.rb +15 -0
  15. data/lib/rubycop/analyzer/ruby/blocks.rb +23 -0
  16. data/lib/rubycop/analyzer/ruby/call.rb +33 -0
  17. data/lib/rubycop/analyzer/ruby/case.rb +24 -0
  18. data/lib/rubycop/analyzer/ruby/constants.rb +49 -0
  19. data/lib/rubycop/analyzer/ruby/definitions.rb +27 -0
  20. data/lib/rubycop/analyzer/ruby/for.rb +17 -0
  21. data/lib/rubycop/analyzer/ruby/hash.rb +13 -0
  22. data/lib/rubycop/analyzer/ruby/if.rb +33 -0
  23. data/lib/rubycop/analyzer/ruby/list.rb +17 -0
  24. data/lib/rubycop/analyzer/ruby/node.rb +11 -0
  25. data/lib/rubycop/analyzer/ruby/operators.rb +54 -0
  26. data/lib/rubycop/analyzer/ruby/params.rb +23 -0
  27. data/lib/rubycop/analyzer/ruby/position.rb +15 -0
  28. data/lib/rubycop/analyzer/ruby/range.rb +17 -0
  29. data/lib/rubycop/analyzer/ruby/statements.rb +34 -0
  30. data/lib/rubycop/analyzer/ruby/string.rb +26 -0
  31. data/lib/rubycop/analyzer/ruby/tokens.rb +46 -0
  32. data/lib/rubycop/analyzer/ruby/variables.rb +26 -0
  33. data/lib/rubycop/analyzer/ruby/while.rb +29 -0
  34. data/lib/rubycop/version.rb +3 -0
  35. data/rubycop.gemspec +25 -0
  36. data/spec/node_builder_spec.rb +374 -0
  37. data/spec/policy_spec.rb +405 -0
  38. metadata +97 -0
@@ -0,0 +1,354 @@
1
+ require 'set'
2
+
3
+ module Rubycop
4
+ module Analyzer
5
+ class Policy
6
+ def initialize
7
+ @const_list = GrayList.new
8
+ initialize_const_blacklist
9
+ end
10
+
11
+ def inspect
12
+ '#<%s:0x%x>' % [self.class.name, object_id]
13
+ end
14
+
15
+ def blacklist_const(const)
16
+ @const_list.blacklist(const)
17
+ end
18
+
19
+ def const_allowed?(const)
20
+ @const_list.allow?(const)
21
+ end
22
+
23
+ def whitelist_const(const)
24
+ @const_list.whitelist(const)
25
+ end
26
+
27
+ def visit(node)
28
+ klass = node.class.ancestors.detect do |ancestor|
29
+ respond_to?("visit_#{ancestor.name.split('::').last}")
30
+ end
31
+ if klass
32
+ send("visit_#{klass.name.split('::').last}", node)
33
+ else
34
+ warn "unhandled node type: #{node.inspect}:#{node.class.name}"
35
+ true
36
+ end
37
+ end
38
+
39
+ def visit_Alias(node)
40
+ false # never allowed
41
+ end
42
+
43
+ def visit_Args(node)
44
+ node.elements.all? { |e| visit(e) }
45
+ end
46
+
47
+ def visit_Array(node)
48
+ node.elements.all? { |e| visit(e) }
49
+ end
50
+
51
+ def visit_Assoc(node)
52
+ visit(node.key) && visit(node.value)
53
+ end
54
+
55
+ def visit_Binary(node)
56
+ visit(node.lvalue) && visit(node.rvalue)
57
+ end
58
+
59
+ def visit_Block(node)
60
+ (node.params.nil? || visit(node.params)) && node.elements.all? { |e| visit(e) }
61
+ end
62
+
63
+ CALL_BLACKLIST = %w[
64
+ abort
65
+ alias_method
66
+ at_exit
67
+ autoload
68
+ binding
69
+ callcc
70
+ caller
71
+ class_eval
72
+ const_get
73
+ const_set
74
+ eval
75
+ exec
76
+ exit
77
+ fail
78
+ fork
79
+ gets
80
+ global_variables
81
+ instance_eval
82
+ load
83
+ loop
84
+ method
85
+ module_eval
86
+ open
87
+ readline
88
+ readlines
89
+ redo
90
+ remove_const
91
+ require
92
+ send
93
+ set_trace_func
94
+ sleep
95
+ spawn
96
+ srand
97
+ syscall
98
+ system
99
+ trap
100
+ undef
101
+ __callee__
102
+ __method__
103
+ ].to_set.freeze
104
+
105
+ def visit_Call(node)
106
+ !CALL_BLACKLIST.include?(node.identifier.token.to_s) && [node.target, node.arguments, node.block].compact.all? { |e| visit(e) }
107
+ end
108
+
109
+ def visit_Case(node)
110
+ visit(node.expression) && visit(node.block)
111
+ end
112
+
113
+ def visit_ChainedBlock(node)
114
+ node.elements.all? { |e| visit(e) } && node.blocks.all? { |e| visit(e) } && (node.params.nil? || visit(node.params))
115
+ end
116
+
117
+ def visit_Class(node)
118
+ visit(node.const) && (node.superclass.nil? || visit(node.superclass)) && visit(node.body)
119
+ end
120
+
121
+ def visit_ClassVariable(node)
122
+ false # never allowed
123
+ end
124
+
125
+ def visit_ClassVariableAssignment(node)
126
+ false # never allowed
127
+ end
128
+
129
+ def visit_Char(node)
130
+ true
131
+ end
132
+
133
+ def visit_Constant(node)
134
+ const_allowed?(node.token)
135
+ end
136
+
137
+ def visit_ConstantAssignment(node)
138
+ visit(node.lvalue) && visit(node.rvalue)
139
+ end
140
+
141
+ def visit_Defined(node)
142
+ false # never allowed (though it's probably safe)
143
+ end
144
+
145
+ def visit_Else(node)
146
+ node.elements.all? { |e| visit(e) }
147
+ end
148
+
149
+ def visit_ExecutableString(node)
150
+ false # never allowed
151
+ end
152
+
153
+ def visit_Float(node)
154
+ true
155
+ end
156
+
157
+ def visit_For(node)
158
+ visit(node.variable) && visit(node.range) && visit(node.statements)
159
+ end
160
+
161
+ def visit_GlobalVariable(node)
162
+ false # never allowed
163
+ end
164
+
165
+ def visit_GlobalVariableAssignment(node)
166
+ false # never allowed
167
+ end
168
+
169
+ def visit_Hash(node)
170
+ node.assocs.nil? || node.assocs.all? { |e| visit(e) }
171
+ end
172
+
173
+ def visit_Identifier(node)
174
+ !CALL_BLACKLIST.include?(node.token)
175
+ end
176
+
177
+ def visit_If(node)
178
+ visit(node.expression) && node.elements.all? { |e| visit(e) } && node.blocks.all? { |e| visit(e) }
179
+ end
180
+ alias_method :visit_Unless, :visit_If
181
+
182
+ def visit_IfMod(node)
183
+ visit(node.expression) && node.elements.all? { |e| visit(e) }
184
+ end
185
+ alias_method :visit_UnlessMod, :visit_IfMod
186
+
187
+ def visit_IfOp(node)
188
+ visit(node.condition) && visit(node.then_part) && visit(node.else_part)
189
+ end
190
+
191
+ def visit_InstanceVariable(node)
192
+ true
193
+ end
194
+
195
+ def visit_InstanceVariableAssignment(node)
196
+ visit(node.rvalue)
197
+ end
198
+
199
+ def visit_Integer(node)
200
+ true
201
+ end
202
+
203
+ KEYWORD_WHITELIST = %w[
204
+ false
205
+ nil
206
+ self
207
+ true
208
+ ].to_set.freeze
209
+
210
+ def visit_Keyword(node)
211
+ KEYWORD_WHITELIST.include?(node.token)
212
+ end
213
+
214
+ def visit_Label(node)
215
+ true
216
+ end
217
+
218
+ def visit_LocalVariableAssignment(node)
219
+ visit(node.rvalue)
220
+ end
221
+
222
+ def visit_Method(node)
223
+ [node.target, node.params, node.body].compact.all? { |e| visit(e) }
224
+ end
225
+
226
+ def visit_Module(node)
227
+ visit(node.const) && visit(node.body)
228
+ end
229
+
230
+ def visit_MultiAssignment(node)
231
+ visit(node.lvalue) && visit(node.rvalue)
232
+ end
233
+
234
+ def visit_MultiAssignmentList(node)
235
+ node.elements.all? { |e| visit(e) }
236
+ end
237
+
238
+ def visit_Params(node)
239
+ node.elements.all? { |e| visit(e) }
240
+ end
241
+
242
+ def visit_Program(node)
243
+ node.elements.all? { |e| visit(e) }
244
+ end
245
+
246
+ def visit_Range(node)
247
+ visit(node.min) && visit(node.max)
248
+ end
249
+
250
+ def visit_RescueMod(node)
251
+ node.elements.all? { |e| visit(e) } && visit(node.expression)
252
+ end
253
+
254
+ def visit_RescueParams(node)
255
+ node.elements.all? { |e| visit(e) }
256
+ end
257
+
258
+ def visit_SingletonClass(node)
259
+ visit(node.superclass) && visit(node.body)
260
+ end
261
+
262
+ def visit_SplatArg(node)
263
+ visit(node.arg)
264
+ end
265
+
266
+ def visit_Statements(node)
267
+ node.elements.all? { |e| visit(e) }
268
+ end
269
+
270
+ def visit_String(node)
271
+ # embedded strings can have statements in them, so check those
272
+ node.elements.reject { |e| e.is_a?(::String) }.all? { |e| visit(e) }
273
+ end
274
+
275
+ def visit_StringConcat(node)
276
+ node.elements.all? { |e| visit(e) }
277
+ end
278
+
279
+ def visit_Symbol(node)
280
+ true
281
+ end
282
+
283
+ def visit_Unary(node)
284
+ visit(node.operand)
285
+ end
286
+
287
+ def visit_Until(node)
288
+ false # never allowed
289
+ end
290
+ alias_method :visit_UntilMod, :visit_Until
291
+
292
+ def visit_When(node)
293
+ visit(node.expression) && node.elements.all? { |e| visit(e) }
294
+ end
295
+
296
+ def visit_While(node)
297
+ false # never allowed
298
+ end
299
+ alias_method :visit_WhileMod, :visit_While
300
+
301
+ private
302
+
303
+ CONST_BLACKLIST = %w[
304
+ ARGF
305
+ ARGV
306
+ Array
307
+ Base64
308
+ Class
309
+ Dir
310
+ ENV
311
+ Enumerable
312
+ Error
313
+ Exception
314
+ Fiber
315
+ File
316
+ FileUtils
317
+ GC
318
+ Gem
319
+ Hash
320
+ IO
321
+ IRB
322
+ Kernel
323
+ Module
324
+ Net
325
+ Object
326
+ ObjectSpace
327
+ OpenSSL
328
+ OpenURI
329
+ PLATFORM
330
+ Proc
331
+ Process
332
+ RUBY_COPYRIGHT
333
+ RUBY_DESCRIPTION
334
+ RUBY_ENGINE
335
+ RUBY_PATCHLEVEL
336
+ RUBY_PLATFORM
337
+ RUBY_RELEASE_DATE
338
+ RUBY_VERSION
339
+ Rails
340
+ STDERR
341
+ STDIN
342
+ STDOUT
343
+ String
344
+ TOPLEVEL_BINDING
345
+ Thread
346
+ VERSION
347
+ ].freeze
348
+
349
+ def initialize_const_blacklist
350
+ CONST_BLACKLIST.each { |const| blacklist_const(const) }
351
+ end
352
+ end
353
+ end
354
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubycop/analyzer/ruby/node'
2
+
3
+ require 'rubycop/analyzer/ruby/list'
4
+ require 'rubycop/analyzer/ruby/array'
5
+ require 'rubycop/analyzer/ruby/args'
6
+ require 'rubycop/analyzer/ruby/assignment'
7
+ require 'rubycop/analyzer/ruby/assoc'
8
+ require 'rubycop/analyzer/ruby/statements'
9
+ require 'rubycop/analyzer/ruby/blocks'
10
+ require 'rubycop/analyzer/ruby/call'
11
+ require 'rubycop/analyzer/ruby/case'
12
+ require 'rubycop/analyzer/ruby/tokens'
13
+ require 'rubycop/analyzer/ruby/constants'
14
+ require 'rubycop/analyzer/ruby/definitions'
15
+ require 'rubycop/analyzer/ruby/for'
16
+ require 'rubycop/analyzer/ruby/hash'
17
+ require 'rubycop/analyzer/ruby/if'
18
+ require 'rubycop/analyzer/ruby/operators'
19
+ require 'rubycop/analyzer/ruby/params'
20
+ require 'rubycop/analyzer/ruby/position'
21
+ require 'rubycop/analyzer/ruby/range'
22
+ require 'rubycop/analyzer/ruby/string'
23
+ require 'rubycop/analyzer/ruby/variables'
24
+ require 'rubycop/analyzer/ruby/while'
@@ -0,0 +1,28 @@
1
+ module Rubycop
2
+ module Analyzer
3
+ module Ruby
4
+ class Args < List
5
+ attr_reader :block
6
+
7
+ def add_block(block)
8
+ @block = block
9
+ end
10
+
11
+ def to_array
12
+ Array.new(@elements)
13
+ end
14
+ end
15
+
16
+ class Arg < Node
17
+ def initialize(arg)
18
+ @arg = arg
19
+ end
20
+
21
+ attr_reader :arg
22
+ end
23
+
24
+ class SplatArg < Arg
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ module Rubycop
2
+ module Analyzer
3
+ module Ruby
4
+ class Array < List
5
+ # def inspect
6
+ # '[%s]' % @elements.collect { |e| e.inspect }.join(', ')
7
+ # end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ module Rubycop
2
+ module Analyzer
3
+ module Ruby
4
+ class Assignment < Node
5
+ def initialize(lvalue, rvalue, operator)
6
+ @lvalue = lvalue
7
+ @rvalue = rvalue
8
+ @operator = operator
9
+ end
10
+
11
+ attr_reader :lvalue
12
+ attr_reader :rvalue
13
+ attr_reader :operator
14
+
15
+ # def inspect
16
+ # "#{@lvalue.inspect} #{@operator} #{@rvalue.inspect}"
17
+ # end
18
+ end
19
+
20
+ class ClassVariableAssignment < Assignment
21
+ end
22
+
23
+ class ConstantAssignment < Assignment
24
+ end
25
+
26
+ class GlobalVariableAssignment < Assignment
27
+ end
28
+
29
+ class InstanceVariableAssignment < Assignment
30
+ end
31
+
32
+ class LocalVariableAssignment < Assignment
33
+ end
34
+
35
+ class MultiAssignment < Assignment
36
+ end
37
+
38
+ class MultiAssignmentList < List
39
+ def assignment(rvalue, operator)
40
+ MultiAssignment.new(self, rvalue, operator)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end