reek 3.0.4 → 3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +6 -0
  3. data/README.md +41 -20
  4. data/features/configuration_files/directory_specific_directives.feature +280 -0
  5. data/features/configuration_files/masking_smells.feature +0 -14
  6. data/features/step_definitions/sample_file_steps.rb +2 -2
  7. data/lib/reek/ast/sexp_extensions.rb +72 -60
  8. data/lib/reek/cli/application.rb +2 -5
  9. data/lib/reek/cli/reek_command.rb +1 -1
  10. data/lib/reek/configuration/app_configuration.rb +115 -61
  11. data/lib/reek/configuration/configuration_file_finder.rb +19 -0
  12. data/lib/reek/context/code_context.rb +102 -29
  13. data/lib/reek/context/method_context.rb +11 -48
  14. data/lib/reek/context/module_context.rb +2 -6
  15. data/lib/reek/context/root_context.rb +7 -14
  16. data/lib/reek/examiner.rb +15 -10
  17. data/lib/reek/report/report.rb +5 -4
  18. data/lib/reek/smells/boolean_parameter.rb +1 -1
  19. data/lib/reek/smells/duplicate_method_call.rb +1 -5
  20. data/lib/reek/smells/irresponsible_module.rb +2 -2
  21. data/lib/reek/smells/smell_repository.rb +30 -16
  22. data/lib/reek/smells/utility_function.rb +1 -1
  23. data/lib/reek/source/source_code.rb +10 -6
  24. data/lib/reek/source/source_locator.rb +27 -26
  25. data/lib/reek/spec.rb +8 -6
  26. data/lib/reek/spec/should_reek.rb +6 -2
  27. data/lib/reek/spec/should_reek_of.rb +5 -2
  28. data/lib/reek/spec/should_reek_only_of.rb +1 -1
  29. data/lib/reek/tree_walker.rb +49 -21
  30. data/lib/reek/version.rb +1 -1
  31. data/spec/reek/ast/sexp_extensions_spec.rb +4 -12
  32. data/spec/reek/configuration/app_configuration_spec.rb +80 -52
  33. data/spec/reek/configuration/configuration_file_finder_spec.rb +27 -15
  34. data/spec/reek/context/code_context_spec.rb +66 -17
  35. data/spec/reek/context/method_context_spec.rb +66 -64
  36. data/spec/reek/context/root_context_spec.rb +3 -1
  37. data/spec/reek/context/singleton_method_context_spec.rb +1 -2
  38. data/spec/reek/examiner_spec.rb +5 -8
  39. data/spec/reek/report/xml_report_spec.rb +3 -2
  40. data/spec/reek/smells/attribute_spec.rb +12 -17
  41. data/spec/reek/smells/duplicate_method_call_spec.rb +17 -25
  42. data/spec/reek/smells/feature_envy_spec.rb +4 -5
  43. data/spec/reek/smells/irresponsible_module_spec.rb +43 -0
  44. data/spec/reek/smells/nested_iterators_spec.rb +12 -26
  45. data/spec/reek/smells/too_many_statements_spec.rb +2 -210
  46. data/spec/reek/smells/utility_function_spec.rb +49 -5
  47. data/spec/reek/source/source_locator_spec.rb +39 -43
  48. data/spec/reek/spec/should_reek_of_spec.rb +3 -2
  49. data/spec/reek/spec/should_reek_spec.rb +25 -17
  50. data/spec/reek/tree_walker_spec.rb +214 -23
  51. data/spec/samples/configuration/full_configuration.reek +9 -0
  52. data/spec/spec_helper.rb +10 -10
  53. metadata +4 -3
  54. data/features/configuration_files/overrides_defaults.feature +0 -15
@@ -151,23 +151,20 @@ RSpec.describe Reek::Smells::DuplicateMethodCall do
151
151
  end
152
152
 
153
153
  context 'allowing up to 3 calls' do
154
- before :each do
155
- @config = { DuplicateMethodCall:
156
- { Reek::Smells::DuplicateMethodCall::MAX_ALLOWED_CALLS_KEY => 3 } }
154
+ let(:config) do
155
+ { Reek::Smells::DuplicateMethodCall =>
156
+ { Reek::Smells::DuplicateMethodCall::MAX_ALLOWED_CALLS_KEY => 3 } }
157
157
  end
