reek 1.2.2 → 1.2.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.
- data/History.txt +4 -0
- data/Rakefile +0 -1
- data/config/defaults.reek +4 -6
- data/features/masking_smells.feature +9 -9
- data/features/options.feature +2 -2
- data/features/profile.feature +34 -0
- data/features/rake_task.feature +74 -0
- data/features/reports.feature +1 -1
- data/features/samples.feature +4 -4
- data/features/stdin.feature +1 -1
- data/features/step_definitions/reek_steps.rb +11 -7
- data/features/support/env.rb +26 -18
- data/lib/reek.rb +1 -1
- data/lib/reek/adapters/application.rb +9 -2
- data/lib/reek/adapters/command_line.rb +2 -2
- data/lib/reek/adapters/source.rb +4 -1
- data/lib/reek/adapters/spec.rb +1 -3
- data/lib/reek/block_context.rb +14 -8
- data/lib/reek/class_context.rb +6 -66
- data/lib/reek/code_context.rb +10 -0
- data/lib/reek/code_parser.rb +25 -53
- data/lib/reek/configuration.rb +12 -6
- data/lib/reek/if_context.rb +2 -3
- data/lib/reek/method_context.rb +3 -12
- data/lib/reek/module_context.rb +30 -22
- data/lib/reek/name.rb +2 -0
- data/lib/reek/object_refs.rb +0 -3
- data/lib/reek/sexp_formatter.rb +0 -2
- data/lib/reek/smells/class_variable.rb +17 -4
- data/lib/reek/smells/control_couple.rb +3 -10
- data/lib/reek/smells/data_clump.rb +10 -10
- data/lib/reek/smells/feature_envy.rb +1 -8
- data/lib/reek/smells/large_class.rb +3 -3
- data/lib/reek/smells/simulated_polymorphism.rb +17 -3
- data/lib/reek/smells/smell_detector.rb +11 -2
- data/lib/reek/smells/utility_function.rb +1 -1
- data/lib/reek/sniffer.rb +0 -8
- data/lib/reek/stop_context.rb +1 -1
- data/lib/reek/tree_dresser.rb +74 -0
- data/reek.gemspec +3 -3
- data/spec/reek/block_context_spec.rb +6 -6
- data/spec/reek/class_context_spec.rb +2 -23
- data/spec/reek/code_context_spec.rb +149 -67
- data/spec/reek/code_parser_spec.rb +0 -102
- data/spec/reek/method_context_spec.rb +4 -4
- data/spec/reek/singleton_method_context_spec.rb +1 -1
- data/spec/reek/smells/attribute_spec.rb +1 -1
- data/spec/reek/smells/class_variable_spec.rb +96 -7
- data/spec/reek/smells/control_couple_spec.rb +1 -1
- data/spec/reek/smells/data_clump_spec.rb +31 -13
- data/spec/reek/smells/feature_envy_spec.rb +1 -1
- data/spec/reek/smells/large_class_spec.rb +32 -18
- data/spec/reek/smells/simulated_polymorphism_spec.rb +66 -18
- data/spec/reek/sniffer_spec.rb +1 -0
- data/spec/samples/not_quite_masked/dirty.rb +2 -0
- data/spec/spec_helper.rb +1 -1
- data/tasks/reek.rake +1 -1
- metadata +5 -3
- data/lib/reek/exceptions.reek +0 -20
@@ -9,37 +9,37 @@ describe BlockContext do
|
|
9
9
|
|
10
10
|
it "should record single parameter" do
|
11
11
|
element = StopContext.new
|
12
|
-
element = BlockContext.new(element, s(s(:lasgn, :x), nil))
|
12
|
+
element = BlockContext.new(element, s(:iter, nil, s(:lasgn, :x), nil))
|
13
13
|
element.variable_names.should == [Name.new(:x)]
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should record single parameter within a method" do
|
17
17
|
element = StopContext.new
|
18
18
|
element = MethodContext.new(element, s(:defn, :help))
|
19
|
-
element = BlockContext.new(element, s(s(:lasgn, :x), nil))
|
19
|
+
element = BlockContext.new(element, s(:iter, nil, s(:lasgn, :x), nil))
|
20
20
|
element.variable_names.should == [Name.new(:x)]
|
21
21
|
end
|
22
22
|
|
23
23
|
it "records multiple parameters" do
|
24
24
|
element = StopContext.new
|
25
|
-
element = BlockContext.new(element, s(s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))), nil))
|
25
|
+
element = BlockContext.new(element, s(:iter, nil, s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))), nil))
|
26
26
|
element.variable_names.should == [Name.new(:x), Name.new(:y)]
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should not pass parameters upward" do
|
30
30
|
mc = MethodContext.new(StopContext.new, s(:defn, :help, s(:args)))
|
31
|
-
element = BlockContext.new(mc, s(s(:lasgn, :x)))
|
31
|
+
element = BlockContext.new(mc, s(:iter, nil, s(:lasgn, :x)))
|
32
32
|
mc.variable_names.should be_empty
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'records local variables' do
|
36
|
-
bctx = BlockContext.new(StopContext.new, nil)
|
36
|
+
bctx = BlockContext.new(StopContext.new, s(nil, nil))
|
37
37
|
bctx.record_local_variable(:q2)
|
38
38
|
bctx.variable_names.should include(Name.new(:q2))
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'copes with a yield to an ivar' do
|
42
|
-
scope = BlockContext.new(StopContext.new,
|
42
|
+
scope = BlockContext.new(StopContext.new, s(:iter, nil, s(:iasgn, :@list), s(:self)))
|
43
43
|
scope.record_instance_variable(:@list)
|
44
44
|
scope.variable_names.should == [:@list]
|
45
45
|
end
|
@@ -150,33 +150,12 @@ describe CodeContext, 'find class' do
|
|
150
150
|
end
|
151
151
|
|
152
152
|
describe ClassContext do
|
153
|
-
it '
|
153
|
+
it 'does not report empty class in another module' do
|
154
154
|
'class Treetop::Runtime::SyntaxNode; end'.should_not reek
|
155
155
|
end
|
156
156
|
|
157
|
-
it '
|
157
|
+
it 'deals with :: scoped names' do
|
158
158
|
element = ClassContext.create(StopContext.new, [:colon2, [:colon2, [:const, :Treetop], :Runtime], :SyntaxNode])
|
159
159
|
element.num_methods.should == 0
|
160
160
|
end
|
161
|
-
|
162
|
-
it 'counts conditionals correctly' do
|
163
|
-
src = <<EOS
|
164
|
-
class Scrunch
|
165
|
-
def first
|
166
|
-
return @field == :sym ? 0 : 3;
|
167
|
-
end
|
168
|
-
def second
|
169
|
-
if @field == :sym
|
170
|
-
@other += " quarts"
|
171
|
-
end
|
172
|
-
end
|
173
|
-
def third
|
174
|
-
raise 'flu!' unless @field == :sym
|
175
|
-
end
|
176
|
-
end
|
177
|
-
EOS
|
178
|
-
|
179
|
-
ctx = ClassContext.from_s(src)
|
180
|
-
ctx.conditionals.length.should == 3
|
181
|
-
end
|
182
161
|
end
|
@@ -9,85 +9,167 @@ require 'reek/stop_context'
|
|
9
9
|
|
10
10
|
include Reek
|
11
11
|
|
12
|
-
describe CodeContext
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
describe CodeContext do
|
13
|
+
context 'to_s' do
|
14
|
+
it "reports the full context" do
|
15
|
+
element = StopContext.new
|
16
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
17
|
+
element = ClassContext.new(element, [0, :klass], s())
|
18
|
+
element = MethodContext.new(element, [0, :bad])
|
19
|
+
element = BlockContext.new(element, s(nil, nil))
|
20
|
+
element.to_s.should match(/bad/)
|
21
|
+
element.to_s.should match(/klass/)
|
22
|
+
element.to_s.should match(/mod/)
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
it 'reports the method name via if context' do
|
26
|
+
element1 = StopContext.new
|
27
|
+
element2 = MethodContext.new(element1, [0, :bad])
|
28
|
+
element3 = IfContext.new(element2, [0,1])
|
29
|
+
BlockContext.new(element3, s(nil, nil)).to_s.should match(/bad/)
|
30
|
+
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
it 'reports the method name via nested blocks' do
|
33
|
+
element1 = StopContext.new
|
34
|
+
element2 = MethodContext.new(element1, [0, :bad])
|
35
|
+
element3 = BlockContext.new(element2, s(nil, nil))
|
36
|
+
BlockContext.new(element3, s(nil, nil)).to_s.should match(/bad/)
|
37
|
+
end
|
37
38
|
end
|
38
|
-
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
40
|
+
context 'instance variables' do
|
41
|
+
it 'should pass instance variables down to the first class' do
|
42
|
+
element = StopContext.new
|
43
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
44
|
+
class_element = ClassContext.new(element, [0, :klass], s())
|
45
|
+
element = MethodContext.new(class_element, [0, :bad])
|
46
|
+
element = BlockContext.new(element, s(nil, nil))
|
47
|
+
element.record_instance_variable(:fred)
|
48
|
+
class_element.variable_names.size.should == 1
|
49
|
+
class_element.variable_names.should include(Name.new(:fred))
|
50
|
+
end
|
50
51
|
end
|
51
|
-
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
53
|
+
context 'generics' do
|
54
|
+
it 'should pass unknown method calls down the stack' do
|
55
|
+
stop = StopContext.new
|
56
|
+
def stop.bananas(arg1, arg2) arg1 + arg2 + 43 end
|
57
|
+
element = ModuleContext.new(stop, Name.new(:mod), s(:module, :mod, nil))
|
58
|
+
class_element = ClassContext.new(element, [0, :klass], s())
|
59
|
+
element = MethodContext.new(class_element, [0, :bad])
|
60
|
+
element = BlockContext.new(element, s(nil, nil))
|
61
|
+
element.bananas(17, -5).should == 55
|
62
|
+
end
|
62
63
|
end
|
63
|
-
end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
65
|
+
context 'name matching' do
|
66
|
+
it 'should recognise itself in a collection of names' do
|
67
|
+
element = StopContext.new
|
68
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
69
|
+
element.matches?(['banana', 'mod']).should == true
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should recognise itself in a collection of REs' do
|
73
|
+
element = StopContext.new
|
74
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
75
|
+
element.matches?([/banana/, /mod/]).should == true
|
76
|
+
end
|
71
77
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
78
|
+
it 'should recognise its fq name in a collection of names' do
|
79
|
+
element = StopContext.new
|
80
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
81
|
+
element = ClassContext.create(element, s(:class, :klass))
|
82
|
+
element.matches?(['banana', 'mod']).should == true
|
83
|
+
element.matches?(['banana', 'mod::klass']).should == true
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should recognise its fq name in a collection of names' do
|
87
|
+
element = StopContext.new
|
88
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
89
|
+
element = ClassContext.create(element, s(:class, :klass))
|
90
|
+
element.matches?([/banana/, /mod/]).should == true
|
91
|
+
element.matches?([/banana/, /mod::klass/]).should == true
|
92
|
+
end
|
76
93
|
end
|
77
94
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
95
|
+
context 'enumerating syntax elements' do
|
96
|
+
context 'in an empty module' do
|
97
|
+
before :each do
|
98
|
+
@module_name = 'Emptiness'
|
99
|
+
src = "module #{@module_name}; end"
|
100
|
+
ast = src.to_reek_source.syntax_tree
|
101
|
+
@ctx = CodeContext.new(nil, ast)
|
102
|
+
end
|
103
|
+
it 'yields no calls' do
|
104
|
+
@ctx.each(:call, []) {|exp| raise "#{exp} yielded by empty module!"}
|
105
|
+
end
|
106
|
+
it 'yields one module' do
|
107
|
+
mods = 0
|
108
|
+
@ctx.each(:module, []) {|exp| mods += 1}
|
109
|
+
mods.should == 1
|
110
|
+
end
|
111
|
+
it "yields the module's full AST" do
|
112
|
+
@ctx.each(:module, []) {|exp| exp[1].should == @module_name.to_sym}
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'with no block' do
|
116
|
+
it 'returns an empty array of ifs' do
|
117
|
+
@ctx.each(:if, []).should be_empty
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'with a nested element' do
|
123
|
+
before :each do
|
124
|
+
@module_name = 'Loneliness'
|
125
|
+
@method_name = 'calloo'
|
126
|
+
src = "module #{@module_name}; def #{@method_name}; puts('hello') end; end"
|
127
|
+
ast = src.to_reek_source.syntax_tree
|
128
|
+
@ctx = CodeContext.new(nil, ast)
|
129
|
+
end
|
130
|
+
it 'yields no ifs' do
|
131
|
+
@ctx.each(:if, []) {|exp| raise "#{exp} yielded by empty module!"}
|
132
|
+
end
|
133
|
+
it 'yields one module' do
|
134
|
+
@ctx.each(:module, []).length.should == 1
|
135
|
+
end
|
136
|
+
it "yields the module's full AST" do
|
137
|
+
@ctx.each(:module, []) {|exp| exp[1].should == @module_name.to_sym}
|
138
|
+
end
|
139
|
+
it 'yields one method' do
|
140
|
+
@ctx.each(:defn, []).length.should == 1
|
141
|
+
end
|
142
|
+
it "yields the method's full AST" do
|
143
|
+
@ctx.each(:defn, []) {|exp| exp[1].should == @method_name.to_sym}
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'pruning the traversal' do
|
147
|
+
it 'ignores the call inside the method' do
|
148
|
+
@ctx.each(:call, [:defn]).should be_empty
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'finds 3 ifs in a class' do
|
154
|
+
src = <<EOS
|
155
|
+
class Scrunch
|
156
|
+
def first
|
157
|
+
return @field == :sym ? 0 : 3;
|
158
|
+
end
|
159
|
+
def second
|
160
|
+
if @field == :sym
|
161
|
+
@other += " quarts"
|
162
|
+
end
|
84
163
|
end
|
164
|
+
def third
|
165
|
+
raise 'flu!' unless @field == :sym
|
166
|
+
end
|
167
|
+
end
|
168
|
+
EOS
|
85
169
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
element.matches?([/banana/, /mod/]).should == true
|
91
|
-
element.matches?([/banana/, /mod::klass/]).should == true
|
170
|
+
ast = src.to_reek_source.syntax_tree
|
171
|
+
ctx = CodeContext.new(nil, ast)
|
172
|
+
ctx.each(:if, []).length.should == 3
|
173
|
+
end
|
92
174
|
end
|
93
175
|
end
|
@@ -39,108 +39,6 @@ describe CodeParser do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
describe CodeParser do
|
42
|
-
context 'with no class variables' do
|
43
|
-
it 'records nothing in the class' do
|
44
|
-
klass = ClassContext.from_s('class Fred; end')
|
45
|
-
klass.class_variables.should be_empty
|
46
|
-
end
|
47
|
-
it 'records nothing in the module' do
|
48
|
-
ctx = ModuleContext.from_s('module Fred; end')
|
49
|
-
ctx.class_variables.should be_empty
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
context 'with one class variable' do
|
54
|
-
shared_examples_for 'one variable found' do
|
55
|
-
it 'records the class variable' do
|
56
|
-
@ctx.class_variables.should include(Name.new(:@@tools))
|
57
|
-
end
|
58
|
-
it 'records only that class variable' do
|
59
|
-
@ctx.class_variables.length.should == 1
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
context 'declared in a class' do
|
64
|
-
before :each do
|
65
|
-
@ctx = ClassContext.from_s('class Fred; @@tools = {}; end')
|
66
|
-
end
|
67
|
-
|
68
|
-
it_should_behave_like 'one variable found'
|
69
|
-
end
|
70
|
-
|
71
|
-
context 'used in a class' do
|
72
|
-
before :each do
|
73
|
-
@ctx = ClassContext.from_s('class Fred; def jim() @@tools = {}; end; end')
|
74
|
-
end
|
75
|
-
|
76
|
-
it_should_behave_like 'one variable found'
|
77
|
-
end
|
78
|
-
|
79
|
-
context 'indexed in a class' do
|
80
|
-
before :each do
|
81
|
-
@ctx = ClassContext.from_s('class Fred; def jim() @@tools[mash] = {}; end; end')
|
82
|
-
end
|
83
|
-
|
84
|
-
it_should_behave_like 'one variable found'
|
85
|
-
end
|
86
|
-
|
87
|
-
context 'declared and used in a class' do
|
88
|
-
before :each do
|
89
|
-
@ctx = ClassContext.from_s('class Fred; @@tools = {}; def jim() @@tools = {}; end; end')
|
90
|
-
end
|
91
|
-
|
92
|
-
it_should_behave_like 'one variable found'
|
93
|
-
end
|
94
|
-
|
95
|
-
context 'used twice in a class' do
|
96
|
-
before :each do
|
97
|
-
@ctx = ClassContext.from_s('class Fred; def jeff() @@tools = {}; end; def jim() @@tools = {}; end; end')
|
98
|
-
end
|
99
|
-
|
100
|
-
it_should_behave_like 'one variable found'
|
101
|
-
end
|
102
|
-
|
103
|
-
context 'declared in a module' do
|
104
|
-
before :each do
|
105
|
-
@ctx = ClassContext.from_s('module Fred; @@tools = {}; end')
|
106
|
-
end
|
107
|
-
|
108
|
-
it_should_behave_like 'one variable found'
|
109
|
-
end
|
110
|
-
|
111
|
-
context 'used in a module' do
|
112
|
-
before :each do
|
113
|
-
@ctx = ClassContext.from_s('module Fred; def jim() @@tools = {}; end; end')
|
114
|
-
end
|
115
|
-
|
116
|
-
it_should_behave_like 'one variable found'
|
117
|
-
end
|
118
|
-
|
119
|
-
context 'indexed in a module' do
|
120
|
-
before :each do
|
121
|
-
@ctx = ClassContext.from_s('module Fred; def jim() @@tools[mash] = {}; end; end')
|
122
|
-
end
|
123
|
-
|
124
|
-
it_should_behave_like 'one variable found'
|
125
|
-
end
|
126
|
-
|
127
|
-
context 'declared and used in a module' do
|
128
|
-
before :each do
|
129
|
-
@ctx = ClassContext.from_s('module Fred; @@tools = {}; def jim() @@tools = {}; end; end')
|
130
|
-
end
|
131
|
-
|
132
|
-
it_should_behave_like 'one variable found'
|
133
|
-
end
|
134
|
-
|
135
|
-
context 'used twice in a module' do
|
136
|
-
before :each do
|
137
|
-
@ctx = ClassContext.from_s('module Fred; def jeff() @@tools = {}; end; def jim() @@tools = {}; end; end')
|
138
|
-
end
|
139
|
-
|
140
|
-
it_should_behave_like 'one variable found'
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
42
|
context 'with no attributes' do
|
145
43
|
it 'records nothing in the class' do
|
146
44
|
klass = ClassContext.from_s('class Fred; end')
|
@@ -7,7 +7,7 @@ include Reek
|
|
7
7
|
|
8
8
|
describe MethodContext, 'matching' do
|
9
9
|
before :each do
|
10
|
-
@element = MethodContext.new(StopContext.new,
|
10
|
+
@element = MethodContext.new(StopContext.new, s(0, :mod), s(:module, :mod, nil))
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'should recognise itself in a collection of names' do
|
@@ -24,9 +24,9 @@ end
|
|
24
24
|
describe MethodContext, 'matching fq names' do
|
25
25
|
before :each do
|
26
26
|
element = StopContext.new
|
27
|
-
element = ModuleContext.new(element, Name.new(:mod))
|
28
|
-
element = ClassContext.new(element, Name.new(:klass))
|
29
|
-
@element = MethodContext.new(element,
|
27
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
28
|
+
element = ClassContext.new(element, Name.new(:klass), s())
|
29
|
+
@element = MethodContext.new(element, s(0, :meth))
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'should recognise itself in a collection of names' do
|
@@ -10,7 +10,7 @@ describe SingletonMethodContext, 'outer_name' do
|
|
10
10
|
|
11
11
|
it "should report full context" do
|
12
12
|
element = StopContext.new
|
13
|
-
element = ModuleContext.new(element, Name.new(:mod))
|
13
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
14
14
|
element = SingletonMethodContext.new(element, s(:defs, s(:call, nil, :a, s(:arglist)), :b, s(:args)))
|
15
15
|
element.outer_name.should match(/mod::a\.b/)
|
16
16
|
end
|