reek 1.2.7.2 → 1.2.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/History.txt +9 -1
  2. data/config/defaults.reek +4 -4
  3. data/features/masking_smells.feature +14 -57
  4. data/features/options.feature +1 -2
  5. data/features/rake_task.feature +6 -6
  6. data/features/reports.feature +8 -38
  7. data/features/samples.feature +181 -181
  8. data/features/stdin.feature +3 -3
  9. data/lib/reek.rb +1 -1
  10. data/lib/reek/cli/command_line.rb +2 -7
  11. data/lib/reek/cli/reek_command.rb +6 -6
  12. data/lib/reek/cli/report.rb +27 -49
  13. data/lib/reek/core/code_parser.rb +6 -15
  14. data/lib/reek/core/method_context.rb +1 -1
  15. data/lib/reek/core/module_context.rb +0 -18
  16. data/lib/reek/core/smell_configuration.rb +0 -4
  17. data/lib/reek/core/sniffer.rb +11 -19
  18. data/lib/reek/core/warning_collector.rb +27 -0
  19. data/lib/reek/examiner.rb +43 -35
  20. data/lib/reek/rake/task.rb +2 -0
  21. data/lib/reek/smell_warning.rb +10 -25
  22. data/lib/reek/smells/boolean_parameter.rb +1 -1
  23. data/lib/reek/smells/control_couple.rb +4 -1
  24. data/lib/reek/smells/data_clump.rb +40 -33
  25. data/lib/reek/smells/feature_envy.rb +1 -1
  26. data/lib/reek/smells/long_parameter_list.rb +4 -1
  27. data/lib/reek/smells/long_yield_list.rb +6 -3
  28. data/lib/reek/smells/simulated_polymorphism.rb +1 -1
  29. data/lib/reek/smells/smell_detector.rb +4 -32
  30. data/lib/reek/smells/uncommunicative_method_name.rb +1 -1
  31. data/lib/reek/smells/uncommunicative_module_name.rb +1 -1
  32. data/lib/reek/smells/uncommunicative_parameter_name.rb +2 -2
  33. data/lib/reek/smells/uncommunicative_variable_name.rb +11 -18
  34. data/lib/reek/smells/utility_function.rb +7 -4
  35. data/lib/reek/source/reference_collector.rb +9 -2
  36. data/lib/reek/source/source_locator.rb +6 -0
  37. data/lib/reek/spec/should_reek.rb +3 -6
  38. data/lib/reek/spec/should_reek_only_of.rb +4 -3
  39. data/reek.gemspec +4 -4
  40. data/spec/reek/cli/reek_command_spec.rb +3 -4
  41. data/spec/reek/cli/report_spec.rb +10 -6
  42. data/spec/reek/cli/yaml_command_spec.rb +1 -1
  43. data/spec/reek/core/code_context_spec.rb +1 -3
  44. data/spec/reek/core/module_context_spec.rb +1 -1
  45. data/spec/reek/core/warning_collector_spec.rb +27 -0
  46. data/spec/reek/examiner_spec.rb +80 -19
  47. data/spec/reek/smell_warning_spec.rb +4 -61
  48. data/spec/reek/smells/attribute_spec.rb +4 -7
  49. data/spec/reek/smells/behaves_like_variable_detector.rb +2 -2
  50. data/spec/reek/smells/class_variable_spec.rb +0 -1
  51. data/spec/reek/smells/control_couple_spec.rb +8 -15
  52. data/spec/reek/smells/data_clump_spec.rb +85 -1
  53. data/spec/reek/smells/duplication_spec.rb +7 -8
  54. data/spec/reek/smells/feature_envy_spec.rb +2 -32
  55. data/spec/reek/smells/long_parameter_list_spec.rb +9 -16
  56. data/spec/reek/smells/long_yield_list_spec.rb +8 -15
  57. data/spec/reek/smells/smell_detector_shared.rb +12 -0
  58. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +9 -10
  59. data/spec/reek/smells/utility_function_spec.rb +11 -15
  60. data/spec/reek/spec/should_reek_only_of_spec.rb +6 -6
  61. data/spec/reek/spec/should_reek_spec.rb +3 -3
  62. metadata +36 -22
  63. data/lib/reek/core/class_context.rb +0 -22
  64. data/lib/reek/core/detector_stack.rb +0 -33
  65. data/lib/reek/core/masking_collection.rb +0 -52
  66. data/spec/reek/core/class_context_spec.rb +0 -53
  67. data/spec/reek/core/masking_collection_spec.rb +0 -235