158
+ let(:configuration) { test_configuration_for(config) }
158
159
 
159
160
  it 'does not report double calls' do
160
161
  src = 'def double_thing() @other.thing + @other.thing end'
161
- with_test_config(@config) do
162
- expect(src).not_to reek_of(:DuplicateMethodCall)
163
- end
162
+ expect(src).not_to reek_of(:DuplicateMethodCall, {}, configuration)
164
163
  end
165
164
 
166
165
  it 'does not report triple calls' do
167
166
  src = 'def double_thing() @other.thing + @other.thing + @other.thing end'
168
- with_test_config(@config) do
169
- expect(src).not_to reek_of(:DuplicateMethodCall)
170
- end
167
+ expect(src).not_to reek_of(:DuplicateMethodCall, {}, configuration)
171
168
  end
172
169
 
173
170
  it 'reports quadruple calls' do
@@ -176,41 +173,36 @@ RSpec.describe Reek::Smells::DuplicateMethodCall do
176
173
  @other.thing + @other.thing + @other.thing + @other.thing
177
174
  end
178
175
  '
179
- with_test_config(@config) do
180
- expect(src).to reek_of(:DuplicateMethodCall, name: '@other.thing', count: 4)
181
- end
176
+ expect(src).to reek_of(:DuplicateMethodCall,
177
+ { name: '@other.thing', count: 4 },
178
+ configuration)
182
179
  end
183
180
  end
184
181
 
185
182
  context 'allowing calls to some methods' do
186
- before :each do
187
- @config = { DuplicateMethodCall:
188
- { Reek::Smells::DuplicateMethodCall::ALLOW_CALLS_KEY =>
189
- ['@some.thing', /puts/] } }
183
+ let(:config) do
184
+ { Reek::Smells::DuplicateMethodCall =>
185
+ { Reek::Smells::DuplicateMethodCall::ALLOW_CALLS_KEY =>
186
+ ['@some.thing', /puts/] } }
190
187
  end
188
+ let(:configuration) { test_configuration_for(config) }
191
189
 
192
190
  it 'does not report calls to some methods' do
193
191
  src = 'def double_some_thing() @some.thing + @some.thing end'
194
192
 
195
- with_test_config(@config) do
196
- expect(src).not_to reek_of(:DuplicateMethodCall)
197
- end
193
+ expect(src).not_to reek_of(:DuplicateMethodCall, {}, configuration)
198
194
  end
199
195
 
200
196
  it 'reports calls to other methods' do
201
197
  src = 'def double_other_thing() @other.thing + @other.thing end'
202
198
 
203
- with_test_config(@config) do
204
- expect(src).to reek_of(:DuplicateMethodCall, name: '@other.thing')
205
- end
199
+ expect(src).to reek_of(:DuplicateMethodCall, { name: '@other.thing' }, configuration)
206
200
  end
207
201
 
208
202
  it 'does not report calls to methods specifed with a regular expression' do
209
203
  src = 'def double_puts() puts @other.thing; puts @other.thing end'
210
204
 
211
- with_test_config(@config) do
212
- expect(src).to reek_of(:DuplicateMethodCall, name: '@other.thing')
213
- end
205
+ expect(src).to reek_of(:DuplicateMethodCall, { name: '@other.thing' }, configuration)
214
206
  end
215
207
  end
216
208
  end
@@ -1,5 +1,6 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_relative '../../../lib/reek/smells/feature_envy'
3
+ require_relative '../../../lib/reek/examiner'
3
4
  require_relative 'smell_detector_shared'
4
5
 
5
6
  RSpec.describe Reek::Smells::FeatureEnvy do
@@ -213,13 +214,13 @@ end
213
214
 
214
215
  RSpec.describe Reek::Smells::FeatureEnvy do
215
216
  before(:each) do
216
- @source_name = 'dummy_source'
217
+ @source_name = 'string'
217
218
  @detector = build(:smell_detector, smell_type: :FeatureEnvy, source: @source_name)
