reek 3.7.1 → 3.8.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/.rubocop.yml +8 -1
- data/CHANGELOG.md +5 -0
- data/README.md +1 -1
- data/defaults.reek +3 -0
- data/docs/Code-Smells.md +1 -0
- data/docs/How-reek-works-internally.md +3 -2
- data/docs/Unused-Private-Method.md +47 -0
- data/features/samples.feature +22 -2
- data/features/step_definitions/sample_file_steps.rb +3 -0
- data/lib/reek/ast/node.rb +1 -2
- data/lib/reek/ast/object_refs.rb +30 -7
- data/lib/reek/code_comment.rb +25 -19
- data/lib/reek/context/class_context.rb +11 -0
- data/lib/reek/context/code_context.rb +58 -78
- data/lib/reek/context/module_context.rb +18 -0
- data/lib/reek/context/send_context.rb +17 -0
- data/lib/reek/context/singleton_method_context.rb +0 -3
- data/lib/reek/context/statement_counter.rb +32 -0
- data/lib/reek/context/visibility_tracker.rb +54 -0
- data/lib/reek/context_builder.rb +473 -0
- data/lib/reek/examiner.rb +14 -14
- data/lib/reek/smells/feature_envy.rb +3 -3
- data/lib/reek/smells/smell_detector.rb +1 -0
- data/lib/reek/smells/smell_repository.rb +11 -0
- data/lib/reek/smells/too_many_statements.rb +1 -1
- data/lib/reek/smells/unused_private_method.rb +82 -0
- data/lib/reek/smells/utility_function.rb +1 -1
- data/lib/reek/smells.rb +1 -0
- data/lib/reek/version.rb +1 -1
- data/spec/reek/ast/object_refs_spec.rb +20 -20
- data/spec/reek/cli/input_spec.rb +55 -0
- data/spec/reek/code_comment_spec.rb +10 -0
- data/spec/reek/context/code_context_spec.rb +8 -0
- data/spec/reek/context/module_context_spec.rb +10 -8
- data/spec/reek/context_builder_spec.rb +221 -0
- data/spec/reek/examiner_spec.rb +13 -0
- data/spec/reek/smells/boolean_parameter_spec.rb +2 -0
- data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
- data/spec/reek/smells/feature_envy_spec.rb +1 -1
- data/spec/reek/smells/too_many_statements_spec.rb +3 -3
- data/spec/reek/smells/unused_private_method_spec.rb +110 -0
- data/spec/spec_helper.rb +7 -0
- data/tasks/console.rake +5 -0
- metadata +13 -5
- data/lib/reek/tree_walker.rb +0 -237
- data/spec/reek/context/singleton_method_context_spec.rb +0 -16
- data/spec/reek/tree_walker_spec.rb +0 -237
@@ -0,0 +1,82 @@
|
|
1
|
+
require_relative 'smell_detector'
|
2
|
+
require_relative 'smell_warning'
|
3
|
+
require_relative '../context/method_context'
|
4
|
+
|
5
|
+
module Reek
|
6
|
+
module Smells
|
7
|
+
#
|
8
|
+
# Classes should use their private methods. Otherwise this is dead
|
9
|
+
# code which is confusing and bad for maintenance.
|
10
|
+
#
|
11
|
+
# See {file:docs/Unused-Private-Method.md} for details.
|
12
|
+
#
|
13
|
+
class UnusedPrivateMethod < SmellDetector
|
14
|
+
# Class for storing `hits` which are unused private methods
|
15
|
+
# we found in the given context. `name` and `line` are then used to
|
16
|
+
# construct SmellWarnings.
|
17
|
+
class Hit
|
18
|
+
attr_reader :name, :line
|
19
|
+
|
20
|
+
def initialize(context)
|
21
|
+
@name = context.name
|
22
|
+
@line = context.exp.line
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.contexts
|
27
|
+
[:class]
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# @param ctx [Context::ClassContext]
|
32
|
+
# @return [Array<SmellWarning>]
|
33
|
+
#
|
34
|
+
# :reek:FeatureEnvy
|
35
|
+
def inspect(ctx)
|
36
|
+
hits(ctx).map do |hit|
|
37
|
+
name = hit.name
|
38
|
+
smell_warning(
|
39
|
+
context: ctx,
|
40
|
+
lines: [hit.line],
|
41
|
+
message: "has the unused private instance method `#{name}`",
|
42
|
+
parameters: { name: name })
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
#
|
49
|
+
# @param ctx [Context::ClassContext]
|
50
|
+
# @return [Array<Hit>]
|
51
|
+
#
|
52
|
+
def hits(ctx)
|
53
|
+
unused_private_methods(ctx).map do |defined_method|
|
54
|
+
Hit.new(defined_method) unless ignore_method?(ctx, defined_method)
|
55
|
+
end.compact
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# @param ctx [Context::ClassContext]
|
60
|
+
# @return [Array<Context::MethodContext]
|
61
|
+
#
|
62
|
+
# :reek:UtilityFunction
|
63
|
+
def unused_private_methods(ctx)
|
64
|
+
defined_private_methods = ctx.defined_instance_methods(visibility: :private)
|
65
|
+
called_method_names = ctx.instance_method_calls.map(&:name)
|
66
|
+
|
67
|
+
defined_private_methods.select do |defined_method|
|
68
|
+
!called_method_names.include?(defined_method.name)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# @param ctx [Context::ClassContext]
|
74
|
+
# @return [Boolean]
|
75
|
+
#
|
76
|
+
def ignore_method?(ctx, method)
|
77
|
+
ignore_methods = value(EXCLUDE_KEY, ctx, DEFAULT_EXCLUDE_SET)
|
78
|
+
ignore_methods.any? { |ignore_method| method.name[ignore_method] }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -59,7 +59,7 @@ module Reek
|
|
59
59
|
# :reek:TooManyStatements: { max_statements: 6 }
|
60
60
|
def inspect(ctx)
|
61
61
|
return [] if ctx.singleton_method?
|
62
|
-
return [] if ctx.
|
62
|
+
return [] if ctx.number_of_statements == 0
|
63
63
|
return [] if ctx.references_self?
|
64
64
|
return [] if num_helper_methods(ctx).zero?
|
65
65
|
return [] if ignore_method?(ctx)
|
data/lib/reek/smells.rb
CHANGED
@@ -21,4 +21,5 @@ require_relative 'smells/uncommunicative_module_name'
|
|
21
21
|
require_relative 'smells/uncommunicative_parameter_name'
|
22
22
|
require_relative 'smells/uncommunicative_variable_name'
|
23
23
|
require_relative 'smells/unused_parameters'
|
24
|
+
require_relative 'smells/unused_private_method'
|
24
25
|
require_relative 'smells/utility_function'
|
data/lib/reek/version.rb
CHANGED
@@ -13,9 +13,9 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
13
13
|
context 'with references to a, b, and a' do
|
14
14
|
context 'with no refs to self' do
|
15
15
|
before(:each) do
|
16
|
-
refs.
|
17
|
-
refs.
|
18
|
-
refs.
|
16
|
+
refs.record_reference(name: :a)
|
17
|
+
refs.record_reference(name: :b)
|
18
|
+
refs.record_reference(name: :a)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'should report no refs to self' do
|
@@ -32,7 +32,7 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
32
32
|
|
33
33
|
context 'with one reference to self' do
|
34
34
|
before(:each) do
|
35
|
-
refs.
|
35
|
+
refs.record_reference(name: :self)
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'should report 1 ref to self' do
|
@@ -53,13 +53,13 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
53
53
|
|
54
54
|
context 'with many refs to self' do
|
55
55
|
before(:each) do
|
56
|
-
refs.
|
57
|
-
refs.
|
58
|
-
refs.
|
59
|
-
refs.
|
60
|
-
refs.
|
61
|
-
refs.
|
62
|
-
refs.
|
56
|
+
refs.record_reference(name: :self)
|
57
|
+
refs.record_reference(name: :self)
|
58
|
+
refs.record_reference(name: :a)
|
59
|
+
refs.record_reference(name: :self)
|
60
|
+
refs.record_reference(name: :b)
|
61
|
+
refs.record_reference(name: :a)
|
62
|
+
refs.record_reference(name: :self)
|
63
63
|
end
|
64
64
|
|
65
65
|
it 'should report all refs to self' do
|
@@ -77,11 +77,11 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
77
77
|
|
78
78
|
context 'when self is not the only max' do
|
79
79
|
before(:each) do
|
80
|
-
refs.
|
81
|
-
refs.
|
82
|
-
refs.
|
83
|
-
refs.
|
84
|
-
refs.
|
80
|
+
refs.record_reference(name: :a)
|
81
|
+
refs.record_reference(name: :self)
|
82
|
+
refs.record_reference(name: :self)
|
83
|
+
refs.record_reference(name: :b)
|
84
|
+
refs.record_reference(name: :a)
|
85
85
|
end
|
86
86
|
|
87
87
|
it 'should report all refs to self' do
|
@@ -100,10 +100,10 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
100
100
|
|
101
101
|
context 'when self is not among the max' do
|
102
102
|
before(:each) do
|
103
|
-
refs.
|
104
|
-
refs.
|
105
|
-
refs.
|
106
|
-
refs.
|
103
|
+
refs.record_reference(name: :a)
|
104
|
+
refs.record_reference(name: :b)
|
105
|
+
refs.record_reference(name: :a)
|
106
|
+
refs.record_reference(name: :b)
|
107
107
|
end
|
108
108
|
|
109
109
|
it 'should report all refs to self' do
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_lib 'reek/cli/input'
|
3
|
+
|
4
|
+
RSpec.describe Reek::CLI::Input do
|
5
|
+
# dummy class which includes module under test
|
6
|
+
class DummyClass
|
7
|
+
include Reek::CLI::Input
|
8
|
+
|
9
|
+
def argv; end
|
10
|
+
end
|
11
|
+
|
12
|
+
subject { DummyClass.new }
|
13
|
+
|
14
|
+
describe '#sources' do
|
15
|
+
context 'when no source files given' do
|
16
|
+
before do
|
17
|
+
allow(subject).to receive(:argv).and_return([])
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'and input was piped' do
|
21
|
+
before do
|
22
|
+
allow_any_instance_of(IO).to receive(:tty?).and_return(false)
|
23
|
+
expect(subject).to receive(:source_from_pipe).and_call_original
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should use source form pipe' do
|
27
|
+
expect(subject.sources).to_not be_empty
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'and input was not piped' do
|
32
|
+
before do
|
33
|
+
allow_any_instance_of(IO).to receive(:tty?).and_return(true)
|
34
|
+
expect(subject).to receive(:working_directory_as_source).
|
35
|
+
and_call_original
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should use working directory as source' do
|
39
|
+
expect(subject.sources).to_not be_empty
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when source files given' do
|
45
|
+
before do
|
46
|
+
allow(subject).to receive(:argv).and_return(['.'])
|
47
|
+
expect(subject).to receive(:sources_from_argv).and_call_original
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should use sources from argv' do
|
51
|
+
expect(subject.sources).to_not be_empty
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -81,5 +81,15 @@ RSpec.describe Reek::CodeComment do
|
|
81
81
|
config = described_class.new('# :reek: Duplication').config
|
82
82
|
expect(config).not_to include('Duplication')
|
83
83
|
end
|
84
|
+
|
85
|
+
it 'removes the configuration options from the comment' do
|
86
|
+
subject = described_class.new('
|
87
|
+
# Actual
|
88
|
+
# :reek:Duplication: { enabled: false }
|
89
|
+
# :reek:NestedIterators: { enabled: true }
|
90
|
+
# comment
|
91
|
+
')
|
92
|
+
expect(subject.send(:sanitized_comment)).to eq('Actual comment')
|
93
|
+
end
|
84
94
|
end
|
85
95
|
end
|
@@ -17,21 +17,27 @@ RSpec.describe Reek::Context::CodeContext do
|
|
17
17
|
it 'gets its short name from the exp' do
|
18
18
|
expect(ctx.name).to eq(exp_name)
|
19
19
|
end
|
20
|
+
|
20
21
|
it 'does not match an empty list' do
|
21
22
|
expect(ctx.matches?([])).to eq(false)
|
22
23
|
end
|
24
|
+
|
23
25
|
it 'does not match when its own short name is not given' do
|
24
26
|
expect(ctx.matches?(['banana'])).to eq(false)
|
25
27
|
end
|
28
|
+
|
26
29
|
it 'does not let pipe-ended Strings make matching ignore the rest' do
|
27
30
|
expect(ctx.matches?(['banana|'])).to eq(false)
|
28
31
|
end
|
32
|
+
|
29
33
|
it 'recognises its own short name' do
|
30
34
|
expect(ctx.matches?(['banana', exp_name])).to eq(true)
|
31
35
|
end
|
36
|
+
|
32
37
|
it 'recognises its short name as a regex' do
|
33
38
|
expect(ctx.matches?([/banana/, /#{exp_name}/])).to eq(true)
|
34
39
|
end
|
40
|
+
|
35
41
|
it 'does not blow up on []-ended Strings' do
|
36
42
|
expect(ctx.matches?(['banana[]', exp_name])).to eq(true)
|
37
43
|
end
|
@@ -49,9 +55,11 @@ RSpec.describe Reek::Context::CodeContext do
|
|
49
55
|
it 'creates the correct full name' do
|
50
56
|
expect(ctx.full_name).to eq(full_name)
|
51
57
|
end
|
58
|
+
|
52
59
|
it 'recognises its own full name' do
|
53
60
|
expect(ctx.matches?(['banana', full_name])).to eq(true)
|
54
61
|
end
|
62
|
+
|
55
63
|
it 'recognises its full name as a regex' do
|
56
64
|
expect(ctx.matches?([/banana/, /#{full_name}/])).to eq(true)
|
57
65
|
end
|
@@ -12,18 +12,20 @@ RSpec.describe Reek::Context::ModuleContext do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'should not report module with empty class' do
|
15
|
-
expect('
|
16
|
-
module
|
17
|
-
|
18
|
-
|
15
|
+
expect('
|
16
|
+
# module for test
|
17
|
+
module Fred
|
18
|
+
# module for test
|
19
|
+
class Jim; end; end').not_to reek
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
23
|
RSpec.describe Reek::Context::ModuleContext do
|
23
24
|
it 'should recognise global constant' do
|
24
|
-
expect('
|
25
|
-
module
|
26
|
-
|
27
|
-
|
25
|
+
expect('
|
26
|
+
# module for test
|
27
|
+
module ::Global
|
28
|
+
# module for test
|
29
|
+
class Inside; end; end').not_to reek
|
28
30
|
end
|
29
31
|
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require_lib 'reek/context_builder'
|
3
|
+
|
4
|
+
RSpec.describe Reek::ContextBuilder do
|
5
|
+
describe '#initialize' do
|
6
|
+
describe 'the structure of the context_tree' 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
|
+
|
13
|
+
it 'starts with a root node' do
|
14
|
+
expect(context_tree.type).to eq(:root)
|
15
|
+
expect(context_tree).to be_a(Reek::Context::RootContext)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'has one child' do
|
19
|
+
expect(context_tree.children.size).to eq(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'the root node' do
|
23
|
+
let(:module_context) { context_tree.children.first }
|
24
|
+
|
25
|
+
it 'has one module_context' do
|
26
|
+
expect(module_context).to be_a(Reek::Context::ModuleContext)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'holds a reference to the parent context' do
|
30
|
+
expect(module_context.send(:context)).to eq(context_tree)
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'the module node' do
|
34
|
+
let(:method_context) { module_context.children.first }
|
35
|
+
|
36
|
+
it 'has one method_context' do
|
37
|
+
expect(method_context).to be_a(Reek::Context::MethodContext)
|
38
|
+
expect(module_context.children.size).to eq(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'holds a reference to the parent context' do
|
42
|
+
expect(method_context.send(:context)).to eq(module_context)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'statement counting' do
|
50
|
+
def tree(code)
|
51
|
+
described_class.new(syntax_tree(code)).context_tree
|
52
|
+
end
|
53
|
+
|
54
|
+
def number_of_statements_for(code)
|
55
|
+
tree(code).children.first.number_of_statements
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'counts 1 assignment' do
|
59
|
+
code = 'def one() val = 4; end'
|
60
|
+
expect(number_of_statements_for(code)).to eq(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'counts 3 assignments' do
|
64
|
+
code = 'def one() val = 4; val = 4; val = 4; end'
|
65
|
+
expect(number_of_statements_for(code)).to eq(3)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'counts 1 attr assignment' do
|
69
|
+
code = 'def one() val[0] = 4; end'
|
70
|
+
expect(number_of_statements_for(code)).to eq(1)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'counts 1 increment assignment' do
|
74
|
+
code = 'def one() val += 4; end'
|
75
|
+
expect(number_of_statements_for(code)).to eq(1)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'counts 1 increment attr assignment' do
|
79
|
+
code = 'def one() val[0] += 4; end'
|
80
|
+
expect(number_of_statements_for(code)).to eq(1)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'counts 1 nested assignment' do
|
84
|
+
code = 'def one() val = fred = 4; end'
|
85
|
+
expect(number_of_statements_for(code)).to eq(1)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'counts returns' do
|
89
|
+
code = 'def one() val = 4; true; end'
|
90
|
+
expect(number_of_statements_for(code)).to eq(2)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'counts nil returns' do
|
94
|
+
code = 'def one() val = 4; nil; end'
|
95
|
+
expect(number_of_statements_for(code)).to eq(2)
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'with control statements' do
|
99
|
+
it 'counts 3 statements in a conditional expression' do
|
100
|
+
code = 'def one() if val == 4; callee(); callee(); callee(); end; end'
|
101
|
+
expect(number_of_statements_for(code)).to eq(3)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'counts 3 statements in an else' do
|
105
|
+
code = <<-EOS
|
106
|
+
def one()
|
107
|
+
if val == 4
|
108
|
+
callee(); callee(); callee()
|
109
|
+
else
|
110
|
+
callee(); callee(); callee()
|
111
|
+
end
|
112
|
+
end
|
113
|
+
EOS
|
114
|
+
|
115
|
+
expect(number_of_statements_for(code)).to eq(6)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'does not count constant assignment with or equals' do
|
119
|
+
code = 'class Hi; CONST ||= 1; end'
|
120
|
+
expect(number_of_statements_for(code)).to eq(0)
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'does not count multi constant assignment' do
|
124
|
+
code = 'class Hi; CONST, OTHER_CONST = 1, 2; end'
|
125
|
+
expect(number_of_statements_for(code)).to eq(0)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'does not count empty conditional expression' do
|
129
|
+
code = 'def one() if val == 4; ; end; end'
|
130
|
+
expect(number_of_statements_for(code)).to eq(0)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'does not count empty else' do
|
134
|
+
code = 'def one() if val == 4; ; else; ; end; end'
|
135
|
+
expect(number_of_statements_for(code)).to eq(0)
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'counts extra statements in an if condition' do
|
139
|
+
code = 'def one() if begin val = callee(); val < 4 end; end; end'
|
140
|
+
expect(number_of_statements_for(code)).to eq(1)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'counts 3 statements in a while loop' do
|
144
|
+
code = 'def one() while val < 4; callee(); callee(); callee(); end; end'
|
145
|
+
expect(number_of_statements_for(code)).to eq(3)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'counts extra statements in a while condition' do
|
149
|
+
code = 'def one() while begin val = callee(); val < 4 end; end; end'
|
150
|
+
expect(number_of_statements_for(code)).to eq(1)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'counts 3 statements in a until loop' do
|
154
|
+
code = 'def one() until val < 4; callee(); callee(); callee(); end; end'
|
155
|
+
expect(number_of_statements_for(code)).to eq(3)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'counts 3 statements in a for loop' do
|
159
|
+
code = 'def one() for i in 0..4; callee(); callee(); callee(); end; end'
|
160
|
+
expect(number_of_statements_for(code)).to eq(3)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'counts 3 statements in a rescue' do
|
164
|
+
code = <<-EOS
|
165
|
+
def one()
|
166
|
+
begin
|
167
|
+
callee(); callee(); callee()
|
168
|
+
rescue
|
169
|
+
callee(); callee(); callee()
|
170
|
+
end
|
171
|
+
end
|
172
|
+
EOS
|
173
|
+
expect(number_of_statements_for(code)).to eq(6)
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'counts 3 statements in a when' do
|
177
|
+
code = <<-EOS
|
178
|
+
def one()
|
179
|
+
case fred
|
180
|
+
when "hi" then callee(); callee()
|
181
|
+
when "lo" then callee()
|
182
|
+
end
|
183
|
+
end
|
184
|
+
EOS
|
185
|
+
expect(number_of_statements_for(code)).to eq(3)
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'counts 3 statements in a case else' do
|
189
|
+
code = <<-EOS
|
190
|
+
def one()
|
191
|
+
case fred
|
192
|
+
when "hi" then callee(); callee(); callee()
|
193
|
+
else callee(); callee(); callee()
|
194
|
+
end
|
195
|
+
end
|
196
|
+
EOS
|
197
|
+
expect(number_of_statements_for(code)).to eq(6)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'does not count empty case' do
|
201
|
+
code = 'def one() case fred; when "hi"; ; when "lo"; ; end; end'
|
202
|
+
expect(number_of_statements_for(code)).to eq(0)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'does not count empty case else' do
|
206
|
+
code = 'def one() case fred; when "hi"; ; else; ; end; end'
|
207
|
+
expect(number_of_statements_for(code)).to eq(0)
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'counts 4 statements in an iterator' do
|
211
|
+
code = 'def one() fred.each do; callee(); callee(); callee(); end; end'
|
212
|
+
expect(number_of_statements_for(code)).to eq(4)
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'counts 1 statement in a singleton method' do
|
216
|
+
code = 'def self.foo; callee(); end'
|
217
|
+
expect(number_of_statements_for(code)).to eq(1)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
data/spec/reek/examiner_spec.rb
CHANGED
@@ -14,9 +14,11 @@ RSpec.shared_examples_for 'one smell found' do
|
|
14
14
|
it 'is smelly' do
|
15
15
|
expect(examiner).to be_smelly
|
16
16
|
end
|
17
|
+
|
17
18
|
it 'reports the smell' do
|
18
19
|
expect(examiner.smells.length).to eq(1)
|
19
20
|
end
|
21
|
+
|
20
22
|
it 'reports the correct smell' do
|
21
23
|
expect(examiner.smells[0].smell_category).to eq(expected_first_smell)
|
22
24
|
end
|
@@ -53,4 +55,15 @@ RSpec.describe Reek::Examiner do
|
|
53
55
|
|
54
56
|
it_should_behave_like 'no smells found'
|
55
57
|
end
|
58
|
+
|
59
|
+
describe '#smells' do
|
60
|
+
it 'returns the detected smell warnings' do
|
61
|
+
code = 'def foo; bar.call_me(); bar.call_me(); end'
|
62
|
+
examiner = described_class.new code, ['DuplicateMethodCall']
|
63
|
+
|
64
|
+
smell = examiner.smells.first
|
65
|
+
expect(smell).to be_a(Reek::Smells::SmellWarning)
|
66
|
+
expect(smell.message).to eq('calls bar.call_me 2 times')
|
67
|
+
end
|
68
|
+
end
|
56
69
|
end
|
@@ -53,10 +53,12 @@ RSpec.describe Reek::Smells::BooleanParameter do
|
|
53
53
|
src = 'def self.cc(arga = true) end'
|
54
54
|
expect(src).to reek_of(:BooleanParameter, name: 'arga')
|
55
55
|
end
|
56
|
+
|
56
57
|
it 'reports a parameter defaulted to false' do
|
57
58
|
src = 'def fred.cc(arga = false) end'
|
58
59
|
expect(src).to reek_of(:BooleanParameter, name: 'arga')
|
59
60
|
end
|
61
|
+
|
60
62
|
it 'reports two parameters defaulted to booleans' do
|
61
63
|
src = 'def Module.cc(nowt, arga = true, argb = false, &blk) end'
|
62
64
|
expect(src).to reek_of(:BooleanParameter, name: 'arga')
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require_relative '../../spec_helper'
|
2
2
|
require_lib 'reek/smells/duplicate_method_call'
|
3
3
|
require_lib 'reek/context/code_context'
|
4
|
-
require_lib 'reek/
|
4
|
+
require_lib 'reek/context_builder'
|
5
5
|
require_relative 'smell_detector_shared'
|
6
6
|
|
7
7
|
RSpec.describe Reek::Smells::DuplicateMethodCall do
|
@@ -190,7 +190,7 @@ RSpec.describe Reek::Smells::FeatureEnvy do
|
|
190
190
|
@report = Report.new
|
191
191
|
cf = SmellConfig.new
|
192
192
|
cf = cf.load_local(@dir) if @dir
|
193
|
-
|
193
|
+
ContextBuilder.new(@report, cf.smell_listeners).check_source(@source)
|
194
194
|
end
|
195
195
|
@report
|
196
196
|
end
|
@@ -46,10 +46,10 @@ RSpec.describe Reek::Smells::TooManyStatements do
|
|
46
46
|
it_should_behave_like 'SmellDetector'
|
47
47
|
|
48
48
|
context 'when the method has 30 statements' do
|
49
|
-
let(:
|
49
|
+
let(:number_of_statements) { 30 }
|
50
50
|
let(:smells) do
|
51
51
|
ctx = double('method_context').as_null_object
|
52
|
-
expect(ctx).to receive(:
|
52
|
+
expect(ctx).to receive(:number_of_statements).and_return(number_of_statements)
|
53
53
|
expect(ctx).to receive(:config_for).with(described_class).and_return({})
|
54
54
|
detector.inspect(ctx)
|
55
55
|
end
|
@@ -59,7 +59,7 @@ RSpec.describe Reek::Smells::TooManyStatements do
|
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'reports the number of statements' do
|
62
|
-
expect(smells[0].parameters[:count]).to eq(
|
62
|
+
expect(smells[0].parameters[:count]).to eq(number_of_statements)
|
63
63
|
end
|
64
64
|
|
65
65
|
it 'reports the correct smell sub class' do
|