reek 3.4.1 → 3.5.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/CHANGELOG.md +4 -0
- data/docs/API.md +1 -1
- data/docs/Nested-Iterators.md +15 -0
- data/features/configuration_files/directory_specific_directives.feature +9 -9
- data/features/samples.feature +4 -8
- data/features/support/env.rb +3 -1
- data/lib/reek/ast/node.rb +0 -14
- data/lib/reek/ast/sexp_extensions.rb +26 -22
- data/lib/reek/configuration/app_configuration.rb +7 -4
- data/lib/reek/configuration/configuration_validator.rb +20 -3
- data/lib/reek/configuration/default_directive.rb +3 -1
- data/lib/reek/configuration/directory_directives.rb +1 -1
- data/lib/reek/smells/boolean_parameter.rb +1 -1
- data/lib/reek/smells/nested_iterators.rb +3 -3
- data/lib/reek/smells/too_many_instance_variables.rb +1 -1
- data/lib/reek/smells/uncommunicative_variable_name.rb +3 -3
- data/lib/reek/tree_walker.rb +7 -6
- data/lib/reek/version.rb +1 -1
- data/spec/factories/factories.rb +11 -0
- data/spec/reek/configuration/app_configuration_spec.rb +32 -13
- data/spec/reek/context/code_context_spec.rb +1 -1
- data/spec/reek/smells/nested_iterators_spec.rb +70 -23
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 777f1ba21321886d04fe6cb9be80e175250f8490
|
4
|
+
data.tar.gz: 574babd716780ea8013ceb2b485dda1e3eaa521d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d5a1ea491b81408ae7c6ee5dc8699b21e7df7ecdfa888d3826556acb677dabf6a2a145677c89e783f5aaaf58b914ab3138ce444e393a19e476e3c6940ff6bbe
|
7
|
+
data.tar.gz: cd1ab7b709b02c9d2756fc716aee8418295bebf48757fdaff1dab3fca689bb84a8d5b359fc6026e4f3d8450d80128b36e00ba12138f904596a9752ad200f69e4
|
data/CHANGELOG.md
CHANGED
data/docs/API.md
CHANGED
@@ -76,7 +76,7 @@ IrresponsibleModule:
|
|
76
76
|
You can now use either
|
77
77
|
|
78
78
|
```Ruby
|
79
|
-
Reek::Configuration::AppConfiguration.from_path Pathname.new('config.reek
|
79
|
+
Reek::Configuration::AppConfiguration.from_path Pathname.new('config.reek')
|
80
80
|
```
|
81
81
|
|
82
82
|
but you can also pass a hash via `Reek::Configuration::AppConfiguration.from_map`.
|
data/docs/Nested-Iterators.md
CHANGED
@@ -33,6 +33,21 @@ test.rb -- 1 warning:
|
|
33
33
|
|
34
34
|
Nested Iterators reports failing methods only once.
|
35
35
|
`Object#tap` is ignored by default and thus does not count as iterator.
|
36
|
+
Furthermore iterators without block arguments are not counted, e.g.:
|
37
|
+
|
38
|
+
|
39
|
+
```Ruby
|
40
|
+
def foo
|
41
|
+
before do
|
42
|
+
item.each do |part|
|
43
|
+
puts part
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
would not smell of NestedIterators (given a maximum allowed nesting of 1) since the
|
50
|
+
`before` would not be counted (because it doesn't pass any arguments to the block).
|
36
51
|
|
37
52
|
## Configuration
|
38
53
|
|
@@ -24,8 +24,8 @@ Feature: Directory directives
|
|
24
24
|
class UsersController < ApplicationController
|
25
25
|
def show
|
26
26
|
respond_with do |format|
|
27
|
-
format.json { @user.to_custom_json }
|
28
|
-
format.
|
27
|
+
format.json { |json| @user.to_custom_json }
|
28
|
+
format.xml { |xml| @user.to_fancy_xml }
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -94,7 +94,7 @@ Feature: Directory directives
|
|
94
94
|
class UsersController < ApplicationController
|
95
95
|
def show
|
96
96
|
respond_with do |format|
|
97
|
-
format.json { @user.to_custom_json }
|
97
|
+
format.json { |json| @user.to_custom_json }
|
98
98
|
end
|
99
99
|
end
|
100
100
|
end
|
@@ -145,7 +145,7 @@ Feature: Directory directives
|
|
145
145
|
class UsersController < ApplicationController
|
146
146
|
def show
|
147
147
|
respond_with do |format|
|
148
|
-
format.json { @user.to_custom_json }
|
148
|
+
format.json { |json| @user.to_custom_json }
|
149
149
|
end
|
150
150
|
end
|
151
151
|
end
|
@@ -155,7 +155,7 @@ Feature: Directory directives
|
|
155
155
|
class ProjectController < ApplicationController
|
156
156
|
def show
|
157
157
|
respond_with do |format|
|
158
|
-
format.json { @project.to_custom_json }
|
158
|
+
format.json { |json| @project.to_custom_json }
|
159
159
|
end
|
160
160
|
end
|
161
161
|
end
|
@@ -251,8 +251,8 @@ Feature: Directory directives
|
|
251
251
|
"""
|
252
252
|
class Klass
|
253
253
|
def meth
|
254
|
-
respond_to do
|
255
|
-
answer_to do
|
254
|
+
respond_to do |arg|
|
255
|
+
answer_to do |arg|
|
256
256
|
end
|
257
257
|
end
|
258
258
|
end
|
@@ -262,8 +262,8 @@ Feature: Directory directives
|
|
262
262
|
"""
|
263
263
|
class Klazz
|
264
264
|
def meth
|
265
|
-
respond_to do
|
266
|
-
answer_to do
|
265
|
+
respond_to do |arg|
|
266
|
+
answer_to do |arg|
|
267
267
|
end
|
268
268
|
end
|
269
269
|
end
|
data/features/samples.feature
CHANGED
@@ -59,7 +59,7 @@ Feature: Basic smell detection
|
|
59
59
|
Module#inline calls Inline.const_get(lang) 2 times (DuplicateMethodCall)
|
60
60
|
Module#inline calls options[:testing] 2 times (DuplicateMethodCall)
|
61
61
|
Module#inline has approx 12 statements (TooManyStatements)
|
62
|
-
optparse.rb --
|
62
|
+
optparse.rb -- 117 warnings:
|
63
63
|
OptionParser has at least 42 methods (TooManyMethods)
|
64
64
|
OptionParser has at least 6 instance variables (TooManyInstanceVariables)
|
65
65
|
OptionParser has the variable name 'f' (UncommunicativeVariableName)
|
@@ -73,7 +73,6 @@ Feature: Basic smell detection
|
|
73
73
|
OptionParser tests not_style at least 3 times (RepeatedConditional)
|
74
74
|
OptionParser tests s at least 7 times (RepeatedConditional)
|
75
75
|
OptionParser#banner is a writable attribute (Attribute)
|
76
|
-
OptionParser#complete contains iterators nested 2 deep (NestedIterators)
|
77
76
|
OptionParser#complete has 4 parameters (LongParameterList)
|
78
77
|
OptionParser#complete has approx 6 statements (TooManyStatements)
|
79
78
|
OptionParser#complete has boolean parameter 'icase' (BooleanParameter)
|
@@ -93,7 +92,7 @@ Feature: Basic smell detection
|
|
93
92
|
OptionParser#make_switch calls sdesc << "-#{q}" 2 times (DuplicateMethodCall)
|
94
93
|
OptionParser#make_switch calls search(:atype, FalseClass) 2 times (DuplicateMethodCall)
|
95
94
|
OptionParser#make_switch calls search(:atype, o) 6 times (DuplicateMethodCall)
|
96
|
-
OptionParser#make_switch contains iterators nested
|
95
|
+
OptionParser#make_switch contains iterators nested 2 deep (NestedIterators)
|
97
96
|
OptionParser#make_switch has approx 72 statements (TooManyStatements)
|
98
97
|
OptionParser#make_switch has the variable name 'a' (UncommunicativeVariableName)
|
99
98
|
OptionParser#make_switch has the variable name 'c' (UncommunicativeVariableName)
|
@@ -114,7 +113,6 @@ Feature: Basic smell detection
|
|
114
113
|
OptionParser#parse_in_order calls setter.call(sw.switch_name, val) 2 times (DuplicateMethodCall)
|
115
114
|
OptionParser#parse_in_order calls sw.block 2 times (DuplicateMethodCall)
|
116
115
|
OptionParser#parse_in_order calls sw.switch_name 2 times (DuplicateMethodCall)
|
117
|
-
OptionParser#parse_in_order contains iterators nested 3 deep (NestedIterators)
|
118
116
|
OptionParser#parse_in_order has approx 35 statements (TooManyStatements)
|
119
117
|
OptionParser#permute calls argv[0] 2 times (DuplicateMethodCall)
|
120
118
|
OptionParser#permute refers to argv more than self (maybe move it to another class?) (FeatureEnvy)
|
@@ -133,7 +131,6 @@ Feature: Basic smell detection
|
|
133
131
|
OptionParser::Arguable has initialize method (ModuleInitialize)
|
134
132
|
OptionParser::Arguable#options has approx 6 statements (TooManyStatements)
|
135
133
|
OptionParser::Arguable#options= is controlled by argument opt (ControlParameter)
|
136
|
-
OptionParser::CompletingHash#match contains iterators nested 2 deep (NestedIterators)
|
137
134
|
OptionParser::Completion#complete calls candidates.size 2 times (DuplicateMethodCall)
|
138
135
|
OptionParser::Completion#complete calls k.id2name 2 times (DuplicateMethodCall)
|
139
136
|
OptionParser::Completion#complete has approx 23 statements (TooManyStatements)
|
@@ -172,7 +169,6 @@ Feature: Basic smell detection
|
|
172
169
|
OptionParser::Switch#summarize calls left.shift 2 times (DuplicateMethodCall)
|
173
170
|
OptionParser::Switch#summarize calls left[(-1)] 3 times (DuplicateMethodCall)
|
174
171
|
OptionParser::Switch#summarize calls s.length 3 times (DuplicateMethodCall)
|
175
|
-
OptionParser::Switch#summarize contains iterators nested 2 deep (NestedIterators)
|
176
172
|
OptionParser::Switch#summarize has 5 parameters (LongParameterList)
|
177
173
|
OptionParser::Switch#summarize has approx 28 statements (TooManyStatements)
|
178
174
|
OptionParser::Switch#summarize has the variable name 'l' (UncommunicativeVariableName)
|
@@ -215,7 +211,7 @@ Feature: Basic smell detection
|
|
215
211
|
RedCloth#blocks is controlled by argument deep_code (ControlParameter)
|
216
212
|
RedCloth#blocks refers to blk more than self (maybe move it to another class?) (FeatureEnvy)
|
217
213
|
RedCloth#clean_html calls tags[tag] 2 times (DuplicateMethodCall)
|
218
|
-
RedCloth#clean_html contains iterators nested
|
214
|
+
RedCloth#clean_html contains iterators nested 2 deep (NestedIterators)
|
219
215
|
RedCloth#clean_html doesn't depend on instance state (maybe move it to another class?) (UtilityFunction)
|
220
216
|
RedCloth#clean_html has approx 15 statements (TooManyStatements)
|
221
217
|
RedCloth#clean_html has the variable name 'q' (UncommunicativeVariableName)
|
@@ -283,5 +279,5 @@ Feature: Basic smell detection
|
|
283
279
|
RedCloth#textile_popup_help has the parameter name 'windowW' (UncommunicativeParameterName)
|
284
280
|
RedCloth#to_html has approx 26 statements (TooManyStatements)
|
285
281
|
RedCloth#v_align doesn't depend on instance state (maybe move it to another class?) (UtilityFunction)
|
286
|
-
|
282
|
+
265 total warnings
|
287
283
|
"""
|
data/features/support/env.rb
CHANGED
data/lib/reek/ast/node.rb
CHANGED
@@ -31,20 +31,10 @@ module Reek
|
|
31
31
|
comment_lines.map(&:text).join("\n")
|
32
32
|
end
|
33
33
|
|
34
|
-
# @deprecated
|
35
|
-
def [](index)
|
36
|
-
elements[index]
|
37
|
-
end
|
38
|
-
|
39
34
|
def line
|
40
35
|
loc && loc.line
|
41
36
|
end
|
42
37
|
|
43
|
-
# @deprecated
|
44
|
-
def first
|
45
|
-
type
|
46
|
-
end
|
47
|
-
|
48
38
|
#
|
49
39
|
# Carries out a depth-first traversal of this syntax tree, yielding
|
50
40
|
# every Sexp of type `target_type`. The traversal ignores any node
|
@@ -129,10 +119,6 @@ module Reek
|
|
129
119
|
def each_sexp
|
130
120
|
children.each { |elem| yield elem if elem.is_a? ::Parser::AST::Node }
|
131
121
|
end
|
132
|
-
|
133
|
-
def elements
|
134
|
-
[type, *children]
|
135
|
-
end
|
136
122
|
end
|
137
123
|
end
|
138
124
|
end
|
@@ -120,10 +120,10 @@ module Reek
|
|
120
120
|
|
121
121
|
# Base module for utility methods for :and and :or nodes.
|
122
122
|
module LogicOperatorBase
|
123
|
-
def condition()
|
123
|
+
def condition() children.first end
|
124
124
|
|
125
125
|
def body_nodes(type, ignoring = [])
|
126
|
-
|
126
|
+
children[1].find_nodes type, ignoring
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
@@ -139,12 +139,12 @@ module Reek
|
|
139
139
|
|
140
140
|
# Utility methods for :attrasgn nodes.
|
141
141
|
module AttrasgnNode
|
142
|
-
def args()
|
142
|
+
def args() children[2] end
|
143
143
|
end
|
144
144
|
|
145
145
|
# Utility methods for :case nodes.
|
146
146
|
module CaseNode
|
147
|
-
def condition()
|
147
|
+
def condition() children.first end
|
148
148
|
|
149
149
|
def body_nodes(type, ignoring = [])
|
150
150
|
children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
|
@@ -177,7 +177,7 @@ module Reek
|
|
177
177
|
end
|
178
178
|
|
179
179
|
def arg_names
|
180
|
-
args.map { |arg| arg
|
180
|
+
args.map { |arg| arg.children.first }
|
181
181
|
end
|
182
182
|
|
183
183
|
def module_creation_call?
|
@@ -215,7 +215,7 @@ module Reek
|
|
215
215
|
|
216
216
|
# Base module for utility methods for nodes representing variables.
|
217
217
|
module VariableBase
|
218
|
-
def name()
|
218
|
+
def name() children.first end
|
219
219
|
end
|
220
220
|
|
221
221
|
# Utility methods for :cvar nodes.
|
@@ -301,11 +301,11 @@ module Reek
|
|
301
301
|
|
302
302
|
# Utility methods for :def nodes.
|
303
303
|
module DefNode
|
304
|
-
def name()
|
305
|
-
def argslist()
|
304
|
+
def name() children.first end
|
305
|
+
def argslist() children[1] end
|
306
306
|
|
307
307
|
def body
|
308
|
-
|
308
|
+
children[2]
|
309
309
|
end
|
310
310
|
|
311
311
|
def full_name(outer)
|
@@ -323,12 +323,12 @@ module Reek
|
|
323
323
|
|
324
324
|
# Utility methods for :defs nodes.
|
325
325
|
module DefsNode
|
326
|
-
def receiver()
|
327
|
-
def name()
|
328
|
-
def argslist()
|
326
|
+
def receiver() children.first end
|
327
|
+
def name() children[1] end
|
328
|
+
def argslist() children[2] end
|
329
329
|
|
330
330
|
def body
|
331
|
-
|
331
|
+
children[3]
|
332
332
|
end
|
333
333
|
|
334
334
|
include MethodNodeBase
|
@@ -345,7 +345,7 @@ module Reek
|
|
345
345
|
|
346
346
|
# Utility methods for :if nodes.
|
347
347
|
module IfNode
|
348
|
-
def condition()
|
348
|
+
def condition() children.first end
|
349
349
|
|
350
350
|
def body_nodes(type, ignoring = [])
|
351
351
|
children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
|
@@ -354,23 +354,27 @@ module Reek
|
|
354
354
|
|
355
355
|
# Utility methods for :block nodes.
|
356
356
|
module BlockNode
|
357
|
-
def call()
|
358
|
-
def args()
|
359
|
-
def block()
|
360
|
-
def parameters()
|
357
|
+
def call() children.first end
|
358
|
+
def args() children[1] end
|
359
|
+
def block() children[2] end
|
360
|
+
def parameters() children[1] || [] end
|
361
361
|
|
362
362
|
def parameter_names
|
363
|
-
parameters
|
363
|
+
parameters.children
|
364
364
|
end
|
365
365
|
|
366
366
|
def simple_name
|
367
367
|
:block
|
368
368
|
end
|
369
|
+
|
370
|
+
def without_block_arguments?
|
371
|
+
args.components.empty?
|
372
|
+
end
|
369
373
|
end
|
370
374
|
|
371
375
|
# Utility methods for :lit nodes.
|
372
376
|
module LitNode
|
373
|
-
def value()
|
377
|
+
def value() children.first end
|
374
378
|
end
|
375
379
|
|
376
380
|
# Utility methods for :const nodes.
|
@@ -425,7 +429,7 @@ module Reek
|
|
425
429
|
# Utility methods for :class nodes.
|
426
430
|
module ClassNode
|
427
431
|
include ModuleNode
|
428
|
-
def superclass()
|
432
|
+
def superclass() children[1] end
|
429
433
|
end
|
430
434
|
|
431
435
|
# Utility methods for :casgn nodes.
|
@@ -464,7 +468,7 @@ module Reek
|
|
464
468
|
|
465
469
|
# Utility methods for :yield nodes.
|
466
470
|
module YieldNode
|
467
|
-
def args()
|
471
|
+
def args() children end
|
468
472
|
|
469
473
|
def arg_names
|
470
474
|
args.map { |arg| arg[1] }
|
@@ -42,10 +42,9 @@ module Reek
|
|
42
42
|
def self.from_map(map = {})
|
43
43
|
allocate.tap do |instance|
|
44
44
|
instance.instance_eval do
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
self.excluded_paths = map.fetch(:excluded_paths, []).extend(ExcludedPaths)
|
45
|
+
load_values map.fetch(:directory_directives, {})
|
46
|
+
load_values map.fetch(:default_directive, {})
|
47
|
+
load_values EXCLUDE_PATHS_KEY => map.fetch(:excluded_paths, [])
|
49
48
|
end
|
50
49
|
end
|
51
50
|
end
|
@@ -90,6 +89,10 @@ module Reek
|
|
90
89
|
def find_and_load(path: nil)
|
91
90
|
configuration_file = ConfigurationFileFinder.find_and_load(path: path)
|
92
91
|
|
92
|
+
load_values(configuration_file)
|
93
|
+
end
|
94
|
+
|
95
|
+
def load_values(configuration_file)
|
93
96
|
configuration_file.each do |key, value|
|
94
97
|
case
|
95
98
|
when key == EXCLUDE_PATHS_KEY
|
@@ -8,9 +8,26 @@ module Reek
|
|
8
8
|
|
9
9
|
# :reek:UtilityFunction
|
10
10
|
def smell_type?(key)
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
case key
|
12
|
+
when Class
|
13
|
+
true
|
14
|
+
when String
|
15
|
+
begin
|
16
|
+
Reek::Smells.const_defined? key
|
17
|
+
rescue NameError
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# :reek:UtilityFunction
|
24
|
+
def key_to_smell_detector(key)
|
25
|
+
case key
|
26
|
+
when Class
|
27
|
+
key
|
28
|
+
else
|
29
|
+
Reek::Smells.const_get key
|
30
|
+
end
|
14
31
|
end
|
15
32
|
|
16
33
|
def error_message_for_missing_directory(pathname)
|
@@ -32,7 +32,7 @@ module Reek
|
|
32
32
|
with_valid_directory(path) do |directory|
|
33
33
|
self[directory] = config.each_with_object({}) do |(key, value), hash|
|
34
34
|
abort(error_message_for_invalid_smell_type(key)) unless smell_type?(key)
|
35
|
-
hash[
|
35
|
+
hash[key_to_smell_detector(key)] = value
|
36
36
|
end
|
37
37
|
end
|
38
38
|
self
|
@@ -25,7 +25,7 @@ module Reek
|
|
25
25
|
# :reek:FeatureEnvy
|
26
26
|
def examine_context(ctx)
|
27
27
|
ctx.default_assignments.select do |_param, value|
|
28
|
-
[:true, :false].include?(value
|
28
|
+
[:true, :false].include?(value.type)
|
29
29
|
end.map do |parameter, _value|
|
30
30
|
smell_warning(
|
31
31
|
context: ctx,
|
@@ -44,7 +44,6 @@ module Reek
|
|
44
44
|
else
|
45
45
|
[]
|
46
46
|
end
|
47
|
-
# BUG: no longer reports nesting outside methods (eg. in Optparse)
|
48
47
|
end
|
49
48
|
|
50
49
|
private
|
@@ -72,9 +71,10 @@ module Reek
|
|
72
71
|
result
|
73
72
|
end
|
74
73
|
|
74
|
+
# :reek:FeatureEnvy
|
75
75
|
def ignored_iterator?(exp)
|
76
|
-
|
77
|
-
|
76
|
+
ignore_iterators.any? { |pattern| /#{pattern}/ =~ exp.call.method_name } ||
|
77
|
+
exp.without_block_arguments?
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
@@ -39,7 +39,7 @@ module Reek
|
|
39
39
|
#
|
40
40
|
def examine_context(ctx)
|
41
41
|
max_allowed_ivars = value(MAX_ALLOWED_IVARS_KEY, ctx, DEFAULT_MAX_IVARS)
|
42
|
-
count = ctx.local_nodes(:ivasgn).map { |ivasgn| ivasgn
|
42
|
+
count = ctx.local_nodes(:ivasgn).map { |ivasgn| ivasgn.children.first }.uniq.length
|
43
43
|
return [] if count <= max_allowed_ivars
|
44
44
|
[smell_warning(
|
45
45
|
context: ctx,
|
@@ -83,18 +83,18 @@ module Reek
|
|
83
83
|
def find_assignment_variable_names(exp, accumulator)
|
84
84
|
assignment_nodes = exp.each_node(:lvasgn, [:class, :module, :defs, :def])
|
85
85
|
|
86
|
-
case exp.
|
86
|
+
case exp.type
|
87
87
|
when :class, :module
|
88
88
|
assignment_nodes += exp.each_node(:ivasgn, [:class, :module])
|
89
89
|
end
|
90
90
|
|
91
|
-
assignment_nodes.each { |asgn| accumulator[asgn
|
91
|
+
assignment_nodes.each { |asgn| accumulator[asgn.children.first].push(asgn.line) }
|
92
92
|
end
|
93
93
|
|
94
94
|
# :reek:FeatureEnvy
|
95
95
|
# :reek:TooManyStatements: { max_statements: 6 }
|
96
96
|
def find_block_argument_variable_names(exp, accumulator)
|
97
|
-
arg_search_exp = case exp.
|
97
|
+
arg_search_exp = case exp.type
|
98
98
|
when :class, :module
|
99
99
|
exp
|
100
100
|
when :defs, :def
|
data/lib/reek/tree_walker.rb
CHANGED
@@ -140,14 +140,15 @@ module Reek
|
|
140
140
|
alias_method :process_kwbegin, :process_begin
|
141
141
|
|
142
142
|
def process_if(exp)
|
143
|
-
|
144
|
-
count_clause(
|
143
|
+
children = exp.children
|
144
|
+
count_clause(children[1])
|
145
|
+
count_clause(children[2])
|
145
146
|
element.count_statements(-1)
|
146
147
|
process_default(exp)
|
147
148
|
end
|
148
149
|
|
149
150
|
def process_while(exp)
|
150
|
-
count_clause(exp[
|
151
|
+
count_clause(exp.children[1])
|
151
152
|
element.count_statements(-1)
|
152
153
|
process_default(exp)
|
153
154
|
end
|
@@ -155,19 +156,19 @@ module Reek
|
|
155
156
|
alias_method :process_until, :process_while
|
156
157
|
|
157
158
|
def process_for(exp)
|
158
|
-
count_clause(exp[
|
159
|
+
count_clause(exp.children[2])
|
159
160
|
element.count_statements(-1)
|
160
161
|
process_default(exp)
|
161
162
|
end
|
162
163
|
|
163
164
|
def process_rescue(exp)
|
164
|
-
count_clause(exp
|
165
|
+
count_clause(exp.children.first)
|
165
166
|
element.count_statements(-1)
|
166
167
|
process_default(exp)
|
167
168
|
end
|
168
169
|
|
169
170
|
def process_resbody(exp)
|
170
|
-
count_statement_list(exp[
|
171
|
+
count_statement_list(exp.children[1..-1].compact)
|
171
172
|
process_default(exp)
|
172
173
|
end
|
173
174
|
|
data/lib/reek/version.rb
CHANGED
data/spec/factories/factories.rb
CHANGED
@@ -11,6 +11,17 @@ FactoryGirl.define do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
factory :method_context, class: Reek::Context::MethodContext do
|
15
|
+
skip_create
|
16
|
+
transient do
|
17
|
+
source 'def foo; end'
|
18
|
+
end
|
19
|
+
|
20
|
+
initialize_with do
|
21
|
+
new(nil, Reek::Source::SourceCode.from(source).syntax_tree)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
14
25
|
factory :smell_detector, class: Reek::Smells::SmellDetector do
|
15
26
|
skip_create
|
16
27
|
transient do
|
@@ -38,7 +38,31 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
describe '#from_map' do
|
41
|
-
|
41
|
+
let(:default_directive_value) do
|
42
|
+
{ 'IrresponsibleModule' => { 'enabled' => false } }
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:directory_directives_value) do
|
46
|
+
{ 'spec/samples/three_clean_files' =>
|
47
|
+
{ 'UtilityFunction' => { 'enabled' => false } } }
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:exclude_paths_value) do
|
51
|
+
['spec/samples/two_smelly_files',
|
52
|
+
'spec/samples/source_with_non_ruby_files']
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'properly sets the configuration from simple data structures' do
|
56
|
+
config = described_class.from_map(directory_directives: directory_directives_value,
|
57
|
+
default_directive: default_directive_value,
|
58
|
+
excluded_paths: exclude_paths_value)
|
59
|
+
|
60
|
+
expect(config.send(:excluded_paths)).to eq(expected_excluded_paths)
|
61
|
+
expect(config.send(:default_directive)).to eq(expected_default_directive)
|
62
|
+
expect(config.send(:directory_directives)).to eq(expected_directory_directives)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'properly sets the configuration from native structures' do
|
42
66
|
config = described_class.from_map(directory_directives: expected_directory_directives,
|
43
67
|
default_directive: expected_default_directive,
|
44
68
|
excluded_paths: expected_excluded_paths)
|
@@ -51,17 +75,18 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
51
75
|
end
|
52
76
|
|
53
77
|
describe '#directive_for' do
|
78
|
+
let(:source_via) { 'spec/samples/three_clean_files/dummy.rb' }
|
79
|
+
|
54
80
|
context 'our source is in a directory for which we have a directive' do
|
55
81
|
let(:baz_config) { { Reek::Smells::IrresponsibleModule => { enabled: false } } }
|
56
82
|
let(:bang_config) { { Reek::Smells::Attribute => { enabled: true } } }
|
57
83
|
|
58
84
|
let(:directory_directives) do
|
59
85
|
{
|
60
|
-
|
61
|
-
|
86
|
+
'spec/samples/two_smelly_files' => baz_config,
|
87
|
+
'spec/samples/three_clean_files' => bang_config
|
62
88
|
}
|
63
89
|
end
|
64
|
-
let(:source_via) { 'foo/bar/bang/dummy.rb' }
|
65
90
|
|
66
91
|
it 'returns the corresponding directive' do
|
67
92
|
configuration = described_class.from_map directory_directives: directory_directives
|
@@ -70,22 +95,16 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
70
95
|
end
|
71
96
|
|
72
97
|
context 'our source is not in a directory for which we have a directive' do
|
73
|
-
let(:
|
74
|
-
{ Reek::Smells::IrresponsibleModule => { enabled: false } }
|
75
|
-
end
|
98
|
+
let(:default_directive) { { Reek::Smells::IrresponsibleModule => { enabled: false } } }
|
76
99
|
let(:attribute_config) { { Reek::Smells::Attribute => { enabled: false } } }
|
77
|
-
let(:default_directive) do
|
78
|
-
irresponsible_module_config
|
79
|
-
end
|
80
100
|
let(:directory_directives) do
|
81
|
-
{
|
101
|
+
{ 'spec/samples/two_smelly_files' => attribute_config }
|
82
102
|
end
|
83
|
-
let(:source_via) { 'foo/bar/bang/dummy.rb' }
|
84
103
|
|
85
104
|
it 'returns the default directive' do
|
86
105
|
configuration = described_class.from_map directory_directives: directory_directives,
|
87
106
|
default_directive: default_directive
|
88
|
-
expect(configuration.directive_for(source_via)).to eq(
|
107
|
+
expect(configuration.directive_for(source_via)).to eq(default_directive)
|
89
108
|
end
|
90
109
|
end
|
91
110
|
end
|
@@ -118,7 +118,7 @@ RSpec.describe Reek::Context::CodeContext do
|
|
118
118
|
end
|
119
119
|
|
120
120
|
it "yields the method's full AST" do
|
121
|
-
ctx.each_node(:def, []) { |exp| expect(exp
|
121
|
+
ctx.each_node(:def, []) { |exp| expect(exp.children.first).to eq(:calloo) }
|
122
122
|
end
|
123
123
|
|
124
124
|
context 'pruning the traversal' do
|
@@ -49,27 +49,9 @@ RSpec.describe Reek::Smells::NestedIterators do
|
|
49
49
|
it 'detects an iterator with an empty block' do
|
50
50
|
src = <<-EOS
|
51
51
|
def foo
|
52
|
-
bar
|
53
|
-
|
54
|
-
|
55
|
-
expect(src).to reek_of(:NestedIterators)
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'should report nested iterators only once per method' do
|
59
|
-
src = <<-EOS
|
60
|
-
def bad(fred)
|
61
|
-
@fred.each {|item| item.each {|part| @joe.send} }
|
62
|
-
@jim.each {|ting| ting.each {|piece| @hal.send} }
|
63
|
-
end
|
64
|
-
EOS
|
65
|
-
expect(src).to reek_of(:NestedIterators)
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'reports nested iterators only once per method even if levels are different' do
|
69
|
-
src = <<-EOS
|
70
|
-
def bad(fred)
|
71
|
-
@fred.each {|item| item.each {|part| part.foo} }
|
72
|
-
@jim.each {|ting| ting.each {|piece| piece.each {|atom| atom.foo } } }
|
52
|
+
bar do |bar|
|
53
|
+
baz {|baz| }
|
54
|
+
end
|
73
55
|
end
|
74
56
|
EOS
|
75
57
|
expect(src).to reek_of(:NestedIterators)
|
@@ -103,7 +85,7 @@ RSpec.describe Reek::Smells::NestedIterators do
|
|
103
85
|
expect(src).to reek_of(:NestedIterators, count: 3)
|
104
86
|
end
|
105
87
|
|
106
|
-
it 'handles the case where super
|
88
|
+
it 'handles the case where super receives a block' do
|
107
89
|
src = <<-EOS
|
108
90
|
def super_call_with_block
|
109
91
|
super do |k|
|
@@ -115,7 +97,7 @@ RSpec.describe Reek::Smells::NestedIterators do
|
|
115
97
|
expect(src).to reek_of(:NestedIterators)
|
116
98
|
end
|
117
99
|
|
118
|
-
it 'handles the case where super
|
100
|
+
it 'handles the case where super receives a block and arguments' do
|
119
101
|
src = <<-EOS
|
120
102
|
def super_call_with_block
|
121
103
|
super(foo) do |k|
|
@@ -127,6 +109,71 @@ RSpec.describe Reek::Smells::NestedIterators do
|
|
127
109
|
expect(src).to reek_of(:NestedIterators)
|
128
110
|
end
|
129
111
|
|
112
|
+
describe 'examine_context / warnings' do
|
113
|
+
let(:detector) { build(:smell_detector, smell_type: :NestedIterators) }
|
114
|
+
|
115
|
+
it 'reports correctly' do
|
116
|
+
source = <<-EOS
|
117
|
+
def foo
|
118
|
+
bar do |bar|
|
119
|
+
baz {|baz| }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
EOS
|
123
|
+
warnings = detector.examine_context(build(:method_context, source: source))
|
124
|
+
warning = warnings.first
|
125
|
+
|
126
|
+
expect(warning.smell_category).to eq(Reek::Smells::NestedIterators.smell_category)
|
127
|
+
expect(warning.smell_type).to eq(Reek::Smells::NestedIterators.smell_type)
|
128
|
+
expect(warning.parameters[:name]).to eq('foo')
|
129
|
+
expect(warning.lines).to eq([3])
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should report nested iterators only once per method' do
|
133
|
+
source = <<-EOS
|
134
|
+
def bad(fred)
|
135
|
+
@fred.each {|item| item.each {|part| @joe.send} }
|
136
|
+
@jim.each {|ting| ting.each {|piece| @hal.send} }
|
137
|
+
end
|
138
|
+
EOS
|
139
|
+
|
140
|
+
warnings = detector.examine_context(build(:method_context, source: source))
|
141
|
+
expect(warnings.size).to eq(1)
|
142
|
+
warning = warnings.first
|
143
|
+
expect(warning.parameters[:name]).to eq('bad')
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'reports nested iterators only once per method even if levels are different' do
|
147
|
+
source = <<-EOS
|
148
|
+
def bad(fred)
|
149
|
+
@fred.each {|item| item.each {|part| part.foo} }
|
150
|
+
@jim.each {|ting| ting.each {|piece| piece.each {|atom| atom.foo } } }
|
151
|
+
end
|
152
|
+
EOS
|
153
|
+
warnings = detector.examine_context(build(:method_context, source: source))
|
154
|
+
expect(warnings.size).to eq(1)
|
155
|
+
warning = warnings.first
|
156
|
+
|
157
|
+
expect(warning.parameters[:name]).to eq('bad')
|
158
|
+
expect(warning.lines).to eq([3])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe 'iterators without block arguments' do
|
163
|
+
it 'does not count those iterators' do
|
164
|
+
source = <<-EOS
|
165
|
+
def foo
|
166
|
+
before do
|
167
|
+
item.each do |part|
|
168
|
+
puts part
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
EOS
|
173
|
+
expect(source).not_to reek_of(:NestedIterators)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
130
177
|
context 'when the allowed nesting depth is 3' do
|
131
178
|
let(:configuration) do
|
132
179
|
config = { Reek::Smells::NestedIterators =>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reek
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Rutherford
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2015-09-
|
14
|
+
date: 2015-09-28 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: parser
|