@@ -1,6 +1,5 @@
1
1
  require File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'spec_helper')
2
2
  require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'smells', 'attribute')
3
- require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'core', 'class_context')
4
3
  require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'core', 'module_context')
5
4
  require File.join(File.dirname(File.expand_path(__FILE__)), 'smell_detector_shared')
6
5
 
@@ -16,13 +15,11 @@ describe Attribute do
16
15
  it_should_behave_like 'SmellDetector'
17
16
 
18
17
  context 'with no attributes' do
19
- it 'records nothing in the class' do
20
- ctx = ClassContext.from_s('class Fred; end')
21
- @detector.attributes_in(ctx).should be_empty
22
- end
23
18
  it 'records nothing in the module' do
24
- ctx = ModuleContext.from_s('module Fred; end')
25
- @detector.attributes_in(ctx).should be_empty
19
+ src = 'module Fred; end'
20
+ ctx = CodeContext.new(nil, src.to_reek_source.syntax_tree)
21
+ @detector.examine_context(ctx)
22
+ @detector.smells_found.should be_empty
26
23
  end
27
24
  end
28
25
 
@@ -2,7 +2,7 @@ shared_examples_for 'a variable detector' do
2
2
  context 'with no variables' do
3
3
  it "doesn't record a smell" do
4
4
  @detector.examine_context(@ctx)
5
- @detector.num_smells.should == 0
5
+ @detector.smells_found.length.should == 0
6
6
  end
7
7
  end
8
8
 
@@ -14,7 +14,7 @@ shared_examples_for 'a variable detector' do
14
14
  end
15
15
 
16
16
  it 'records only one smell' do
17
- @detector.num_smells.should == 1
17
+ @detector.smells_found.length.should == 1
18
18
  end
19
19
  it 'mentions the variable name in the report' do
20
20
  @detector.should have_smell([/something/])
@@ -1,6 +1,5 @@
1
1
  require File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'spec_helper')
2
2
  require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'smells', 'class_variable')
3
- require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'core', 'class_context')
4
3
  require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'core', 'module_context')
5
4
  require File.join(File.dirname(File.expand_path(__FILE__)), 'smell_detector_shared')
6
5
 
@@ -39,25 +39,18 @@ end
39
39
  EOS
40
40
  source = src.to_reek_source
41
41
  sniffer = Sniffer.new(source)
