ruby-lint 0.9.1 → 1.0.0.pre.preview1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CONTRIBUTING.md +9 -11
  4. data/Gemfile +6 -3
  5. data/MANIFEST +7 -1
  6. data/README.md +15 -24
  7. data/benchmark/bootup.rb +13 -0
  8. data/checksum/ruby-lint-0.9.1.gem.sha512 +1 -0
  9. data/doc/code_analysis.md +20 -0
  10. data/doc/css/common.css +1 -0
  11. data/doc/images/flow.png +0 -0
  12. data/lib/ruby-lint.rb +1 -3
  13. data/lib/ruby-lint/analysis/base.rb +12 -2
  14. data/lib/ruby-lint/analysis/undefined_variables.rb +3 -2
  15. data/lib/ruby-lint/analysis/unused_variables.rb +3 -2
  16. data/lib/ruby-lint/ast/node.rb +1 -1
  17. data/lib/ruby-lint/benchmark/average.rb +115 -0
  18. data/lib/ruby-lint/cli/analyze.rb +19 -1
  19. data/lib/ruby-lint/constant_loader.rb +1 -3
  20. data/lib/ruby-lint/constant_path.rb +112 -0
  21. data/lib/ruby-lint/definition_builder/base.rb +0 -2
  22. data/lib/ruby-lint/definition_builder/ruby_module.rb +1 -1
  23. data/lib/ruby-lint/definitions/core/array.rb +304 -73
  24. data/lib/ruby-lint/definitions/core/fixnum.rb +575 -19
  25. data/lib/ruby-lint/definitions/core/float.rb +2650 -95
  26. data/lib/ruby-lint/definitions/core/hash.rb +926 -85
  27. data/lib/ruby-lint/definitions/core/ruby_copyright.rb +3 -1
  28. data/lib/ruby-lint/definitions/core/ruby_description.rb +3 -1
  29. data/lib/ruby-lint/definitions/core/ruby_engine.rb +3 -1
  30. data/lib/ruby-lint/definitions/core/ruby_patchlevel.rb +3 -1
  31. data/lib/ruby-lint/definitions/core/ruby_platform.rb +3 -1
  32. data/lib/ruby-lint/definitions/core/ruby_release_date.rb +3 -1
  33. data/lib/ruby-lint/definitions/core/ruby_version.rb +3 -1
  34. data/lib/ruby-lint/definitions/core/string.rb +847 -134
  35. data/lib/ruby-lint/definitions/core/string_io.rb +370 -25
  36. data/lib/ruby-lint/definitions/core/struct.rb +611 -146
  37. data/lib/ruby-lint/file_loader.rb +5 -6
  38. data/lib/ruby-lint/file_scanner.rb +44 -11
  39. data/lib/ruby-lint/inspector.rb +12 -2
  40. data/lib/ruby-lint/runner.rb +6 -2
  41. data/lib/ruby-lint/version.rb +1 -1
  42. data/lib/ruby-lint/virtual_machine.rb +19 -5
  43. data/ruby-lint.gemspec +1 -3
  44. data/spec/ruby-lint/analysis/argument_amount_spec.rb +5 -5
  45. data/spec/ruby-lint/analysis/base_spec.rb +4 -0
  46. data/spec/ruby-lint/analysis/shadowing_variables_spec.rb +4 -4
  47. data/spec/ruby-lint/analysis/undefined_methods_spec.rb +6 -6
  48. data/spec/ruby-lint/analysis/undefined_variables_spec.rb +5 -5
  49. data/spec/ruby-lint/analysis/unused_variables_spec.rb +12 -12
  50. data/spec/ruby-lint/cli/analyze_spec.rb +10 -0
  51. data/spec/ruby-lint/constant_path.rb +63 -0
  52. data/spec/ruby-lint/definition/ruby_object_spec.rb +2 -2
  53. data/spec/ruby-lint/report_spec.rb +2 -2
  54. data/spec/ruby-lint/runner_spec.rb +17 -0
  55. data/spec/ruby-lint/virtual_machine/assignments/assignment_arguments_spec.rb +14 -0
  56. data/spec/ruby-lint/virtual_machine/assignments/constants_spec.rb +23 -0
  57. data/spec/ruby-lint/virtual_machine/location_spec.rb +4 -4
  58. data/spec/ruby-lint/virtual_machine/method_call_tracking_spec.rb +4 -4
  59. data/task/build.rake +2 -7
  60. data/task/doc.rake +3 -1
  61. data/task/generate.rake +3 -0
  62. metadata +20 -36
  63. checksums.yaml.gz.asc +0 -17
  64. data.tar.gz.asc +0 -17
  65. data/lib/ruby-lint/helper/constant_paths.rb +0 -50
  66. metadata.gz.asc +0 -17