218
219
  end
219
220
 
220
221
  it_should_behave_like 'SmellDetector'
221
222
 
222
- context 'when reporting yaml' do
223
+ context 'when a smell is reported' do
223
224
  before :each do
224
225
  @receiver = 'other'
225
226
  src = <<-EOS
@@ -230,9 +231,7 @@ RSpec.describe Reek::Smells::FeatureEnvy do
230
231
  #{@receiver}.fred
231
232
  end
232
233
  EOS
233
- source = Reek::Source::SourceCode.from(src)
234
- @mctx = Reek::TreeWalker.new.process_def(source.syntax_tree)
235
- @smells = @detector.examine_context(@mctx)
234
+ @smells = Reek::Examiner.new(src, ['FeatureEnvy']).smells
236
235
  end
237
236
 
238
237
  it 'reports only that smell' do
@@ -133,6 +133,18 @@ RSpec.describe Reek::Smells::IrresponsibleModule do
133
133
  expect(src).to reek_of(:IrresponsibleModule)
134
134
  end
135
135
 
136
+ it 'does not report namespace modules that have a nested class through assignment' do
137
+ src = <<-EOS
138
+ module Qux
139
+ # Foo is responsible
140
+ Foo = Class.new Bar do
141
+ def quux; end
142
+ end
143
+ end
144
+ EOS
145
+ expect(src).not_to reek_of(:IrresponsibleModule)
146
+ end
147
+
136
148
  it 'reports classes that have a defined superclass' do
137
149
  src = <<-EOS
138
150
  class Foo < Bar; end
@@ -140,6 +152,37 @@ RSpec.describe Reek::Smells::IrresponsibleModule do
140
152
  expect(src).to reek_of(:IrresponsibleModule)
141
153
  end
142
154
 
155
+ it 'reports classes defined through assignment' do
156
+ src = <<-EOS
157
+ # Qux is responsible, but Foo is not
158
+ module Qux
159
+ Foo = Class.new Bar
160
+ end
161
+ EOS
162
+ expect(src).to reek_of(:IrresponsibleModule, name: 'Foo')
163
+ end
164
+
165
+ it 'reports structs defined through assignment' do
166
+ src = <<-EOS
167
+ # Qux is responsible, but Foo is not
168
+ module Qux
169
+ Foo = Struct.new(:x, :y)
170
+ end
171
+ EOS
172
+ expect(src).to reek_of(:IrresponsibleModule, name: 'Foo')
173
+ end
174
+
175
+ it 'does not report constants that are not classes' do
176
+ src = <<-EOS
177
+ module Qux
178
+ Foo = 23
179
+ Bar = Hash.new
180
+ Quuz = 'foo'.freeze
181
+ end
182
+ EOS
183
+ expect(src).not_to reek_of(:IrresponsibleModule)
184
+ end
185
+
143
186
  context 'when a smell is reported' do
144
187
  before do
145
188
  @source_name = 'dummy_source'
@@ -124,9 +124,10 @@ RSpec.describe Reek::Smells::NestedIterators do
124
124
 
125
125
  context 'when the allowed nesting depth is 3' do
126
126
  before :each do
