reek 1.2.7.1 → 1.2.7.2

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 (68) hide show
  1. data/History.txt +13 -1
  2. data/config/defaults.reek +4 -1
  3. data/features/masking_smells.feature +7 -7
  4. data/features/rake_task.feature +2 -2
  5. data/features/reports.feature +3 -3
  6. data/features/samples.feature +5 -2
  7. data/features/yaml.feature +0 -39
  8. data/lib/reek.rb +1 -1
  9. data/lib/reek/cli/command_line.rb +3 -3
  10. data/lib/reek/cli/reek_command.rb +5 -6
  11. data/lib/reek/cli/report.rb +9 -20
  12. data/lib/reek/cli/yaml_command.rb +1 -1
  13. data/lib/reek/core/class_context.rb +1 -2
  14. data/lib/reek/core/code_context.rb +10 -27
  15. data/lib/reek/core/code_parser.rb +1 -18
  16. data/lib/reek/core/detector_stack.rb +4 -0
  17. data/lib/reek/core/masking_collection.rb +6 -0
  18. data/lib/reek/core/method_context.rb +8 -56
  19. data/lib/reek/core/module_context.rb +6 -32
  20. data/lib/reek/core/object_refs.rb +36 -36
  21. data/lib/reek/core/singleton_method_context.rb +10 -21
  22. data/lib/reek/core/sniffer.rb +3 -2
  23. data/lib/reek/examiner.rb +39 -31
  24. data/lib/reek/smell_warning.rb +8 -0
  25. data/lib/reek/smells/attribute.rb +4 -2
  26. data/lib/reek/smells/class_variable.rb +3 -3
  27. data/lib/reek/smells/control_couple.rb +1 -2
  28. data/lib/reek/smells/data_clump.rb +86 -9
  29. data/lib/reek/smells/duplication.rb +2 -3
  30. data/lib/reek/smells/feature_envy.rb +9 -4
  31. data/lib/reek/smells/simulated_polymorphism.rb +1 -2
  32. data/lib/reek/smells/smell_detector.rb +0 -6
  33. data/lib/reek/smells/uncommunicative_method_name.rb +8 -2
  34. data/lib/reek/smells/uncommunicative_parameter_name.rb +1 -1
  35. data/lib/reek/smells/uncommunicative_variable_name.rb +1 -1
  36. data/lib/reek/smells/utility_function.rb +17 -5
  37. data/lib/reek/source/reference_collector.rb +21 -0
  38. data/lib/reek/source/sexp_formatter.rb +1 -0
  39. data/lib/reek/source/tree_dresser.rb +67 -9
  40. data/lib/reek/spec/should_reek.rb +1 -1
  41. data/lib/reek/spec/should_reek_of.rb +1 -1
  42. data/lib/reek/spec/should_reek_only_of.rb +1 -1
  43. data/reek.gemspec +3 -3
  44. data/spec/reek/cli/reek_command_spec.rb +3 -2
  45. data/spec/reek/cli/report_spec.rb +2 -2
  46. data/spec/reek/cli/yaml_command_spec.rb +2 -2
  47. data/spec/reek/core/code_context_spec.rb +39 -54
  48. data/spec/reek/core/method_context_spec.rb +7 -26
  49. data/spec/reek/core/module_context_spec.rb +0 -15
  50. data/spec/reek/core/singleton_method_context_spec.rb +0 -6
  51. data/spec/reek/examiner_spec.rb +6 -6
  52. data/spec/reek/smells/attribute_spec.rb +30 -32
  53. data/spec/reek/smells/class_variable_spec.rb +15 -18
  54. data/spec/reek/smells/data_clump_spec.rb +22 -6
  55. data/spec/reek/smells/duplication_spec.rb +33 -19
  56. data/spec/reek/smells/feature_envy_spec.rb +82 -88
  57. data/spec/reek/smells/large_class_spec.rb +1 -1
  58. data/spec/reek/smells/smell_detector_shared.rb +1 -1
  59. data/spec/reek/smells/uncommunicative_method_name_spec.rb +37 -35
  60. data/spec/reek/smells/utility_function_spec.rb +7 -0
  61. data/spec/reek/source/reference_collector_spec.rb +53 -0
  62. data/spec/reek/source/tree_dresser_spec.rb +10 -0
  63. data/spec/reek/spec/should_reek_only_of_spec.rb +1 -1
  64. data/spec/spec_helper.rb +7 -0
  65. metadata +4 -5
  66. data/features/profile.feature +0 -34
  67. data/lib/reek/core/block_context.rb +0 -18
  68. data/spec/reek/core/block_context_spec.rb +0 -26