@@ -27,8 +27,6 @@ module RubyLint
27
27
  # @return [Set]
28
28
  #
29
29
  class FileLoader < Iterator
30
- include Helper::ConstantPaths
31
-
32
30
  attr_reader :file_scanner, :parser, :nodes, :comments, :debug, :paths
33
31
 
34
32
  ##
@@ -45,9 +43,10 @@ module RubyLint
45
43
  # @param [RubyLint::AST::Node] node
46
44
  #
47
45
  def on_const(node)
48
- segments = constant_segments(node)
49
- constant_path = segments.join('::')
50
- files = file_scanner.scan(constant_path)
46
+ const_path = ConstantPath.new(node)
47
+
48
+ files = file_scanner.scan(const_path.to_s)
49
+ last_name = const_path.constant_segments.last.last
51
50
 
52
51
  paths << node.file
53
52
 
@@ -58,7 +57,7 @@ module RubyLint
58
57
 
59
58
  debug_message("Processing extra file: #{path}")
60
59
 
61
- process_file(segments[-1], path)
60
+ process_file(last_name, path)
62
61
  end
63
62
  end
64
63
 
@@ -23,6 +23,13 @@ module RubyLint
23
23
 
24
24
  @directories = directories
25
25
  @ignore = ignore || []
26
+
27
+ # Hash that will contain the matching file paths for a given constant.
28
+ @constant_paths_cache = {}
29
+
30
+ # Globbing all files at once and then comparing those results is faster
31
+ # than running a Dir.glob for every call to #scan.
32
+ @glob_cache = Dir.glob("#{directories.join(',')}/**/*.rb")
26
33
  end
27
34
 
28
35
  ##
@@ -34,17 +41,31 @@ module RubyLint
34
41
  # @return [Array]
35
42
  #
36
43
  def scan(constant)
37
- segment = constant_to_path(constant)
38
- paths = Dir.glob(glob_pattern(segment))
44
+ unless constant_paths_cached?(constant)
45
+ build_constant_paths_cache(constant)
46
+ end
47
+
48
+ return @constant_paths_cache[constant]
49
+ end
50
+
51
+ private
52
+
53
+ ##
54
+ # Searches all the files that could potentially define the given constant
55
+ # and caches them.
56
+ #
57
+ # @param [String] constant
58
+ #
59
+ def build_constant_paths_cache(constant)
60
+ paths = match_globbed_files(constant_to_path(constant))
39
61
 
40
62
  # Lets see if we can find anything when using dashes for the directory
41
63
  # names instead of underscores.
42
64
  if paths.empty?
43
- segment = constant_to_dashed_path(constant)
44
- paths = Dir.glob(glob_pattern(segment))
65
+ paths = match_globbed_files(constant_to_dashed_path(constant))
45
66
  end
46
67
 
47
- paths.map! { |path| File.expand_path(path) }
68
+ paths.map! { |p| File.expand_path(p) }
48
69
 
49
70
  ignore.each do |pattern|
50
71
  paths.reject! do |path|
@@ -52,23 +73,35 @@ module RubyLint
52
73
  end
53
74
  end
54
75
 
55
- # Ensure that the order is from top-level -> deeply nested files instead
56
- # of a random order.
76
+ # Ensure that the order is from top-level -> deeply nested files
77
+ # instead of a random order.
57
78
  paths.sort! do |left, right|
58
79
  left.length <=> right.length
59
80
  end
60
81
 
61
- return paths
82
+ @constant_paths_cache[constant] = paths
62
83
  end
63
84
 
