reek 1.2.2 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -17,7 +17,7 @@ describe Attribute do
|
|
17
17
|
[ClassContext, ModuleContext].each do |klass|
|
18
18
|
context "in a #{klass}" do
|
19
19
|
before :each do
|
20
|
-
@ctx = klass.create(StopContext.new,
|
20
|
+
@ctx = klass.create(StopContext.new, s(:null, :Fred))
|
21
21
|
end
|
22
22
|
|
23
23
|
it_should_behave_like 'a variable detector'
|
@@ -3,24 +3,113 @@ require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
|
3
3
|
require 'reek/smells/class_variable'
|
4
4
|
require 'reek/class_context'
|
5
5
|
|
6
|
-
require 'spec/reek/smells/behaves_like_variable_detector'
|
7
|
-
|
8
6
|
include Reek
|
9
7
|
include Reek::Smells
|
10
8
|
|
11
9
|
describe ClassVariable do
|
12
10
|
before :each do
|
13
11
|
@detector = ClassVariable.new
|
14
|
-
@record_variable = :record_class_variable
|
15
12
|
end
|
16
13
|
|
17
|
-
|
18
|
-
|
14
|
+
context 'with no class variables' do
|
15
|
+
it 'records nothing in the class' do
|
16
|
+
ctx = ClassContext.from_s('class Fred; end')
|
17
|
+
@detector.class_variables_in(ctx).should be_empty
|
18
|
+
end
|
19
|
+
it 'records nothing in the module' do
|
20
|
+
ctx = ModuleContext.from_s('module Fred; end')
|
21
|
+
@detector.class_variables_in(ctx).should be_empty
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with one class variable' do
|
26
|
+
shared_examples_for 'one variable found' do
|
27
|
+
it 'records the class variable' do
|
28
|
+
@detector.class_variables_in(@ctx).should include(:@@tools)
|
29
|
+
end
|
30
|
+
it 'records only that class variable' do
|
31
|
+
@detector.class_variables_in(@ctx).length.should == 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'declared in a class' do
|
36
|
+
before :each do
|
37
|
+
@ctx = ClassContext.from_s('class Fred; @@tools = {}; end')
|
38
|
+
end
|
39
|
+
|
40
|
+
it_should_behave_like 'one variable found'
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'used in a class' do
|
44
|
+
before :each do
|
45
|
+
@ctx = ClassContext.from_s('class Fred; def jim() @@tools = {}; end; end')
|
46
|
+
end
|
47
|
+
|
48
|
+
it_should_behave_like 'one variable found'
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'indexed in a class' do
|
52
|
+
before :each do
|
53
|
+
@ctx = ClassContext.from_s('class Fred; def jim() @@tools[mash] = {}; end; end')
|
54
|
+
end
|
55
|
+
|
56
|
+
it_should_behave_like 'one variable found'
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'declared and used in a class' do
|
60
|
+
before :each do
|
61
|
+
@ctx = ClassContext.from_s('class Fred; @@tools = {}; def jim() @@tools = {}; end; end')
|
62
|
+
end
|
63
|
+
|
64
|
+
it_should_behave_like 'one variable found'
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'used twice in a class' do
|
68
|
+
before :each do
|
69
|
+
@ctx = ClassContext.from_s('class Fred; def jeff() @@tools = {}; end; def jim() @@tools = {}; end; end')
|
70
|
+
end
|
71
|
+
|
72
|
+
it_should_behave_like 'one variable found'
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'declared in a module' do
|
76
|
+
before :each do
|
77
|
+
@ctx = ClassContext.from_s('module Fred; @@tools = {}; end')
|
78
|
+
end
|
79
|
+
|
80
|
+
it_should_behave_like 'one variable found'
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'used in a module' do
|
84
|
+
before :each do
|
85
|
+
@ctx = ClassContext.from_s('module Fred; def jim() @@tools = {}; end; end')
|
86
|
+
end
|
87
|
+
|
88
|
+
it_should_behave_like 'one variable found'
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'indexed in a module' do
|
92
|
+
before :each do
|
93
|
+
@ctx = ClassContext.from_s('module Fred; def jim() @@tools[mash] = {}; end; end')
|
94
|
+
end
|
95
|
+
|
96
|
+
it_should_behave_like 'one variable found'
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'declared and used in a module' do
|
100
|
+
before :each do
|
101
|
+
@ctx = ClassContext.from_s('module Fred; @@tools = {}; def jim() @@tools = {}; end; end')
|
102
|
+
end
|
103
|
+
|
104
|
+
it_should_behave_like 'one variable found'
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'used twice in a module' do
|
19
108
|
before :each do
|
20
|
-
@ctx =
|
109
|
+
@ctx = ClassContext.from_s('module Fred; def jeff() @@tools = {}; end; def jim() @@tools = {}; end; end')
|
21
110
|
end
|
22
111
|
|
23
|
-
it_should_behave_like '
|
112
|
+
it_should_behave_like 'one variable found'
|
24
113
|
end
|
25
114
|
end
|
26
115
|
end
|
@@ -6,7 +6,7 @@ include Reek::Smells
|
|
6
6
|
|
7
7
|
describe ControlCouple do
|
8
8
|
it 'should report a ternary check on a parameter' do
|
9
|
-
'def simple(arga) arga ? @ivar : 3 end'.should
|
9
|
+
'def simple(arga) arga ? @ivar : 3 end'.should reek_only_of(:ControlCouple, /arga/)
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'should not report a ternary check on an ivar' do
|
@@ -4,10 +4,10 @@ require 'reek/smells/data_clump'
|
|
4
4
|
|
5
5
|
include Reek::Smells
|
6
6
|
|
7
|
-
|
8
|
-
it '
|
7
|
+
shared_examples_for 'a data clump detector' do
|
8
|
+
it 'does not report small parameter sets' do
|
9
9
|
src = <<EOS
|
10
|
-
|
10
|
+
#{@context} Scrunch
|
11
11
|
def first(pa) @field == :sym ? 0 : 3; end
|
12
12
|
def second(pa) @field == :sym; end
|
13
13
|
def third(pa) pa - pb + @fred; end
|
@@ -17,9 +17,9 @@ EOS
|
|
17
17
|
src.should_not reek
|
18
18
|
end
|
19
19
|
|
20
|
-
it '
|
20
|
+
it 'reports 3 identical pairs in a class' do
|
21
21
|
src = <<EOS
|
22
|
-
|
22
|
+
#{@context} Scrunch
|
23
23
|
def first(pa, pb) @field == :sym ? 0 : 3; end
|
24
24
|
def second(pa, pb) @field == :sym; end
|
25
25
|
def third(pa, pb) pa - pb + @fred; end
|
@@ -29,9 +29,9 @@ EOS
|
|
29
29
|
src.should reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
|
30
30
|
end
|
31
31
|
|
32
|
-
it '
|
32
|
+
it 'reports 3 swapped pairs in a class' do
|
33
33
|
src = <<EOS
|
34
|
-
|
34
|
+
#{@context} Scrunch
|
35
35
|
def one(pa, pb) @field == :sym ? 0 : 3; end
|
36
36
|
def two(pb, pa) @field == :sym; end
|
37
37
|
def tri(pa, pb) pa - pb + @fred; end
|
@@ -41,9 +41,9 @@ EOS
|
|
41
41
|
src.should reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
|
42
42
|
end
|
43
43
|
|
44
|
-
it '
|
44
|
+
it 'reports 3 identical parameter sets in a class' do
|
45
45
|
src = <<EOS
|
46
|
-
|
46
|
+
#{@context} Scrunch
|
47
47
|
def first(pa, pb, pc) @field == :sym ? 0 : 3; end
|
48
48
|
def second(pa, pb, pc) @field == :sym; end
|
49
49
|
def third(pa, pb, pc) pa - pb + @fred; end
|
@@ -56,9 +56,9 @@ EOS
|
|
56
56
|
src.should_not reek_of(:DataClump, /\[pb, pc\]/, /3 methods/)
|
57
57
|
end
|
58
58
|
|
59
|
-
it '
|
59
|
+
it 'recognises re-ordered identical parameter sets' do
|
60
60
|
src = <<EOS
|
61
|
-
|
61
|
+
#{@context} Scrunch
|
62
62
|
def first(pb, pa, pc) @field == :sym ? 0 : 3; end
|
63
63
|
def second(pc, pb, pa) @field == :sym; end
|
64
64
|
def third(pa, pb, pc) pa - pb + @fred; end
|
@@ -71,9 +71,9 @@ EOS
|
|
71
71
|
src.should_not reek_of(:DataClump, /\[pb, pc\]/, /3 methods/)
|
72
72
|
end
|
73
73
|
|
74
|
-
it '
|
74
|
+
it 'counts only identical parameter sets' do
|
75
75
|
src = <<EOS
|
76
|
-
|
76
|
+
#{@context} RedCloth
|
77
77
|
def fa(p1, p2, p3, conten) end
|
78
78
|
def fb(p1, p2, p3, conten) end
|
79
79
|
def fc(name, windowW, windowH) end
|
@@ -82,6 +82,24 @@ EOS
|
|
82
82
|
|
83
83
|
src.should_not reek_of(:DataClump)
|
84
84
|
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe DataClump do
|
88
|
+
context 'in a class' do
|
89
|
+
before :each do
|
90
|
+
@context = 'class'
|
91
|
+
end
|
92
|
+
|
93
|
+
it_should_behave_like 'a data clump detector'
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'in a module' do
|
97
|
+
before :each do
|
98
|
+
@context = 'module'
|
99
|
+
end
|
100
|
+
|
101
|
+
it_should_behave_like 'a data clump detector'
|
102
|
+
end
|
85
103
|
|
86
104
|
# TODO: include singleton methods in the calcs
|
87
105
|
end
|
@@ -9,21 +9,30 @@ include Reek
|
|
9
9
|
include Reek::Smells
|
10
10
|
|
11
11
|
describe LargeClass, 'when exceptions are listed' do
|
12
|
-
|
13
12
|
before(:each) do
|
14
|
-
@
|
15
|
-
|
13
|
+
@class_name = 'Humungous'
|
14
|
+
src = <<EOS
|
15
|
+
class #{@class_name}
|
16
|
+
def me01x()3 end;def me02x()3 end;def me03x()3 end;def me04x()3 end;def me05x()3 end
|
17
|
+
def me11x()3 end;def me12x()3 end;def me13x()3 end;def me14x()3 end;def me15x()3 end
|
18
|
+
def me21x()3 end;def me22x()3 end;def me23x()3 end;def me24x()3 end;def me25x()3 end
|
19
|
+
def me31x()3 end;def me32x()3 end;def me33x()3 end;def me34x()3 end;def me35x()3 end
|
20
|
+
def me41x()3 end;def me42x()3 end;def me43x()3 end;def me44x()3 end;def me45x()3 end
|
21
|
+
def me51x()3 end
|
22
|
+
end
|
23
|
+
EOS
|
24
|
+
@ctx = ClassContext.from_s(src)
|
16
25
|
@config = LargeClass.default_config
|
17
26
|
end
|
18
27
|
|
19
28
|
it 'should ignore first excepted name' do
|
20
|
-
@config[LargeClass::EXCLUDE_KEY] = [
|
29
|
+
@config[LargeClass::EXCLUDE_KEY] = [@class_name]
|
21
30
|
lc = LargeClass.new(@config)
|
22
31
|
lc.examine(@ctx).should == false
|
23
32
|
end
|
24
33
|
|
25
34
|
it 'should ignore second excepted name' do
|
26
|
-
@config[LargeClass::EXCLUDE_KEY] = ['Oversized',
|
35
|
+
@config[LargeClass::EXCLUDE_KEY] = ['Oversized', @class_name]
|
27
36
|
lc = LargeClass.new(@config)
|
28
37
|
lc.examine(@ctx).should == false
|
29
38
|
end
|
@@ -37,7 +46,7 @@ end
|
|
37
46
|
|
38
47
|
describe LargeClass, 'checking source code' do
|
39
48
|
|
40
|
-
|
49
|
+
context 'counting instance variables' do
|
41
50
|
it 'should not report empty class' do
|
42
51
|
ClassContext.from_s('class Empty;end').should have(0).variable_names
|
43
52
|
end
|
@@ -68,18 +77,7 @@ EOS
|
|
68
77
|
end
|
69
78
|
end
|
70
79
|
|
71
|
-
|
72
|
-
it 'should not report empty class' do
|
73
|
-
ClassContext.from_s('class Empty;end').num_methods.should == 0
|
74
|
-
end
|
75
|
-
|
76
|
-
it 'should count 1 method' do
|
77
|
-
ClassContext.from_s('class Empty;def ivars() @aa=@ab; end;end').num_methods.should == 1
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'should count 2 methods' do
|
81
|
-
ClassContext.from_s('class Empty;def meth1() @aa=@ab;end;def meth2() @aa=@ab;end;end').num_methods.should == 2
|
82
|
-
end
|
80
|
+
context 'counting methods' do
|
83
81
|
|
84
82
|
it 'should not report 25 methods' do
|
85
83
|
src = <<EOS
|
@@ -108,4 +106,20 @@ EOS
|
|
108
106
|
src.should reek_of(:LargeClass)
|
109
107
|
end
|
110
108
|
end
|
109
|
+
|
110
|
+
context 'with a nested module' do
|
111
|
+
it 'stops at a nested module' do
|
112
|
+
src = <<EOS
|
113
|
+
class Full
|
114
|
+
def me01x()3 end;def me02x()3 end;def me03x()3 end;def me04x()3 end;def me05x()3 end
|
115
|
+
def me11x()3 end;def me12x()3 end;def me13x()3 end;def me14x()3 end;def me15x()3 end
|
116
|
+
def me21x()3 end;def me22x()3 end;def me23x()3 end;def me24x()3 end;def me25x()3 end
|
117
|
+
def me31x()3 end;def me32x()3 end;def me33x()3 end;def me34x()3 end;def me35x()3 end
|
118
|
+
module Hidden; def me41x()3 end;def me42x()3 end;def me43x()3 end;def me44x()3 end;def me45x()3 end; end
|
119
|
+
def me51x()3 end
|
120
|
+
end
|
121
|
+
EOS
|
122
|
+
src.should_not reek_of(:LargeClass)
|
123
|
+
end
|
124
|
+
end
|
111
125
|
end
|
@@ -1,49 +1,97 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
2
|
|
3
3
|
require 'reek/smells/simulated_polymorphism'
|
4
|
+
require 'reek/code_context'
|
4
5
|
|
6
|
+
include Reek
|
5
7
|
include Reek::Smells
|
6
8
|
|
7
9
|
describe SimulatedPolymorphism do
|
8
|
-
|
9
|
-
|
10
|
+
before :each do
|
11
|
+
@detector = SimulatedPolymorphism.new
|
12
|
+
end
|
13
|
+
context 'with no conditionals' do
|
14
|
+
it 'gathers an empty hash' do
|
15
|
+
ast = 'module Stable; end'.to_reek_source.syntax_tree
|
16
|
+
ctx = CodeContext.new(nil, ast)
|
17
|
+
@detector.conditional_counts(ctx).length.should == 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with a test of block_given?' do
|
22
|
+
it 'does not record the condition' do
|
23
|
+
ast = 'def fred() yield(3) if block_given?; end'.to_reek_source.syntax_tree
|
24
|
+
ctx = CodeContext.new(nil, ast)
|
25
|
+
@detector.conditional_counts(ctx).length.should == 0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with three identical conditionals' do
|
30
|
+
before :each do
|
31
|
+
cond = '@field == :sym'
|
32
|
+
@cond_expr = cond.to_reek_source.syntax_tree
|
33
|
+
src = <<EOS
|
10
34
|
class Scrunch
|
11
35
|
def first
|
12
|
-
return
|
36
|
+
return #{cond} ? 0 : 3;
|
13
37
|
end
|
14
38
|
def second
|
15
|
-
if
|
39
|
+
if #{cond}
|
16
40
|
@other += " quarts"
|
17
41
|
end
|
18
42
|
end
|
19
43
|
def third
|
20
|
-
raise 'flu!' unless
|
44
|
+
raise 'flu!' unless #{cond}
|
21
45
|
end
|
22
46
|
end
|
23
47
|
EOS
|
24
48
|
|
25
|
-
|
49
|
+
ast = src.to_reek_source.syntax_tree
|
50
|
+
ctx = CodeContext.new(nil, ast)
|
51
|
+
@conds = @detector.conditional_counts(ctx)
|
52
|
+
end
|
53
|
+
it 'finds one matching conditional' do
|
54
|
+
@conds.length.should == 1
|
55
|
+
end
|
56
|
+
it 'returns the condition expr' do
|
57
|
+
@conds.keys[0].should == @cond_expr
|
58
|
+
end
|
59
|
+
it 'knows there are three copies' do
|
60
|
+
@conds[@cond_expr].should == 3
|
61
|
+
end
|
26
62
|
end
|
27
63
|
|
28
|
-
|
29
|
-
|
64
|
+
context 'with a matching if and case' do
|
65
|
+
before :each do
|
66
|
+
cond = '@field == :sym'
|
67
|
+
@cond_expr = cond.to_reek_source.syntax_tree
|
68
|
+
src = <<EOS
|
30
69
|
class Scrunch
|
31
|
-
def
|
32
|
-
return
|
70
|
+
def alpha
|
71
|
+
return #{cond} ? 0 : 2;
|
33
72
|
end
|
34
|
-
def
|
35
|
-
case
|
36
|
-
when :
|
37
|
-
@
|
73
|
+
def beta
|
74
|
+
case #{cond}
|
75
|
+
when :symbol
|
76
|
+
@tother += " pints"
|
38
77
|
end
|
39
78
|
end
|
40
|
-
def third
|
41
|
-
raise 'flu!' unless @field
|
42
|
-
end
|
43
79
|
end
|
44
80
|
EOS
|
45
81
|
|
46
|
-
|
82
|
+
ast = src.to_reek_source.syntax_tree
|
83
|
+
ctx = CodeContext.new(nil, ast)
|
84
|
+
@conds = @detector.conditional_counts(ctx)
|
85
|
+
end
|
86
|
+
it 'finds exactly one conditional' do
|
87
|
+
@conds.length.should == 1
|
88
|
+
end
|
89
|
+
it 'returns the condition expr' do
|
90
|
+
@conds.keys[0].should == @cond_expr
|
91
|
+
end
|
92
|
+
it 'knows there are three copies' do
|
93
|
+
@conds[@cond_expr].should == 2
|
94
|
+
end
|
47
95
|
end
|
48
96
|
|
49
97
|
# And count code in superclasses, if we have it
|