reek 3.8.3 → 3.9.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +5 -0
- data/Gemfile +16 -6
- data/README.md +1 -0
- data/features/command_line_interface/smells_count.feature +1 -1
- data/features/command_line_interface/stdin.feature +1 -1
- data/features/configuration_loading.feature +2 -2
- data/features/programmatic_access.feature +2 -2
- data/features/rake_task/rake_task.feature +4 -4
- data/features/reports/json.feature +2 -2
- data/features/reports/reports.feature +6 -6
- data/features/reports/yaml.feature +2 -2
- data/features/samples.feature +19 -19
- data/features/step_definitions/sample_file_steps.rb +1 -1
- data/lib/reek/ast/node.rb +12 -1
- data/lib/reek/ast/sexp_extensions.rb +1 -0
- data/lib/reek/ast/sexp_extensions/constant.rb +9 -0
- data/lib/reek/ast/sexp_extensions/methods.rb +1 -20
- data/lib/reek/ast/sexp_extensions/module.rb +2 -2
- data/lib/reek/ast/sexp_extensions/self.rb +12 -0
- data/lib/reek/ast/sexp_extensions/send.rb +4 -9
- data/lib/reek/ast/sexp_extensions/super.rb +1 -1
- data/lib/reek/ast/sexp_extensions/variables.rb +5 -0
- data/lib/reek/context/attribute_context.rb +12 -0
- data/lib/reek/context/code_context.rb +28 -26
- data/lib/reek/context/ghost_context.rb +54 -0
- data/lib/reek/context/method_context.rb +28 -1
- data/lib/reek/context/module_context.rb +55 -1
- data/lib/reek/context/root_context.rb +8 -0
- data/lib/reek/context/singleton_attribute_context.rb +15 -0
- data/lib/reek/context/singleton_method_context.rb +20 -0
- data/lib/reek/context/visibility_tracker.rb +25 -16
- data/lib/reek/context_builder.rb +61 -31
- data/lib/reek/examiner.rb +0 -6
- data/lib/reek/smells/control_parameter.rb +1 -1
- data/lib/reek/smells/nested_iterators.rb +1 -1
- data/lib/reek/smells/nil_check.rb +2 -2
- data/lib/reek/smells/utility_function.rb +1 -2
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +1 -12
- data/spec/reek/ast/node_spec.rb +51 -3
- data/spec/reek/ast/sexp_extensions_spec.rb +16 -3
- data/spec/reek/context/code_context_spec.rb +12 -31
- data/spec/reek/context/ghost_context_spec.rb +60 -0
- data/spec/reek/context/module_context_spec.rb +22 -2
- data/spec/reek/context_builder_spec.rb +225 -2
- data/spec/reek/examiner_spec.rb +1 -1
- data/spec/reek/smells/attribute_spec.rb +35 -0
- data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
- data/spec/reek/tree_dresser_spec.rb +0 -1
- data/spec/samples/checkstyle.xml +2 -2
- metadata +8 -152
- data/lib/reek/ast/sexp_formatter.rb +0 -31
- data/spec/reek/ast/sexp_formatter_spec.rb +0 -35
@@ -45,11 +45,11 @@ RSpec.describe Reek::Context::CodeContext do
|
|
45
45
|
context 'when there is an outer' do
|
46
46
|
let(:ctx) { Reek::Context::CodeContext.new(outer, exp) }
|
47
47
|
let(:outer_name) { 'another_random sting' }
|
48
|
-
let(:outer) { double('
|
48
|
+
let(:outer) { Reek::Context::CodeContext.new(nil, double('exp1')) }
|
49
49
|
|
50
50
|
before :each do
|
51
|
+
ctx.register_with_parent outer
|
51
52
|
allow(outer).to receive(:full_name).at_least(:once).and_return(outer_name)
|
52
|
-
allow(outer).to receive(:config).and_return({})
|
53
53
|
end
|
54
54
|
|
55
55
|
it 'creates the correct full name' do
|
@@ -174,6 +174,7 @@ RSpec.describe Reek::Context::CodeContext do
|
|
174
174
|
let(:sniffer) { double('sniffer') }
|
175
175
|
|
176
176
|
before :each do
|
177
|
+
context.register_with_parent(outer)
|
177
178
|
allow(sniffer).to receive(:smell_type).and_return('DuplicateMethodCall')
|
178
179
|
end
|
179
180
|
|
@@ -184,7 +185,7 @@ RSpec.describe Reek::Context::CodeContext do
|
|
184
185
|
end
|
185
186
|
|
186
187
|
context 'when there is an outer context' do
|
187
|
-
let(:outer) { double('
|
188
|
+
let(:outer) { Reek::Context::CodeContext.new(nil, double('exp1')) }
|
188
189
|
|
189
190
|
before :each do
|
190
191
|
allow(outer).to receive(:config_for).with(sniffer).and_return(
|
@@ -198,37 +199,16 @@ RSpec.describe Reek::Context::CodeContext do
|
|
198
199
|
end
|
199
200
|
end
|
200
201
|
|
201
|
-
describe '#
|
202
|
+
describe '#register_with_parent' do
|
202
203
|
let(:context) { Reek::Context::CodeContext.new(nil, double('exp1')) }
|
203
204
|
let(:first_child) { Reek::Context::CodeContext.new(context, double('exp2')) }
|
204
205
|
let(:second_child) { Reek::Context::CodeContext.new(context, double('exp3')) }
|
205
206
|
|
206
|
-
it
|
207
|
-
|
208
|
-
|
209
|
-
expect(context.children).to eq [first_child, second_child]
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
describe '#track_visibility' do
|
214
|
-
let(:context) { Reek::Context::CodeContext.new(nil, double('exp1')) }
|
215
|
-
let(:first_child) { Reek::Context::CodeContext.new(context, double('exp2', name: :foo)) }
|
216
|
-
let(:second_child) { Reek::Context::CodeContext.new(context, double('exp3')) }
|
217
|
-
|
218
|
-
it 'sets visibility on subsequent child contexts' do
|
219
|
-
context.append_child_context first_child
|
220
|
-
context.track_visibility :private, []
|
221
|
-
context.append_child_context second_child
|
222
|
-
expect(first_child.visibility).to eq :public
|
223
|
-
expect(second_child.visibility).to eq :private
|
224
|
-
end
|
207
|
+
it "appends the element to the parent context's list of children" do
|
208
|
+
first_child.register_with_parent context
|
209
|
+
second_child.register_with_parent context
|
225
210
|
|
226
|
-
|
227
|
-
context.append_child_context first_child
|
228
|
-
context.track_visibility :private, [first_child.name]
|
229
|
-
context.append_child_context second_child
|
230
|
-
expect(first_child.visibility).to eq :private
|
231
|
-
expect(second_child.visibility).to eq :public
|
211
|
+
expect(context.children).to eq [first_child, second_child]
|
232
212
|
end
|
233
213
|
end
|
234
214
|
|
@@ -238,8 +218,9 @@ RSpec.describe Reek::Context::CodeContext do
|
|
238
218
|
let(:second_child) { Reek::Context::CodeContext.new(context, double('exp3')) }
|
239
219
|
|
240
220
|
it 'yields each child' do
|
241
|
-
|
242
|
-
|
221
|
+
first_child.register_with_parent context
|
222
|
+
second_child.register_with_parent context
|
223
|
+
|
243
224
|
result = []
|
244
225
|
context.each do |ctx|
|
245
226
|
result << ctx
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_lib 'reek/context/code_context'
|
3
|
+
require_lib 'reek/context/ghost_context'
|
4
|
+
|
5
|
+
RSpec.describe Reek::Context::GhostContext do
|
6
|
+
let(:exp) { double('exp') }
|
7
|
+
let(:parent) { Reek::Context::CodeContext.new(nil, exp) }
|
8
|
+
|
9
|
+
describe '#register_with_parent' do
|
10
|
+
it 'does not append itself to its parent' do
|
11
|
+
ghost = described_class.new(parent, nil)
|
12
|
+
ghost.register_with_parent(parent)
|
13
|
+
expect(parent.children).not_to include ghost
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#append_child_context' do
|
18
|
+
let(:ghost) { described_class.new(parent, nil) }
|
19
|
+
|
20
|
+
before do
|
21
|
+
ghost.register_with_parent(parent)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'appends the child to the grandparent context' do
|
25
|
+
child = Reek::Context::CodeContext.new(ghost, sexp(:foo))
|
26
|
+
child.register_with_parent(ghost)
|
27
|
+
|
28
|
+
expect(parent.children).to include child
|
29
|
+
end
|
30
|
+
|
31
|
+
it "sets the child's parent to the grandparent context" do
|
32
|
+
child = Reek::Context::CodeContext.new(ghost, sexp(:foo))
|
33
|
+
child.register_with_parent(ghost)
|
34
|
+
|
35
|
+
expect(child.parent).to eq parent
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'appends the child to the list of children' do
|
39
|
+
child = Reek::Context::CodeContext.new(ghost, sexp(:foo))
|
40
|
+
child.register_with_parent(ghost)
|
41
|
+
|
42
|
+
expect(ghost.children).to include child
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'if the grandparent is also a ghost' do
|
46
|
+
let(:child_ghost) { described_class.new(ghost, nil) }
|
47
|
+
|
48
|
+
before do
|
49
|
+
child_ghost.register_with_parent(ghost)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'sets the childs parent to its remote ancestor' do
|
53
|
+
child = Reek::Context::CodeContext.new(child_ghost, sexp(:foo))
|
54
|
+
child.register_with_parent(child_ghost)
|
55
|
+
|
56
|
+
expect(child.parent).to eq parent
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -18,9 +18,7 @@ RSpec.describe Reek::Context::ModuleContext do
|
|
18
18
|
# module for test
|
19
19
|
class Jim; end; end').not_to reek
|
20
20
|
end
|
21
|
-
end
|
22
21
|
|
23
|
-
RSpec.describe Reek::Context::ModuleContext do
|
24
22
|
it 'should recognise global constant' do
|
25
23
|
expect('
|
26
24
|
# module for test
|
@@ -28,4 +26,26 @@ RSpec.describe Reek::Context::ModuleContext do
|
|
28
26
|
# module for test
|
29
27
|
class Inside; end; end').not_to reek
|
30
28
|
end
|
29
|
+
|
30
|
+
describe '#track_visibility' do
|
31
|
+
let(:context) { Reek::Context::ModuleContext.new(nil, double('exp1')) }
|
32
|
+
let(:first_child) { Reek::Context::MethodContext.new(context, double('exp2', type: :def, name: :foo)) }
|
33
|
+
let(:second_child) { Reek::Context::MethodContext.new(context, double('exp3', type: :def)) }
|
34
|
+
|
35
|
+
it 'sets visibility on subsequent child contexts' do
|
36
|
+
context.append_child_context first_child
|
37
|
+
context.track_visibility :private, []
|
38
|
+
context.append_child_context second_child
|
39
|
+
expect(first_child.visibility).to eq :public
|
40
|
+
expect(second_child.visibility).to eq :private
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'sets visibility on specifically mentioned child contexts' do
|
44
|
+
context.append_child_context first_child
|
45
|
+
context.track_visibility :private, [first_child.name]
|
46
|
+
context.append_child_context second_child
|
47
|
+
expect(first_child.visibility).to eq :private
|
48
|
+
expect(second_child.visibility).to eq :public
|
49
|
+
end
|
50
|
+
end
|
31
51
|
end
|
@@ -27,7 +27,7 @@ RSpec.describe Reek::ContextBuilder do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'holds a reference to the parent context' do
|
30
|
-
expect(module_context.
|
30
|
+
expect(module_context.parent).to eq(context_tree)
|
31
31
|
end
|
32
32
|
|
33
33
|
describe 'the module node' do
|
@@ -39,7 +39,7 @@ RSpec.describe Reek::ContextBuilder do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'holds a reference to the parent context' do
|
42
|
-
expect(method_context.
|
42
|
+
expect(method_context.parent).to eq(module_context)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -218,4 +218,227 @@ RSpec.describe Reek::ContextBuilder do
|
|
218
218
|
end
|
219
219
|
end
|
220
220
|
end
|
221
|
+
|
222
|
+
describe 'visibility tracking' do
|
223
|
+
def context_tree_for(code)
|
224
|
+
described_class.new(syntax_tree(code)).context_tree
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'does not mark class methods with instance visibility' do
|
228
|
+
code = <<-EOS
|
229
|
+
class Foo
|
230
|
+
private
|
231
|
+
def bar
|
232
|
+
end
|
233
|
+
def self.baz
|
234
|
+
end
|
235
|
+
end
|
236
|
+
EOS
|
237
|
+
|
238
|
+
root = context_tree_for(code)
|
239
|
+
module_context = root.children.first
|
240
|
+
method_contexts = module_context.children
|
241
|
+
expect(method_contexts[0].visibility).to eq :private
|
242
|
+
expect(method_contexts[1].visibility).to eq :public
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'only marks existing instance methods using later instance method modifiers' do
|
246
|
+
code = <<-EOS
|
247
|
+
class Foo
|
248
|
+
def bar
|
249
|
+
end
|
250
|
+
|
251
|
+
def baz
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.bar
|
255
|
+
end
|
256
|
+
|
257
|
+
class << self
|
258
|
+
def bar
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
private :bar, :baz
|
263
|
+
end
|
264
|
+
EOS
|
265
|
+
|
266
|
+
root = context_tree_for(code)
|
267
|
+
module_context = root.children.first
|
268
|
+
method_contexts = module_context.children
|
269
|
+
expect(method_contexts[0].visibility).to eq :private
|
270
|
+
expect(method_contexts[1].visibility).to eq :private
|
271
|
+
expect(method_contexts[2].visibility).to eq :public
|
272
|
+
expect(method_contexts[3].visibility).to eq :public
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'only marks existing instance attributes using later instance method modifiers' do
|
276
|
+
code = <<-EOS
|
277
|
+
class Foo
|
278
|
+
attr_writer :bar
|
279
|
+
|
280
|
+
class << self
|
281
|
+
attr_writer :bar
|
282
|
+
end
|
283
|
+
|
284
|
+
private :bar
|
285
|
+
end
|
286
|
+
EOS
|
287
|
+
|
288
|
+
root = context_tree_for(code)
|
289
|
+
module_context = root.children.first
|
290
|
+
method_contexts = module_context.children
|
291
|
+
expect(method_contexts[0].visibility).to eq :private
|
292
|
+
expect(method_contexts[1].visibility).to eq :public
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'marks class method visibility using private_class_method' do
|
296
|
+
code = <<-EOS
|
297
|
+
class Foo
|
298
|
+
def self.baz
|
299
|
+
end
|
300
|
+
|
301
|
+
private_class_method :baz
|
302
|
+
end
|
303
|
+
EOS
|
304
|
+
|
305
|
+
root = context_tree_for(code)
|
306
|
+
module_context = root.children.first
|
307
|
+
method_contexts = module_context.children
|
308
|
+
expect(method_contexts[0].visibility).to eq :private
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'marks class method visibility using public_class_method' do
|
312
|
+
code = <<-EOS
|
313
|
+
class Foo
|
314
|
+
class << self
|
315
|
+
private
|
316
|
+
|
317
|
+
def baz
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
public_class_method :baz
|
322
|
+
end
|
323
|
+
EOS
|
324
|
+
|
325
|
+
root = context_tree_for(code)
|
326
|
+
module_context = root.children.first
|
327
|
+
method_contexts = module_context.children
|
328
|
+
expect(method_contexts[0].visibility).to eq :public
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'correctly skips nested modules' do
|
332
|
+
code = <<-EOS
|
333
|
+
class Foo
|
334
|
+
class Bar
|
335
|
+
def baz
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def baz
|
340
|
+
end
|
341
|
+
|
342
|
+
def self.bar
|
343
|
+
end
|
344
|
+
|
345
|
+
private :baz
|
346
|
+
private_class_method :bar
|
347
|
+
end
|
348
|
+
EOS
|
349
|
+
|
350
|
+
root = context_tree_for(code)
|
351
|
+
foo_context = root.children.first
|
352
|
+
bar_context = foo_context.children.first
|
353
|
+
nested_baz_context = bar_context.children.first
|
354
|
+
expect(nested_baz_context.visibility).to eq :public
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
describe '#context_tree' do
|
359
|
+
it 'creates the proper context for all kinds of singleton methods' do
|
360
|
+
src = <<-EOS
|
361
|
+
class Car
|
362
|
+
def self.start; end
|
363
|
+
|
364
|
+
class << self
|
365
|
+
def drive; end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
EOS
|
369
|
+
|
370
|
+
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
371
|
+
context_tree = Reek::ContextBuilder.new(syntax_tree).context_tree
|
372
|
+
|
373
|
+
class_node = context_tree.children.first
|
374
|
+
start_method = class_node.children.first
|
375
|
+
drive_method = class_node.children.last
|
376
|
+
|
377
|
+
expect(start_method).to be_instance_of Reek::Context::SingletonMethodContext
|
378
|
+
expect(drive_method).to be_instance_of Reek::Context::SingletonMethodContext
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'returns something sensible for nested metaclasses' do
|
382
|
+
src = <<-EOS
|
383
|
+
class Foo
|
384
|
+
class << self
|
385
|
+
class << self
|
386
|
+
def bar; end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
EOS
|
391
|
+
|
392
|
+
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
393
|
+
context_tree = Reek::ContextBuilder.new(syntax_tree).context_tree
|
394
|
+
|
395
|
+
class_context = context_tree.children.first
|
396
|
+
method_context = class_context.children.first
|
397
|
+
|
398
|
+
expect(method_context).to be_instance_of Reek::Context::SingletonMethodContext
|
399
|
+
expect(method_context.parent).to eq class_context
|
400
|
+
end
|
401
|
+
|
402
|
+
it 'returns something sensible for nested method definitions' do
|
403
|
+
src = <<-EOS
|
404
|
+
class Foo
|
405
|
+
def foo
|
406
|
+
def bar
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
EOS
|
411
|
+
|
412
|
+
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
413
|
+
context_tree = Reek::ContextBuilder.new(syntax_tree).context_tree
|
414
|
+
|
415
|
+
class_context = context_tree.children.first
|
416
|
+
foo_context = class_context.children.first
|
417
|
+
|
418
|
+
bar_context = foo_context.children.first
|
419
|
+
expect(bar_context).to be_instance_of Reek::Context::MethodContext
|
420
|
+
expect(bar_context.parent).to eq foo_context
|
421
|
+
end
|
422
|
+
|
423
|
+
it 'returns something sensible for method definitions nested in singleton methods' do
|
424
|
+
src = <<-EOS
|
425
|
+
class Foo
|
426
|
+
def self.foo
|
427
|
+
def bar
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
EOS
|
432
|
+
|
433
|
+
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
434
|
+
context_tree = Reek::ContextBuilder.new(syntax_tree).context_tree
|
435
|
+
|
436
|
+
class_context = context_tree.children.first
|
437
|
+
foo_context = class_context.children.first
|
438
|
+
|
439
|
+
bar_context = foo_context.children.first
|
440
|
+
expect(bar_context).to be_instance_of Reek::Context::SingletonMethodContext
|
441
|
+
expect(bar_context.parent).to eq foo_context
|
442
|
+
end
|
443
|
+
end
|
221
444
|
end
|
data/spec/reek/examiner_spec.rb
CHANGED
@@ -63,7 +63,7 @@ RSpec.describe Reek::Examiner do
|
|
63
63
|
|
64
64
|
smell = examiner.smells.first
|
65
65
|
expect(smell).to be_a(Reek::Smells::SmellWarning)
|
66
|
-
expect(smell.message).to eq('calls bar.call_me 2 times')
|
66
|
+
expect(smell.message).to eq('calls bar.call_me() 2 times')
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -137,5 +137,40 @@ RSpec.describe Reek::Smells::Attribute do
|
|
137
137
|
'
|
138
138
|
expect(src).to reek_of(:Attribute)
|
139
139
|
end
|
140
|
+
|
141
|
+
it 'records attr_writer defining a class attribute' do
|
142
|
+
src = <<-EOS
|
143
|
+
class Klass
|
144
|
+
class << self
|
145
|
+
attr_writer :my_attr
|
146
|
+
end
|
147
|
+
end
|
148
|
+
EOS
|
149
|
+
expect(src).to reek_of(:Attribute, name: 'my_attr')
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'does not record private class attributes' do
|
153
|
+
src = <<-EOS
|
154
|
+
class Klass
|
155
|
+
class << self
|
156
|
+
private
|
157
|
+
attr_writer :my_attr
|
158
|
+
end
|
159
|
+
end
|
160
|
+
EOS
|
161
|
+
expect(src).not_to reek_of(:Attribute, name: 'my_attr')
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'tracks visibility in metaclasses separately' do
|
165
|
+
src = <<-EOS
|
166
|
+
class Klass
|
167
|
+
private
|
168
|
+
class << self
|
169
|
+
attr_writer :my_attr
|
170
|
+
end
|
171
|
+
end
|
172
|
+
EOS
|
173
|
+
expect(src).to reek_of(:Attribute, name: 'my_attr')
|
174
|
+
end
|
140
175
|
end
|
141
176
|
end
|