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