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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +9 -0
  3. data/.github/workflows/ruby.yml +52 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +3 -1
  6. data/.rubocop_todo.yml +27 -20
  7. data/.simplecov +1 -0
  8. data/CHANGELOG.md +24 -0
  9. data/Dockerfile +1 -0
  10. data/Gemfile +14 -17
  11. data/README.md +11 -11
  12. data/bin/code_climate_reek +12 -2
  13. data/docs/Attribute.md +1 -1
  14. data/docs/Boolean-Parameter.md +2 -2
  15. data/docs/Control-Couple.md +1 -1
  16. data/docs/Nil-Check.md +4 -1
  17. data/docs/templates/default/docstring/setup.rb +1 -3
  18. data/features/command_line_interface/options.feature +2 -3
  19. data/features/configuration_files/schema_validation.feature +1 -1
  20. data/features/reports/codeclimate.feature +2 -2
  21. data/features/reports/json.feature +3 -3
  22. data/features/reports/reports.feature +4 -4
  23. data/features/reports/yaml.feature +3 -3
  24. data/features/step_definitions/reek_steps.rb +5 -1
  25. data/features/step_definitions/sample_file_steps.rb +2 -2
  26. data/features/support/env.rb +0 -1
  27. data/lib/reek/ast/node.rb +1 -1
  28. data/lib/reek/ast/sexp_extensions/arguments.rb +11 -0
  29. data/lib/reek/cli/options.rb +3 -3
  30. data/lib/reek/code_comment.rb +36 -29
  31. data/lib/reek/configuration/app_configuration.rb +4 -3
  32. data/lib/reek/configuration/configuration_converter.rb +2 -2
  33. data/lib/reek/configuration/directory_directives.rb +9 -3
  34. data/lib/reek/configuration/excluded_paths.rb +2 -1
  35. data/lib/reek/context/code_context.rb +1 -1
  36. data/lib/reek/context/module_context.rb +3 -1
  37. data/lib/reek/context/refinement_context.rb +16 -0
  38. data/lib/reek/context_builder.rb +16 -2
  39. data/lib/reek/errors/legacy_comment_separator_error.rb +36 -0
  40. data/lib/reek/report/code_climate/code_climate_configuration.yml +1 -1
  41. data/lib/reek/report/code_climate/code_climate_report.rb +2 -1
  42. data/lib/reek/report/simple_warning_formatter.rb +0 -7
  43. data/lib/reek/report.rb +5 -7
  44. data/lib/reek/smell_detectors/base_detector.rb +1 -9
  45. data/lib/reek/smell_detectors/boolean_parameter.rb +3 -1
  46. data/lib/reek/smell_detectors/data_clump.rb +23 -56
  47. data/lib/reek/smell_detectors/nil_check.rb +1 -12
  48. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +1 -1
  49. data/lib/reek/smell_warning.rb +1 -2
  50. data/lib/reek/source/source_locator.rb +13 -10
  51. data/lib/reek/spec/smell_matcher.rb +2 -1
  52. data/lib/reek/version.rb +1 -1
  53. data/lib/reek.rb +1 -0
  54. data/reek.gemspec +13 -6
  55. data/spec/performance/reek/smell_detectors/runtime_speed_spec.rb +2 -4
  56. data/spec/quality/documentation_spec.rb +2 -1
  57. data/spec/reek/ast/sexp_extensions_spec.rb +15 -33
  58. data/spec/reek/code_comment_spec.rb +41 -42
  59. data/spec/reek/configuration/directory_directives_spec.rb +6 -0
  60. data/spec/reek/configuration/excluded_paths_spec.rb +12 -3
  61. data/spec/reek/context_builder_spec.rb +110 -113
  62. data/spec/reek/report/code_climate/code_climate_configuration_spec.rb +1 -3
  63. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +26 -26
  64. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  65. data/spec/reek/report/location_formatter_spec.rb +3 -3
  66. data/spec/reek/smell_detectors/base_detector_spec.rb +3 -13
  67. data/spec/reek/smell_detectors/data_clump_spec.rb +14 -0
  68. data/spec/reek/smell_detectors/missing_safe_method_spec.rb +8 -2
  69. data/spec/reek/smell_detectors/nil_check_spec.rb +3 -3
  70. data/spec/reek/smell_detectors/utility_function_spec.rb +16 -0
  71. data/spec/reek/smell_warning_spec.rb +12 -12
  72. data/spec/reek/source/source_code_spec.rb +13 -0
  73. data/spec/reek/spec/should_reek_of_spec.rb +0 -1
  74. data/spec/reek/spec/should_reek_only_of_spec.rb +6 -6
  75. data/spec/reek/spec/smell_matcher_spec.rb +1 -1
  76. data/spec/spec_helper.rb +19 -5
  77. data/tasks/configuration.rake +1 -2
  78. metadata +24 -42
  79. data/.travis.yml +0 -35
  80. 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