64
- private
85
+ ##
86
+ # @return [Array]
87
+ #
88
+ def match_globbed_files(segment)
89
+ return @glob_cache.select { |p| p.include?(segment) }
90
+ end
91
+
92
+ ##
93
+ # @return [TrueClass|FalseClass]
94
+ #
95
+ def constant_paths_cached?(constant)
96
+ return @constant_paths_cache.key?(constant)
97
+ end
65
98
 
66
99
  ##
67
100
  # @param [String] constant
68
101
  # @return [String]
69
102
  #
70
103
  def constant_to_path(constant)
71
- return constant.gsub('::', '/').snake_case
104
+ return constant.gsub('::', '/').snake_case + '.rb'
72
105
  end
73
106
 
74
107
  ##
@@ -79,7 +112,7 @@ module RubyLint
79
112
  last = segments[-1]
80
113
  path = segments[0..-2].join('/').snake_case.gsub('_', '-')
81
114
 
82
- return "#{path}/#{last.snake_case}"
115
+ return "#{path}/#{last.snake_case}.rb"
83
116
  end
84
117
 
85
118
  ##
@@ -132,9 +132,19 @@ module RubyLint
132
132
  # @return [Array]
133
133
  #
134
134
  def get_methods(getter = :methods)
135
- diff = constant.send(getter) - Object.send(getter)
135
+ diff = constant.send(getter) - Object.send(getter)
136
+ methods = diff | constant.send(getter, false)
137
+
138
+ # If the constant manually defines the initialize method (= private)
139
+ # we'll also want to include it.
140
+ if getter == :instance_methods \
141
+ and constant.is_a?(Class) \
142
+ and constant.private_method_defined?(:initialize) \
143
+ and constant.instance_method(:initialize).source_location
144
+ methods = methods | [:initialize]
145
+ end
136
146
 
137
- return diff | constant.send(getter, false)
147
+ return methods
138
148
  end
139
149
 
140
150
  ##
@@ -144,7 +144,7 @@ module RubyLint
144
144
  :level => :error,
145
145
  :message => diagnostic.message,
146
146
  :line => diagnostic.location.line,
147
- :column => diagnostic.location.column,
147
+ :column => diagnostic.location.column + 1,
148
148
  :file => diagnostic.location.source_buffer.name
149
149
  )
150
150
  end
@@ -170,7 +170,11 @@ module RubyLint
170
170
  # @param [RubyLint::Report] report
171
171
  #
172
172
  def run_analysis(ast, vm, report)
173
- configuration.analysis_classes.each do |const|
173
+ classes = configuration.analysis_classes.select do |const|
174
+ const.analyze?(ast, vm)
175
+ end
176
+
177
+ classes.each do |const|
174
178
  instance = const.new(:vm => vm, :report => report)
175
179
  instance.iterate(ast)
176
180
  end
@@ -1,3 +1,3 @@
1
1
  module RubyLint
2
- VERSION = '0.9.1'
2
+ VERSION = '1.0.0-preview1'
3
3
  end # RubyLint
@@ -70,8 +70,6 @@ module RubyLint
70
70
  # @return [RubyLint::Docstring::Mapping]
71
71
  #
72
72
  class VirtualMachine < Iterator
73
- include Helper::ConstantPaths
74
-
75
73
  attr_reader :associations,
76
74
  :comments,
77
75
  :definitions,
@@ -99,7 +97,17 @@ module RubyLint
99
97
  #
100
98
  # @return [Array]
101
99
  #
102
- PRIMITIVES = [:int, :float, :str, :sym, :true, :false, :nil]
100
+ PRIMITIVES = [
101
+ :int,
102
+ :float,
103
+ :str,
104
+ :sym,
105
+ :true,
106
+ :false,
107
+ :nil,
108
+ :erange,
109
+ :irange
110
+ ]
103
111
 
104
112
  ##
105
113
  # Returns a Hash containing the method call evaluators to use for `(send)`
@@ -241,7 +249,7 @@ module RubyLint
241
249
  scope = current_scope
242
250
 
243
251
  if node.children[0]
244
- scope = resolve_constant_path(node.children[0])
252
+ scope = ConstantPath.new(node.children[0]).resolve(current_scope)
245
253
 
246
254
  return unless scope
247
255
  end
