rubycop 0.5.1 → 1.0.1

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 (71) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +1 -1
  3. data/README.md +43 -2
  4. data/Rakefile +3 -1
  5. data/lib/ruby_cop.rb +10 -0
  6. data/lib/ruby_cop/gray_list.rb +26 -0
  7. data/lib/ruby_cop/node_builder.rb +521 -0
  8. data/lib/ruby_cop/policy.rb +354 -0
  9. data/lib/ruby_cop/ruby.rb +23 -0
  10. data/lib/ruby_cop/ruby/args.rb +26 -0
  11. data/lib/ruby_cop/ruby/array.rb +13 -0
  12. data/lib/ruby_cop/ruby/assignment.rb +43 -0
  13. data/lib/ruby_cop/ruby/assoc.rb +13 -0
  14. data/lib/ruby_cop/ruby/blocks.rb +21 -0
  15. data/lib/ruby_cop/ruby/call.rb +31 -0
  16. data/lib/ruby_cop/ruby/case.rb +22 -0
  17. data/lib/ruby_cop/ruby/constants.rb +47 -0
  18. data/lib/ruby_cop/ruby/definitions.rb +25 -0
  19. data/lib/ruby_cop/ruby/for.rb +15 -0
  20. data/lib/ruby_cop/ruby/hash.rb +11 -0
  21. data/lib/ruby_cop/ruby/if.rb +31 -0
  22. data/lib/ruby_cop/ruby/list.rb +15 -0
  23. data/lib/ruby_cop/ruby/node.rb +9 -0
  24. data/lib/ruby_cop/ruby/operators.rb +52 -0
  25. data/lib/ruby_cop/ruby/params.rb +21 -0
  26. data/lib/ruby_cop/ruby/position.rb +13 -0
  27. data/lib/ruby_cop/ruby/range.rb +15 -0
  28. data/lib/ruby_cop/ruby/statements.rb +32 -0
  29. data/lib/ruby_cop/ruby/string.rb +24 -0
  30. data/lib/ruby_cop/ruby/tokens.rb +44 -0
  31. data/lib/ruby_cop/ruby/variables.rb +24 -0
  32. data/lib/ruby_cop/ruby/while.rb +27 -0
  33. data/lib/ruby_cop/version.rb +3 -0
  34. data/ruby_cop.gemspec +25 -0
  35. data/spec/{node_builder_spec.rb → analyzer/node_builder_spec.rb} +3 -3
  36. data/spec/{policy_spec.rb → analyzer/policy_spec.rb} +6 -8
  37. data/spec/spec_helper.rb +13 -0
  38. data/tasks/rspec.rake +8 -0
  39. data/tasks/yard.rake +2 -0
  40. metadata +69 -44
  41. data/lib/rubycop.rb +0 -6
  42. data/lib/rubycop/analyzer.rb +0 -6
  43. data/lib/rubycop/analyzer/gray_list.rb +0 -28
  44. data/lib/rubycop/analyzer/node_builder.rb +0 -523
  45. data/lib/rubycop/analyzer/policy.rb +0 -356
  46. data/lib/rubycop/analyzer/ruby.rb +0 -24
  47. data/lib/rubycop/analyzer/ruby/args.rb +0 -28
  48. data/lib/rubycop/analyzer/ruby/array.rb +0 -11
  49. data/lib/rubycop/analyzer/ruby/assignment.rb +0 -45
  50. data/lib/rubycop/analyzer/ruby/assoc.rb +0 -15
  51. data/lib/rubycop/analyzer/ruby/blocks.rb +0 -23
  52. data/lib/rubycop/analyzer/ruby/call.rb +0 -33
  53. data/lib/rubycop/analyzer/ruby/case.rb +0 -24
  54. data/lib/rubycop/analyzer/ruby/constants.rb +0 -49
  55. data/lib/rubycop/analyzer/ruby/definitions.rb +0 -27
  56. data/lib/rubycop/analyzer/ruby/for.rb +0 -17
  57. data/lib/rubycop/analyzer/ruby/hash.rb +0 -13
  58. data/lib/rubycop/analyzer/ruby/if.rb +0 -33
  59. data/lib/rubycop/analyzer/ruby/list.rb +0 -17
  60. data/lib/rubycop/analyzer/ruby/node.rb +0 -11
  61. data/lib/rubycop/analyzer/ruby/operators.rb +0 -54
  62. data/lib/rubycop/analyzer/ruby/params.rb +0 -23
  63. data/lib/rubycop/analyzer/ruby/position.rb +0 -15
  64. data/lib/rubycop/analyzer/ruby/range.rb +0 -17
  65. data/lib/rubycop/analyzer/ruby/statements.rb +0 -34
  66. data/lib/rubycop/analyzer/ruby/string.rb +0 -26
  67. data/lib/rubycop/analyzer/ruby/tokens.rb +0 -46
  68. data/lib/rubycop/analyzer/ruby/variables.rb +0 -26
  69. data/lib/rubycop/analyzer/ruby/while.rb +0 -29
  70. data/lib/rubycop/version.rb +0 -3
  71. data/rubycop.gemspec +0 -25