- let(:walker) do
7
- code = 'class Car; def drive; end; end'
8
- described_class.new(syntax_tree(code))
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 'starts with a root node' do
15
- expect(context_tree.type).to eq(:root)
16
- expect(context_tree).to be_a(Reek::Context::RootContext)
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 'has one child' do
20
- expect(context_tree.children.size).to eq(1)
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
- describe 'the root node' do
24
- it 'has one module_context' do
25
- expect(module_context).to be_a(Reek::Context::ModuleContext)
26
- end
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
- it 'holds a reference to the parent context' do
29
- expect(module_context.parent).to eq(context_tree)
30
- end
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
- describe 'the module node' do
34
- it 'has one method_context' do
35
- expect(method_context).to be_a(Reek::Context::MethodContext)
36
- expect(module_context.children.size).to eq(1)
37
- end
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
- it 'holds a reference to the parent context' do
40
- expect(method_context.parent).to eq(module_context)
41
- end
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 do |descendant|
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
- build(:smell_warning,
12
- smell_type: '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')
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
- build(:smell_warning,
39
- smell_type: 'ManualDispatch',
40
- context: 'Alfa#bravo',
41
- message: 'manually dispatches method call',
42
- lines: [4],
43
- source: 'a/ruby/source/file.rb')
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
- build(:smell_warning,
54
- smell_type: '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')
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
- build(:smell_warning,
84
- smell_type: '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')
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
- build(:smell_warning,
8
- smell_type: 'UtilityFunction',
9
- context: 'context foo',
10
- message: 'message bar',
11
- lines: [1, 2],
12
- source: 'a/ruby/source/file.rb')
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) { build(:smell_warning, lines: [11, 9, 250, 100]) }
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) { build(:smell_warning, lines: [11, 9, 250, 100]) }
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) { build(:smell_warning, lines: [11, 9, 250, 100]) }
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 = create(:smell_warning, smell_type: 'Foo', context: 'Foo#bar')
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 = create(:smell_warning, smell_type: 'Foo', context: 'Foo#bar')
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 = create(:smell_warning, smell_type: 'TooManyStatements', context: 'Foo#bar')
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: { exclude: [ bravo! ] }
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
- expect(source).not_to reek_of(:MissingSafeMethod)
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 'reports when scope uses &.' do
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).to reek_of(:NilCheck)
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, 4])
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) { build(:smell_warning, smell_type: 'DuplicateMethodCall') }
28
- let(:second) { build(:smell_warning, smell_type: 'FeatureEnvy') }
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) { build(:smell_warning, smell_type: 'FeatureEnvy', lines: [2]) }
35
- let(:second) { build(:smell_warning, smell_type: 'FeatureEnvy', lines: [3]) }
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) { build(:smell_warning, smell_type: 'DuplicateMethodCall', context: 'first') }
41
+ let(:first) { build_smell_warning(smell_type: 'DuplicateMethodCall', context: 'first') }
42
42
  let(:second) do
43
- build(:smell_warning, smell_type: 'DuplicateMethodCall', context: 'second')
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
- build(:smell_warning, smell_type: 'DuplicateMethodCall',
51
+ build_smell_warning(smell_type: 'DuplicateMethodCall',
52
52
  context: 'ctx', message: 'first message')
53
53
  end
54
54
  let(:second) do
55
- build(:smell_warning, smell_type: 'DuplicateMethodCall',
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
- build(:smell_warning, smell_type: 'FeatureEnvy', message: 'second message')
64
+ build_smell_warning(smell_type: 'FeatureEnvy', message: 'second message')
65
65
  end
66
66
  let(:second) do
67
- build(:smell_warning, smell_type: 'UtilityFunction', message: 'first message')
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
- build(:smell_warning, smell_type: 'DuplicateMethodCall',
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
- build(:smell_warning, smell_type: 'UncommunicativeVariableName',
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
@@ -1,7 +1,6 @@
1
1
  require 'pathname'
2
2
  require_relative '../../spec_helper'
3
3
  require_lib 'reek/spec'
4
- require 'active_support/core_ext/hash/except'
5
4
 
6
5
  RSpec.describe Reek::Spec::ShouldReekOf do
7
6
  describe 'smell type selection' do
@@ -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) { [build(:smell_warning, smell_type: 'ControlParameter')] }
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
- build(:smell_warning, smell_type: 'ControlParameter'),
52
- build(:smell_warning, smell_type: 'FeatureEnvy')
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
- build(:smell_warning, smell_type: 'ControlParameter'),
63
- build(:smell_warning, smell_type: expected_smell_type.to_s,
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
- [build(:smell_warning, smell_type: expected_smell_type.to_s,
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
- build(:smell_warning, smell_type: 'UncommunicativeVariableName',
6
+ build_smell_warning(smell_type: 'UncommunicativeVariableName',
7
7
  message: "has the variable name '@s'",
8
8
  parameters: { test: 'something' })
9
9
  end