@@ -881,6 +889,12 @@ Received: #{arguments.length}
881
889
 
882
890
  buffer_assignment_value(variable.value)
883
891
 
892
+ # Primarily used by #after_send to support variable assignments as method
893
+ # call arguments.
894
+ if value and !value_stack.empty?
895
+ value_stack.push(value)
896
+ end
897
+
884
898
  add_variable(variable)
885
899
  end
886
900
 
@@ -969,7 +983,7 @@ Received: #{arguments.length}
969
983
  #
970
984
  def definition_for_node(node)
971
985
  if node.const? and node.children[0]
972
- definition = resolve_constant_path(node)
986
+ definition = ConstantPath.new(node).resolve(current_scope)
973
987
  else
974
988
  definition = current_scope.lookup(node.type, node.name)
975
989
  end
@@ -30,15 +30,13 @@ using the handle "yorickpeterse".
30
30
  s.required_ruby_version = '>= 1.9.3'
31
31
 
32
32
  s.add_dependency 'parser', ['>= 2.0.0']
33
- s.add_dependency 'racc', ['>= 1.4.10']
34
- s.add_dependency 'slop'
33
+ s.add_dependency 'slop', ['~> 3.4', '>= 3.4.7']
35
34
 
36
35
  s.add_development_dependency 'rake'
37
36
  s.add_development_dependency 'rspec', ['>= 2.14']
38
37
  s.add_development_dependency 'yard'
39
38
  s.add_development_dependency 'pry-rescue'
40
39
  s.add_development_dependency 'simplecov'
41
- s.add_development_dependency 'rubygems-openpgp'
42
40
  s.add_development_dependency 'coveralls'
43
41
  s.add_development_dependency 'json'
44
42
  s.add_development_dependency 'kramdown'
@@ -15,7 +15,7 @@ example
15
15
  entry.is_a?(RubyLint::Report::Entry).should == true
16
16
 
17
17
  entry.line.should == 4
18
- entry.column.should == 0
18
+ entry.column.should == 1
19
19
  entry.message.should == 'wrong number of arguments (expected 2 but got 0)'
20
20
  end
21
21
 
@@ -33,7 +33,7 @@ example
33
33
  entry.is_a?(RubyLint::Report::Entry).should == true
34
34
 
35
35
  entry.line.should == 4
36
- entry.column.should == 0
36
+ entry.column.should == 1
37
37
  entry.message.should == 'wrong number of arguments ' \
38
38
  '(expected 2..3 but got 0)'
39
39
  end
@@ -52,7 +52,7 @@ example
52
52
  entry.is_a?(RubyLint::Report::Entry).should == true
53
53
 
54
54
  entry.line.should == 4
55
- entry.column.should == 0
55
+ entry.column.should == 1
56
56
  entry.message.should == 'wrong number of arguments ' \
57
57
  '(expected 2 but got 0)'
58
58
  end
@@ -107,11 +107,11 @@ Person.new(10, 20)
107
107
  first, second = report.entries
108
108
 
109
109
  first.line.should == 6
110
- first.column.should == 0
110
+ first.column.should == 1
111
111
  first.message.should == 'wrong number of arguments (expected 1 but got 0)'
112
112
 
113
113
  second.line.should == 7
114
- second.column.should == 0
114
+ second.column.should == 1
115
115
  second.message.should == 'wrong number of arguments (expected 1 but got 2)'
116
116
  end
117
117
  end
@@ -9,4 +9,8 @@ describe RubyLint::Analysis::Base do
9
9
 
10
10
  blk.should_not raise_error
11
11
  end
12
+
13
+ example 'enable analysis by default' do
14
+ RubyLint::Analysis::Base.analyze?(double(:ast), double(:vm)).should == true
15
+ end
12
16
  end
@@ -16,7 +16,7 @@ end
16
16
  entry.is_a?(RubyLint::Report::Entry).should == true
17
17
 
18
18
  entry.line.should == 3
19
- entry.column.should == 18
19
+ entry.column.should == 19
20
20
  entry.message.should == 'shadowing outer local variable number'
21
21
  end
22
22
 
@@ -53,7 +53,7 @@ end
53
53
  entry.is_a?(RubyLint::Report::Entry).should == true
