ruby-lint 0.0.1a

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 (80) hide show
  1. data/.gitignore +5 -0
  2. data/.rbenv-version +1 -0
  3. data/.yardopts +10 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +19 -0
  6. data/MANIFEST +79 -0
  7. data/README.md +48 -0
  8. data/Rakefile +14 -0
  9. data/bin/rlint +6 -0
  10. data/doc/.gitkeep +0 -0
  11. data/doc/build/.gitkeep +0 -0
  12. data/doc/css/.gitkeep +0 -0
  13. data/doc/css/common.css +68 -0
  14. data/lib/rlint/analyze/coding_style.rb +407 -0
  15. data/lib/rlint/analyze/definitions.rb +244 -0
  16. data/lib/rlint/analyze/method_validation.rb +104 -0
  17. data/lib/rlint/analyze/shadowing_variables.rb +37 -0
  18. data/lib/rlint/analyze/undefined_variables.rb +99 -0
  19. data/lib/rlint/analyze/unused_variables.rb +103 -0
  20. data/lib/rlint/callback.rb +67 -0
  21. data/lib/rlint/cli.rb +167 -0
  22. data/lib/rlint/constant_importer.rb +102 -0
  23. data/lib/rlint/definition.rb +230 -0
  24. data/lib/rlint/formatter/text.rb +54 -0
  25. data/lib/rlint/helper/definition_resolver.rb +143 -0
  26. data/lib/rlint/helper/scoping.rb +138 -0
  27. data/lib/rlint/iterator.rb +193 -0
  28. data/lib/rlint/options.rb +58 -0
  29. data/lib/rlint/parser.rb +1252 -0
  30. data/lib/rlint/parser_error.rb +42 -0
  31. data/lib/rlint/report.rb +98 -0
  32. data/lib/rlint/token/assignment_token.rb +46 -0
  33. data/lib/rlint/token/begin_rescue_token.rb +57 -0
  34. data/lib/rlint/token/block_token.rb +17 -0
  35. data/lib/rlint/token/case_token.rb +44 -0
  36. data/lib/rlint/token/class_token.rb +24 -0
  37. data/lib/rlint/token/method_definition_token.rb +64 -0
  38. data/lib/rlint/token/method_token.rb +58 -0
  39. data/lib/rlint/token/parameters_token.rb +99 -0
  40. data/lib/rlint/token/regexp_token.rb +15 -0
  41. data/lib/rlint/token/statement_token.rb +69 -0
  42. data/lib/rlint/token/token.rb +162 -0
  43. data/lib/rlint/token/variable_token.rb +18 -0
  44. data/lib/rlint/version.rb +3 -0
  45. data/lib/rlint.rb +36 -0
  46. data/ruby-lint.gemspec +23 -0
  47. data/spec/benchmarks/memory.rb +52 -0
  48. data/spec/benchmarks/parse_parser.rb +16 -0
  49. data/spec/helper.rb +4 -0
  50. data/spec/rlint/analyze/coding_style.rb +224 -0
  51. data/spec/rlint/analyze/definitions/classes.rb +114 -0
  52. data/spec/rlint/analyze/definitions/methods.rb +91 -0
  53. data/spec/rlint/analyze/definitions/modules.rb +207 -0
  54. data/spec/rlint/analyze/definitions/variables.rb +103 -0
  55. data/spec/rlint/analyze/method_validation.rb +177 -0
  56. data/spec/rlint/analyze/shadowing_variables.rb +30 -0
  57. data/spec/rlint/analyze/undefined_variables.rb +230 -0
  58. data/spec/rlint/analyze/unused_variables.rb +225 -0
  59. data/spec/rlint/callback.rb +28 -0
  60. data/spec/rlint/constant_importer.rb +27 -0
  61. data/spec/rlint/definition.rb +96 -0
  62. data/spec/rlint/formatter/text.rb +21 -0
  63. data/spec/rlint/iterator.rb +452 -0
  64. data/spec/rlint/parser/arrays.rb +147 -0
  65. data/spec/rlint/parser/classes.rb +152 -0
  66. data/spec/rlint/parser/errors.rb +19 -0
  67. data/spec/rlint/parser/hashes.rb +136 -0
  68. data/spec/rlint/parser/methods.rb +249 -0
  69. data/spec/rlint/parser/modules.rb +49 -0
  70. data/spec/rlint/parser/objects.rb +39 -0
  71. data/spec/rlint/parser/operators.rb +75 -0
  72. data/spec/rlint/parser/procs.rb +113 -0
  73. data/spec/rlint/parser/ranges.rb +49 -0
  74. data/spec/rlint/parser/regexp.rb +31 -0
  75. data/spec/rlint/parser/scalars.rb +93 -0
  76. data/spec/rlint/parser/statements.rb +550 -0
  77. data/spec/rlint/parser/variables.rb +181 -0
  78. data/spec/rlint/report.rb +30 -0
  79. data/task/test.rake +6 -0
  80. metadata +188 -0
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+
3
+ describe 'Rlint::Callback' do
4
+ it 'Add an error to a report using Rlint::Callback' do
5
+ report = Rlint::Report.new
6
+ callback = Rlint::Callback.new(report)
7
+
8
+ report.levels.each do |level|
9
+ report.messages[level].nil?.should == true
10
+
11
+ callback.send(level, "test #{level}", 1, 1)
12
+
13
+ report.messages[level].class.should == Array
14
+ report.messages[level].length.should == 1
15
+
16
+ report.messages[level][0][:message].should == "test #{level}"
17
+ end
18
+ end
19
+
20
+ it 'Ignore errors (when disabled) when adding one using Rlint::Callback' do
21
+ report = Rlint::Report.new('(rlint)', [:warning])
22
+ callback = Rlint::Callback.new(report)
23
+
24
+ callback.send(:error, 'test error', 1, 1)
25
+
26
+ report.messages[:error].nil?.should == true
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+
3
+ describe 'Rlint::ConstantImporter' do
4
+ it 'Import methods from the Kernel constant' do
5
+ imported = Rlint::ConstantImporter.import([:Kernel])
6
+
7
+ imported.key?('Kernel').should == true
8
+
9
+ found = imported['Kernel'].lookup(:method, 'puts')
10
+
11
+ found.class.should == Rlint::Definition
12
+
13
+ found.token.class.should == Rlint::Token::MethodDefinitionToken
14
+ found.token.name.should == 'puts'
15
+ found.token.visibility.should == :public
16
+
17
+ found.token.parameters.class.should == Rlint::Token::ParametersToken
18
+
19
+ found.token.parameters.value.class.should == Array
20
+ found.token.parameters.value.length.should == 0
21
+
22
+ found.token.parameters.rest.class.should == Rlint::Token::VariableToken
23
+ found.token.parameters.rest.name.should == ''
24
+ found.token.parameters.rest.type.should == :local_variable
25
+ found.token.parameters.rest.event.should == :local_variable
26
+ end
27
+ end
@@ -0,0 +1,96 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+
3
+ describe 'Rlint::Definition' do
4
+ it 'Look up a symbol in a scope' do
5
+ scope = Rlint::Definition.new
6
+
7
+ scope.lookup(:local_variable, 'number').should == nil
8
+
9
+ scope.add(:local_variable, 'number', 10)
10
+
11
+ scope.lookup(:local_variable, 'number').should == 10
12
+ end
13
+
14
+ it 'Look up a symbol in a parent scope' do
15
+ parent = Rlint::Definition.new
16
+ child = Rlint::Definition.new(parent)
17
+
18
+ parent.add(:local_variable, 'number', 10)
19
+ parent.add(:global_variable, '$number', 20)
20
+
21
+ parent.lookup(:local_variable, 'number').should == 10
22
+ parent.lookup(:global_variable, '$number').should == 20
23
+
24
+ child.lookup(:local_variable, 'number').should == nil
25
+ child.lookup(:global_variable, '$number').should == 20
26
+ end
27
+
28
+ it 'Create a scope with default Ruby constants and methods' do
29
+ scope = Rlint::Definition.new(nil, :lazy => true, :kernel => true)
30
+ found = scope.lookup(:constant, 'Kernel')
31
+
32
+ found.class.should == Rlint::Definition
33
+
34
+ method = found.lookup(:method, 'warn')
35
+
36
+ method.class.should == Rlint::Definition
37
+
38
+ method.token.class.should == Rlint::Token::MethodDefinitionToken
39
+ method.token.name.should == 'warn'
40
+
41
+ method.token.parameters.value.class.should == Array
42
+ method.token.parameters.value.length.should == 1
43
+
44
+ param = method.token.parameters.value[0]
45
+
46
+ param.class.should == Rlint::Token::VariableToken
47
+ param.name.should == ''
48
+ param.type.should == :local_variable
49
+ end
50
+
51
+ it 'Lazy import the Time class' do
52
+ scope = Rlint::Definition.new
53
+
54
+ scope.lookup(:constant, 'Time').nil?.should == true
55
+
56
+ scope = Rlint::Definition.new(nil, :lazy => true)
57
+
58
+ scope.lookup(:constant, 'Time').class.should == Rlint::Definition
59
+ end
60
+
61
+ it 'Lazy import the Encoding class and a child constant' do
62
+ scope = Rlint::Definition.new(nil, :lazy => true)
63
+ enc = scope.lookup(:constant, 'Encoding')
64
+
65
+ enc.class.should == Rlint::Definition
66
+
67
+ enc.lookup(:constant, 'BINARY').class.should == Rlint::Definition
68
+ end
69
+
70
+ it 'Lazy import Rlint::Definition' do
71
+ scope = Rlint::Definition.new(nil, :lazy => true)
72
+
73
+ rlint = scope.lookup(:constant, 'Rlint')
74
+
75
+ rlint.class.should == Rlint::Definition
76
+
77
+ rlint_scope = rlint.lookup(:constant, 'Definition')
78
+
79
+ rlint_scope.class.should == Rlint::Definition
80
+
81
+ rlint_scope.lookup(:constant, 'LOOKUP_PARENT').class.should == Rlint::Definition
82
+ end
83
+
84
+ it 'Create a scope with multiple parent scopes' do
85
+ scope_1 = Rlint::Definition.new
86
+ scope_2 = Rlint::Definition.new
87
+
88
+ scope_1.add(:method, 'method_1', true)
89
+ scope_2.add(:method, 'method_2', true)
90
+
91
+ scope_3 = Rlint::Definition.new([scope_1, scope_2])
92
+
93
+ scope_3.lookup(:method, 'method_1').should == true
94
+ scope_3.lookup(:method, 'method_2').should == true
95
+ end
96
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../../../helper', __FILE__)
2
+
3
+ describe 'Rlint::Formatter::Text' do
4
+ it 'Format a report in plain text' do
5
+ report_a = Rlint::Report.new('a.rb')
6
+ report_b = Rlint::Report.new('b.rb')
7
+ formatter = Rlint::Formatter::Text.new
8
+
9
+ report_a.add(:info, 'test info', 2, 1)
10
+
11
+ report_b.add(:error, 'test error', 1, 1)
12
+ report_b.add(:info, 'test info b.rb', 3, 1)
13
+
14
+ output_a = 'a.rb: info: line 2, column 1: test info'
15
+ output_b = "b.rb: error: line 1, column 1: test error\n" \
16
+ 'b.rb: info: line 3, column 1: test info b.rb'
17
+
18
+ formatter.format(report_a).should == output_a
19
+ formatter.format(report_b).should == output_b
20
+ end
21
+ end
@@ -0,0 +1,452 @@
1
+ require File.expand_path('../../helper', __FILE__)
2
+
3
+ describe 'Rlint::Iterator' do
4
+ it 'Iterate over a simple AST' do
5
+ code = <<-CODE
6
+ number = 10
7
+ number
8
+ CODE
9
+
10
+ tokens = Rlint::Parser.new(code).parse
11
+ iterator = Rlint::Iterator.new
12
+
13
+ callback = Class.new(Rlint::Callback) do
14
+ attr_reader :assigned
15
+ attr_reader :referenced
16
+
17
+ def on_assignment(token)
18
+ @assigned = true
19
+ end
20
+
21
+ def on_local_variable(token)
22
+ @referenced = true
23
+ end
24
+ end
25
+
26
+ iterator.bind(callback)
27
+
28
+ iterator.run(tokens)
29
+
30
+ iterator.callbacks[0].assigned.should == true
31
+ iterator.callbacks[0].referenced.should == true
32
+ end
33
+
34
+ it 'Iterate over a multi dimensional AST' do
35
+ code = <<-CODE
36
+ class Foo
37
+ def initialize
38
+ @number = 10
39
+ end
40
+ end
41
+ CODE
42
+
43
+ tokens = Rlint::Parser.new(code).parse
44
+ iterator = Rlint::Iterator.new
45
+ callback = Class.new(Rlint::Callback) do
46
+ attr_reader :class_name
47
+ attr_reader :method_name
48
+ attr_reader :assigned
49
+
50
+ def on_class(token)
51
+ @class_name = token.name[0]
52
+ end
53
+
54
+ def on_method_definition(token)
55
+ @method_name = token.name
56
+ end
57
+
58
+ def on_assignment(token)
59
+ @assigned = true
60
+ end
61
+ end
62
+
63
+ iterator.bind(callback)
64
+
65
+ iterator.run(tokens)
66
+
67
+ iterator.callbacks[0].class_name.should == 'Foo'
68
+ iterator.callbacks[0].method_name.should == 'initialize'
69
+ iterator.callbacks[0].assigned.should == true
70
+ end
71
+
72
+ it 'Call an event after iterating over a node' do
73
+ code = <<-CODE
74
+ def some_method
75
+ return 10
76
+ end
77
+ CODE
78
+
79
+ tokens = Rlint::Parser.new(code).parse
80
+ iterator = Rlint::Iterator.new
81
+ callback = Class.new(Rlint::Callback) do
82
+ attr_reader :before
83
+ attr_reader :after
84
+
85
+ def on_method_definition(token)
86
+ @before = token.name
87
+ end
88
+
89
+ def after_method_definition(token)
90
+ @after = token.name
91
+ end
92
+ end
93
+
94
+ iterator.bind(callback)
95
+ iterator.run(tokens)
96
+
97
+ iterator.callbacks[0].before.should == 'some_method'
98
+ iterator.callbacks[0].after.should == 'some_method'
99
+ end
100
+
101
+ it 'Iterate over a begin/rescue statement' do
102
+ code = <<-CODE
103
+ begin
104
+ raise
105
+ rescue RuntimeError => e
106
+ puts 'runtime error!'
107
+ end
108
+ CODE
109
+
110
+ tokens = Rlint::Parser.new(code).parse
111
+ iterator = Rlint::Iterator.new
112
+ callback = Class.new(Rlint::Callback) do
113
+ attr_reader :method_names
114
+ attr_reader :exception
115
+ attr_reader :exception_var
116
+
117
+ def initialize(*args)
118
+ super
119
+
120
+ @method_names = []
121
+ end
122
+
123
+ def on_method(token)
124
+ @method_names << token.name
125
+ end
126
+
127
+ def on_constant(token)
128
+ @exception = token.name
129
+ end
130
+
131
+ def on_identifier(token)
132
+ @exception_var = token.name
133
+ end
134
+ end
135
+
136
+ iterator.bind(callback)
137
+ iterator.run(tokens)
138
+
139
+ obj = iterator.callbacks[0]
140
+
141
+ obj.method_names.should == ['raise', 'puts']
142
+ obj.exception.should == 'RuntimeError'
143
+ obj.exception_var.should == 'e'
144
+ end
145
+
146
+ it 'Iterate over an if statement' do
147
+ code = <<-CODE
148
+ a, b, c, d, e, f = [1, 2, 3, 4, 5, 6]
149
+
150
+ if a == b
151
+ first
152
+ elsif c == d
153
+ second
154
+ elsif e == f
155
+ third
156
+ else
157
+ fourth
158
+ end
159
+ CODE
160
+
161
+ tokens = Rlint::Parser.new(code).parse
162
+ iterator = Rlint::Iterator.new
163
+ callback = Class.new(Rlint::Callback) do
164
+ attr_reader :variables
165
+ attr_reader :methods
166
+
167
+ def initialize(*args)
168
+ super
169
+
170
+ @variables = []
171
+ @methods = []
172
+ end
173
+
174
+ def on_local_variable(token)
175
+ @variables << token.name
176
+ end
177
+
178
+ def on_method(token)
179
+ @methods << token.name
180
+ end
181
+ end
182
+
183
+ iterator.bind(callback)
184
+ iterator.run(tokens)
185
+
186
+ obj = iterator.callbacks[0]
187
+
188
+ obj.variables.should == %w{a b c d e f}
189
+ obj.methods.should == %w{first second third fourth}
190
+ end
191
+
192
+ it 'Iterate over a case statement' do
193
+ code = <<-CODE
194
+ case number
195
+ when 10
196
+ first
197
+ when 20
198
+ second
199
+ when 30
200
+ third
201
+ else
202
+ fourth
203
+ end
204
+ CODE
205
+
206
+ tokens = Rlint::Parser.new(code).parse
207
+ iterator = Rlint::Iterator.new
208
+ callback = Class.new(Rlint::Callback) do
209
+ attr_reader :methods
210
+ attr_reader :numbers
211
+
212
+ def initialize(*args)
213
+ super
214
+
215
+ @methods = []
216
+ @numbers = []
217
+ end
218
+
219
+ def on_method(token)
220
+ @methods << token.name
221
+ end
222
+
223
+ def on_integer(token)
224
+ @numbers << token.value
225
+ end
226
+ end
227
+
228
+ iterator.bind(callback)
229
+ iterator.run(tokens)
230
+
231
+ obj = iterator.callbacks[0]
232
+
233
+ obj.numbers.should == %w{10 20 30}
234
+ obj.methods.should == %w{number first second third fourth}
235
+ end
236
+
237
+ it 'Iterate over a for loop' do
238
+ code = <<-CODE
239
+ for key, value in {:name => 'Ruby'}
240
+ puts key
241
+ end
242
+ CODE
243
+
244
+ tokens = Rlint::Parser.new(code).parse
245
+ iterator = Rlint::Iterator.new
246
+ callback = Class.new(Rlint::Callback) do
247
+ attr_reader :symbol
248
+ attr_reader :string
249
+ attr_reader :names
250
+
251
+ def initialize(*args)
252
+ super
253
+
254
+ @names = []
255
+ end
256
+
257
+ def on_local_variable(token)
258
+ @names << token.name
259
+ end
260
+
261
+ def on_identifier(token)
262
+ @names << token.name
263
+ end
264
+
265
+ def on_symbol(token)
266
+ @symbol = token.name
267
+ end
268
+
269
+ def on_string(token)
270
+ @string = token.value
271
+ end
272
+ end
273
+
274
+ iterator.bind(callback)
275
+ iterator.run(tokens)
276
+
277
+ obj = iterator.callbacks[0]
278
+
279
+ obj.symbol.should == 'name'
280
+ obj.string.should == 'Ruby'
281
+ obj.names.should == %w{key value key}
282
+ end
283
+
284
+ it 'Iterate over a while statement' do
285
+ code = <<-CODE
286
+ while foo == 10
287
+ puts 20
288
+ end
289
+ CODE
290
+
291
+ tokens = Rlint::Parser.new(code).parse
292
+ iterator = Rlint::Iterator.new
293
+ callback = Class.new(Rlint::Callback) do
294
+ attr_reader :numbers
295
+ attr_reader :methods
296
+
297
+ def initialize(*args)
298
+ super
299
+
300
+ @numbers = []
301
+ @methods = []
302
+ end
303
+
304
+ def on_identifier(token)
305
+ @methods << token.name
306
+ end
307
+
308
+ def on_method(token)
309
+ @methods << token.name
310
+ end
311
+
312
+ def on_integer(token)
313
+ @numbers << token.value
314
+ end
315
+ end
316
+
317
+ iterator.bind(callback)
318
+ iterator.run(tokens)
319
+
320
+ obj = iterator.callbacks[0]
321
+
322
+ obj.methods.should == %w{foo puts}
323
+ obj.numbers.should == %w{10 20}
324
+ end
325
+
326
+ it 'Iterate over a method definition' do
327
+ code = <<-CODE
328
+ def foobar(required, optional = 10, *rest, more, &block)
329
+ return 20
330
+ end
331
+ CODE
332
+
333
+ tokens = Rlint::Parser.new(code).parse
334
+ iterator = Rlint::Iterator.new
335
+ callback = Class.new(Rlint::Callback) do
336
+ attr_reader :variables
337
+ attr_reader :numbers
338
+
339
+ def initialize(*args)
340
+ super
341
+
342
+ @variables = []
343
+ @numbers = []
344
+ end
345
+
346
+ def on_local_variable(token)
347
+ @variables << token.name
348
+ end
349
+
350
+ def on_integer(token)
351
+ @numbers << token.value
352
+ end
353
+ end
354
+
355
+ iterator.bind(callback)
356
+ iterator.run(tokens)
357
+
358
+ obj = iterator.callbacks[0]
359
+
360
+ obj.numbers.should == %w{10 20}
361
+ obj.variables.should == %w{required optional rest more block}
362
+ end
363
+
364
+ it 'Iterate over a method call' do
365
+ tokens = Rlint::Parser.new('puts "Foo", "Bar"').parse
366
+ iterator = Rlint::Iterator.new
367
+ callback = Class.new(Rlint::Callback) do
368
+ attr_reader :params
369
+
370
+ def initialize(*args)
371
+ @params = []
372
+ end
373
+
374
+ def on_string(token)
375
+ @params << token.value
376
+ end
377
+ end
378
+
379
+ iterator.bind(callback)
380
+ iterator.run(tokens)
381
+
382
+ obj = iterator.callbacks[0]
383
+
384
+ obj.params.should == %w{Foo Bar}
385
+ end
386
+
387
+ it 'Call an event before and after iterating over all nodes' do
388
+ code = <<-CODE
389
+ class Person
390
+ def initialize
391
+ @name = 'Matz'
392
+ end
393
+ end
394
+ CODE
395
+
396
+ tokens = Rlint::Parser.new(code).parse
397
+ iterator = Rlint::Iterator.new
398
+ callback = Class.new(Rlint::Callback) do
399
+ attr_reader :start
400
+ attr_reader :finish
401
+
402
+ def initialize(*args)
403
+ super
404
+
405
+ @start = 0
406
+ @finish = 0
407
+ end
408
+
409
+ def on_start
410
+ @start += 1
411
+ end
412
+
413
+ def on_finish
414
+ @finish += 1
415
+ end
416
+ end
417
+
418
+ iterator.bind(callback)
419
+ iterator.run(tokens)
420
+
421
+ iterator.callbacks[0].start.should == 1
422
+ iterator.callbacks[0].finish.should == 1
423
+ end
424
+
425
+ it 'Share data between callback classes' do
426
+ code = <<-CODE
427
+ def example_method
428
+ return 10
429
+ end
430
+ CODE
431
+
432
+ tokens = Rlint::Parser.new(code).parse
433
+ iterator = Rlint::Iterator.new
434
+
435
+ setter = Class.new(Rlint::Callback) do
436
+ def on_method_definition(token)
437
+ @storage[:method_name] = token.name
438
+ end
439
+ end
440
+
441
+ getter = Class.new(Rlint::Callback) do
442
+ attr_reader :storage
443
+ end
444
+
445
+ iterator.bind(setter)
446
+ iterator.bind(getter)
447
+ iterator.run(tokens)
448
+
449
+ iterator.callbacks[1].storage.class.should == Hash
450
+ iterator.callbacks[1].storage[:method_name].should == 'example_method'
451
+ end
452
+ end