reek 5.4.0 → 6.0.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +27 -6
- data/.rubocop_todo.yml +4 -4
- data/.simplecov +1 -0
- data/.travis.yml +13 -11
- data/CHANGELOG.md +27 -1
- data/Dockerfile +2 -1
- data/Gemfile +15 -17
- data/README.md +15 -11
- data/bin/code_climate_reek +12 -2
- data/docs/Attribute.md +1 -1
- data/docs/Control-Couple.md +1 -1
- data/docs/Nil-Check.md +4 -1
- data/features/command_line_interface/options.feature +2 -3
- 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 +4 -0
- data/features/support/env.rb +1 -2
- data/lib/reek/ast/sexp_extensions/arguments.rb +11 -0
- data/lib/reek/cli/command/todo_list_command.rb +7 -2
- data/lib/reek/cli/options.rb +2 -2
- data/lib/reek/code_comment.rb +45 -38
- data/lib/reek/configuration/configuration_converter.rb +2 -2
- data/lib/reek/configuration/directory_directives.rb +7 -1
- data/lib/reek/errors/legacy_comment_separator_error.rb +36 -0
- data/lib/reek/examiner.rb +3 -3
- data/lib/reek/report.rb +5 -7
- 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/text_report.rb +2 -2
- data/lib/reek/smell_detectors/base_detector.rb +2 -10
- 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/subclassed_from_core_class.rb +3 -7
- data/lib/reek/smell_detectors/too_many_constants.rb +1 -1
- data/lib/reek/smell_warning.rb +18 -14
- data/lib/reek/source/source_code.rb +3 -2
- data/lib/reek/spec/smell_matcher.rb +2 -1
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +5 -6
- data/spec/reek/ast/sexp_extensions_spec.rb +15 -33
- data/spec/reek/cli/application_spec.rb +1 -1
- data/spec/reek/code_comment_spec.rb +41 -42
- data/spec/reek/configuration/directory_directives_spec.rb +6 -0
- data/spec/reek/context_builder_spec.rb +110 -113
- data/spec/reek/examiner_spec.rb +1 -0
- 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/code_climate/code_climate_report_spec.rb +1 -1
- data/spec/reek/report/json_report_spec.rb +1 -1
- data/spec/reek/report/location_formatter_spec.rb +3 -3
- data/spec/reek/report/text_report_spec.rb +1 -7
- data/spec/reek/report/yaml_report_spec.rb +1 -1
- data/spec/reek/smell_configuration_spec.rb +2 -0
- data/spec/reek/smell_detectors/base_detector_spec.rb +3 -16
- 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_warning_spec.rb +17 -28
- 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 -13
- data/spec/reek/spec/smell_matcher_spec.rb +1 -2
- data/spec/spec_helper.rb +20 -6
- metadata +11 -26
- data/spec/factories/factories.rb +0 -48
@@ -89,6 +89,12 @@ RSpec.describe Reek::Configuration::DirectoryDirectives do
|
|
89
89
|
expect(hit.to_s).to eq('bar/**/.spec/*')
|
90
90
|
end
|
91
91
|
|
92
|
+
it 'returns the corresponding directory when source_base_dir is an absolute_path' do
|
93
|
+
source_base_dir = Pathname.new('foo/bar').expand_path
|
94
|
+
hit = directives.send :best_match_for, source_base_dir
|
95
|
+
expect(hit.to_s).to eq('foo/bar')
|
96
|
+
end
|
97
|
+
|
92
98
|
it 'does not match an arbitrary directory when source_base_dir contains a character that could match the .' do
|
93
99
|
source_base_dir = 'bar/something/aspec/direct'
|
94
100
|
hit = directives.send :best_match_for, source_base_dir
|
@@ -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
|
data/spec/reek/examiner_spec.rb
CHANGED
@@ -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
|
@@ -6,13 +6,7 @@ require_lib 'reek/report/simple_warning_formatter'
|
|
6
6
|
require 'rainbow'
|
7
7
|
|
8
8
|
RSpec.describe Reek::Report::TextReport do
|
9
|
-
let(:
|
10
|
-
{
|
11
|
-
warning_formatter: Reek::Report::SimpleWarningFormatter.new,
|
12
|
-
heading_formatter: Reek::Report::QuietHeadingFormatter
|
13
|
-
}
|
14
|
-
end
|
15
|
-
let(:instance) { described_class.new report_options }
|
9
|
+
let(:instance) { described_class.new }
|
16
10
|
|
17
11
|
context 'with a single empty source' do
|
18
12
|
before do
|
@@ -18,10 +18,12 @@ RSpec.describe Reek::SmellConfiguration do
|
|
18
18
|
it { expect(smell_config.merge('enabled' => true)).to eq(base_config) }
|
19
19
|
it { expect(smell_config.merge('exclude' => [])).to eq(base_config) }
|
20
20
|
it { expect(smell_config.merge('accept' => ['_'])).to eq(base_config) }
|
21
|
+
|
21
22
|
it do
|
22
23
|
updated = smell_config.merge('reject' => [/^.$/, /[0-9]$/, /[A-Z]/])
|
23
24
|
expect(updated).to eq(base_config)
|
24
25
|
end
|
26
|
+
|
25
27
|
it do
|
26
28
|
updated = smell_config.merge('accept' => ['_'], 'enabled' => true)
|
27
29
|
expect(updated).to eq(base_config)
|