@@ -13,37 +13,37 @@ describe ClassVariable do
13
13
  @class_variable = '@@things'
14
14
  end
15
15
 
16
+ it_should_behave_like 'SmellDetector'
17
+
16
18
  context 'with no class variables' do
17
19
  it 'records nothing in the class' do
18
- ctx = ClassContext.from_s('class Fred; end')
19
- @detector.class_variables_in(ctx).should be_empty
20
+ exp = ast(:class, :Fred)
21
+ @detector.class_variables_in(exp).should be_empty
20
22
  end
21
23
  it 'records nothing in the module' do
22
- ctx = ModuleContext.from_s('module Fred; end')
23
- @detector.class_variables_in(ctx).should be_empty
24
+ exp = ast(:module, :Fred)
25
+ @detector.class_variables_in(exp).should be_empty
24
26
  end
25
27
  end
26
28
 
27
29
  context 'with one class variable' do
28
30
  shared_examples_for 'one variable found' do
29
31
  before :each do
30
- @detector.examine_context(@ctx)
31
- @smells = @detector.smells_found
32
+ ast = @src.to_reek_source.syntax_tree
33
+ @cvars = @detector.class_variables_in(ast).to_a
32
34
  end
33
35
  it 'records only that class variable' do
34
- @smells.length.should == 1
36
+ @cvars.length.should == 1
35
37
  end
36
38
  it 'records the variable name' do
37
- @smells.each do |warning|
38
- warning.smell['variable'].should == @class_variable
39
- end
39
+ @cvars[0].to_s.should == @class_variable
40
40
  end
41
41
  end
42
42
 
43
43
  ['class', 'module'].each do |scope|
44
44
  context "declared in a #{scope}" do
45
45
  before :each do
46
- @ctx = ClassContext.from_s("#{scope} Fred; #{@class_variable} = {}; end")
46
+ @src = "#{scope} Fred; #{@class_variable} = {}; end"
47
47
  end
48
48
 
49
49
  it_should_behave_like 'one variable found'
@@ -51,7 +51,7 @@ describe ClassVariable do
51
51
 
52
52
  context "used in a #{scope}" do
53
53
  before :each do
54
- @ctx = ClassContext.from_s("#{scope} Fred; def jim() #{@class_variable} = {}; end; end")
54
+ @src = "#{scope} Fred; def jim() #{@class_variable} = {}; end; end"
55
55
  end
56
56
 
57
57
  it_should_behave_like 'one variable found'
@@ -59,7 +59,7 @@ describe ClassVariable do
59
59
 
60
60
  context "indexed in a #{scope}" do
61
61
  before :each do
62
- @ctx = ClassContext.from_s("#{scope} Fred; def jim() #{@class_variable}[mash] = {}; end; end")
62
+ @src = "#{scope} Fred; def jim() #{@class_variable}[mash] = {}; end; end"
63
63
  end
64
64
 
65
65
  it_should_behave_like 'one variable found'
@@ -67,7 +67,7 @@ describe ClassVariable do
67
67
 
68
68
  context "declared and used in a #{scope}" do
69
69
  before :each do
70
- @ctx = ClassContext.from_s("#{scope} Fred; #{@class_variable} = {}; def jim() #{@class_variable} = {}; end; end")
70
+ @src = "#{scope} Fred; #{@class_variable} = {}; def jim() #{@class_variable} = {}; end; end"
71
71
  end
72
72
 
73
73
  it_should_behave_like 'one variable found'
@@ -75,14 +75,11 @@ describe ClassVariable do
75
75
 
76
76
  context "used twice in a #{scope}" do