42
- @mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
43
- @detector.examine_context(@mctx)
44
- warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
45
- @yaml = warning.to_yaml
46
- end
47
- it 'reports the source' do
48
- @yaml.should match(/source:\s*#{@source_name}/)
49
- end
50
- it 'reports the class' do
51
- @yaml.should match(/class:\s*ControlCouple/)
52
- end
53
- it 'reports the subclass' do
54
- @yaml.should match(/subclass:\s*ControlParameter/)
42
+ mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
43
+ @detector.examine_context(mctx)
44
+ @warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
55
45
  end
46
+
47
+ it_should_behave_like 'common fields set correctly'
48
+
56
49
  it 'reports the control parameter' do
57
- @yaml.should match(/parameter:\s*arg/)
50
+ @warning.smell['parameter'].should == 'arg'
58
51
  end
59
52
  it 'reports all conditional locations' do
60
- @yaml.should match(/lines:\s*- 3\s*- 6/)
53
+ @warning.lines.should == [3,6]
61
54
  end
62
55
  end
63
56
  end
@@ -28,7 +28,7 @@ EOS
28
28
  def third(pa, pb) pa - pb + @fred; end
29
29
  end
30
30
  EOS
31
- ctx = ModuleContext.from_s(@src)
31
+ ctx = CodeContext.new(nil, @src.to_reek_source.syntax_tree)
32
32
  detector = DataClump.new('newt')
33
33
  detector.examine(ctx)
34
34
  @smells = detector.smells_found.to_a
@@ -139,4 +139,88 @@ describe DataClump do
139
139
  end
140
140
 
141
141
  it_should_behave_like 'SmellDetector'
142
+
143
+ it 'get a real example right' do
144
+ src = <<-EOS
145
+ module Inline
146
+ def generate(src, options) end
147
+ def c (src, options) end
148
+ def c_singleton (src, options) end
149
+ def c_raw (src, options) end
150
+ def c_raw_singleton (src, options) end
142
151
  end
152
+ EOS
153
+ ctx = CodeContext.new(nil, src.to_reek_source.syntax_tree)
154
+ detector = DataClump.new('newt')
155
+ detector.examine(ctx)
156
+ smells = detector.smells_found.to_a
157
+ smells.length.should == 1
158
+ warning = smells[0]
159
+ warning.smell[DataClump::OCCURRENCES_KEY].should == 5
160
+ end
161
+ end
162
+
163
+ #---------------------------------------------------------------------------------
164
+ #
165
+ #def occurrences(potential_clump, all_methods)
166
+ # all_methods.select do |method|
167
+ # potential_clump - method == []
168
+ # end.length
169
+ #end
170
+ #
171
+ #describe 'occurrences' do
172
+ # it 'counts correctly' do
173
+ # params = [[:a1, :a2], [:a1, :a2]]
174
+ # potential_clump = [:a1, :a2]
175
+ # occurrences(potential_clump, params).should == 2
176
+ # end
177
+ #end
178
+ #
179
+ #def immediate_clumps(root, other_params, all_methods)
180
+ # result = []
181
+ # other_params.map do |param|
182
+ # potential_clump = (root + [param])
183
+ # if occurrences(potential_clump, all_methods) >= 2
184
+ # result << potential_clump
185
+ # result = result + immediate_clumps(potential_clump, other_params - [param], all_methods)
186
+ # end
187
+ # end.compact
188
+ # result
189
+ #end
190
+ #
191
+ #def clumps_containing(root, other_params, all_methods)
192
+ # return [] unless other_params
193
+ # immediate_clumps(root, other_params, all_methods) + clumps_containing([other_params[0]], other_params[1..-1], all_methods)
194
+ #end
195
+ #
196
+ #def clumps_in(all_methods)
197
+ # all_params = all_methods.flatten.sort {|a,b| a.to_s <=> b.to_s}.uniq
198
+ # clumps_containing([all_params[0]], all_params[1..-1], all_methods)
199
+ #end
200
+ #
201
+ #describe 'set of parameters' do
202
+ # it 'finds the trivial clump' do
203
+ # params = [[:a1, :a2], [:a1, :a2]]
204
+ # clumps_in(params).should == [[:a1, :a2]]
205
+ # end
206
+ #
207
+ # it 'finds the trivial size-3 clump' do
208
+ # params = [[:a1, :a2, :a3], [:a1, :a2, :a3]]
209
+ # clumps_in(params).should == [[:a1, :a2, :a3]]
210
+ # end
211
+ #
212
+ # it 'doesnt find non clump' do
213
+ # params = [[:a1, :a2], [:a1, :a3]]
214
+ # clumps_in(params).should == []
215
+ # end
216
+ #
217
+ # it 'finds the trivial sub-clump' do
218
+ # params = [[:a1, :a2], [:a3, :a1, :a2]]
219
+ # clumps_in(params).should == [[:a1, :a2]]
220
+ # end
221
+ #
222
+ # it 'finds the non-a1 clump' do
223
+ # params = [[:a1, :a3, :a2], [:a3, :a2]]
224
+ # clumps_in(params).should == [[:a2, :a3]]
225
+ # end
226
+ #end
@@ -62,7 +62,7 @@ describe Duplication do
62
62
 
63
63
  it_should_behave_like 'SmellDetector'
64
64
 
65
- context 'looking at the YAML' do
65
+ context 'when a smell is reported' do
66
66
  before :each do
67
67
  src = <<EOS
68
68
  def double_thing(other)
@@ -75,23 +75,22 @@ EOS
75
75
  sniffer = Core::Sniffer.new(source)
76
76
  @mctx = Core::CodeParser.new(sniffer).process_defn(source.syntax_tree)
77
77
  @detector.examine(@mctx)
78
- warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
79
- @yaml = warning.to_yaml
78
+ @warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
80
79
  end
81
80
  it 'reports the source' do
82
- @yaml.should match(/source:\s*#{@source_name}/)
81
+ @warning.source.should == @source_name
83
82
  end
84
83
  it 'reports the class' do
85
- @yaml.should match(/class:\s*Duplication/)
84
+ @warning.smell_class.should == 'Duplication'
86
85
  end
87
86
  it 'reports the subclass' do
88
- @yaml.should match(/subclass:\s*DuplicateMethodCall/)
87
+ @warning.subclass.should == 'DuplicateMethodCall'
89
88
  end
90
89
  it 'reports the call' do
91
- @yaml.should match(/call:\s*other\[\@thing\]/)
90
+ @warning.smell['call'].should == 'other[@thing]'
92
91
  end
93
92
  it 'reports the correct lines' do
94
- @yaml.should match(/lines:\s*- 2\s*- 4/)
93
+ @warning.lines.should == [2,4]
95
94
  end
96
95
  end
97
96
  end
@@ -73,36 +73,6 @@ EOS
73
73
  end
74
74
  end
75
75
 
76
- context 'when an envious receiver exists' do
77
- before :each do
78
- @source_name = 'green as a cucumber'
79
- @receiver = 'blah'
80
- @ctx = mock('method_context', :null_object => true)
81
- @ctx.should_receive(:envious_receivers).and_return({ast(:lvar, @receiver) => 4})
82
- @detector = FeatureEnvy.new(@source_name)
83
- @detector.examine_context(@ctx)
84
- @smells = @detector.smells_found.to_a
85
- end
86
- it 'reports only that smell' do
87
- @smells.length.should == 1
88
- end
89
- it 'reports the source' do
90
- @smells[0].source.should == @source_name
91
- end
92
- it 'reports the class' do
93
- @smells[0].smell_class.should == FeatureEnvy::SMELL_CLASS
94
- end
95
- it 'reports the subclass' do
96
- @smells[0].subclass.should == FeatureEnvy::SMELL_SUBCLASS
97
- end
98
- it 'reports the envious receiver' do
99
- @smells[0].smell[FeatureEnvy::RECEIVER_KEY].should == @receiver
100
- end
101
- it 'reports the number of references' do
102
- @smells[0].smell[FeatureEnvy::REFERENCES_KEY].should == 4
103
- end
104
- end
105
-
106
76
  it 'should report highest affinity' do
107
77
  src = <<EOS
108
78
  def total_envy
@@ -186,8 +156,8 @@ EOS
186
156
  it 'interprets << correctly' do
187
157
  ruby = <<EOS
188
158
  def report_on(report)
189
- if @is_masked
190
- report.record_masked_smell(self)
159
+ if @is_doubled
160
+ report.record_doubled_smell(self)
191
161
  else
192
162
  report << self
193
163
  end
@@ -66,7 +66,7 @@ describe LongParameterList do
66
66
 
67
67
  it_should_behave_like 'SmellDetector'
68
68
 
69
- context 'looking at the YAML' do
69
+ context 'when a smell is reported' do
70
70
  before :each do
71
71
  src = <<EOS
72
72
  def badguy(arga, argb, argc, argd)
@@ -76,26 +76,19 @@ end
76
76
  EOS
77
77
  source = src.to_reek_source
78
78
  sniffer = Sniffer.new(source)
79
- @mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
80
- @detector.examine_context(@mctx)
81
- warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
82
- @yaml = warning.to_yaml
83
- end
84
- it 'reports the source' do
85
- @yaml.should match(/source:\s*#{@source_name}/)
86
- end
87
- it 'reports the class' do
88
- @yaml.should match(/\sclass:\s*LongParameterList/)
89
- end
90
- it 'reports the subclass' do
91
- @yaml.should match(/subclass:\s*LongParameterList/)
79
+ mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
80
+ @detector.examine_context(mctx)
81
+ @warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
92
82
  end
83
+
84
+ it_should_behave_like 'common fields set correctly'
85
+
93
86
  it 'reports the number of parameters' do
94
- @yaml.should match(/parameter_count:[\s]*#{@num_parameters}/)
87
+ @warning.smell['parameter_count'].should == 4
95
88
  # SMELL: many tests duplicate the names of the YAML fields
96
89
  end
97
90
  it 'reports the line number of the method' do
98
- @yaml.should match(/lines:\s*- 1/)
91
+ @warning.lines.should == [1]
99
92
  end
100
93
  end
101
94
  end
@@ -40,26 +40,19 @@ def simple(arga, argb, &blk)
40
40
  EOS
41
41
  source = src.to_reek_source
42
42
  sniffer = Sniffer.new(source)
43
- @mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
44
- @detector.examine_context(@mctx)
45
- warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
46
- @yaml = warning.to_yaml
47
- end
48
- it 'reports the source' do
49
- @yaml.should match(/source:\s*#{@source_name}/)
50
- end
51
- it 'reports the class' do
52
- @yaml.should match(/class:\s*LongParameterList/)
53
- end
54
- it 'reports the subclass' do
55
- @yaml.should match(/subclass:\s*LongYieldList/)
43
+ mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
44
+ @detector.examine_context(mctx)
45
+ @warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
56
46
  end
47
+
48
+ it_should_behave_like 'common fields set correctly'
49
+
57
50
  it 'reports the number of parameters' do
58
- @yaml.should match(/parameter_count:[\s]*#{@num_parameters}/)
51
+ @warning.smell['parameter_count'].should == 4
59
52
  # SMELL: many tests duplicate the names of the YAML fields
60
53
  end
61
54
  it 'reports the line number of the method' do
62
- @yaml.should match(/lines:\s*- 3/)
55
+ @warning.lines.should == [3]
63
56
  end
64
57
  end
65
58
  end
@@ -26,3 +26,15 @@ shared_examples_for 'SmellDetector' do
26
26
  end
27
27
  end
28
28
  end
29
+
30
+ shared_examples_for 'common fields set correctly' do
31
+ it 'reports the source' do
32
+ @warning.source.should == @source_name
33
+ end
34
+ it 'reports the class' do
35
+ @warning.smell_class.should == @detector.class::SMELL_CLASS
36
+ end
37
+ it 'reports the subclass' do
38
+ @warning.subclass.should == @detector.class::SMELL_SUBCLASS
39
+ end
40
+ end
@@ -78,7 +78,7 @@ EOS
78
78
  end
79
79
  end
80
80
 
81
- context 'looking at the YAML' do
81
+ context 'when a smell is reported' do
82
82
  before :each do
83
83
  src = <<EOS
84
84
  def bad
@@ -91,25 +91,24 @@ end
91
91
  EOS
92
92
  source = src.to_reek_source
93
93
  sniffer = Core::Sniffer.new(source)
94
- @mctx = Core::CodeParser.new(sniffer).process_defn(source.syntax_tree)
95
- @detector.examine(@mctx)
96
- warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
97
- @yaml = warning.to_yaml
94
+ mctx = Core::CodeParser.new(sniffer).process_defn(source.syntax_tree)
95
+ @detector.examine(mctx)
96
+ @warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
98
97
  end
99
98
  it 'reports the source' do
100
- @yaml.should match(/source:\s*#{@source_name}/)
99
+ @warning.source.should == @source_name
101
100
  end
102
101
  it 'reports the class' do
103
- @yaml.should match(/class:\s*UncommunicativeName/)
102
+ @warning.smell_class.should == 'UncommunicativeName'
104
103
  end
105
104
  it 'reports the subclass' do
106
- @yaml.should match(/subclass:\s*UncommunicativeVariableName/)
105
+ @warning.subclass.should == 'UncommunicativeVariableName'
107
106
  end
108
107
  it 'reports the variable name' do
109
- @yaml.should match(/variable_name:\s*x2/)
108
+ @warning.smell['variable_name'].should == 'x2'
110
109
  end
111
110
  it 'reports all line numbers' do
112
- @yaml.should match(/lines:\s*- 3\s*- 5/)
111
+ @warning.lines.should == [3,5]
113
112
  end
114
113
  end
115
114
 
@@ -51,6 +51,9 @@ class Object; def is_maybe?() false end end'.should_not reek
51
51
  it 'reports two calls' do
52
52
  'def simple(arga) arga.to_s + arga.to_i end'.should reek_of(:UtilityFunction, /simple/)
53
53
  end
54
+ it 'counts a local call in a param initializer' do
55
+ 'def simple(arga=local) arga.to_s end'.should_not reek_of(:UtilityFunction)
56
+ end
54
57
  it 'should count usages of self'do
55
58
  'def <=>(other) Options[:sort_order].compare(self, other) end'.should_not reek
56
59
  end
@@ -94,7 +97,7 @@ describe UtilityFunction do
94
97
 
95
98
  it_should_behave_like 'SmellDetector'
96
99
 
97
- context 'looking at the YAML' do
100
+ context 'when a smells is reported' do
98
101
  before :each do
99
102
  src = <<EOS
100
103
  def simple(arga)
@@ -103,22 +106,15 @@ end
103
106
  EOS
104
107
  source = src.to_reek_source
105
108
  sniffer = Sniffer.new(source)
106
- @mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
107
- @detector.examine_context(@mctx)
108
- warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
109
- @yaml = warning.to_yaml
110
- end
111
- it 'reports the source' do
112
- @yaml.should match(/source:\s*#{@source_name}/)
113
- end
114
- it 'reports the class' do
115
- @yaml.should match(/\sclass:\s*LowCohesion/)
116
- end
117
- it 'reports the subclass' do
118
- @yaml.should match(/subclass:\s*UtilityFunction/)
109
+ mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
110
+ @detector.examine_context(mctx)
111
+ @warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
119
112
  end
113
+
114
+ it_should_behave_like 'common fields set correctly'
115
+
120
116
  it 'reports the line number of the method' do
121
- @yaml.should match(/lines:\s*- 1/)
117
+ @warning.lines.should == [1]
122
118
  end
123
119
  end
124
120
  end