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