reek 4.7.3 → 4.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +17 -12
- data/.rubocop.yml +1 -0
- data/.travis.yml +4 -2
- data/CHANGELOG.md +10 -0
- data/Gemfile +3 -3
- data/README.md +19 -4
- data/lib/reek/ast/node.rb +37 -49
- data/lib/reek/ast/reference_collector.rb +2 -4
- data/lib/reek/ast/sexp_extensions/case.rb +1 -1
- data/lib/reek/ast/sexp_extensions/if.rb +8 -1
- data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
- data/lib/reek/ast/sexp_extensions/methods.rb +3 -5
- data/lib/reek/configuration/configuration_file_finder.rb +3 -3
- data/lib/reek/context/code_context.rb +4 -7
- data/lib/reek/context/method_context.rb +5 -10
- data/lib/reek/context/module_context.rb +3 -3
- data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +9 -9
- data/lib/reek/errors/bad_detector_in_comment_error.rb +7 -7
- data/lib/reek/errors/base_error.rb +3 -0
- data/lib/reek/errors/encoding_error.rb +16 -11
- data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +7 -7
- data/lib/reek/errors/incomprehensible_source_error.rb +20 -22
- data/lib/reek/examiner.rb +18 -14
- data/lib/reek/logging_error_handler.rb +7 -5
- data/lib/reek/smell_detectors/class_variable.rb +3 -10
- data/lib/reek/smell_detectors/duplicate_method_call.rb +1 -1
- data/lib/reek/smell_detectors/instance_variable_assumption.rb +1 -9
- data/lib/reek/smell_detectors/manual_dispatch.rb +1 -1
- data/lib/reek/smell_detectors/module_initialize.rb +1 -1
- data/lib/reek/smell_detectors/nested_iterators.rb +2 -1
- data/lib/reek/smell_detectors/too_many_constants.rb +1 -1
- data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +2 -2
- data/lib/reek/smell_detectors/utility_function.rb +1 -1
- data/lib/reek/source/source_code.rb +9 -23
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +2 -2
- data/spec/factories/factories.rb +2 -13
- data/spec/reek/ast/node_spec.rb +98 -5
- data/spec/reek/ast/reference_collector_spec.rb +1 -1
- data/spec/reek/ast/sexp_extensions_spec.rb +2 -2
- data/spec/reek/cli/application_spec.rb +39 -41
- data/spec/reek/cli/command/todo_list_command_spec.rb +2 -2
- data/spec/reek/code_comment_spec.rb +32 -32
- data/spec/reek/configuration/app_configuration_spec.rb +3 -3
- data/spec/reek/configuration/configuration_file_finder_spec.rb +1 -1
- data/spec/reek/configuration/directory_directives_spec.rb +3 -3
- data/spec/reek/configuration/excluded_paths_spec.rb +1 -1
- data/spec/reek/context/code_context_spec.rb +59 -95
- data/spec/reek/context/ghost_context_spec.rb +1 -1
- data/spec/reek/context/root_context_spec.rb +1 -1
- data/spec/reek/errors/base_error_spec.rb +13 -0
- data/spec/reek/examiner_spec.rb +72 -29
- data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -80
- data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
- data/spec/reek/report/xml_report_spec.rb +2 -2
- data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
- data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
- data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
- data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
- data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
- data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
- data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +3 -3
- data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
- data/spec/reek/smell_detectors/unused_private_method_spec.rb +9 -9
- data/spec/reek/smell_detectors/utility_function_spec.rb +5 -5
- data/spec/reek/smell_warning_spec.rb +8 -8
- data/spec/reek/source/source_code_spec.rb +5 -25
- data/spec/reek/source/source_locator_spec.rb +6 -6
- data/spec/reek/spec/should_reek_of_spec.rb +7 -7
- data/spec/reek/spec/should_reek_spec.rb +2 -2
- data/spec/reek/spec/smell_matcher_spec.rb +3 -3
- data/spec/reek/tree_dresser_spec.rb +11 -12
- data/spec/spec_helper.rb +3 -12
- metadata +10 -9
@@ -5,42 +5,42 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
|
|
5
5
|
describe '#compute' do
|
6
6
|
let(:computed) { described_class.new(warning).compute }
|
7
7
|
|
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
|
+
build(:smell_warning,
|
12
|
+
smell_detector: Reek::SmellDetectors::UtilityFunction.new,
|
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
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
context 'with code at a specific location' do
|
25
|
-
let(:lines) { [1] }
|
19
|
+
context 'with code at a specific location' do
|
20
|
+
let(:lines) { [1] }
|
26
21
|
|
27
|
-
|
28
|
-
|
22
|
+
it 'computes the fingerprint' do
|
23
|
+
expect(computed).to eq expected_fingerprint
|
24
|
+
end
|
25
|
+
end
|
29
26
|
|
30
|
-
|
31
|
-
|
27
|
+
context 'with code at a different location' do
|
28
|
+
let(:lines) { [5] }
|
32
29
|
|
33
|
-
|
30
|
+
it 'computes the same fingerprint' do
|
31
|
+
expect(computed).to eq expected_fingerprint
|
32
|
+
end
|
33
|
+
end
|
34
34
|
end
|
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
|
+
build(:smell_warning,
|
39
|
+
smell_detector: Reek::SmellDetectors::ManualDispatch.new,
|
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
|
@@ -48,77 +48,79 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
|
|
48
48
|
end
|
49
49
|
end
|
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
|
+
build(:smell_warning,
|
54
|
+
smell_detector: Reek::SmellDetectors::ClassVariable.new,
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
62
|
+
context 'when the name is one thing' do
|
63
|
+
let(:name) { 'bravo' }
|
64
|
+
let(:expected_fingerprint) { '9c3fd378178118a67e9509f87cae24f9' }
|
66
65
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
it_behaves_like 'computes a fingerprint with identifying parameters'
|
72
|
-
end
|
66
|
+
it 'computes the fingerprint' do
|
67
|
+
expect(computed).to eq expected_fingerprint
|
68
|
+
end
|
69
|
+
end
|
73
70
|
|
74
|
-
|
75
|
-
|
76
|
-
|
71
|
+
context 'when the name is another thing' do
|
72
|
+
let(:name) { 'echo' }
|
73
|
+
let(:expected_fingerprint) { 'd2a6d2703ce04cca65e7300b7de4b89f' }
|
77
74
|
|
78
|
-
|
75
|
+
it 'computes another fingerprint' do
|
76
|
+
expect(computed).to eq expected_fingerprint
|
77
|
+
end
|
78
|
+
end
|
79
79
|
end
|
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
|
+
build(:smell_warning,
|
84
|
+
smell_detector: Reek::SmellDetectors::DuplicateMethodCall.new,
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
context 'when the parameters are provided' do
|
98
|
-
let(:name) { 'bravo' }
|
99
|
-
let(:count) { 5 }
|
100
|
-
let(:lines) { [1, 7, 10, 13, 15] }
|
101
|
-
let(:expected_fingerprint) { '238733f4f51ba5473dcbe94a43ec5400' }
|
92
|
+
context 'when the parameters are provided' do
|
93
|
+
let(:name) { 'bravo' }
|
94
|
+
let(:count) { 5 }
|
95
|
+
let(:lines) { [1, 7, 10, 13, 15] }
|
96
|
+
let(:expected_fingerprint) { '238733f4f51ba5473dcbe94a43ec5400' }
|
102
97
|
|
103
|
-
|
104
|
-
|
98
|
+
it 'computes the fingerprint' do
|
99
|
+
expect(computed).to eq expected_fingerprint
|
100
|
+
end
|
101
|
+
end
|
105
102
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
103
|
+
context 'when the non-identifying parameters change' do
|
104
|
+
let(:name) { 'bravo' }
|
105
|
+
let(:count) { 9 }
|
106
|
+
let(:lines) { [1, 7, 10, 13, 15, 17, 19, 20, 25] }
|
107
|
+
let(:expected_fingerprint) { '238733f4f51ba5473dcbe94a43ec5400' }
|
111
108
|
|
112
|
-
|
113
|
-
|
109
|
+
it 'computes the same fingerprint' do
|
110
|
+
expect(computed).to eq expected_fingerprint
|
111
|
+
end
|
112
|
+
end
|
114
113
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
114
|
+
context 'when the identifying parameters change' do
|
115
|
+
let(:name) { 'echo' }
|
116
|
+
let(:count) { 5 }
|
117
|
+
let(:lines) { [1, 7, 10, 13, 15] }
|
118
|
+
let(:expected_fingerprint) { 'e0c35e9223cc19bdb9a04fb3e60573e1' }
|
120
119
|
|
121
|
-
|
120
|
+
it 'computes a different fingerprint' do
|
121
|
+
expect(computed).to eq expected_fingerprint
|
122
|
+
end
|
123
|
+
end
|
122
124
|
end
|
123
125
|
end
|
124
126
|
end
|
@@ -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
|
+
build(:smell_warning,
|
8
|
+
smell_detector: Reek::SmellDetectors::UtilityFunction.new,
|
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 }
|
@@ -5,7 +5,7 @@ require_lib 'reek/report/xml_report'
|
|
5
5
|
RSpec.describe Reek::Report::XMLReport do
|
6
6
|
let(:xml_report) { described_class.new }
|
7
7
|
|
8
|
-
context 'empty source' do
|
8
|
+
context 'with an empty source' do
|
9
9
|
it 'prints empty checkstyle XML' do
|
10
10
|
xml_report.add_examiner Reek::Examiner.new('')
|
11
11
|
xml = "<?xml version='1.0'?>\n<checkstyle/>\n"
|
@@ -13,7 +13,7 @@ RSpec.describe Reek::Report::XMLReport do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
context 'source with
|
16
|
+
context 'with a source with violations' do
|
17
17
|
it 'prints non-empty checkstyle XML' do
|
18
18
|
xml_report.add_examiner Reek::Examiner.new(SMELLY_FILE)
|
19
19
|
xml = SAMPLES_PATH.join('checkstyle.xml').read
|
@@ -27,7 +27,7 @@ RSpec.describe Reek::SmellDetectors::BooleanParameter do
|
|
27
27
|
and reek_of(:BooleanParameter, lines: [1], context: 'alfa', parameter: 'charlie')
|
28
28
|
end
|
29
29
|
|
30
|
-
context '
|
30
|
+
context 'when examining an instance method' do
|
31
31
|
it 'reports a parameter defaulted to false' do
|
32
32
|
src = 'def alfa(bravo = false) end'
|
33
33
|
expect(src).to reek_of(:BooleanParameter)
|
@@ -70,7 +70,7 @@ RSpec.describe Reek::SmellDetectors::BooleanParameter do
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
context '
|
73
|
+
context 'when examining a singleton method' do
|
74
74
|
it 'reports a parameter defaulted to true' do
|
75
75
|
src = 'def self.alfa(bravo = true) end'
|
76
76
|
expect(src).to reek_of(:BooleanParameter)
|
@@ -63,49 +63,43 @@ RSpec.describe Reek::SmellDetectors::ClassVariable do
|
|
63
63
|
end
|
64
64
|
|
65
65
|
['class', 'module'].each do |scope|
|
66
|
-
context "
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
@@charlie = {}
|
73
|
-
end
|
66
|
+
context "when examining a #{scope}" do
|
67
|
+
it 'reports a class variable set in a method' do
|
68
|
+
src = <<-EOS
|
69
|
+
#{scope} Alfa
|
70
|
+
def bravo
|
71
|
+
@@charlie = {}
|
74
72
|
end
|
75
|
-
|
73
|
+
end
|
74
|
+
EOS
|
76
75
|
|
77
|
-
|
78
|
-
end
|
76
|
+
expect(src).to reek_of(:ClassVariable, name: '@@charlie')
|
79
77
|
end
|
80
78
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
puts @@charlie
|
87
|
-
end
|
79
|
+
it 'reports a class variable used in a method' do
|
80
|
+
src = <<-EOS
|
81
|
+
#{scope} Alfa
|
82
|
+
def bravo
|
83
|
+
puts @@charlie
|
88
84
|
end
|
89
|
-
|
85
|
+
end
|
86
|
+
EOS
|
90
87
|
|
91
|
-
|
92
|
-
end
|
88
|
+
expect(src).to reek_of(:ClassVariable, name: '@@charlie')
|
93
89
|
end
|
94
90
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
@@bravo = 42
|
91
|
+
it "reports a class variable set in the #{scope} body and used in a method" do
|
92
|
+
src = <<-EOS
|
93
|
+
#{scope} Alfa
|
94
|
+
@@bravo = 42
|
100
95
|
|
101
|
-
|
102
|
-
|
103
|
-
end
|
96
|
+
def charlie
|
97
|
+
puts @@bravo
|
104
98
|
end
|
105
|
-
|
99
|
+
end
|
100
|
+
EOS
|
106
101
|
|
107
|
-
|
108
|
-
end
|
102
|
+
expect(src).to reek_of(:ClassVariable, name: '@@bravo')
|
109
103
|
end
|
110
104
|
end
|
111
105
|
end
|
@@ -30,7 +30,7 @@ RSpec.describe Reek::SmellDetectors::ControlParameter do
|
|
30
30
|
and reek_of(:ControlParameter, lines: [3], argument: 'charlie')
|
31
31
|
end
|
32
32
|
|
33
|
-
context 'parameter not used to determine code path' do
|
33
|
+
context 'when a parameter is not used to determine code path' do
|
34
34
|
it 'does not report a ternary check on an ivar' do
|
35
35
|
src = 'def alfa(bravo) @charlie ? bravo : false end'
|
36
36
|
expect(src).not_to reek_of(:ControlParameter)
|
@@ -52,7 +52,7 @@ RSpec.describe Reek::SmellDetectors::ControlParameter do
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
context 'parameter only used to determine code path' do
|
55
|
+
context 'when a parameter is only used to determine code path' do
|
56
56
|
it 'reports a ternary check on a parameter' do
|
57
57
|
src = 'def alfa(bravo); bravo ? true : false; end'
|
58
58
|
expect(src).to reek_of(:ControlParameter)
|
@@ -144,7 +144,7 @@ RSpec.describe Reek::SmellDetectors::ControlParameter do
|
|
144
144
|
expect(src).to reek_of(:ControlParameter)
|
145
145
|
end
|
146
146
|
|
147
|
-
it 'reports on nested if statements where the inner if is a control parameter' do
|
147
|
+
it 'reports on nested suffix if statements where the inner if is a control parameter' do
|
148
148
|
src = <<-EOS
|
149
149
|
def nested(bravo)
|
150
150
|
if true
|
@@ -157,6 +157,36 @@ RSpec.describe Reek::SmellDetectors::ControlParameter do
|
|
157
157
|
expect(src).to reek_of(:ControlParameter)
|
158
158
|
end
|
159
159
|
|
160
|
+
it 'reports on nested full if statements where the inner if is a control parameter' do
|
161
|
+
src = <<-EOS
|
162
|
+
def alfa(bravo)
|
163
|
+
if true
|
164
|
+
charlie
|
165
|
+
else
|
166
|
+
if bravo
|
167
|
+
delta
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
EOS
|
172
|
+
|
173
|
+
expect(src).to reek_of(:ControlParameter)
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'reports on elsif statements' do
|
177
|
+
src = <<-EOS
|
178
|
+
def alfa(bravo)
|
179
|
+
if true
|
180
|
+
charlie
|
181
|
+
elsif bravo
|
182
|
+
delta
|
183
|
+
end
|
184
|
+
end
|
185
|
+
EOS
|
186
|
+
|
187
|
+
expect(src).to reek_of(:ControlParameter)
|
188
|
+
end
|
189
|
+
|
160
190
|
it 'reports on explicit comparison in the condition' do
|
161
191
|
src = 'def alfa(bravo); if bravo == charlie then charlie end; end'
|
162
192
|
expect(src).to reek_of(:ControlParameter)
|
@@ -188,7 +218,7 @@ RSpec.describe Reek::SmellDetectors::ControlParameter do
|
|
188
218
|
end
|
189
219
|
end
|
190
220
|
|
191
|
-
context 'parameter used besides determining code path' do
|
221
|
+
context 'when a parameter is used besides determining code path' do
|
192
222
|
it 'does not report on if conditional expression' do
|
193
223
|
src = 'def alfa(bravo); if bravo then charlie(bravo); end end'
|
194
224
|
expect(src).not_to reek_of(:ControlParameter)
|
@@ -150,7 +150,7 @@ RSpec.describe Reek::SmellDetectors::DuplicateMethodCall do
|
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
153
|
-
context 'non-repeated method calls' do
|
153
|
+
context 'with non-repeated method calls' do
|
154
154
|
it 'does not report similar calls' do
|
155
155
|
src = 'def alfa(bravo) bravo.charlie == self.charlie end'
|
156
156
|
expect(src).not_to reek_of(:DuplicateMethodCall)
|
@@ -162,7 +162,7 @@ RSpec.describe Reek::SmellDetectors::DuplicateMethodCall do
|
|
162
162
|
end
|
163
163
|
end
|
164
164
|
|
165
|
-
context 'allowing up to 3 calls' do
|
165
|
+
context 'when allowing up to 3 calls' do
|
166
166
|
let(:config) do
|
167
167
|
{ Reek::SmellDetectors::DuplicateMethodCall::MAX_ALLOWED_CALLS_KEY => 3 }
|
168
168
|
end
|
@@ -189,7 +189,7 @@ RSpec.describe Reek::SmellDetectors::DuplicateMethodCall do
|
|
189
189
|
end
|
190
190
|
end
|
191
191
|
|
192
|
-
context 'allowing calls to some methods' do
|
192
|
+
context 'when allowing calls to some methods' do
|
193
193
|
it 'does not report calls to some methods' do
|
194
194
|
config = { Reek::SmellDetectors::DuplicateMethodCall::ALLOW_CALLS_KEY => ['@bravo.charlie'] }
|
195
195
|
src = 'def alfa; @bravo.charlie + @bravo.charlie; end'
|
@@ -50,6 +50,20 @@ RSpec.describe Reek::SmellDetectors::ModuleInitialize do
|
|
50
50
|
expect(src).not_to reek_of(:ModuleInitialize)
|
51
51
|
end
|
52
52
|
|
53
|
+
it 'reports nothing for a method named initialize in a nested dynamic class' do
|
54
|
+
src = <<-EOF
|
55
|
+
module Alfa
|
56
|
+
def self.bravo
|
57
|
+
Class.new do
|
58
|
+
def initialize; end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
EOF
|
63
|
+
|
64
|
+
expect(src).not_to reek_of(:ModuleInitialize)
|
65
|
+
end
|
66
|
+
|
53
67
|
it 'can be disabled via comment' do
|
54
68
|
src = <<-EOS
|
55
69
|
# :reek:ModuleInitialize
|