data/.gitignore CHANGED
@@ -1,4 +1,6 @@
1
1
  *.gem
2
2
  .bundle
3
+ .rvmrc
3
4
  Gemfile.lock
5
+ doc
4
6
  pkg/*
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in rubycop.gemspec
3
+ # Specify your gem's dependencies in ruby_cop.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,3 +1,44 @@
1
- Rubycop is a semantic analyzer for ruby.
1
+ RubyCop
2
+ =======
2
3
 
3
- TODO: fill out this README
4
+ RubyCop scans Ruby code and tells you whether it's safe or not. We use
5
+ it at [CodeSchool][1] to check user-submitted code before we eval it on
6
+ the server.
7
+
8
+ RubyCop is made up of two pieces: NodeBuilder and Policy. NodeBuilder is
9
+ responsible for parsing Ruby code (using Ripper) and building an AST
10
+ (Abstract Syntax Tree). Policy then scans the AST and tells you whether
11
+ the code is safe or not.
12
+
13
+ ## Requirements
14
+
15
+ RubyCop requires Ruby 1.9, though it should work under 1.8 if you
16
+ include the "ripper" gem.
17
+
18
+ ## Usage
19
+
20
+ Here's a quick example of building the AST, and evaluating it with the
21
+ Policy:
22
+
23
+ >> require "ruby_cop"
24
+ => true
25
+ >> policy = RubyCop::Policy.new
26
+ >> ast = RubyCop::NodeBuilder.build("x = 1 + 2")
27
+ >> ast.accept(policy)
28
+ => true
29
+
30
+ And if you pass in some unsafe code:
31
+
32
+ >> ast = RubyCop::NodeBuilder.build("x = `ls -la`")
33
+ >> ast.accept(policy)
34
+ => false
35
+
36
+ ## Drawbacks
37
+
38
+ Ruby is a very dynamic language, so this kind of static analysis will
39
+ only get you so far. RubyCop blocks obvious things like backticks and
40
+ unsafe Kernel methods (#eval, #exec, #fork, etc.), but has to err on the
41
+ side of safety in other places. For instance, #send is considered
42
+ unsafe.
43
+
44
+ [1]: http://www.codeschool.com/
data/Rakefile CHANGED
@@ -1 +1,3 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+ Dir['tasks/*.rake'].each { |file| load(file) }
@@ -0,0 +1,10 @@
1
+ module RubyCop
2
+ # Your code goes here...
3
+ end
4
+
5
+ require 'ripper'
6
+
7
+ require 'ruby_cop/ruby'
8
+ require 'ruby_cop/node_builder'
9
+ require 'ruby_cop/gray_list'
10
+ require 'ruby_cop/policy'
@@ -0,0 +1,26 @@
1
+ require 'set'
2
+
3
+ module RubyCop
4
+ # Combination blacklist and whitelist.
5
+ class GrayList
6
+ def initialize
7
+ @blacklist = Set.new
8
+ @whitelist = Set.new
9
+ end
10
+
11
+ # An item is allowed if it's whitelisted, or if it's not blacklisted.
12
+ def allow?(item)
13
+ @whitelist.include?(item) || !@blacklist.include?(item)
14
+ end
15
+
16
+ def blacklist(item)
17
+ @whitelist.delete(item)
18
+ @blacklist.add(item)
19
+ end
20
+
21
+ def whitelist(item)
22
+ @blacklist.delete(item)
23
+ @whitelist.add(item)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,521 @@
1
+ module RubyCop
2
+ class NodeBuilder < Ripper::SexpBuilder
3
+ def initialize(src, filename=nil, lineno=nil)
4
+ @src = src ||= filename && File.read(filename) || ''
5
+ @filename = filename
6
+ super
7
+ end
8
+
9
+ class << self
10
+ def build(src, filename=nil)
11
+ new(src, filename).parse
12
+ end
13
+ end
14
+
15
+ def on_alias(new_name, old_name)
16
+ Ruby::Alias.new(to_ident(new_name), to_ident(old_name))
17
+ end
18
+
19
+ def on_aref(target, args)
20
+ Ruby::Call.new(target, ident(:[]), args)
21
+ end
22
+
23
+ def on_aref_field(target, args)
24
+ Ruby::Call.new(target, ident(:[]), args)
25
+ end
26
+
27
+ def on_arg_paren(args)
28
+ args
29
+ end
30
+
31
+ def on_args_add(args, arg)
32
+ args.add(arg); args
33
+ end
34
+
35
+ def on_args_add_block(args, block)
36
+ args.add_block(block) if block; args
37
+ end
38
+
39
+ def on_args_new
40
+ Ruby::Args.new
41
+ end
42
+
43
+ def on_array(args)
44
+ args ? args.to_array : Ruby::Array.new
45
+ end
46
+
47
+ def on_assign(lvalue, rvalue)
48
+ lvalue.assignment(rvalue, ident(:'='))
49
+ end
50
+
51
+ def on_assoc_new(key, value)
52
+ Ruby::Assoc.new(key, value)
53
+ end
54
+
55
+ def on_assoclist_from_args(args)
56
+ args
57
+ end
58
+
59
+ def on_bare_assoc_hash(assocs)
60
+ Ruby::Hash.new(assocs)
61
+ end
62
+
63
+ def on_BEGIN(statements)
64
+ Ruby::Call.new(nil, ident(:BEGIN), nil, statements)
65
+ end
66
+
67
+ def on_begin(body)
68
+ body.is_a?(Ruby::ChainedBlock) ? body : body.to_chained_block
69
+ end
70
+
71
+ def on_binary(lvalue, operator, rvalue)
72
+ Ruby::Binary.new(lvalue, rvalue, operator)
73
+ end
74
+
75
+ def on_blockarg(arg)
76
+ arg
77
+ end
78
+
79
+ def on_block_var(params, something)
80
+ params
81
+ end
82
+
83
+ def on_bodystmt(body, rescue_block, else_block, ensure_block)
84
+ statements = [rescue_block, else_block, ensure_block].compact
85
+ statements.empty? ? body : body.to_chained_block(statements)
86
+ end
87
+
88
+ def on_brace_block(params, statements)
89
+ statements.to_block(params)
90
+ end
91
+
92
+ def on_break(args)
93
+ Ruby::Call.new(nil, ident(:break), args)
94
+ end
95
+
96
+ def on_call(target, separator, identifier)
97
+ Ruby::Call.new(target, identifier)
98
+ end
99
+
100
+ def on_case(args, when_block)
101
+ Ruby::Case.new(args, when_block)
102
+ end
103
+
104
+ def on_CHAR(token)
105
+ Ruby::Char.new(token, position)
106
+ end
107
+
108
+ def on_class(const, superclass, body)
109
+ Ruby::Class.new(const, superclass, body)
110
+ end
111
+
112
+ def on_class_name_error(ident)
113
+ raise SyntaxError, 'class/module name must be CONSTANT'
114
+ end
115
+
116
+ def on_command(identifier, args)
117
+ Ruby::Call.new(nil, identifier, args)
118
+ end
119
+
120
+ def on_command_call(target, separator, identifier, args)
121
+ Ruby::Call.new(target, identifier, args)
122
+ end
123
+
124
+ def on_const(token)
125
+ Ruby::Constant.new(token, position)
126
+ end
127
+
128
+ def on_const_path_field(namespace, const)
129
+ const.namespace = namespace; const
130
+ end
131
+
132
+ def on_const_path_ref(namespace, const)
133
+ const.namespace = namespace; const
134
+ end
135
+
136
+ def on_const_ref(const)
137
+ const
138
+ end
139
+
140
+ def on_cvar(token)
141
+ Ruby::ClassVariable.new(token, position)
142
+ end
143
+
144
+ def on_def(identifier, params, body)
145
+ Ruby::Method.new(nil, identifier, params, body)
146
+ end
147
+
148
+ def on_defs(target, separator, identifier, params, body)
149
+ Ruby::Method.new(target, identifier, params, body)
150
+ end
151
+
152
+ def on_defined(ref)
153
+ Ruby::Defined.new(ref)
154
+ end
155
+
156
+ def on_do_block(params, statements)
157
+ statements.to_block(params)
158
+ end
159
+
160
+ def on_dot2(min, max)
161
+ Ruby::Range.new(min, max, false)
162
+ end
163
+
164
+ def on_dot3(min, max)
165
+ Ruby::Range.new(min, max, true)
166
+ end
167
+
168
+ def on_dyna_symbol(symbol)
169
+ symbol.to_dyna_symbol
170
+ end
171
+
172
+ def on_else(statements)
173
+ Ruby::Else.new(statements)
174
+ end
175
+
176
+ def on_END(statements)
177
+ Ruby::Call.new(nil, ident(:END), nil, statements)
178
+ end
179
+
180
+ def on_ensure(statements)
181
+ statements
182
+ end
183
+
184
+ def on_if(expression, statements, else_block)
185
+ Ruby::If.new(expression, statements, else_block)
186
+ end
187
+ alias_method :on_elsif, :on_if
188
+
189
+ def on_ifop(condition, then_part, else_part)
190
+ Ruby::IfOp.new(condition, then_part, else_part)
191
+ end
192
+
193
+ def on_if_mod(expression, statement)
194
+ Ruby::IfMod.new(expression, statement)
195
+ end
196
+
197
+ def on_fcall(identifier)
198
+ Ruby::Call.new(nil, identifier)
199
+ end
200
+
201
+ def on_field(target, separator, identifier)
202
+ Ruby::Call.new(target, identifier)
203
+ end
204
+
205
+ def on_float(token)
206
+ Ruby::Float.new(token, position)
207
+ end
208
+
209
+ def on_for(variable, range, statements)
210
+ Ruby::For.new(variable, range, statements)
211
+ end
212
+
213
+ def on_gvar(token)
214
+ Ruby::GlobalVariable.new(token, position)
215
+ end
216
+
217
+ def on_hash(assocs)
218
+ Ruby::Hash.new(assocs)
219
+ end
220
+
221
+ def on_ident(token)
222
+ ident(token)
223
+ end
224
+
225
+ def on_int(token)
226
+ Ruby::Integer.new(token, position)
227
+ end
228
+
229
+ def on_ivar(token)
230
+ Ruby::InstanceVariable.new(token, position)
231
+ end
232
+
233
+ def on_kw(token)
234
+ Ruby::Keyword.new(token, position)
235
+ end
236
+
237
+ def on_label(token)
238
+ Ruby::Label.new(token, position)
239
+ end
240
+
241
+ def on_lambda(params, statements)
242
+ Ruby::Block.new(statements, params)
243
+ end
244
+
245
+ def on_massign(lvalue, rvalue)
246
+ lvalue.assignment(rvalue, ident(:'='))
247
+ end
248
+
249
+ def on_method_add_arg(call, args)
250
+ call.arguments = args; call
251
+ end
252
+
253
+ def on_method_add_block(call, block)
254
+ call.block = block; call
255
+ end
256
+
257
+ def on_mlhs_add(assignment, ref)
258
+ assignment.add(ref); assignment
259
+ end
260
+
261
+ def on_mlhs_add_star(assignment, ref)
262
+ assignment.add(Ruby::SplatArg.new(ref)); assignment
263
+ end
264
+
265
+ def on_mlhs_new
266
+ Ruby::MultiAssignmentList.new
267
+ end
268
+
269
+ def on_module(const, body)
270
+ Ruby::Module.new(const, body)
271
+ end
272
+
273
+ def on_mrhs_add(assignment, ref)
274
+ assignment.add(ref); assignment
275
+ end
276
+
277
+ def on_mrhs_new_from_args(args)
278
+ Ruby::MultiAssignmentList.new(args.elements)
279
+ end
280
+
281
+ def on_next(args)
282
+ Ruby::Call.new(nil, ident(:next), args)
283
+ end
284
+
285
+ def on_op(operator)
286
+ operator.intern
287
+ end
288
+
289
+ def on_opassign(lvalue, operator, rvalue)
290
+ lvalue.assignment(rvalue, operator)
291
+ end
292
+
293
+ def on_params(params, optionals, rest, something, block)
294
+ Ruby::Params.new(params, optionals, rest, block)
295
+ end
296
+
297
+ def on_paren(node)
298
+ node
299
+ end
300
+
301
+ def on_parse_error(message)
302
+ raise SyntaxError, message
303
+ end
304
+
305
+ def on_program(statements)
306
+ statements.to_program(@src, @filename)
307
+ end
308
+
309
+ def on_qwords_add(array, word)
310
+ array.add(Ruby::String.new(word)); array
311
+ end
312
+
313
+ def on_qwords_new
314
+ Ruby::Array.new
315
+ end
316
+
317
+ def on_redo
318
+ Ruby::Call.new(nil, ident(:redo))
319
+ end
320
+
321
+ def on_regexp_add(regexp, content)
322
+ regexp.add(content); regexp
323
+ end
324
+
325
+ def on_regexp_literal(regexp, rdelim)
326
+ regexp
327
+ end
328
+
329
+ def on_regexp_new
330
+ Ruby::Regexp.new
331
+ end
332
+
333
+ def on_rescue(types, var, statements, block)
334
+ statements.to_chained_block(block, Ruby::RescueParams.new(types, var))
335
+ end
336
+
337
+ def on_rescue_mod(expression, statements)
338
+ Ruby::RescueMod.new(expression, statements)
339
+ end
340
+
341
+ def on_rest_param(param)
342
+ param
343
+ end
344
+
345
+ def on_retry
346
+ Ruby::Call.new(nil, ident(:retry))
347
+ end
348
+
349
+ def on_return(args)
350
+ Ruby::Call.new(nil, ident(:return), args)
351
+ end
352
+
353
+ def on_sclass(superclass, body)
354
+ Ruby::SingletonClass.new(superclass, body)
355
+ end
356
+
357
+ def on_stmts_add(target, statement)
358
+ target.add(statement) if statement; target
359
+ end
360
+
361
+ def on_stmts_new
362
+ Ruby::Statements.new
363
+ end
364
+
365
+ def on_string_add(string, content)
366
+ string.add(content); string
367
+ end
368
+
369
+ def on_string_concat(*strings)
370
+ Ruby::StringConcat.new(strings)
371
+ end
372
+
373
+ def on_string_content
374
+ Ruby::String.new
375
+ end
376
+
377
+ # weird string syntax that I didn't know existed until writing this lib.
378
+ # ex. "safe level is #$SAFE" => "safe level is 0"
379
+ def on_string_dvar(variable)
380
+ variable
381
+ end
382
+
383
+ def on_string_embexpr(expression)
384
+ expression
385
+ end
386
+
387
+ def on_string_literal(string)
388
+ string
389
+ end
390
+
391
+ def on_super(args)
392
+ Ruby::Call.new(nil, ident(:super), args)
393
+ end
394
+
395
+ def on_symbol(token)
396
+ Ruby::Symbol.new(token, position)
397
+ end
398
+
399
+ def on_symbol_literal(symbol)
400
+ symbol
401
+ end
402
+
403
+ def on_top_const_field(field)
404
+ field
405
+ end
406
+
407
+ def on_top_const_ref(const)
408
+ const
409
+ end
410
+
411
+ def on_tstring_content(token)
412
+ token
413
+ end
414
+
415
+ def on_unary(operator, operand)
416
+ Ruby::Unary.new(operator, operand)
417
+ end
418
+
419
+ def on_undef(args)
420
+ Ruby::Call.new(nil, ident(:undef), Ruby::Args.new(args.collect { |e| to_ident(e) }))
421
+ end
422
+
423
+ def on_unless(expression, statements, else_block)
424
+ Ruby::Unless.new(expression, statements, else_block)
425
+ end
426
+
427
+ def on_unless_mod(expression, statement)
428
+ Ruby::UnlessMod.new(expression, statement)
429
+ end
430
+
431
+ def on_until(expression, statements)
432
+ Ruby::Until.new(expression, statements)
433
+ end
434
+
435
+ def on_until_mod(expression, statement)
436
+ Ruby::UntilMod.new(expression, statement)
437
+ end
438
+
439
+ def on_var_alias(new_name, old_name)
440
+ Ruby::Alias.new(to_ident(new_name), to_ident(old_name))
441
+ end
442
+
443
+ def on_var_field(field)
444
+ field
445
+ end
446
+
447
+ def on_var_ref(ref)
448
+ ref
449
+ end
450
+
451
+ def on_void_stmt
452
+ nil
453
+ end
454
+
455
+ def on_when(expression, statements, next_block)
456
+ Ruby::When.new(expression, statements, next_block)
457
+ end
458
+
459
+ def on_while(expression, statements)
460
+ Ruby::While.new(expression, statements)
461
+ end
462
+
463
+ def on_while_mod(expression, statement)
464
+ Ruby::WhileMod.new(expression, statement)
465
+ end
466
+
467
+ def on_word_add(string, word)
468
+ string.add(word); string
469
+ end
470
+
471
+ def on_words_add(array, word)
472
+ array.add(word); array
473
+ end
474
+
475
+ def on_word_new
476
+ Ruby::String.new
477
+ end
478
+
479
+ def on_words_new
480
+ Ruby::Array.new
481
+ end
482
+
483
+ def on_xstring_add(string, content)
484
+ on_string_add(string, content)
485
+ end
486
+
487
+ def on_xstring_new
488
+ Ruby::ExecutableString.new
489
+ end
490
+
491
+ def on_xstring_literal(string)
492
+ string
493
+ end
494
+
495
+ def on_yield(args)
496
+ Ruby::Call.new(nil, ident(:yield), args)
497
+ end
498
+
499
+ def on_yield0
500
+ Ruby::Call.new(nil, ident(:yield))
501
+ end
502
+
503
+ def on_zsuper(*)
504
+ Ruby::Call.new(nil, ident(:super))
505
+ end
506
+
507
+ private
508
+
509
+ def ident(ident)
510
+ Ruby::Identifier.new(ident, position)
511
+ end
512
+
513
+ def to_ident(ident_or_sym)
514
+ ident_or_sym.is_a?(Ruby::Identifier) ? ident_or_sym : ident(ident_or_sym)
515
+ end
516
+
517
+ def position
518
+ Ruby::Position.new(lineno, column)
519
+ end
520
+ end
521
+ end