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