54
54
 
55
55
  entry.line.should == 5
56
- entry.column.should == 18
56
+ entry.column.should == 19
57
57
  entry.message.should == 'shadowing outer local variable number'
58
58
  end
59
59
 
@@ -77,9 +77,9 @@ end
77
77
  first, second = report.entries
78
78
 
79
79
  first.line.should == 3
80
- first.column.should == 17
80
+ first.column.should == 18
81
81
 
82
82
  second.line.should == 7
83
- second.column.should == 17
83
+ second.column.should == 18
84
84
  end
85
85
  end
@@ -8,7 +8,7 @@ describe RubyLint::Analysis::UndefinedMethods do
8
8
  entry.is_a?(RubyLint::Report::Entry).should == true
9
9
 
10
10
  entry.line.should == 1
11
- entry.column.should == 0
11
+ entry.column.should == 1
12
12
  entry.message.should == 'undefined method example_method'
13
13
  end
14
14
 
@@ -28,7 +28,7 @@ String.example_method
28
28
  entry.is_a?(RubyLint::Report::Entry).should == true
29
29
 
30
30
  entry.line.should == 6
31
- entry.column.should == 0
31
+ entry.column.should == 1
32
32
  entry.message.should == 'undefined method example_method on String'
33
33
  end
34
34
 
@@ -40,7 +40,7 @@ String.example_method
40
40
  entry.is_a?(RubyLint::Report::Entry).should == true
41
41
 
42
42
  entry.line.should == 1
43
- entry.column.should == 0
43
+ entry.column.should == 1
44
44
  entry.message.should == 'undefined method example_method ' \
45
45
  'on an instance of String'
46
46
  end
@@ -69,7 +69,7 @@ name
69
69
  entry.is_a?(RubyLint::Report::Entry).should == true
70
70
 
71
71
  entry.line.should == 11
72
- entry.column.should == 0
72
+ entry.column.should == 1
73
73
  entry.message.should == 'undefined method name'
74
74
  end
75
75
 
@@ -132,7 +132,7 @@ name.downcasex
132
132
  entry = report.entries[0]
133
133
 
134
134
  entry.line.should == 4
135
- entry.column.should == 0
135
+ entry.column.should == 1
136
136
  entry.message.should == 'undefined method downcasex on an instance of String'
137
137
  end
138
138
 
@@ -156,7 +156,7 @@ user.invalid
156
156
  entry = report.entries[0]
157
157
 
158
158
  entry.line.should == 9
159
- entry.column.should == 0
159
+ entry.column.should == 1
160
160
  entry.message.should == 'undefined method invalid on an instance of User'
161
161
  end
162
162
 
@@ -8,7 +8,7 @@ describe RubyLint::Analysis::UndefinedVariables do
8
8
  entry.is_a?(RubyLint::Report::Entry).should == true
9
9
 
10
10
  entry.line.should == 1
11
- entry.column.should == 0
11
+ entry.column.should == 1
12
12
  entry.message.should == 'undefined instance variable @number'
13
13
  end
14
14
 
@@ -19,7 +19,7 @@ describe RubyLint::Analysis::UndefinedVariables do
19
19
  entry.is_a?(RubyLint::Report::Entry).should == true
20
20
 
21
21
  entry.line.should == 1
22
- entry.column.should == 0
22
+ entry.column.should == 1
23
23
  entry.message.should == 'undefined class variable @@number'
24
24
  end
25
25
 
@@ -30,7 +30,7 @@ describe RubyLint::Analysis::UndefinedVariables do
30
30
  entry.is_a?(RubyLint::Report::Entry).should == true
31
31
 
32
32
  entry.line.should == 1
33
- entry.column.should == 0
33
+ entry.column.should == 1
34
34
  entry.message.should == 'undefined global variable $number'
35
35
  end
36
36
 
@@ -41,7 +41,7 @@ describe RubyLint::Analysis::UndefinedVariables do
41
41
  entry.is_a?(RubyLint::Report::Entry).should == true
42
42
 
43
43
  entry.line.should == 1
44
- entry.column.should == 0
44
+ entry.column.should == 1
45
45
  entry.message.should == 'undefined constant NUMBER'
46
46
  end