127
- @config = { NestedIterators:
127
+ @config = { Reek::Smells::NestedIterators =>
128
128
  { Reek::Smells::NestedIterators::MAX_ALLOWED_NESTING_KEY => 3 } }
129
129
  end
130
+ let(:configuration) { test_configuration_for(@config) }
130
131
 
131
132
  it 'should not report nested iterators 2 levels deep' do
132
133
  src = <<-EOS
@@ -135,9 +136,7 @@ RSpec.describe Reek::Smells::NestedIterators do
135
136
  end
136
137
  EOS
137
138
 
138
- with_test_config(@config) do
139
- expect(src).not_to reek_of(:NestedIterators)
140
- end
139
+ expect(src).not_to reek_of(:NestedIterators, {}, configuration)
141
140
  end
142
141
 
143
142
  it 'should not report nested iterators 3 levels deep' do
@@ -147,9 +146,7 @@ RSpec.describe Reek::Smells::NestedIterators do
147
146
  end
148
147
  EOS
149
148
 
150
- with_test_config(@config) do
151
- expect(src).not_to reek_of(:NestedIterators)
152
- end
149
+ expect(src).not_to reek_of(:NestedIterators, {}, configuration)
153
150
  end
154
151
 
155
152
  it 'should report nested iterators 4 levels deep' do
@@ -159,30 +156,25 @@ RSpec.describe Reek::Smells::NestedIterators do
159
156
  end
160
157
  EOS
161
158
 
162
- with_test_config(@config) do
163
- expect(src).to reek_of(:NestedIterators)
164
- end
159
+ expect(src).to reek_of(:NestedIterators, {}, configuration)
165
160
  end
166
161
  end
167
162
 
168
163
  context 'when ignoring iterators' do
169
164
  before :each do
170
- @config = { NestedIterators:
165
+ @config = { Reek::Smells::NestedIterators =>
171
166
  { Reek::Smells::NestedIterators::IGNORE_ITERATORS_KEY => ['ignore_me'] } }
172
167
  end
168
+ let(:configuration) { test_configuration_for(@config) }
173
169
 
174
170
  it 'should not report nesting the ignored iterator inside another' do
175
171
  src = 'def bad(fred) @fred.each {|item| item.ignore_me {|ting| ting.ting} } end'
176
- with_test_config(@config) do
177
- expect(src).not_to reek_of(:NestedIterators)
178
- end
172
+ expect(src).not_to reek_of(:NestedIterators, {}, configuration)
179
173
  end
180
174
 
181
175
  it 'should not report nesting inside the ignored iterator' do
182
176
  src = 'def bad(fred) @fred.ignore_me {|item| item.each {|ting| ting.ting} } end'
183
- with_test_config(@config) do
184
- expect(src).not_to reek_of(:NestedIterators)
185
- end
177
+ expect(src).not_to reek_of(:NestedIterators, {}, configuration)
186
178
  end
187
179
 
188
180
  it 'should report nested iterators inside the ignored iterator' do
@@ -191,9 +183,7 @@ RSpec.describe Reek::Smells::NestedIterators do
191
183
  @fred.ignore_me {|item| item.each {|ting| ting.each {|other| other.other} } }
192
184
  end
193
185
  '
194
- with_test_config(@config) do
195
- expect(src).to reek_of(:NestedIterators, count: 2)
196
- end
186
+ expect(src).to reek_of(:NestedIterators, { count: 2 }, configuration)
197
187
  end
198
188
 
199
189
  it 'should report nested iterators outside the ignored iterator' do
@@ -202,9 +192,7 @@ RSpec.describe Reek::Smells::NestedIterators do
202
192
  @fred.each {|item| item.each {|ting| ting.ignore_me {|other| other.other} } }
203
193
  end
204
194
  '
205
- with_test_config(@config) do
206
- expect(src).to reek_of(:NestedIterators, count: 2)
207
- end
195
+ expect(src).to reek_of(:NestedIterators, { count: 2 }, configuration)
208
196
  end
209
197
 
210
198
  it 'should report nested iterators with the ignored iterator between them' do
@@ -213,9 +201,7 @@ RSpec.describe Reek::Smells::NestedIterators do
213
201
  @fred.each {|item| item.ignore_me {|ting| ting.ting {|other| other.other} } }
214
202
  end
215
203
  '
216
- with_test_config(@config) do
217
- expect(src).to reek_of(:NestedIterators, count: 2)
218
- end
204
+ expect(src).to reek_of(:NestedIterators, { count: 2 }, configuration)
219
205
  end
220
206
  end
221
207
  end
@@ -1,15 +1,6 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_relative '../../../lib/reek/smells/too_many_statements'
3
3
  require_relative 'smell_detector_shared'
4
- require_relative '../../../lib/reek/source/source_code'
5
-
6
- def process_method(source)
7
- Reek::TreeWalker.new.process_def(Reek::Source::SourceCode.from(source).syntax_tree)
8
- end
9
-
10
- def process_singleton_method(source)
11
- Reek::TreeWalker.new.process_defs(Reek::Source::SourceCode.from(source).syntax_tree)
12
- end
13
4
 
14
5
  RSpec.describe Reek::Smells::TooManyStatements do
15
6
  it 'should not report short methods' do
@@ -19,7 +10,7 @@ RSpec.describe Reek::Smells::TooManyStatements do
19
10
 
20
11
  it 'should report long methods' do
21
12
  src = 'def long() alf = f(1);@bet = 2;@cut = 3;@dit = 4; @emp = 5;@fry = 6;end'
22
- expect(src).to reek_only_of(:TooManyStatements)
13
+ expect(src).to reek_of(:TooManyStatements)
23
14
  end
24
15
 
25
16
  it 'should not report initialize' do
@@ -45,206 +36,7 @@ RSpec.describe Reek::Smells::TooManyStatements do
45
36
  end
46
37
  end
47
38
  EOS
48
- expect(src).to reek_only_of(:TooManyStatements)
49
- end
50
- end
51
-
52
- RSpec.describe Reek::Smells::TooManyStatements do
53
- it 'counts 1 assignment' do
54
- method = process_method('def one() val = 4; end')
55
- expect(method.num_statements).to eq(1)
56
- end
57
-
58
- it 'counts 3 assignments' do
59
- method = process_method('def one() val = 4; val = 4; val = 4; end')
60
- expect(method.num_statements).to eq(3)
61
- end
62
-
63
- it 'counts 1 attr assignment' do
64
- method = process_method('def one() val[0] = 4; end')
65
- expect(method.num_statements).to eq(1)
66
- end
67
-
68
- it 'counts 1 increment assignment' do
69
- method = process_method('def one() val += 4; end')
70
- expect(method.num_statements).to eq(1)
71
- end
72
-
73
- it 'counts 1 increment attr assignment' do
74
- method = process_method('def one() val[0] += 4; end')
75
- expect(method.num_statements).to eq(1)
76
- end
77
-
78
- it 'counts 1 nested assignment' do
79
- method = process_method('def one() val = fred = 4; end')
80
- expect(method.num_statements).to eq(1)
81
- end
82
-
83
- it 'counts returns' do
84
- method = process_method('def one() val = 4; true; end')
85
- expect(method.num_statements).to eq(2)
86
- end
87
-
88
- it 'counts nil returns' do
89
- method = process_method('def one() val = 4; nil; end')
90
- expect(method.num_statements).to eq(2)
91
- end
92
- end
93
-
94
- RSpec.describe Reek::Smells::TooManyStatements, 'does not count control statements' do
95
- it 'counts 1 statement in a conditional expression' do
96
- method = process_method('def one() if val == 4; callee(); end; end')
97
- expect(method.num_statements).to eq(1)
98
- end
99
-
100
- it 'counts 3 statements in a conditional expression' do
101
- method = process_method('def one() if val == 4; callee(); callee(); callee(); end; end')
102
- expect(method.num_statements).to eq(3)
103
- end
104
-
105
- it 'counts 1 statements in an else' do
106
- method = process_method('def one() if val == 4; callee(); else; callee(); end; end')
107
- expect(method.num_statements).to eq(2)
108
- end
109
-
110
- it 'counts 3 statements in an else' do
111
- method = process_method('
112
- def one()
113
- if val == 4
114
- callee(); callee(); callee()
115
- else
116
- callee(); callee(); callee()
117
- end
118
- end
119
- ')
120
- expect(method.num_statements).to eq(6)
121
- end
122
-
123
- it 'does not count empty conditional expression' do
124
- method = process_method('def one() if val == 4; ; end; end')
125
- expect(method.num_statements).to eq(0)
126
- end
127
-
128
- it 'does not count empty else' do
129
- method = process_method('def one() if val == 4; ; else; ; end; end')
130
- expect(method.num_statements).to eq(0)
131
- end
132
-
133
- it 'counts extra statements in an if condition' do
134
- method = process_method('def one() if begin val = callee(); val < 4 end; end; end')
135
- expect(method.num_statements).to eq(1)
136
- end
137
-
138
- it 'counts 1 statement in a while loop' do
139
- method = process_method('def one() while val < 4; callee(); end; end')
140
- expect(method.num_statements).to eq(1)
141
- end
142
-
143
- it 'counts 3 statements in a while loop' do
144
- source = 'def one() while val < 4; callee(); callee(); callee(); end; end'
145
- expect(process_method(source).num_statements).to eq(3)
146
- end
147
-
148
- it 'counts extra statements in a while condition' do
149
- method = process_method('def one() while begin val = callee(); val < 4 end; end; end')
150
- expect(method.num_statements).to eq(1)
151
- end
152
-
153
- it 'counts 1 statement in a until loop' do
154
- method = process_method('def one() until val < 4; callee(); end; end')
155
- expect(method.num_statements).to eq(1)
156
- end
157
-
158
- it 'counts 3 statements in a until loop' do
159
- source = 'def one() until val < 4; callee(); callee(); callee(); end; end'
160
- expect(process_method(source).num_statements).to eq(3)
161
- end
162
-
163
- it 'counts 1 statement in a for loop' do
164
- method = process_method('def one() for i in 0..4; callee(); end; end')
165
- expect(method.num_statements).to eq(1)
166
- end
167
-
168
- it 'counts 3 statements in a for loop' do
169
- source = 'def one() for i in 0..4; callee(); callee(); callee(); end; end'
170
- expect(process_method(source).num_statements).to eq(3)
171
- end
172
-
173
- it 'counts 1 statement in a rescue' do
174
- method = process_method('def one() begin; callee(); rescue; callee(); end; end')
175
- expect(method.num_statements).to eq(2)
176
- end
177
-
178
- it 'counts 3 statements in a rescue' do
179
- method = process_method('
180
- def one()
181
- begin
182
- callee(); callee(); callee()
183
- rescue
184
- callee(); callee(); callee()
185
- end
186
- end
187
- ')
188
- expect(method.num_statements).to eq(6)
189
- end
190
-
191
- it 'counts 1 statement in a when' do
192
- method = process_method('def one() case fred; when "hi"; callee(); end; end')
193
- expect(method.num_statements).to eq(1)
194
- end
195
-
196
- it 'counts 3 statements in a when' do
197
- method = process_method('
198
- def one()
199
- case fred
200
- when "hi" then callee(); callee()
201
- when "lo" then callee()
202
- end
203
- end
204
- ')
205
- expect(method.num_statements).to eq(3)
206
- end
207
-
208
- it 'counts 1 statement in a case else' do
209
- source = 'def one() case fred; when "hi"; callee(); else; callee(); end; end'
210
- expect(process_method(source).num_statements).to eq(2)
211
- end
212
-
213
- it 'counts 3 statements in a case else' do
214
- method = process_method('
215
- def one()
216
- case fred
217
- when "hi" then callee(); callee(); callee()
218
- else callee(); callee(); callee()
219
- end
220
- end
221
- ')
222
- expect(method.num_statements).to eq(6)
223
- end
224
-
225
- it 'does not count empty case' do
226
- method = process_method('def one() case fred; when "hi"; ; when "lo"; ; end; end')
227
- expect(method.num_statements).to eq(0)
228
- end
229
-
230
- it 'does not count empty case else' do
231
- method = process_method('def one() case fred; when "hi"; ; else; ; end; end')
232
- expect(method.num_statements).to eq(0)
233
- end
234
-
235
- it 'counts 2 statement in an iterator' do
236
- method = process_method('def one() fred.each do; callee(); end; end')
237
- expect(method.num_statements).to eq(2)
238
- end
239
-
240
- it 'counts 4 statements in an iterator' do
241
- source = 'def one() fred.each do; callee(); callee(); callee(); end; end'
242
- expect(process_method(source).num_statements).to eq(4)
243
- end
244
-
245
- it 'counts 1 statement in a singleton method' do
246
- method = process_singleton_method('def self.foo; callee(); end')
247
- expect(method.num_statements).to eq(1)
39
+ expect(src).to reek_of(:TooManyStatements)
248
40
  end
249
41
  end
250
42