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,244 @@
1
+ module Rlint
2
+ module Analyze
3
+ ##
4
+ # {Rlint::Analyze::Definitions} is a callback class that is used for
5
+ # building a list of all the definitions (variables, methods, etc) of a
6
+ # block of Ruby code.
7
+ #
8
+ # The resulting instance of {Rlint::Definition} is stored in the `@storage`
9
+ # instance variable under the key `:scope`. This makes it possible for
10
+ # other callback classes to access this data easily.
11
+ #
12
+ class Definitions < Rlint::Callback
13
+ include Helper::Scoping
14
+
15
+ ##
16
+ # A short description of this class.
17
+ #
18
+ # @return [String]
19
+ #
20
+ DESCRIPTION = 'Builds a list of definitions, always enabled.'
21
+
22
+ ##
23
+ # Array containing the key names of the variables that should be exported
24
+ # out of a method definition.
25
+ #
26
+ # @return [Array]
27
+ #
28
+ EXPORT_VARIABLES = [:instance_variable, :class_variable, :constant]
29
+
30
+ ##
31
+ # Hash containing the scoping data to copy over when extending a class
32
+ # using a module.
33
+ #
34
+ # @return [Hash]
35
+ #
36
+ INCLUDE_SYMBOLS = {
37
+ 'include' => {
38
+ :constant => :constant,
39
+ :instance_method => :instance_method
40
+ },
41
+ 'extend' => {
42
+ :constant => :constant,
43
+ :instance_method => :method
44
+ }
45
+ }
46
+
47
+ ##
48
+ # Called when a value is assigned to a variable.
49
+ #
50
+ # @param [Rlint::Token::AssignmentToken] token
51
+ #
52
+ def on_assignment(token)
53
+ if token.type == :global_variable
54
+ variable_scope = @storage[:scope]
55
+ else
56
+ variable_scope = scope
57
+ end
58
+
59
+ # Assignment using a constant path. In this case each path segment
60
+ # should exist (with the exception of the last one) for the assignment
61
+ # to take place.
62
+ if token.name.is_a?(Array)
63
+ name_scope = scope
64
+
65
+ token.name[0..-2].each do |segment|
66
+ name_scope = name_scope.lookup(:constant, segment)
67
+ end
68
+
69
+ if name_scope
70
+ variable_scope = name_scope
71
+ else
72
+ return
73
+ end
74
+
75
+ name = token.name[-1]
76
+ type = :constant
77
+ else
78
+ name = token.name
79
+ type = token.type
80
+ end
81
+
82
+ variable_scope.add(
83
+ type,
84
+ name,
85
+ Definition.new(nil, :token => token, :reset => false)
86
+ )
87
+ end
88
+
89
+ ##
90
+ # Called when a new method is defined.
91
+ #
92
+ # @param [Rlint::Token::MethodDefinitionToken] token
93
+ #
94
+ def on_method_definition(token)
95
+ type = :instance_method
96
+ new_scope = Definition.new(scope, :token => token)
97
+ target = scope
98
+
99
+ token.parameters.each do |param|
100
+ new_scope.add(
101
+ param.type,
102
+ param.name,
103
+ Definition.new(nil, :token => param, :reset => false)
104
+ )
105
+ end
106
+
107
+ # The method is a class method.
108
+ if token.receiver
109
+ type = :method
110
+
111
+ if token.receiver.name != 'self' \
112
+ and token.receiver.name != @namespace[-1]
113
+ found = target.lookup(token.receiver.type, token.receiver.name)
114
+ target = found if found
115
+ end
116
+ end
117
+
118
+ target.add(type, token.name, new_scope)
119
+
120
+ @scopes << new_scope
121
+ end
122
+
123
+ ##
124
+ # Called after a method definition has been processed.
125
+ #
126
+ # @see Rlint::Analyze::Definitions#on_method_definition
127
+ #
128
+ def after_method_definition(token)
129
+ # TODO: exporting these variables should only be done if the method is
130
+ # actually called.
131
+ last_scope = @scopes.pop
132
+
133
+ EXPORT_VARIABLES.each do |key|
134
+ scope.symbols[key] = scope.symbols[key].merge(
135
+ last_scope.symbols[key]
136
+ )
137
+ end
138
+ end
139
+
140
+ ##
141
+ # Called when a class definition is found.
142
+ #
143
+ # @param [Rlint::Token::ClassToken] token
144
+ #
145
+ def on_class(token)
146
+ name = token.name.join('::')
147
+ @namespace << name
148
+ existing = scope.lookup(:constant, name)
149
+
150
+ # If a class has already been defined the scope should not be
151
+ # overwritten.
152
+ if existing
153
+ existing.parent << scope
154
+ @scopes << existing
155
+
156
+ return
157
+ end
158
+
159
+ parent = scope.lookup(:constant, token.parent.join('::'))
160
+ new_scope = Definition.new([parent, scope], :token => token)
161
+
162
+ scope.add(:constant, name, new_scope)
163
+
164
+ @scopes << new_scope
165
+ end
166
+
167
+ ##
168
+ # Called after a class definition was found and processed.
169
+ #
170
+ # @see Rlint::Analyze::Definitions#on_class
171
+ #
172
+ def after_class(token)
173
+ @scopes.pop
174
+ @namespace.pop
175
+ end
176
+
177
+ ##
178
+ # Called when a module is defined.
179
+ #
180
+ # @param [Rlint::Token::Token] token
181
+ #
182
+ def on_module(token)
183
+ name = token.name.join('::')
184
+ @namespace << name
185
+ existing = scope.lookup(:constant, name)
186
+
187
+ # If a module has already been defined the scope should not be
188
+ # overwritten.
189
+ if existing
190
+ existing.parent << scope
191
+ @scopes << existing
192
+
193
+ return
194
+ end
195
+
196
+ new_scope = Definition.new(scope, :token => token)
197
+
198
+ scope.add(:constant, name, new_scope)
199
+
200
+ @scopes << new_scope
201
+ end
202
+
203
+ ##
204
+ # Called after a module definition has been processed.
205
+ #
206
+ # @see Rlint::Analyze::Definitions#on_module
207
+ #
208
+ def after_module(token)
209
+ @scopes.pop
210
+ @namespace.pop
211
+ end
212
+
213
+ ##
214
+ # Called when a method call is found. This callback is used to extend
215
+ # classes using modules.
216
+ #
217
+ # @param [Rlint::Token::MethodToken] token
218
+ #
219
+ def on_method(token)
220
+ if INCLUDE_SYMBOLS.key?(token.name)
221
+ token.parameters.each do |param|
222
+ found = nil
223
+
224
+ # Extract the definition and scope to include.
225
+ if param.type == :constant_path
226
+ found = resolve_definition(param.name)
227
+ elsif param.type == :constant
228
+ found = scope.lookup(:constant, param.name)
229
+ end
230
+
231
+ next unless found
232
+
233
+ # Copy over all the constants and methods.
234
+ INCLUDE_SYMBOLS[token.name].each do |source, target|
235
+ found.symbols[source].each do |name, data|
236
+ scope.add(target, name, data)
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end # Definitions
243
+ end # Analyze
244
+ end # Rlint
@@ -0,0 +1,104 @@
1
+ module Rlint
2
+ module Analyze
3
+ ##
4
+ # {Rlint::Analyze::MethodValidation} is used to validate method calls.
5
+ # Errors are added for calling undefined methods, calling methods with
6
+ # invalid parameters and so on.
7
+ #
8
+ class MethodValidation < Rlint::Callback
9
+ include Helper::DefinitionResolver
10
+
11
+ ##
12
+ # A short description of this class.
13
+ #
14
+ # @return [String]
15
+ #
16
+ DESCRIPTION = 'Validates method calls and the specified parameters.'
17
+
18
+ ##
19
+ # Hash containing the various Ruby classes that are used to represent
20
+ # various types.
21
+ #
22
+ # @return [Hash]
23
+ #
24
+ TYPE_CLASSES = {
25
+ :string => 'String',
26
+ :integer => 'Fixnum', # Fixnum and Bignum share the same methods.
27
+ :float => 'Float',
28
+ :symbol => 'Symbol',
29
+ :array => 'Array',
30
+ :hash => 'Hash',
31
+ :brace_block => 'Proc',
32
+ :lambda => 'Proc',
33
+ :regexp => 'Regexp',
34
+ :range => 'Range'
35
+ }
36
+
37
+ ##
38
+ # Called when a method call is found.
39
+ #
40
+ # @param [Rlint::Token::MethodToken] token
41
+ #
42
+ def on_method(token)
43
+ # Method called on a receiver (e.g. `String.new`).
44
+ if token.receiver
45
+ receiver_name = token.receiver.name
46
+ receiver_scope = scope
47
+ receiver_type = token.receiver.type
48
+ method_type = :instance_method
49
+
50
+ if receiver_name.is_a?(Array)
51
+ return unless valid_constant_path?(token.receiver)
52
+
53
+ receiver_scope = resolve_definition(receiver_name)
54
+ end
55
+
56
+ # Method calls on variables such as `name.upcase`.
57
+ if token.receiver.is_a?(Token::VariableToken) \
58
+ and receiver_type != :constant \
59
+ and receiver_type != :constant_path
60
+ value = receiver_scope.lookup(receiver_type, receiver_name)
61
+
62
+ if !value.nil? and !value.token.value.nil?
63
+ value = value.token.value
64
+ receiver_type = TYPE_CLASSES[value.type]
65
+ end
66
+
67
+ # Extract the class from a method call.
68
+ if value.respond_to?(:receiver)
69
+ while value.respond_to?(:receiver)
70
+ value = value.receiver
71
+ end
72
+
73
+ receiver_type = value.name
74
+ end
75
+
76
+ # Methods called directly on a type such as `'name'.upcase`.
77
+ elsif TYPE_CLASSES[receiver_type]
78
+ receiver_type = TYPE_CLASSES[receiver_type]
79
+
80
+ # Everything else.
81
+ else
82
+ method_type = :method
83
+ receiver_type = receiver_name.is_a?(Array) \
84
+ ? receiver_name[-1] \
85
+ : receiver_name
86
+ end
87
+
88
+ # Retrieve the constant to check for the existence of the method.
89
+ found = receiver_scope.lookup(:constant, receiver_type)
90
+
91
+ if found and !definition_exists?(method_type, token, found)
92
+ if method_type == :instance_method
93
+ error = "undefined instance method #{token.name}"
94
+ else
95
+ error = "undefined class method #{token.name}"
96
+ end
97
+
98
+ error(error, token.line, token.column)
99
+ end
100
+ end
101
+ end
102
+ end # MethodValidation
103
+ end # Analyze
104
+ end # Rlint
@@ -0,0 +1,37 @@
1
+ module Rlint
2
+ module Analyze
3
+ ##
4
+ # {Rlint::Analyze::ShadowingVariables} is used to add warnings when block
5
+ # parameters shadow outer local variables.
6
+ #
7
+ class ShadowingVariables < Rlint::Callback
8
+ include Helper::DefinitionResolver
9
+
10
+ ##
11
+ # A short description of this class.
12
+ #
13
+ # @return [String]
14
+ #
15
+ DESCRIPTION = 'Checks for variables that shadow other variables.'
16
+
17
+ ##
18
+ # Called when a block is found. This callback is used to check if the
19
+ # parameters of the block shadow existing local variables defined in the
20
+ # outer scope.
21
+ #
22
+ # @param [Rlint::Token::BlockToken] token The token of the block.
23
+ #
24
+ def on_block(token)
25
+ token.parameters.each do |param|
26
+ if scope.lookup(param.type, param.name)
27
+ warning(
28
+ "shadowing outer local variable #{param.name}",
29
+ param.line,
30
+ param.column
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end # ShadowingVariables
36
+ end # Analyze
37
+ end # Rlint
@@ -0,0 +1,99 @@
1
+ module Rlint
2
+ module Analyze
3
+ ##
4
+ # {Rlint::Analyze::UndefinedVariables} is used to add errors for the use of
5
+ # undefined variables.
6
+ #
7
+ class UndefinedVariables < Rlint::Callback
8
+ include Helper::DefinitionResolver
9
+
10
+ ##
11
+ # A short description of this class.
12
+ #
13
+ # @return [String]
14
+ #
15
+ DESCRIPTION = 'Checks for the use of undefined variables.'
16
+
17
+ [
18
+ 'instance_variable',
19
+ 'class_variable',
20
+ 'global_variable',
21
+ 'constant'
22
+ ].each do |name|
23
+ readable = name.gsub('_', ' ')
24
+
25
+ define_method('on_' + name) do |token|
26
+ unless definition_exists?(token.type, token)
27
+ error(
28
+ "undefined #{readable} #{token.name}",
29
+ token.line,
30
+ token.column
31
+ )
32
+ end
33
+ end
34
+ end
35
+
36
+ ##
37
+ # Called when a variable assignment is found. Used to validate constant
38
+ # paths before assigning data to them.
39
+ #
40
+ # @param [Rlint::Token::AssignmentToken] token
41
+ #
42
+ def on_assignment(token)
43
+ on_constant_path(token) if token.name.is_a?(Array)
44
+ end
45
+
46
+ ##
47
+ # Called when a constant path is found.
48
+ #
49
+ # @param [Rlint::Token::VariableToken] token
50
+ #
51
+ def on_constant_path(token)
52
+ current = scope
53
+ segments = []
54
+
55
+ token.name.each do |segment|
56
+ segments << segment
57
+ found = current.lookup(:constant, segment)
58
+
59
+ if found and found.token.line < token.line
60
+ current = found
61
+ else
62
+ error(
63
+ "undefined constant #{segments.join('::')}",
64
+ token.line,
65
+ token.column
66
+ )
67
+
68
+ return
69
+ end
70
+ end
71
+ end
72
+
73
+ ##
74
+ # Called when a method call is found.
75
+ #
76
+ # @param [Rlint::Token::MethodToken] token
77
+ #
78
+ def on_method(token)
79
+ kernel_method = false
80
+ kernel = scope.lookup(:constant, 'Kernel')
81
+
82
+ if kernel.lookup(:method, token.name) \
83
+ or kernel.lookup(:instance_method, token.name)
84
+ kernel_method = true
85
+ end
86
+
87
+ if !token.receiver \
88
+ and !kernel_method \
89
+ and !definition_exists?(:instance_method, token)
90
+ error(
91
+ "undefined local variable or method #{token.name}",
92
+ token.line,
93
+ token.column
94
+ )
95
+ end
96
+ end
97
+ end # UndefinedVariables
98
+ end # Analyze
99
+ end # Rlint