reek 5.6.0 → 6.0.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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +9 -0
- data/.github/workflows/ruby.yml +52 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +3 -1
- data/.rubocop_todo.yml +27 -20
- data/.simplecov +1 -0
- data/CHANGELOG.md +24 -0
- data/Dockerfile +1 -0
- data/Gemfile +14 -17
- data/README.md +11 -11
- data/bin/code_climate_reek +12 -2
- data/docs/Attribute.md +1 -1
- data/docs/Boolean-Parameter.md +2 -2
- data/docs/Control-Couple.md +1 -1
- data/docs/Nil-Check.md +4 -1
- data/docs/templates/default/docstring/setup.rb +1 -3
- data/features/command_line_interface/options.feature +2 -3
- data/features/configuration_files/schema_validation.feature +1 -1
- data/features/reports/codeclimate.feature +2 -2
- data/features/reports/json.feature +3 -3
- data/features/reports/reports.feature +4 -4
- data/features/reports/yaml.feature +3 -3
- data/features/step_definitions/reek_steps.rb +5 -1
- data/features/step_definitions/sample_file_steps.rb +2 -2
- data/features/support/env.rb +0 -1
- data/lib/reek/ast/node.rb +1 -1
- data/lib/reek/ast/sexp_extensions/arguments.rb +11 -0
- data/lib/reek/cli/options.rb +3 -3
- data/lib/reek/code_comment.rb +36 -29
- data/lib/reek/configuration/app_configuration.rb +4 -3
- data/lib/reek/configuration/configuration_converter.rb +2 -2
- data/lib/reek/configuration/directory_directives.rb +9 -3
- data/lib/reek/configuration/excluded_paths.rb +2 -1
- data/lib/reek/context/code_context.rb +1 -1
- data/lib/reek/context/module_context.rb +3 -1
- data/lib/reek/context/refinement_context.rb +16 -0
- data/lib/reek/context_builder.rb +16 -2
- data/lib/reek/errors/legacy_comment_separator_error.rb +36 -0
- data/lib/reek/report/code_climate/code_climate_configuration.yml +1 -1
- data/lib/reek/report/code_climate/code_climate_report.rb +2 -1
- data/lib/reek/report/simple_warning_formatter.rb +0 -7
- data/lib/reek/report.rb +5 -7
- data/lib/reek/smell_detectors/base_detector.rb +1 -9
- data/lib/reek/smell_detectors/boolean_parameter.rb +3 -1
- data/lib/reek/smell_detectors/data_clump.rb +23 -56
- data/lib/reek/smell_detectors/nil_check.rb +1 -12
- data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +1 -1
- data/lib/reek/smell_warning.rb +1 -2
- data/lib/reek/source/source_locator.rb +13 -10
- data/lib/reek/spec/smell_matcher.rb +2 -1
- data/lib/reek/version.rb +1 -1
- data/lib/reek.rb +1 -0
- data/reek.gemspec +13 -6
- data/spec/performance/reek/smell_detectors/runtime_speed_spec.rb +2 -4
- data/spec/quality/documentation_spec.rb +2 -1
- data/spec/reek/ast/sexp_extensions_spec.rb +15 -33
- data/spec/reek/code_comment_spec.rb +41 -42
- data/spec/reek/configuration/directory_directives_spec.rb +6 -0
- data/spec/reek/configuration/excluded_paths_spec.rb +12 -3
- data/spec/reek/context_builder_spec.rb +110 -113
- data/spec/reek/report/code_climate/code_climate_configuration_spec.rb +1 -3
- data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +26 -26
- data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
- data/spec/reek/report/location_formatter_spec.rb +3 -3
- data/spec/reek/smell_detectors/base_detector_spec.rb +3 -13
- data/spec/reek/smell_detectors/data_clump_spec.rb +14 -0
- data/spec/reek/smell_detectors/missing_safe_method_spec.rb +8 -2
- data/spec/reek/smell_detectors/nil_check_spec.rb +3 -3
- data/spec/reek/smell_detectors/utility_function_spec.rb +16 -0
- data/spec/reek/smell_warning_spec.rb +12 -12
- data/spec/reek/source/source_code_spec.rb +13 -0
- data/spec/reek/spec/should_reek_of_spec.rb +0 -1
- data/spec/reek/spec/should_reek_only_of_spec.rb +6 -6
- data/spec/reek/spec/smell_matcher_spec.rb +1 -1
- data/spec/spec_helper.rb +19 -5
- data/tasks/configuration.rake +1 -2
- metadata +24 -42
- data/.travis.yml +0 -35
- data/spec/factories/factories.rb +0 -37
|
@@ -3,42 +3,126 @@ require_lib 'reek/context_builder'
|
|
|
3
3
|
|
|
4
4
|
RSpec.describe Reek::ContextBuilder do
|
|
5
5
|
describe '#context_tree' do
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
context 'with some simple example code' do
|
|
7
|
+
let(:walker) do
|
|
8
|
+
code = 'class Car; def drive; end; end'
|
|
9
|
+
described_class.new(syntax_tree(code))
|
|
10
|
+
end
|
|
11
|
+
let(:context_tree) { walker.context_tree }
|
|
12
|
+
let(:module_context) { context_tree.children.first }
|
|
13
|
+
let(:method_context) { module_context.children.first }
|
|
14
|
+
|
|
15
|
+
describe 'the starting node' do
|
|
16
|
+
it 'is a root node' do
|
|
17
|
+
expect(context_tree.type).to eq(:root)
|
|
18
|
+
expect(context_tree).to be_a(Reek::Context::RootContext)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'has one module_context child' do
|
|
22
|
+
aggregate_failures do
|
|
23
|
+
expect(context_tree.children.count).to eq 1
|
|
24
|
+
expect(module_context).to be_a(Reek::Context::ModuleContext)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe 'the module node' do
|
|
30
|
+
it 'has one method_context child' do
|
|
31
|
+
aggregate_failures do
|
|
32
|
+
expect(method_context).to be_a(Reek::Context::MethodContext)
|
|
33
|
+
expect(module_context.children.size).to eq(1)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'holds a reference to the parent context' do
|
|
38
|
+
expect(method_context.parent).to eq(module_context)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
9
41
|
end
|
|
10
|
-
let(:context_tree) { walker.context_tree }
|
|
11
|
-
let(:module_context) { context_tree.children.first }
|
|
12
|
-
let(:method_context) { module_context.children.first }
|
|
13
42
|
|
|
14
|
-
it '
|
|
15
|
-
|
|
16
|
-
|
|
43
|
+
it 'creates the proper context for all kinds of singleton methods' do
|
|
44
|
+
src = <<-RUBY
|
|
45
|
+
class Car
|
|
46
|
+
def self.start; end
|
|
47
|
+
|
|
48
|
+
class << self
|
|
49
|
+
def drive; end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
RUBY
|
|
53
|
+
|
|
54
|
+
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
|
55
|
+
context_tree = described_class.new(syntax_tree).context_tree
|
|
56
|
+
|
|
57
|
+
class_node = context_tree.children.first
|
|
58
|
+
start_method = class_node.children.first
|
|
59
|
+
drive_method = class_node.children.last
|
|
60
|
+
|
|
61
|
+
expect(start_method).to be_instance_of Reek::Context::SingletonMethodContext
|
|
62
|
+
expect(drive_method).to be_instance_of Reek::Context::SingletonMethodContext
|
|
17
63
|
end
|
|
18
64
|
|
|
19
|
-
it '
|
|
20
|
-
|
|
65
|
+
it 'returns something sensible for nested metaclasses' do
|
|
66
|
+
src = <<-RUBY
|
|
67
|
+
class Foo
|
|
68
|
+
class << self
|
|
69
|
+
class << self
|
|
70
|
+
def bar; end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
RUBY
|
|
75
|
+
|
|
76
|
+
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
|
77
|
+
context_tree = described_class.new(syntax_tree).context_tree
|
|
78
|
+
|
|
79
|
+
class_context = context_tree.children.first
|
|
80
|
+
method_context = class_context.children.first
|
|
81
|
+
|
|
82
|
+
expect(method_context).to be_instance_of Reek::Context::SingletonMethodContext
|
|
83
|
+
expect(method_context.parent).to eq class_context
|
|
21
84
|
end
|
|
22
85
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
86
|
+
it 'returns something sensible for nested method definitions' do
|
|
87
|
+
src = <<-RUBY
|
|
88
|
+
class Foo
|
|
89
|
+
def foo
|
|
90
|
+
def bar
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
RUBY
|
|
27
95
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
96
|
+
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
|
97
|
+
context_tree = described_class.new(syntax_tree).context_tree
|
|
98
|
+
|
|
99
|
+
class_context = context_tree.children.first
|
|
100
|
+
foo_context = class_context.children.first
|
|
101
|
+
|
|
102
|
+
bar_context = foo_context.children.first
|
|
103
|
+
expect(bar_context).to be_instance_of Reek::Context::MethodContext
|
|
104
|
+
expect(bar_context.parent).to eq foo_context
|
|
31
105
|
end
|
|
32
106
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
107
|
+
it 'returns something sensible for method definitions nested in singleton methods' do
|
|
108
|
+
src = <<-RUBY
|
|
109
|
+
class Foo
|
|
110
|
+
def self.foo
|
|
111
|
+
def bar
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
RUBY
|
|
38
116
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
117
|
+
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
|
118
|
+
context_tree = described_class.new(syntax_tree).context_tree
|
|
119
|
+
|
|
120
|
+
class_context = context_tree.children.first
|
|
121
|
+
foo_context = class_context.children.first
|
|
122
|
+
|
|
123
|
+
bar_context = foo_context.children.first
|
|
124
|
+
expect(bar_context).to be_instance_of Reek::Context::SingletonMethodContext
|
|
125
|
+
expect(bar_context.parent).to eq foo_context
|
|
42
126
|
end
|
|
43
127
|
end
|
|
44
128
|
|
|
@@ -370,91 +454,4 @@ RSpec.describe Reek::ContextBuilder do
|
|
|
370
454
|
expect(nested_baz_context.visibility).to eq :public
|
|
371
455
|
end
|
|
372
456
|
end
|
|
373
|
-
|
|
374
|
-
describe '#context_tree' do
|
|
375
|
-
it 'creates the proper context for all kinds of singleton methods' do
|
|
376
|
-
src = <<-RUBY
|
|
377
|
-
class Car
|
|
378
|
-
def self.start; end
|
|
379
|
-
|
|
380
|
-
class << self
|
|
381
|
-
def drive; end
|
|
382
|
-
end
|
|
383
|
-
end
|
|
384
|
-
RUBY
|
|
385
|
-
|
|
386
|
-
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
|
387
|
-
context_tree = described_class.new(syntax_tree).context_tree
|
|
388
|
-
|
|
389
|
-
class_node = context_tree.children.first
|
|
390
|
-
start_method = class_node.children.first
|
|
391
|
-
drive_method = class_node.children.last
|
|
392
|
-
|
|
393
|
-
expect(start_method).to be_instance_of Reek::Context::SingletonMethodContext
|
|
394
|
-
expect(drive_method).to be_instance_of Reek::Context::SingletonMethodContext
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
it 'returns something sensible for nested metaclasses' do
|
|
398
|
-
src = <<-RUBY
|
|
399
|
-
class Foo
|
|
400
|
-
class << self
|
|
401
|
-
class << self
|
|
402
|
-
def bar; end
|
|
403
|
-
end
|
|
404
|
-
end
|
|
405
|
-
end
|
|
406
|
-
RUBY
|
|
407
|
-
|
|
408
|
-
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
|
409
|
-
context_tree = described_class.new(syntax_tree).context_tree
|
|
410
|
-
|
|
411
|
-
class_context = context_tree.children.first
|
|
412
|
-
method_context = class_context.children.first
|
|
413
|
-
|
|
414
|
-
expect(method_context).to be_instance_of Reek::Context::SingletonMethodContext
|
|
415
|
-
expect(method_context.parent).to eq class_context
|
|
416
|
-
end
|
|
417
|
-
|
|
418
|
-
it 'returns something sensible for nested method definitions' do
|
|
419
|
-
src = <<-RUBY
|
|
420
|
-
class Foo
|
|
421
|
-
def foo
|
|
422
|
-
def bar
|
|
423
|
-
end
|
|
424
|
-
end
|
|
425
|
-
end
|
|
426
|
-
RUBY
|
|
427
|
-
|
|
428
|
-
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
|
429
|
-
context_tree = described_class.new(syntax_tree).context_tree
|
|
430
|
-
|
|
431
|
-
class_context = context_tree.children.first
|
|
432
|
-
foo_context = class_context.children.first
|
|
433
|
-
|
|
434
|
-
bar_context = foo_context.children.first
|
|
435
|
-
expect(bar_context).to be_instance_of Reek::Context::MethodContext
|
|
436
|
-
expect(bar_context.parent).to eq foo_context
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
it 'returns something sensible for method definitions nested in singleton methods' do
|
|
440
|
-
src = <<-RUBY
|
|
441
|
-
class Foo
|
|
442
|
-
def self.foo
|
|
443
|
-
def bar
|
|
444
|
-
end
|
|
445
|
-
end
|
|
446
|
-
end
|
|
447
|
-
RUBY
|
|
448
|
-
|
|
449
|
-
syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
|
|
450
|
-
context_tree = described_class.new(syntax_tree).context_tree
|
|
451
|
-
|
|
452
|
-
class_context = context_tree.children.first
|
|
453
|
-
foo_context = class_context.children.first
|
|
454
|
-
|
|
455
|
-
bar_context = foo_context.children.first
|
|
456
|
-
expect(bar_context).to be_instance_of Reek::Context::SingletonMethodContext
|
|
457
|
-
expect(bar_context.parent).to eq foo_context
|
|
458
|
-
end
|
|
459
|
-
end
|
|
460
457
|
end
|
|
@@ -3,9 +3,7 @@ require_lib 'reek/report/code_climate/code_climate_configuration'
|
|
|
3
3
|
|
|
4
4
|
RSpec.describe Reek::Report::CodeClimateConfiguration do
|
|
5
5
|
yml = described_class.load
|
|
6
|
-
smell_types = Reek::SmellDetectors::BaseDetector.descendants.map
|
|
7
|
-
descendant.name.demodulize
|
|
8
|
-
end
|
|
6
|
+
smell_types = Reek::SmellDetectors::BaseDetector.descendants.map(&:smell_type)
|
|
9
7
|
|
|
10
8
|
smell_types.each do |name|
|
|
11
9
|
config = yml.fetch(name)
|
|
@@ -8,12 +8,12 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
|
|
|
8
8
|
context 'when fingerprinting a warning with no parameters' do
|
|
9
9
|
let(:expected_fingerprint) { 'e68badd29db51c92363a7c6a2438d722' }
|
|
10
10
|
let(:warning) do
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
Reek::SmellWarning.new(
|
|
12
|
+
'UtilityFunction',
|
|
13
|
+
context: 'alfa',
|
|
14
|
+
message: "doesn't depend on instance state (maybe move it to another class?)",
|
|
15
|
+
lines: lines,
|
|
16
|
+
source: 'a/ruby/source/file.rb')
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
context 'with code at a specific location' do
|
|
@@ -35,12 +35,12 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
|
|
|
35
35
|
|
|
36
36
|
context 'when the fingerprint should not be computed' do
|
|
37
37
|
let(:warning) do
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
Reek::SmellWarning.new(
|
|
39
|
+
'ManualDispatch',
|
|
40
|
+
context: 'Alfa#bravo',
|
|
41
|
+
message: 'manually dispatches method call',
|
|
42
|
+
lines: [4],
|
|
43
|
+
source: 'a/ruby/source/file.rb')
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
it 'returns nil' do
|
|
@@ -50,13 +50,13 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
|
|
|
50
50
|
|
|
51
51
|
context 'when the smell warning has only identifying parameters' do
|
|
52
52
|
let(:warning) do
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
Reek::SmellWarning.new(
|
|
54
|
+
'ClassVariable',
|
|
55
|
+
context: 'Alfa',
|
|
56
|
+
message: "declares the class variable '@@#{name}'",
|
|
57
|
+
lines: [4],
|
|
58
|
+
parameters: { name: "@@#{name}" },
|
|
59
|
+
source: 'a/ruby/source/file.rb')
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
context 'when the name is one thing' do
|
|
@@ -80,13 +80,13 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
|
|
|
80
80
|
|
|
81
81
|
context 'when the smell warning has identifying and non-identifying parameters' do
|
|
82
82
|
let(:warning) do
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
83
|
+
Reek::SmellWarning.new(
|
|
84
|
+
'DuplicateMethodCall',
|
|
85
|
+
context: "Alfa##{name}",
|
|
86
|
+
message: "calls '#{name}' #{count} times",
|
|
87
|
+
lines: lines,
|
|
88
|
+
parameters: { name: "@@#{name}", count: count },
|
|
89
|
+
source: 'a/ruby/source/file.rb')
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
context 'when the parameters are provided' do
|
|
@@ -4,12 +4,12 @@ require_lib 'reek/report/code_climate/code_climate_formatter'
|
|
|
4
4
|
RSpec.describe Reek::Report::CodeClimateFormatter do
|
|
5
5
|
describe '#render' do
|
|
6
6
|
let(:warning) do
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
Reek::SmellWarning.new(
|
|
8
|
+
'UtilityFunction',
|
|
9
|
+
context: 'context foo',
|
|
10
|
+
message: 'message bar',
|
|
11
|
+
lines: [1, 2],
|
|
12
|
+
source: 'a/ruby/source/file.rb')
|
|
13
13
|
end
|
|
14
14
|
let(:rendered) { described_class.new(warning).render }
|
|
15
15
|
let(:json) { JSON.parse rendered.chop }
|
|
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
|
|
|
2
2
|
require_lib 'reek/report/location_formatter'
|
|
3
3
|
|
|
4
4
|
RSpec.describe Reek::Report::BlankLocationFormatter do
|
|
5
|
-
let(:warning) {
|
|
5
|
+
let(:warning) { build_smell_warning(lines: [11, 9, 250, 100]) }
|
|
6
6
|
|
|
7
7
|
describe '.format' do
|
|
8
8
|
it 'returns a blank String regardless of the warning' do
|
|
@@ -12,7 +12,7 @@ RSpec.describe Reek::Report::BlankLocationFormatter do
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
RSpec.describe Reek::Report::DefaultLocationFormatter do
|
|
15
|
-
let(:warning) {
|
|
15
|
+
let(:warning) { build_smell_warning(lines: [11, 9, 250, 100]) }
|
|
16
16
|
|
|
17
17
|
describe '.format' do
|
|
18
18
|
it 'returns a prefix with sorted line numbers' do
|
|
@@ -22,7 +22,7 @@ RSpec.describe Reek::Report::DefaultLocationFormatter do
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
RSpec.describe Reek::Report::SingleLineLocationFormatter do
|
|
25
|
-
let(:warning) {
|
|
25
|
+
let(:warning) { build_smell_warning(lines: [11, 9, 250, 100]) }
|
|
26
26
|
|
|
27
27
|
describe '.format' do
|
|
28
28
|
it 'returns the first line where the smell was found' do
|
|
@@ -5,13 +5,13 @@ require_lib 'reek/smell_detectors/duplicate_method_call'
|
|
|
5
5
|
RSpec.describe Reek::SmellDetectors::BaseDetector do
|
|
6
6
|
describe '.todo_configuration_for' do
|
|
7
7
|
it 'returns exclusion configuration for the given smells' do
|
|
8
|
-
smell =
|
|
8
|
+
smell = build_smell_warning(smell_type: 'Foo', context: 'Foo#bar')
|
|
9
9
|
result = described_class.todo_configuration_for([smell])
|
|
10
10
|
expect(result).to eq('BaseDetector' => { 'exclude' => ['Foo#bar'] })
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
it 'merges identical contexts' do
|
|
14
|
-
smell =
|
|
14
|
+
smell = build_smell_warning(smell_type: 'Foo', context: 'Foo#bar')
|
|
15
15
|
result = described_class.todo_configuration_for([smell, smell])
|
|
16
16
|
expect(result).to eq('BaseDetector' => { 'exclude' => ['Foo#bar'] })
|
|
17
17
|
end
|
|
@@ -20,7 +20,7 @@ RSpec.describe Reek::SmellDetectors::BaseDetector do
|
|
|
20
20
|
let(:subclass) { Reek::SmellDetectors::TooManyStatements }
|
|
21
21
|
|
|
22
22
|
it 'includes default exclusions' do
|
|
23
|
-
smell =
|
|
23
|
+
smell = build_smell_warning(smell_type: 'TooManyStatements', context: 'Foo#bar')
|
|
24
24
|
result = subclass.todo_configuration_for([smell])
|
|
25
25
|
|
|
26
26
|
aggregate_failures do
|
|
@@ -31,16 +31,6 @@ RSpec.describe Reek::SmellDetectors::BaseDetector do
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
describe '.valid_detector?' do
|
|
35
|
-
it 'returns true for a valid detector' do
|
|
36
|
-
expect(described_class.valid_detector?('DuplicateMethodCall')).to be true
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it 'returns false for an invalid detector' do
|
|
40
|
-
expect(described_class.valid_detector?('Unknown')).to be false
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
34
|
describe '.to_detector' do
|
|
45
35
|
it 'returns the right detector' do
|
|
46
36
|
expect(described_class.to_detector('DuplicateMethodCall')).to eq(Reek::SmellDetectors::DuplicateMethodCall)
|
|
@@ -76,6 +76,20 @@ RSpec.describe Reek::SmellDetectors::DataClump do
|
|
|
76
76
|
parameters: ['echo', 'foxtrot'])
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
+
it 'reports arguments in alphabetical order even if they are never used that way' do
|
|
80
|
+
src = <<-RUBY
|
|
81
|
+
#{scope} Alfa
|
|
82
|
+
def bravo (foxtrot, echo); end
|
|
83
|
+
def charlie(foxtrot, echo); end
|
|
84
|
+
def delta (foxtrot, echo); end
|
|
85
|
+
end
|
|
86
|
+
RUBY
|
|
87
|
+
|
|
88
|
+
expect(src).to reek_of(:DataClump,
|
|
89
|
+
count: 3,
|
|
90
|
+
parameters: ['echo', 'foxtrot'])
|
|
91
|
+
end
|
|
92
|
+
|
|
79
93
|
it 'reports parameter sets that are > 2' do
|
|
80
94
|
src = <<-RUBY
|
|
81
95
|
#{scope} Alfa
|
|
@@ -50,13 +50,19 @@ RSpec.describe Reek::SmellDetectors::MissingSafeMethod do
|
|
|
50
50
|
|
|
51
51
|
it 'does not report methods we excluded via comment' do
|
|
52
52
|
source = <<-RUBY
|
|
53
|
-
# :reek:MissingSafeMethod
|
|
53
|
+
# :reek:MissingSafeMethod { exclude: [ bravo! ] }
|
|
54
54
|
class Alfa
|
|
55
55
|
def bravo!
|
|
56
56
|
end
|
|
57
|
+
|
|
58
|
+
def charlie!
|
|
59
|
+
end
|
|
57
60
|
end
|
|
58
61
|
RUBY
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
aggregate_failures do
|
|
64
|
+
expect(source).not_to reek_of(:MissingSafeMethod, name: 'bravo!')
|
|
65
|
+
expect(source).to reek_of(:MissingSafeMethod, name: 'charlie!')
|
|
66
|
+
end
|
|
61
67
|
end
|
|
62
68
|
end
|
|
@@ -76,14 +76,14 @@ RSpec.describe Reek::SmellDetectors::NilCheck do
|
|
|
76
76
|
expect(src).to reek_of(:NilCheck)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
it '
|
|
79
|
+
it 'does not report when scope uses &.' do
|
|
80
80
|
src = <<-RUBY
|
|
81
81
|
def alfa(bravo)
|
|
82
82
|
bravo&.charlie
|
|
83
83
|
end
|
|
84
84
|
RUBY
|
|
85
85
|
|
|
86
|
-
expect(src).
|
|
86
|
+
expect(src).not_to reek_of(:NilCheck)
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
it 'reports all lines when scope uses multiple nilchecks' do
|
|
@@ -95,6 +95,6 @@ RSpec.describe Reek::SmellDetectors::NilCheck do
|
|
|
95
95
|
end
|
|
96
96
|
RUBY
|
|
97
97
|
|
|
98
|
-
expect(src).to reek_of(:NilCheck, lines: [2, 3
|
|
98
|
+
expect(src).to reek_of(:NilCheck, lines: [2, 3])
|
|
99
99
|
end
|
|
100
100
|
end
|
|
@@ -161,6 +161,22 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
|
|
|
161
161
|
end
|
|
162
162
|
end
|
|
163
163
|
|
|
164
|
+
context 'when examining refinements' do
|
|
165
|
+
it 'reports on the refined class' do
|
|
166
|
+
src = <<-RUBY
|
|
167
|
+
module Alfa
|
|
168
|
+
refine Bravo do
|
|
169
|
+
def bravo(charlie)
|
|
170
|
+
charlie.delta.echo
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
RUBY
|
|
175
|
+
|
|
176
|
+
expect(src).to reek_of(:UtilityFunction, context: 'Bravo#bravo')
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
164
180
|
describe 'method visibility' do
|
|
165
181
|
it 'reports private methods' do
|
|
166
182
|
src = <<-RUBY
|
|
@@ -24,23 +24,23 @@ RSpec.describe Reek::SmellWarning do
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
context 'when smells differ only by detector' do
|
|
27
|
-
let(:first) {
|
|
28
|
-
let(:second) {
|
|
27
|
+
let(:first) { build_smell_warning(smell_type: 'DuplicateMethodCall') }
|
|
28
|
+
let(:second) { build_smell_warning(smell_type: 'FeatureEnvy') }
|
|
29
29
|
|
|
30
30
|
it_behaves_like 'first sorts ahead of second'
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
context 'when smells differ only by lines' do
|
|
34
|
-
let(:first) {
|
|
35
|
-
let(:second) {
|
|
34
|
+
let(:first) { build_smell_warning(smell_type: 'FeatureEnvy', lines: [2]) }
|
|
35
|
+
let(:second) { build_smell_warning(smell_type: 'FeatureEnvy', lines: [3]) }
|
|
36
36
|
|
|
37
37
|
it_behaves_like 'first sorts ahead of second'
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
context 'when smells differ only by context' do
|
|
41
|
-
let(:first) {
|
|
41
|
+
let(:first) { build_smell_warning(smell_type: 'DuplicateMethodCall', context: 'first') }
|
|
42
42
|
let(:second) do
|
|
43
|
-
|
|
43
|
+
build_smell_warning(smell_type: 'DuplicateMethodCall', context: 'second')
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
it_behaves_like 'first sorts ahead of second'
|
|
@@ -48,11 +48,11 @@ RSpec.describe Reek::SmellWarning do
|
|
|
48
48
|
|
|
49
49
|
context 'when smells differ only by message' do
|
|
50
50
|
let(:first) do
|
|
51
|
-
|
|
51
|
+
build_smell_warning(smell_type: 'DuplicateMethodCall',
|
|
52
52
|
context: 'ctx', message: 'first message')
|
|
53
53
|
end
|
|
54
54
|
let(:second) do
|
|
55
|
-
|
|
55
|
+
build_smell_warning(smell_type: 'DuplicateMethodCall',
|
|
56
56
|
context: 'ctx', message: 'second message')
|
|
57
57
|
end
|
|
58
58
|
|
|
@@ -61,10 +61,10 @@ RSpec.describe Reek::SmellWarning do
|
|
|
61
61
|
|
|
62
62
|
context 'when smells differ by name and message' do
|
|
63
63
|
let(:first) do
|
|
64
|
-
|
|
64
|
+
build_smell_warning(smell_type: 'FeatureEnvy', message: 'second message')
|
|
65
65
|
end
|
|
66
66
|
let(:second) do
|
|
67
|
-
|
|
67
|
+
build_smell_warning(smell_type: 'UtilityFunction', message: 'first message')
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
it_behaves_like 'first sorts ahead of second'
|
|
@@ -72,13 +72,13 @@ RSpec.describe Reek::SmellWarning do
|
|
|
72
72
|
|
|
73
73
|
context 'when smells differ everywhere' do
|
|
74
74
|
let(:first) do
|
|
75
|
-
|
|
75
|
+
build_smell_warning(smell_type: 'DuplicateMethodCall',
|
|
76
76
|
context: 'Dirty#a',
|
|
77
77
|
message: 'calls @s.title twice')
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
let(:second) do
|
|
81
|
-
|
|
81
|
+
build_smell_warning(smell_type: 'UncommunicativeVariableName',
|
|
82
82
|
context: 'Dirty',
|
|
83
83
|
message: "has the variable name '@s'")
|
|
84
84
|
end
|
|
@@ -62,5 +62,18 @@ RSpec.describe Reek::Source::SourceCode do
|
|
|
62
62
|
expect { src.syntax_tree }.to raise_error error_class, error_message
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
|
+
|
|
66
|
+
if RUBY_VERSION >= '2.7'
|
|
67
|
+
context 'with ruby 2.7 syntax' do
|
|
68
|
+
context 'with forward_args (`...`)' do
|
|
69
|
+
let(:source_code) { described_class.new(source: 'def alpha(...) bravo(...); end') }
|
|
70
|
+
|
|
71
|
+
it 'returns a :forward_args node' do
|
|
72
|
+
result = source_code.syntax_tree
|
|
73
|
+
expect(result.children[1].type).to eq(:forward_args)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
65
78
|
end
|
|
66
79
|
end
|
|
@@ -40,7 +40,7 @@ RSpec.describe Reek::Spec::ShouldReekOnlyOf do
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
context 'with 1 non-matching smell' do
|
|
43
|
-
let(:smells) { [
|
|
43
|
+
let(:smells) { [build_smell_warning(smell_type: 'ControlParameter')] }
|
|
44
44
|
|
|
45
45
|
it_behaves_like 'no match'
|
|
46
46
|
end
|
|
@@ -48,8 +48,8 @@ RSpec.describe Reek::Spec::ShouldReekOnlyOf do
|
|
|
48
48
|
context 'with 2 non-matching smells' do
|
|
49
49
|
let(:smells) do
|
|
50
50
|
[
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
build_smell_warning(smell_type: 'ControlParameter'),
|
|
52
|
+
build_smell_warning(smell_type: 'FeatureEnvy')
|
|
53
53
|
]
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -59,8 +59,8 @@ RSpec.describe Reek::Spec::ShouldReekOnlyOf do
|
|
|
59
59
|
context 'with 1 non-matching and 1 matching smell' do
|
|
60
60
|
let(:smells) do
|
|
61
61
|
[
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
build_smell_warning(smell_type: 'ControlParameter'),
|
|
63
|
+
build_smell_warning(smell_type: expected_smell_type.to_s,
|
|
64
64
|
message: "message mentioning #{expected_context_name}")
|
|
65
65
|
]
|
|
66
66
|
end
|
|
@@ -70,7 +70,7 @@ RSpec.describe Reek::Spec::ShouldReekOnlyOf do
|
|
|
70
70
|
|
|
71
71
|
context 'with 1 matching smell' do
|
|
72
72
|
let(:smells) do
|
|
73
|
-
[
|
|
73
|
+
[build_smell_warning(smell_type: expected_smell_type.to_s,
|
|
74
74
|
message: "message mentioning #{expected_context_name}")]
|
|
75
75
|
end
|
|
76
76
|
|
|
@@ -3,7 +3,7 @@ require_lib 'reek/spec/smell_matcher'
|
|
|
3
3
|
|
|
4
4
|
RSpec.describe Reek::Spec::SmellMatcher do
|
|
5
5
|
let(:smell_warning) do
|
|
6
|
-
|
|
6
|
+
build_smell_warning(smell_type: 'UncommunicativeVariableName',
|
|
7
7
|
message: "has the variable name '@s'",
|
|
8
8
|
parameters: { test: 'something' })
|
|
9
9
|
end
|