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,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