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.
- data/History.txt +13 -1
- data/config/defaults.reek +4 -1
- data/features/masking_smells.feature +7 -7
- data/features/rake_task.feature +2 -2
- data/features/reports.feature +3 -3
- data/features/samples.feature +5 -2
- data/features/yaml.feature +0 -39
- data/lib/reek.rb +1 -1
- data/lib/reek/cli/command_line.rb +3 -3
- data/lib/reek/cli/reek_command.rb +5 -6
- data/lib/reek/cli/report.rb +9 -20
- data/lib/reek/cli/yaml_command.rb +1 -1
- data/lib/reek/core/class_context.rb +1 -2
- data/lib/reek/core/code_context.rb +10 -27
- data/lib/reek/core/code_parser.rb +1 -18
- data/lib/reek/core/detector_stack.rb +4 -0
- data/lib/reek/core/masking_collection.rb +6 -0
- data/lib/reek/core/method_context.rb +8 -56
- data/lib/reek/core/module_context.rb +6 -32
- data/lib/reek/core/object_refs.rb +36 -36
- data/lib/reek/core/singleton_method_context.rb +10 -21
- data/lib/reek/core/sniffer.rb +3 -2
- data/lib/reek/examiner.rb +39 -31
- data/lib/reek/smell_warning.rb +8 -0
- data/lib/reek/smells/attribute.rb +4 -2
- data/lib/reek/smells/class_variable.rb +3 -3
- data/lib/reek/smells/control_couple.rb +1 -2
- data/lib/reek/smells/data_clump.rb +86 -9
- data/lib/reek/smells/duplication.rb +2 -3
- data/lib/reek/smells/feature_envy.rb +9 -4
- data/lib/reek/smells/simulated_polymorphism.rb +1 -2
- data/lib/reek/smells/smell_detector.rb +0 -6
- data/lib/reek/smells/uncommunicative_method_name.rb +8 -2
- data/lib/reek/smells/uncommunicative_parameter_name.rb +1 -1
- data/lib/reek/smells/uncommunicative_variable_name.rb +1 -1
- data/lib/reek/smells/utility_function.rb +17 -5
- data/lib/reek/source/reference_collector.rb +21 -0
- data/lib/reek/source/sexp_formatter.rb +1 -0
- data/lib/reek/source/tree_dresser.rb +67 -9
- data/lib/reek/spec/should_reek.rb +1 -1
- data/lib/reek/spec/should_reek_of.rb +1 -1
- data/lib/reek/spec/should_reek_only_of.rb +1 -1
- data/reek.gemspec +3 -3
- data/spec/reek/cli/reek_command_spec.rb +3 -2
- data/spec/reek/cli/report_spec.rb +2 -2
- data/spec/reek/cli/yaml_command_spec.rb +2 -2
- data/spec/reek/core/code_context_spec.rb +39 -54
- data/spec/reek/core/method_context_spec.rb +7 -26
- data/spec/reek/core/module_context_spec.rb +0 -15
- data/spec/reek/core/singleton_method_context_spec.rb +0 -6
- data/spec/reek/examiner_spec.rb +6 -6
- data/spec/reek/smells/attribute_spec.rb +30 -32
- data/spec/reek/smells/class_variable_spec.rb +15 -18
- data/spec/reek/smells/data_clump_spec.rb +22 -6
- data/spec/reek/smells/duplication_spec.rb +33 -19
- data/spec/reek/smells/feature_envy_spec.rb +82 -88
- data/spec/reek/smells/large_class_spec.rb +1 -1
- data/spec/reek/smells/smell_detector_shared.rb +1 -1
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +37 -35
- data/spec/reek/smells/utility_function_spec.rb +7 -0
- data/spec/reek/source/reference_collector_spec.rb +53 -0
- data/spec/reek/source/tree_dresser_spec.rb +10 -0
- data/spec/reek/spec/should_reek_only_of_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -0
- metadata +4 -5
- data/features/profile.feature +0 -34
- data/lib/reek/core/block_context.rb +0 -18
- 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
|
-
|
19
|
-
@detector.class_variables_in(
|
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
|
-
|
23
|
-
@detector.class_variables_in(
|
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
|
-
@
|
31
|
-
@
|
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
|
-
@
|
36
|
+
@cvars.length.should == 1
|
35
37
|
end
|
36
38
|
it 'records the variable name' do
|
37
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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}
|
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
|
-
|
34
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
11
|
-
|
12
|
-
'
|
13
|
-
|
14
|
-
|
15
|
-
'
|
16
|
-
|
17
|
-
|
18
|
-
'
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
'
|
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
|
-
|
29
|
-
|
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
|
-
|
10
|
-
'
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
'
|
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
|
-
|
26
|
-
|
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({
|
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
|
-
|
44
|
-
|
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
|
-
@
|
90
|
+
@smells[0].source.should == @source_name
|
48
91
|
end
|
49
92
|
it 'reports the class' do
|
50
|
-
@
|
93
|
+
@smells[0].smell_class.should == FeatureEnvy::SMELL_CLASS
|
51
94
|
end
|
52
95
|
it 'reports the subclass' do
|
53
|
-
@
|
96
|
+
@smells[0].subclass.should == FeatureEnvy::SMELL_SUBCLASS
|
54
97
|
end
|
55
98
|
it 'reports the envious receiver' do
|
56
|
-
@
|
99
|
+
@smells[0].smell[FeatureEnvy::RECEIVER_KEY].should == @receiver
|
57
100
|
end
|
58
101
|
it 'reports the number of references' do
|
59
|
-
@
|
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
|
-
|
214
|
+
#{@receiver}.call
|
223
215
|
self.do_nothing
|
224
|
-
|
225
|
-
|
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
|
-
|
233
|
-
|
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
|
-
@
|
230
|
+
@smells[0].source.should == @source_name
|
237
231
|
end
|
238
232
|
it 'reports the class' do
|
239
|
-
@
|
233
|
+
@smells[0].smell_class.should == FeatureEnvy::SMELL_CLASS
|
240
234
|
end
|
241
235
|
it 'reports the subclass' do
|
242
|
-
@
|
236
|
+
@smells[0].subclass.should == FeatureEnvy::SMELL_SUBCLASS
|
243
237
|
end
|
244
238
|
it 'reports the envious receiver' do
|
245
|
-
@
|
239
|
+
@smells[0].smell[FeatureEnvy::RECEIVER_KEY].should == @receiver
|
246
240
|
end
|
247
241
|
it 'reports the number of references' do
|
248
|
-
@
|
242
|
+
@smells[0].smell[FeatureEnvy::REFERENCES_KEY].should == 3
|
249
243
|
end
|
250
244
|
it 'reports the referring lines' do
|
251
245
|
pending
|
252
|
-
@
|
246
|
+
@smells[0].lines.should == [2, 4, 5]
|
253
247
|
end
|
254
248
|
end
|
255
249
|
end
|