reek 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/History.txt +4 -0
  2. data/Rakefile +0 -1
  3. data/config/defaults.reek +4 -6
  4. data/features/masking_smells.feature +9 -9
  5. data/features/options.feature +2 -2
  6. data/features/profile.feature +34 -0
  7. data/features/rake_task.feature +74 -0
  8. data/features/reports.feature +1 -1
  9. data/features/samples.feature +4 -4
  10. data/features/stdin.feature +1 -1
  11. data/features/step_definitions/reek_steps.rb +11 -7
  12. data/features/support/env.rb +26 -18
  13. data/lib/reek.rb +1 -1
  14. data/lib/reek/adapters/application.rb +9 -2
  15. data/lib/reek/adapters/command_line.rb +2 -2
  16. data/lib/reek/adapters/source.rb +4 -1
  17. data/lib/reek/adapters/spec.rb +1 -3
  18. data/lib/reek/block_context.rb +14 -8
  19. data/lib/reek/class_context.rb +6 -66
  20. data/lib/reek/code_context.rb +10 -0
  21. data/lib/reek/code_parser.rb +25 -53
  22. data/lib/reek/configuration.rb +12 -6
  23. data/lib/reek/if_context.rb +2 -3
  24. data/lib/reek/method_context.rb +3 -12
  25. data/lib/reek/module_context.rb +30 -22
  26. data/lib/reek/name.rb +2 -0
  27. data/lib/reek/object_refs.rb +0 -3
  28. data/lib/reek/sexp_formatter.rb +0 -2
  29. data/lib/reek/smells/class_variable.rb +17 -4
  30. data/lib/reek/smells/control_couple.rb +3 -10
  31. data/lib/reek/smells/data_clump.rb +10 -10
  32. data/lib/reek/smells/feature_envy.rb +1 -8
  33. data/lib/reek/smells/large_class.rb +3 -3
  34. data/lib/reek/smells/simulated_polymorphism.rb +17 -3
  35. data/lib/reek/smells/smell_detector.rb +11 -2
  36. data/lib/reek/smells/utility_function.rb +1 -1
  37. data/lib/reek/sniffer.rb +0 -8
  38. data/lib/reek/stop_context.rb +1 -1
  39. data/lib/reek/tree_dresser.rb +74 -0
  40. data/reek.gemspec +3 -3
  41. data/spec/reek/block_context_spec.rb +6 -6
  42. data/spec/reek/class_context_spec.rb +2 -23
  43. data/spec/reek/code_context_spec.rb +149 -67
  44. data/spec/reek/code_parser_spec.rb +0 -102
  45. data/spec/reek/method_context_spec.rb +4 -4
  46. data/spec/reek/singleton_method_context_spec.rb +1 -1
  47. data/spec/reek/smells/attribute_spec.rb +1 -1
  48. data/spec/reek/smells/class_variable_spec.rb +96 -7
  49. data/spec/reek/smells/control_couple_spec.rb +1 -1
  50. data/spec/reek/smells/data_clump_spec.rb +31 -13
  51. data/spec/reek/smells/feature_envy_spec.rb +1 -1
  52. data/spec/reek/smells/large_class_spec.rb +32 -18
  53. data/spec/reek/smells/simulated_polymorphism_spec.rb +66 -18
  54. data/spec/reek/sniffer_spec.rb +1 -0
  55. data/spec/samples/not_quite_masked/dirty.rb +2 -0
  56. data/spec/spec_helper.rb +1 -1
  57. data/tasks/reek.rake +1 -1
  58. metadata +5 -3
  59. 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, "Fred")
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
- [ClassContext, ModuleContext].each do |klass|
18
- context "in a #{klass}" do
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 = klass.create(StopContext.new, "Fred")
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 'a variable detector'
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 reek_of(:ControlCouple, /arga/)
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
- describe DataClump do
8
- it 'should not report small parameter sets' do
7
+ shared_examples_for 'a data clump detector' do
8
+ it 'does not report small parameter sets' do
9
9
  src = <<EOS
10
- class Scrunch
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 'should report 3 identical pairs in a class' do
20
+ it 'reports 3 identical pairs in a class' do
21
21
  src = <<EOS
22
- class Scrunch
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 'should report 3 swapped pairs in a class' do
32
+ it 'reports 3 swapped pairs in a class' do
33
33
  src = <<EOS
34
- class Scrunch
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 'should report 3 identical parameter sets in a class' do
44
+ it 'reports 3 identical parameter sets in a class' do
45
45
  src = <<EOS
46
- class Scrunch
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 'should recognise re-ordered identical parameter sets' do
59
+ it 'recognises re-ordered identical parameter sets' do
60
60
  src = <<EOS
61
- class Scrunch
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 'should count only identical parameter sets' do
74
+ it 'counts only identical parameter sets' do
75
75
  src = <<EOS
76
- class RedCloth
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
@@ -204,7 +204,7 @@ end
204
204
  describe FeatureEnvy, '#examine' do
205
205
 
206
206
  before :each do
207
- @context = MethodContext.new(StopContext.new, [:defn, :cool])
207
+ @context = MethodContext.new(StopContext.new, s(:defn, :cool))
208
208
  @fe = FeatureEnvy.new
209
209
  end
210
210
 
@@ -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
- @ctx = ClassContext.create(StopContext.new, [0, :Humungous])
15
- 30.times { |num| @ctx.record_method("method#{num}") }
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] = ['Humungous']
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', 'Humungous']
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
- describe 'counting instance variables' do
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
- describe 'counting methods' do
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
- it 'should report 3 similar conditions in a class' do
9
- src = <<EOS
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 @field == :sym ? 0 : 3;
36
+ return #{cond} ? 0 : 3;
13
37
  end
14
38
  def second
15
- if @field == :sym
39
+ if #{cond}
16
40
  @other += " quarts"
17
41
  end
18
42
  end
19
43
  def third
20
- raise 'flu!' unless @field == :sym
44
+ raise 'flu!' unless #{cond}
21
45
  end
22
46
  end
23
47
  EOS
24
48
 
25
- src.should reek_only_of(:SimulatedPolymorphism, /@field == :sym/)
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
- it 'should include case statements in the count' do
29
- src = <<EOS
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 first
32
- return @field ? 0 : 3;
70
+ def alpha
71
+ return #{cond} ? 0 : 2;
33
72
  end
34
- def second
35
- case @field
36
- when :sym
37
- @other += " quarts"
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
- src.should reek_only_of(:SimulatedPolymorphism, /@field/)
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