reek 1.2.7.2 → 1.2.7.3

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