77
77
  before :each do
78
- @ctx = ClassContext.from_s("#{scope} Fred; def jeff() #{@class_variable} = {}; end; def jim() #{@class_variable} = {}; end; end")
78
+ @src = "#{scope} Fred; def jeff() #{@class_variable} = {}; end; def jim() #{@class_variable} = {}; end; end"
79
79
  end
80
80
 
81
81
  it_should_behave_like 'one variable found'
82
82
  end
83
83
  end
84
-
85
84
  end
86
-
87
- it_should_behave_like 'SmellDetector'
88
85
  end
@@ -20,8 +20,9 @@ EOS
20
20
 
21
21
  context 'with 3 identical pairs' do
22
22
  before :each do
23
+ @module_name = 'Scrunch'
23
24
  @src = <<EOS
24
- #{@context} Scrunch
25
+ #{@context} #{@module_name}
25
26
  def first(pa, pb) @field == :sym ? 0 : 3; end
26
27
  def second(pa, pb) @field == :sym; end
27
28
  def third(pa, pb) pa - pb + @fred; end
@@ -30,15 +31,30 @@ EOS
30
31
  ctx = ModuleContext.from_s(@src)
31
32
  detector = DataClump.new('newt')
32
33
  detector.examine(ctx)
33
- warning = detector.smells_found.to_a[0] # SMELL: too cumbersome!
34
- @yaml = warning.to_yaml
34
+ @smells = detector.smells_found.to_a
35
+ @warning = @smells[0] # SMELL: too cumbersome!
36
+ @yaml = @warning.to_yaml
37
+ end
38
+ it 'records only the one smell' do
39
+ @smells.length.should == 1
35
40
  end
36
41
  it 'reports all parameters' do
37
- @yaml.should match(/parameters:[\s-]*pa/)
38
- @yaml.should match(/parameters:[\spa-]*pb/)
42
+ @smells[0].smell[DataClump::PARAMETERS_KEY].should == ['pa', 'pb']
39
43
  end
40
44
  it 'reports the number of occurrences' do
41
- @yaml.should match(/occurrences:\s*3/)
45
+ @smells[0].smell['occurrences'].should == 3
46
+ end
47
+ it 'reports all parameters' do
48
+ @smells[0].smell[DataClump::METHODS_KEY].should == ['first', 'second', 'third']
49
+ end
50
+ it 'reports the declaration line numbers' do
51
+ @smells[0].lines.should == [2,3,4]
52
+ end
53
+ it 'reports the correct smell class' do
54
+ @smells[0].smell_class.should == DataClump::SMELL_CLASS
55
+ end
56
+ it 'reports the context fq name' do
57
+ @smells[0].context.should == @module_name
42
58
  end
43
59
  end
44
60
 
