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
@@ -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, [s(:iasgn, :@list), s(:self)])
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 'should not report empty class in another module' do
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 'should deal with :: scoped names' do
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, 'to_s' do
13
-
14
- it "should report full context" do
15
- element = StopContext.new
16
- element = ModuleContext.new(element, Name.new(:mod))
17
- element = ClassContext.new(element, [0, :klass])
18
- element = MethodContext.new(element, [0, :bad])
19
- element = BlockContext.new(element, nil)
20
- element.to_s.should match(/bad/)
21
- element.to_s.should match(/klass/)
22
- element.to_s.should match(/mod/)
23
- end
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
- it "should report 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, nil).to_s.should match(/bad/)
30
- end
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
- it "should report method name via nested blocks" do
33
- element1 = StopContext.new
34
- element2 = MethodContext.new(element1, [0, :bad])
35
- element3 = BlockContext.new(element2, nil)
36
- BlockContext.new(element3, nil).to_s.should match(/bad/)
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
- describe CodeContext, '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))
44
- class_element = ClassContext.new(element, [0, :klass])
45
- element = MethodContext.new(class_element, [0, :bad])
46
- element = BlockContext.new(element, 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))
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
- describe CodeContext, '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))
58
- class_element = ClassContext.new(element, [0, :klass])
59
- element = MethodContext.new(class_element, [0, :bad])
60
- element = BlockContext.new(element, nil)
61
- element.bananas(17, -5).should == 55
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
- describe CodeContext do
66
- it 'should recognise itself in a collection of names' do
67
- element = StopContext.new
68
- element = ModuleContext.new(element, Name.new(:mod))
69
- element.matches?(['banana', 'mod']).should == true
70
- end
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
- it 'should recognise itself in a collection of REs' do
73
- element = StopContext.new
74
- element = ModuleContext.new(element, Name.new(:mod))
75
- element.matches?([/banana/, /mod/]).should == true
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
- it 'should recognise its fq name in a collection of names' do
79
- element = StopContext.new
80
- element = ModuleContext.new(element, Name.new(:mod))
81
- element = ClassContext.create(element, [0, :klass])
82
- element.matches?(['banana', 'mod']).should == true
83
- element.matches?(['banana', 'mod::klass']).should == true
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
- it 'should recognise its fq name in a collection of names' do
87
- element = StopContext.new
88
- element = ModuleContext.new(element, Name.new(:mod))
89
- element = ClassContext.create(element, [0, :klass])
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, [0, :mod])
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, [0, :meth])
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