ruby-lint 0.0.1a

Sign up to get free protection for your applications and to get access to all the features.
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,207 @@
1
+ require File.expand_path('../../../../helper', __FILE__)
2
+
3
+ describe 'Rlint::Analyze::Definitions: modules' do
4
+ it 'Define a module in the global scope' do
5
+ code = <<-CODE
6
+ module Example
7
+ def example_method
8
+
9
+ end
10
+ end
11
+ CODE
12
+
13
+ tokens = Rlint::Parser.new(code).parse
14
+ iterator = Rlint::Iterator.new
15
+
16
+ iterator.bind(Rlint::Analyze::Definitions)
17
+ iterator.run(tokens)
18
+
19
+ scope = iterator.storage[:scope]
20
+ const = scope.lookup(:constant, 'Example')
21
+
22
+ const.class.should == Rlint::Definition
23
+
24
+ const.token.class.should == Rlint::Token::Token
25
+ const.token.type.should == :module
26
+ const.token.name.should == ['Example']
27
+
28
+ scope.lookup(:instance_method, 'example_method').nil?.should == true
29
+
30
+ method = const.lookup(:instance_method, 'example_method')
31
+
32
+ method.class.should == Rlint::Definition
33
+
34
+ method.token.class.should == Rlint::Token::MethodDefinitionToken
35
+ method.token.name.should == 'example_method'
36
+ end
37
+
38
+ it 'Define a class method in a module' do
39
+ code = <<-CODE
40
+ module Example
41
+ def self.example_method
42
+
43
+ end
44
+ end
45
+ CODE
46
+
47
+ tokens = Rlint::Parser.new(code).parse
48
+ iterator = Rlint::Iterator.new
49
+
50
+ iterator.bind(Rlint::Analyze::Definitions)
51
+ iterator.run(tokens)
52
+
53
+ scope = iterator.storage[:scope]
54
+ const = scope.lookup(:constant, 'Example')
55
+
56
+ const.class.should == Rlint::Definition
57
+
58
+ const.token.class.should == Rlint::Token::Token
59
+ const.token.type.should == :module
60
+ const.token.name.should == ['Example']
61
+
62
+ scope.lookup(:method, 'example_method').nil?.should == true
63
+
64
+ method = const.lookup(:method, 'example_method')
65
+
66
+ method.class.should == Rlint::Definition
67
+
68
+ method.token.class.should == Rlint::Token::MethodDefinitionToken
69
+ method.token.name.should == 'example_method'
70
+ end
71
+
72
+ it 'Include a module into a class' do
73
+ code = <<-CODE
74
+ module A
75
+ def example_method
76
+
77
+ end
78
+ end
79
+
80
+ class B
81
+ include A
82
+ end
83
+ CODE
84
+
85
+ tokens = Rlint::Parser.new(code).parse
86
+ iterator = Rlint::Iterator.new
87
+
88
+ iterator.bind(Rlint::Analyze::Definitions)
89
+ iterator.run(tokens)
90
+
91
+ scope = iterator.storage[:scope]
92
+ const = scope.lookup(:constant, 'B')
93
+
94
+ const.class.should == Rlint::Definition
95
+
96
+ const.lookup(:method, 'example_method').nil?.should == true
97
+
98
+ method = const.lookup(:instance_method, 'example_method')
99
+
100
+ method.class.should == Rlint::Definition
101
+
102
+ method.token.class.should == Rlint::Token::MethodDefinitionToken
103
+ method.token.name.should == 'example_method'
104
+ end
105
+
106
+ it 'Extend a class using a module' do
107
+ code = <<-CODE
108
+ module A
109
+ def example_method
110
+
111
+ end
112
+ end
113
+
114
+ class B
115
+ extend A
116
+ end
117
+ CODE
118
+
119
+ tokens = Rlint::Parser.new(code).parse
120
+ iterator = Rlint::Iterator.new
121
+
122
+ iterator.bind(Rlint::Analyze::Definitions)
123
+ iterator.run(tokens)
124
+
125
+ scope = iterator.storage[:scope]
126
+ const = scope.lookup(:constant, 'B')
127
+
128
+ const.class.should == Rlint::Definition
129
+
130
+ const.lookup(:instance_method, 'example_method').nil?.should == true
131
+
132
+ method = const.lookup(:method, 'example_method')
133
+
134
+ method.class.should == Rlint::Definition
135
+
136
+ method.token.class.should == Rlint::Token::MethodDefinitionToken
137
+ method.token.name.should == 'example_method'
138
+ end
139
+
140
+ it 'Include a module using a constant path' do
141
+ code = <<-CODE
142
+ module A
143
+ module B
144
+ def example_method
145
+
146
+ end
147
+ end
148
+ end
149
+
150
+ class C
151
+ include A::B
152
+ end
153
+ CODE
154
+
155
+ tokens = Rlint::Parser.new(code).parse
156
+ iterator = Rlint::Iterator.new
157
+
158
+ iterator.bind(Rlint::Analyze::Definitions)
159
+ iterator.run(tokens)
160
+
161
+ scope = iterator.storage[:scope]
162
+ const = scope.lookup(:constant, 'C')
163
+
164
+ const.class.should == Rlint::Definition
165
+
166
+ const.lookup(:method, 'example_method').nil?.should == true
167
+
168
+ method = const.lookup(:instance_method, 'example_method')
169
+
170
+ method.class.should == Rlint::Definition
171
+
172
+ method.token.class.should == Rlint::Token::MethodDefinitionToken
173
+ method.token.name.should == 'example_method'
174
+ end
175
+
176
+ it 'Define a module in an existing module' do
177
+ code = <<-CODE
178
+ module Rlint
179
+ module Derp
180
+
181
+ end
182
+ end
183
+ CODE
184
+
185
+ tokens = Rlint::Parser.new(code).parse
186
+ iterator = Rlint::Iterator.new
187
+
188
+ iterator.bind(Rlint::Analyze::Definitions)
189
+ iterator.run(tokens)
190
+
191
+ scope = iterator.storage[:scope]
192
+
193
+ scope.lookup(:constant, 'Derp').nil?.should == true
194
+
195
+ scope.lookup(:constant, 'Rlint').class.should == Rlint::Definition
196
+
197
+ scope.lookup(:constant, 'Rlint') \
198
+ .lookup(:constant, 'Derp') \
199
+ .class \
200
+ .should == Rlint::Definition
201
+
202
+ rlint = scope.lookup(:constant, 'Rlint')
203
+ derp = rlint.lookup(:constant, 'Derp')
204
+
205
+ derp.parent.length.should == 1
206
+ end
207
+ end
@@ -0,0 +1,103 @@
1
+ require File.expand_path('../../../../helper', __FILE__)
2
+
3
+ describe 'Rlint::Analyze::Definitions: variables' do
4
+ it 'Build a list of variables defined in the global scope' do
5
+ code = <<-CODE
6
+ number = 10
7
+ @number = 10
8
+ @@number = 10
9
+ $number = 10
10
+ NUMBER = 10
11
+ CODE
12
+
13
+ tokens = Rlint::Parser.new(code).parse
14
+ iterator = Rlint::Iterator.new
15
+
16
+ iterator.bind(Rlint::Analyze::Definitions)
17
+ iterator.run(tokens)
18
+
19
+ scope = iterator.storage[:scope]
20
+
21
+ scope.class.should == Rlint::Definition
22
+
23
+ lvar = scope.lookup(:local_variable, 'number')
24
+ ivar = scope.lookup(:instance_variable, '@number')
25
+ cvar = scope.lookup(:class_variable, '@@number')
26
+ gvar = scope.lookup(:global_variable, '$number')
27
+ const = scope.lookup(:constant, 'NUMBER')
28
+
29
+ lvar.class.should == Rlint::Definition
30
+ lvar.token.class.should == Rlint::Token::AssignmentToken
31
+ lvar.token.name.should == 'number'
32
+ lvar.token.type.should == :local_variable
33
+ lvar.token.value.class.should == Rlint::Token::Token
34
+ lvar.token.value.type.should == :integer
35
+ lvar.token.value.value.should == '10'
36
+
37
+ ivar.class.should == Rlint::Definition
38
+ ivar.token.class.should == Rlint::Token::AssignmentToken
39
+ ivar.token.name.should == '@number'
40
+ ivar.token.type.should == :instance_variable
41
+ ivar.token.value.class.should == Rlint::Token::Token
42
+ ivar.token.value.type.should == :integer
43
+ ivar.token.value.value.should == '10'
44
+
45
+ cvar.class.should == Rlint::Definition
46
+ cvar.token.class.should == Rlint::Token::AssignmentToken
47
+ cvar.token.name.should == '@@number'
48
+ cvar.token.type.should == :class_variable
49
+ cvar.token.value.class.should == Rlint::Token::Token
50
+ cvar.token.value.type.should == :integer
51
+ cvar.token.value.value.should == '10'
52
+
53
+ gvar.class.should == Rlint::Definition
54
+ gvar.token.class.should == Rlint::Token::AssignmentToken
55
+ gvar.token.name.should == '$number'
56
+ gvar.token.type.should == :global_variable
57
+ gvar.token.value.class.should == Rlint::Token::Token
58
+ gvar.token.value.type.should == :integer
59
+ gvar.token.value.value.should == '10'
60
+
61
+ const.class.should == Rlint::Definition
62
+ const.token.class.should == Rlint::Token::AssignmentToken
63
+ const.token.name.should == 'NUMBER'
64
+ const.token.type.should == :constant
65
+ const.token.value.class.should == Rlint::Token::Token
66
+ const.token.value.type.should == :integer
67
+ const.token.value.value.should == '10'
68
+ end
69
+
70
+ it 'Build a list of variables defined inside a method' do
71
+ code = <<-CODE
72
+ def example_method
73
+ number = 10
74
+ end
75
+ CODE
76
+
77
+ tokens = Rlint::Parser.new(code).parse
78
+ iterator = Rlint::Iterator.new
79
+
80
+ iterator.bind(Rlint::Analyze::Definitions)
81
+ iterator.run(tokens)
82
+
83
+ scope = iterator.storage[:scope]
84
+
85
+ scope.lookup(:local_variable, 'number').nil?.should == true
86
+
87
+ method = scope.lookup(:instance_method, 'example_method')
88
+
89
+ method.class.should == Rlint::Definition
90
+
91
+ method.token.class.should == Rlint::Token::MethodDefinitionToken
92
+ method.token.name.should == 'example_method'
93
+ method.token.value.nil?.should == true
94
+
95
+ variable = method.lookup(:local_variable, 'number')
96
+
97
+ variable.class.should == Rlint::Definition
98
+ variable.token.name.should == 'number'
99
+ variable.token.value.class.should == Rlint::Token::Token
100
+ variable.token.value.type.should == :integer
101
+ variable.token.value.value.should == '10'
102
+ end
103
+ end
@@ -0,0 +1,177 @@
1
+ require File.expand_path('../../../helper', __FILE__)
2
+
3
+ describe 'Rlint::Analyze::MethodValidation' do
4
+ it 'Calling undefined methods' do
5
+ code = <<-CODE
6
+ def defined_method; end
7
+
8
+ undefined_method
9
+ defined_method
10
+ CODE
11
+
12
+ tokens = Rlint::Parser.new(code).parse
13
+ report = Rlint::Report.new
14
+ iterator = Rlint::Iterator.new(report)
15
+
16
+ iterator.bind(Rlint::Analyze::Definitions)
17
+ iterator.bind(Rlint::Analyze::MethodValidation)
18
+ iterator.bind(Rlint::Analyze::UndefinedVariables)
19
+ iterator.run(tokens)
20
+
21
+ report.messages[:error].class.should == Array
22
+ report.messages[:error].length.should == 1
23
+
24
+ error = report.messages[:error][0]
25
+
26
+ error[:message].should == 'undefined local variable or method ' \
27
+ 'undefined_method'
28
+
29
+ error[:line].should == 3
30
+ error[:column].should == 0
31
+ end
32
+
33
+ it 'Calling non existing methods on constants' do
34
+ code = <<-CODE
35
+ String.new
36
+ String.newx
37
+ Foobar.new
38
+ CODE
39
+
40
+ tokens = Rlint::Parser.new(code).parse
41
+ report = Rlint::Report.new
42
+ iterator = Rlint::Iterator.new(report)
43
+
44
+ iterator.bind(Rlint::Analyze::Definitions)
45
+ iterator.bind(Rlint::Analyze::MethodValidation)
46
+ iterator.bind(Rlint::Analyze::UndefinedVariables)
47
+ iterator.run(tokens)
48
+
49
+ report.messages[:error].class.should == Array
50
+ report.messages[:error].length.should == 2
51
+
52
+ errors = report.messages[:error]
53
+
54
+ errors[0][:message].should == 'undefined class method newx'
55
+ errors[0][:line].should == 2
56
+ errors[0][:column].should == 7
57
+
58
+ errors[1][:message].should == 'undefined constant Foobar'
59
+ errors[1][:line].should == 3
60
+ errors[1][:column].should == 0
61
+ end
62
+
63
+ it 'Calling non existing methods on variables and values directly' do
64
+ code = <<-CODE
65
+ name = 'Ruby'
66
+
67
+ name.upcase
68
+ name.upcasexx
69
+ foo.upcase
70
+
71
+ ''.downcase
72
+ ''.downcasex
73
+ CODE
74
+
75
+ tokens = Rlint::Parser.new(code).parse
76
+ report = Rlint::Report.new
77
+ iterator = Rlint::Iterator.new(report)
78
+
79
+ iterator.bind(Rlint::Analyze::Definitions)
80
+ iterator.bind(Rlint::Analyze::MethodValidation)
81
+ iterator.bind(Rlint::Analyze::UndefinedVariables)
82
+ iterator.run(tokens)
83
+
84
+ report.messages[:error].class.should == Array
85
+ report.messages[:error].length.should == 3
86
+
87
+ errors = report.messages[:error]
88
+
89
+ errors[0][:message].should == 'undefined instance method upcasexx'
90
+ errors[0][:line].should == 4
91
+ errors[0][:column].should == 5
92
+
93
+ errors[1][:message].should == 'undefined instance method downcasex'
94
+ errors[1][:line].should == 8
95
+ errors[1][:column].should == 3
96
+
97
+ errors[2][:message].should == 'undefined local variable or method foo'
98
+ errors[2][:line].should == 5
99
+ errors[2][:column].should == 0
100
+ end
101
+
102
+ it 'Method parameters should be added as local variables' do
103
+ code = <<-CODE
104
+ def uppercase(name = 'Ruby', *args, &block)
105
+ name.upcase
106
+ namex.upcase
107
+ args.flatten
108
+ block.call
109
+ end
110
+ CODE
111
+
112
+ tokens = Rlint::Parser.new(code).parse
113
+ report = Rlint::Report.new
114
+ iterator = Rlint::Iterator.new(report)
115
+
116
+ iterator.bind(Rlint::Analyze::Definitions)
117
+ iterator.bind(Rlint::Analyze::MethodValidation)
118
+ iterator.bind(Rlint::Analyze::UndefinedVariables)
119
+ iterator.run(tokens)
120
+
121
+ report.messages[:error].class.should == Array
122
+ report.messages[:error].length.should == 1
123
+
124
+ error = report.messages[:error][0]
125
+
126
+ error[:message].should == 'undefined local variable or method namex'
127
+ error[:line].should == 3
128
+ error[:column].should == 2
129
+ end
130
+
131
+ it 'Calling methods on a constant path' do
132
+ code = <<-CODE
133
+ A::B.invalid_method
134
+
135
+ module A; end
136
+
137
+ A::B.invalid_method
138
+
139
+ module A
140
+ module B
141
+ def self.valid_method
142
+
143
+ end
144
+ end
145
+ end
146
+
147
+ A::B.invalid_method
148
+ A::B.valid_method
149
+ CODE
150
+
151
+ tokens = Rlint::Parser.new(code).parse
152
+ report = Rlint::Report.new
153
+ iterator = Rlint::Iterator.new(report)
154
+
155
+ iterator.bind(Rlint::Analyze::Definitions)
156
+ iterator.bind(Rlint::Analyze::UndefinedVariables)
157
+ iterator.bind(Rlint::Analyze::MethodValidation)
158
+ iterator.run(tokens)
159
+
160
+ report.messages[:error].class.should == Array
161
+ report.messages[:error].length.should == 3
162
+
163
+ errors = report.messages[:error]
164
+
165
+ errors[0][:message].should == 'undefined constant A'
166
+ errors[0][:line].should == 1
167
+ errors[0][:column].should == 0
168
+
169
+ errors[1][:message].should == 'undefined constant A::B'
170
+ errors[1][:line].should == 5
171
+ errors[1][:column].should == 0
172
+
173
+ errors[2][:message].should == 'undefined class method invalid_method'
174
+ errors[2][:line].should == 15
175
+ errors[2][:column].should == 5
176
+ end
177
+ end