rubycop 0.5.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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +3 -0
- data/Rakefile +1 -0
- data/lib/rubycop.rb +6 -0
- data/lib/rubycop/analyzer.rb +6 -0
- data/lib/rubycop/analyzer/gray_list.rb +28 -0
- data/lib/rubycop/analyzer/node_builder.rb +523 -0
- data/lib/rubycop/analyzer/policy.rb +354 -0
- data/lib/rubycop/analyzer/ruby.rb +24 -0
- data/lib/rubycop/analyzer/ruby/args.rb +28 -0
- data/lib/rubycop/analyzer/ruby/array.rb +11 -0
- data/lib/rubycop/analyzer/ruby/assignment.rb +45 -0
- data/lib/rubycop/analyzer/ruby/assoc.rb +15 -0
- data/lib/rubycop/analyzer/ruby/blocks.rb +23 -0
- data/lib/rubycop/analyzer/ruby/call.rb +33 -0
- data/lib/rubycop/analyzer/ruby/case.rb +24 -0
- data/lib/rubycop/analyzer/ruby/constants.rb +49 -0
- data/lib/rubycop/analyzer/ruby/definitions.rb +27 -0
- data/lib/rubycop/analyzer/ruby/for.rb +17 -0
- data/lib/rubycop/analyzer/ruby/hash.rb +13 -0
- data/lib/rubycop/analyzer/ruby/if.rb +33 -0
- data/lib/rubycop/analyzer/ruby/list.rb +17 -0
- data/lib/rubycop/analyzer/ruby/node.rb +11 -0
- data/lib/rubycop/analyzer/ruby/operators.rb +54 -0
- data/lib/rubycop/analyzer/ruby/params.rb +23 -0
- data/lib/rubycop/analyzer/ruby/position.rb +15 -0
- data/lib/rubycop/analyzer/ruby/range.rb +17 -0
- data/lib/rubycop/analyzer/ruby/statements.rb +34 -0
- data/lib/rubycop/analyzer/ruby/string.rb +26 -0
- data/lib/rubycop/analyzer/ruby/tokens.rb +46 -0
- data/lib/rubycop/analyzer/ruby/variables.rb +26 -0
- data/lib/rubycop/analyzer/ruby/while.rb +29 -0
- data/lib/rubycop/version.rb +3 -0
- data/rubycop.gemspec +25 -0
- data/spec/node_builder_spec.rb +374 -0
- data/spec/policy_spec.rb +405 -0
- 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,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
|