reek 1.2.7.1 → 1.2.7.2

Sign up to get free protection for your applications and to get access to all the features.
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/)