@@ -7,26 +7,40 @@ require File.join(File.dirname(File.expand_path(__FILE__)), 'smell_detector_shar
7
7
  include Reek
8
8
  include Reek::Smells
9
9
 
10
- describe Duplication, "repeated method calls" do
11
- it 'reports repeated call' do
12
- 'def double_thing() @other.thing + @other.thing end'.should reek_only_of(:Duplication, /@other.thing/)
13
- end
14
- it 'reports repeated call to lvar' do
15
- 'def double_thing(other) other[@thing] + other[@thing] end'.should reek_only_of(:Duplication, /other\[@thing\]/)
16
- end
17
- it 'reports call parameters' do
18
- 'def double_thing() @other.thing(2,3) + @other.thing(2,3) end'.should reek_only_of(:Duplication, /@other.thing\(2, 3\)/)
19
- end
20
- it 'should report nested calls' do
21
- ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'
22
- ruby.should reek_of(:Duplication, /@other.thing[^\.]/)
23
- ruby.should reek_of(:Duplication, /@other.thing.foo/)
24
- end
25
- it 'should ignore calls to new' do
26
- 'def double_thing() @other.new + @other.new end'.should_not reek
10
+ describe Duplication do
11
+ context "with repeated method calls" do
12
+ it 'reports repeated call' do
13
+ 'def double_thing() @other.thing + @other.thing end'.should reek_only_of(:Duplication, /@other.thing/)
14
+ end
15
+ it 'reports repeated call to lvar' do
16
+ 'def double_thing(other) other[@thing] + other[@thing] end'.should reek_only_of(:Duplication, /other\[@thing\]/)
17
+ end
18
+ it 'reports call parameters' do
19
+ 'def double_thing() @other.thing(2,3) + @other.thing(2,3) end'.should reek_only_of(:Duplication, /@other.thing\(2, 3\)/)
20
+ end
21
+ it 'should report nested calls' do
22
+ ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'
23
+ ruby.should reek_of(:Duplication, /@other.thing[^\.]/)
24
+ ruby.should reek_of(:Duplication, /@other.thing.foo/)
25
+ end
26
+ it 'should ignore calls to new' do
27
+ 'def double_thing() @other.new + @other.new end'.should_not reek
28
+ end
27
29
  end
28
- it 'reports repeated assignment' do
29
- 'def double_thing(thing) @other[thing] = true; @other[thing] = true; end'.should reek_only_of(:Duplication, /@other\[thing\] = true/)
30
+
31
+ context 'with repeated attribute assignment' do
32
+ it 'reports repeated assignment' do
33
+ 'def double_thing(thing) @other[thing] = true; @other[thing] = true; end'.should reek_only_of(:Duplication, /@other\[thing\] = true/)
34
+ end
35
+ it 'does not report multi-assignments' do
36
+ src = <<EOS
37
+ def _parse ctxt
38
+ ctxt.index, result = @ind, @result
39
+ error, ctxt.index = @err, @err_ind
40
+ end
41
+ EOS
42
+ src.should_not reek
43
+ end
30
44
  end
31
45
  end
32
46
 
@@ -6,24 +6,65 @@ include Reek
6
6
  include Reek::Smells
7
7
 
8
8
  describe FeatureEnvy do
9
- it 'should not report use of self' do
10
- 'def simple() self.to_s + self.to_i end'.should_not reek
11
- end
12
-
13
- it 'should not report vcall with no argument' do
14
- 'def simple() func; end'.should_not reek
15
- end
16
-
17
- it 'should not report vcall with argument' do
18
- 'def simple(arga) func(17); end'.should_not reek
19
- end
20
-
21
- it 'should not report single use' do
22
- 'def no_envy(arga) arga.barg(@item) end'.should_not reek
9
+ context 'with no smell' do
10
+ it 'should not report use of self' do
11
+ 'def simple() self.to_s + self.to_i end'.should_not reek
12
+ end
13
+ it 'should not report vcall with no argument' do
14
+ 'def simple() func; end'.should_not reek
15
+ end
16
+ it 'should not report vcall with argument' do
17
+ 'def simple(arga) func(17); end'.should_not reek
18
+ end
19
+ it 'should not report single use' do
20
+ 'def no_envy(arga) arga.barg(@item) end'.should_not reek
21
+ end
22
+ it 'should not report return value' do
23
+ 'def no_envy(arga) arga.barg(@item); arga end'.should_not reek
24
+ end
25
+ it 'should ignore global variables' do
26
+ 'def no_envy() $s2.to_a; $s2[@item] end'.should_not reek
27
+ end
28
+ it 'should not report class methods' do
29
+ 'def simple() self.class.new.flatten_merge(self) end'.should_not reek
30
+ end
31
+ it 'should not report single use of an ivar' do
32
+ 'def no_envy() @item.to_a end'.should_not reek
33
+ end
34
+ it 'should not report returning an ivar' do
35
+ 'def no_envy() @item.to_a; @item end'.should_not reek
36
+ end
37
+ it 'should not report ivar usage in a parameter' do
38
+ 'def no_envy() @item.price + tax(@item) - savings(@item) end'.should_not reek
39
+ end
40
+ it 'should not report single use of an lvar' do
41
+ 'def no_envy() lv = @item; lv.to_a end'.should_not reek
42
+ end
43
+ it 'should not report returning an lvar' do
44
+ 'def no_envy() lv = @item; lv.to_a; lv end'.should_not reek
45
+ end
46
+ it 'ignores lvar usage in a parameter' do
47
+ 'def no_envy() lv = @item; lv.price + tax(lv) - savings(lv); end'.should_not reek
48
+ end
49
+ it 'ignores multiple ivars' do
50
+ src = <<EOS
51
+ def func
52
+ @other.a
53
+ @other.b
54
+ @nother.c
55
+ @nother.d
23
56
  end
24
-
25
- it 'should not report return value' do
26
- 'def no_envy(arga) arga.barg(@item); arga end'.should_not reek
57
+ EOS
58
+ src.should_not reek
59
+ #
60
+ # def other.func(me)
61
+ # a
62
+ # b
63
+ # me.nother_c
64
+ # me.nother_d
65
+ # end
66
+ #
67
+ end
27
68
  end
28
69
 
29
70
  context 'with 2 calls to a parameter' do
@@ -37,26 +78,28 @@ describe FeatureEnvy do
37
78
  @source_name = 'green as a cucumber'
38
79
  @receiver = 'blah'
39
80
  @ctx = mock('method_context', :null_object => true)
40
- @ctx.should_receive(:envious_receivers).and_return({s(:lvar, @receiver) => 4})
81
+ @ctx.should_receive(:envious_receivers).and_return({ast(:lvar, @receiver) => 4})
41
82
  @detector = FeatureEnvy.new(@source_name)
42
83
  @detector.examine_context(@ctx)
43
- warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
44
- @yaml = warning.to_yaml
84
+ @smells = @detector.smells_found.to_a
85
+ end
86
+ it 'reports only that smell' do
87
+ @smells.length.should == 1
45
88
  end
46
89
  it 'reports the source' do
47
- @yaml.should match(/source:\s*#{@source_name}/)
90
+ @smells[0].source.should == @source_name
48
91
  end
49
92
  it 'reports the class' do
50
- @yaml.should match(/\sclass:\s*LowCohesion/)
93
+ @smells[0].smell_class.should == FeatureEnvy::SMELL_CLASS
51
94
  end
52
95
  it 'reports the subclass' do
53
- @yaml.should match(/subclass:\s*FeatureEnvy/)
96
+ @smells[0].subclass.should == FeatureEnvy::SMELL_SUBCLASS
54
97
  end
55
98
  it 'reports the envious receiver' do
56
- @yaml.should match(/receiver:[\s]*#{@receiver}/)
99
+ @smells[0].smell[FeatureEnvy::RECEIVER_KEY].should == @receiver
57
100
  end
58
101
  it 'reports the number of references' do
59
- @yaml.should match(/references:\s*4/)
102
+ @smells[0].smell[FeatureEnvy::REFERENCES_KEY].should == 4
60
103
  end
61
104
  end
62
105
 
@@ -86,26 +129,6 @@ EOS
86
129
  src.should reek_of(:FeatureEnvy, /fred/)
87
130
  end
88
131
 
89
- it 'should ignore global variables' do
90
- 'def no_envy() $s2.to_a; $s2[@item] end'.should_not reek
91
- end
92
-
93
- it 'should not report class methods' do
94
- 'def simple() self.class.new.flatten_merge(self) end'.should_not reek
95
- end
96
-
97
- it 'should not report single use of an ivar' do
98
- 'def no_envy() @item.to_a end'.should_not reek
99
- end
100
-
101
- it 'should not report returning an ivar' do
102
- 'def no_envy() @item.to_a; @item end'.should_not reek
103
- end
104
-
105
- it 'should not report ivar usage in a parameter' do
106
- 'def no_envy() @item.price + tax(@item) - savings(@item) end'.should_not reek
107
- end
108
-
109
132
  it 'should not be fooled by duplication' do
110
133
  'def feed(thing) @cow.feed_to(thing.pig); @duck.feed_to(thing.pig) end'.should reek_only_of(:Duplication, /thing.pig/)
111
134
  end
@@ -114,14 +137,6 @@ EOS
114
137
  'def feed(thing) cow.feed_to(thing.pig); duck.feed_to(thing.pig) end'.should reek_only_of(:Duplication, /thing.pig/)
115
138
  end
116
139
 
117
- it 'should not report single use of an lvar' do
118
- 'def no_envy() lv = @item; lv.to_a end'.should_not reek
119
- end
120
-
121
- it 'should not report returning an lvar' do
122
- 'def no_envy() lv = @item; lv.to_a; lv end'.should_not reek
123
- end
124
-
125
140
  it 'should report many calls to lvar' do
126
141
  'def envy() lv = @item; lv.price + lv.tax; end'.should reek_only_of(:FeatureEnvy, /lv/)
127
142
  #
@@ -134,30 +149,6 @@ EOS
134
149
  # end
135
150
  end
136
151
 
137
- it 'ignores lvar usage in a parameter' do
138
- 'def no_envy() lv = @item; lv.price + tax(lv) - savings(lv); end'.should_not reek
139
- end
140
-
141
- it 'ignores multiple ivars' do
142
- src = <<EOS
143
- def func
144
- @other.a
145
- @other.b
146
- @nother.c
147
- @nother.d
148
- end
149
- EOS
150
- src.should_not reek
151
- #
152
- # def other.func(me)
153
- # a
154
- # b
155
- # me.nother_c
156
- # me.nother_d
157
- # end
158
- #
159
- end
160
-
161
152
  it 'ignores frequent use of a call' do
162
153
  'def func() other.a; other.b; nother.c end'.should_not reek_of(:FeatureEnvy)
163
154
  end
@@ -217,39 +208,42 @@ describe FeatureEnvy do
217
208
 
218
209
  context 'when reporting yaml' do
219
210
  before :each do
211
+ @receiver = 'other'
220
212
  src = <<EOS
221
213
  def envious(other)
222
- other.call
214
+ #{@receiver}.call
223
215
  self.do_nothing
224
- other.other
225
- other.fred
216
+ #{@receiver}.other
217
+ #{@receiver}.fred
226
218
  end
227
219
  EOS
228
220
  source = src.to_reek_source
229
221
  sniffer = Sniffer.new(source)
230
222
  @mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
231
223
  @detector.examine_context(@mctx)
232
- warning = @detector.smells_found.to_a[0] # SMELL: too cumbersome!
233
- @yaml = warning.to_yaml
224
+ @smells = @detector.smells_found.to_a
225
+ end
226
+ it 'reports only that smell' do
227
+ @smells.length.should == 1
234
228
  end
235
229
  it 'reports the source' do
236
- @yaml.should match(/source:\s*#{@source_name}/)
230
+ @smells[0].source.should == @source_name
237
231
  end
238
232
  it 'reports the class' do
239
- @yaml.should match(/class:\s*LowCohesion/)
233
+ @smells[0].smell_class.should == FeatureEnvy::SMELL_CLASS
240
234
  end
241
235
  it 'reports the subclass' do
242
- @yaml.should match(/subclass:\s*FeatureEnvy/)
236
+ @smells[0].subclass.should == FeatureEnvy::SMELL_SUBCLASS
243
237
  end
244
238
  it 'reports the envious receiver' do
245
- @yaml.should match(/receiver:\s*other/)
239
+ @smells[0].smell[FeatureEnvy::RECEIVER_KEY].should == @receiver
246
240
  end
247
241
  it 'reports the number of references' do
248
- @yaml.should match(/references:\s*3/)
242
+ @smells[0].smell[FeatureEnvy::REFERENCES_KEY].should == 3
249
243
  end
250
244
  it 'reports the referring lines' do
251
245
  pending
252
- @yaml.should match(/lines:\s*- 2\s*- 4\s*- 5/)
246
+ @smells[0].lines.should == [2, 4, 5]
253
247
  end
254
248
  end
255
249
  end
@@ -104,7 +104,7 @@ class Full
104
104
  def me51x()3 end
105
105
  end
106
106
  EOS
107
- @yaml = Examiner.new(src).all_smells[0].to_yaml
107
+ @yaml = Examiner.new(src).smells[0].to_yaml
108
108
  end
109
109
  it 'reports the source' do
110
110
  @yaml.should match(/source:\s*string/)