47
47
 
@@ -63,7 +63,7 @@ A::B
63
63
  entry.is_a?(RubyLint::Report::Entry).should == true
64
64
 
65
65
  entry.line.should == 8
66
- entry.column.should == 0
66
+ entry.column.should == 1
67
67
  entry.message.should == 'undefined constant A::B'
68
68
  end
69
69
 
@@ -8,7 +8,7 @@ describe RubyLint::Analysis::UnusedVariables do
8
8
  entry.is_a?(RubyLint::Report::Entry).should == true
9
9
 
10
10
  entry.line.should == 1
11
- entry.column.should == 0
11
+ entry.column.should == 1
12
12
  entry.message.should == 'unused local variable number'
13
13
  end
14
14
 
@@ -17,7 +17,7 @@ describe RubyLint::Analysis::UnusedVariables do
17
17
  entry = report.entries[0]
18
18
 
19
19
  entry.line.should == 1
20
- entry.column.should == 0
20
+ entry.column.should == 1
21
21
  entry.message.should == 'unused instance variable @number'
22
22
  end
23
23
 
@@ -40,7 +40,7 @@ number
40
40
  entry.is_a?(RubyLint::Report::Entry).should == true
41
41
 
42
42
  entry.line.should == 1
43
- entry.column.should == 0
43
+ entry.column.should == 1
44
44
  entry.message.should == 'unused constant NUMBER'
45
45
  end
46
46
 
@@ -73,7 +73,7 @@ A::B = 10
73
73
  entry.is_a?(RubyLint::Report::Entry).should == true
74
74
 
75
75
  entry.line.should == 4
76
- entry.column.should == 0
76
+ entry.column.should == 1
77
77
  entry.message.should == 'unused constant A::B'
78
78
  end
79
79
 
@@ -90,7 +90,7 @@ end
90
90
  entry.is_a?(RubyLint::Report::Entry).should == true
91
91
 
92
92
  entry.line.should == 2
93
- entry.column.should == 2
93
+ entry.column.should == 3
94
94
  entry.message.should == 'unused local variable number'
95
95
  end
96
96
 
@@ -107,7 +107,7 @@ second = first
107
107
  entry = report.entries[0]
108
108
 
109
109
  entry.line.should == 2
110
- entry.column.should == 0
110
+ entry.column.should == 1
111
111
  entry.message.should == 'unused local variable second'
112
112
  end
113
113
 
@@ -155,7 +155,7 @@ end
155
155
 
156
156
  entry.message.should == 'unused argument number'
157
157
  entry.line.should == 1
158
- entry.column.should == 13
158
+ entry.column.should == 14
159
159
  end
160
160
 
161
161
  example 'warn for a unused optional argument' do
@@ -169,7 +169,7 @@ end
169
169
 
170
170
  entry.message.should == 'unused argument amount'
171
171
  entry.line.should == 1
172
- entry.column.should == 13
172
+ entry.column.should == 14
173
173
  end
174
174
 
175
175
  example 'warn for a unused rest argument' do
@@ -183,7 +183,7 @@ end
183
183
 
184
184
  entry.message.should == 'unused argument args'
185
185
  entry.line.should == 1
186
- entry.column.should == 13
186
+ entry.column.should == 14
187
187
  end
188
188
 
189
189
  example 'warn for a unused block argument' do
@@ -197,7 +197,7 @@ end
197
197
 
198
198
  entry.message.should == 'unused argument block'
199
199
  entry.line.should == 1
200
- entry.column.should == 13
200
+ entry.column.should == 14
201
201
  end
202
202
 
203
203
  specific_ruby_version '2.0' do
@@ -212,7 +212,7 @@ end
212
212
 
213
213
  entry.message.should == 'unused argument amount'
214
214
  entry.line.should == 1
215
- entry.column.should == 13
215
+ entry.column.should == 14
216
216
  end
217
217
  end
218
218
 
@@ -241,7 +241,7 @@ end
241
241
 
242
242
  entry.message.should == 'unused argument number'
243
243
  entry.line.should == 1
244
- entry.column.should == 18
244
+ entry.column.should == 19
245
245
  end
246
246
 
247
247
  example 'do not warn for a used argument' do