reek 3.0.4 → 3.1

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