reek 3.8.3 → 3.9.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/.travis.yml +1 -1
- data/CHANGELOG.md +5 -0
- data/Gemfile +16 -6
- data/README.md +1 -0
- data/features/command_line_interface/smells_count.feature +1 -1
- data/features/command_line_interface/stdin.feature +1 -1
- data/features/configuration_loading.feature +2 -2
- data/features/programmatic_access.feature +2 -2
- data/features/rake_task/rake_task.feature +4 -4
- data/features/reports/json.feature +2 -2
- data/features/reports/reports.feature +6 -6
- data/features/reports/yaml.feature +2 -2
- data/features/samples.feature +19 -19
- data/features/step_definitions/sample_file_steps.rb +1 -1
- data/lib/reek/ast/node.rb +12 -1
- data/lib/reek/ast/sexp_extensions.rb +1 -0
- data/lib/reek/ast/sexp_extensions/constant.rb +9 -0
- data/lib/reek/ast/sexp_extensions/methods.rb +1 -20
- data/lib/reek/ast/sexp_extensions/module.rb +2 -2
- data/lib/reek/ast/sexp_extensions/self.rb +12 -0
- data/lib/reek/ast/sexp_extensions/send.rb +4 -9
- data/lib/reek/ast/sexp_extensions/super.rb +1 -1
- data/lib/reek/ast/sexp_extensions/variables.rb +5 -0
- data/lib/reek/context/attribute_context.rb +12 -0
- data/lib/reek/context/code_context.rb +28 -26
- data/lib/reek/context/ghost_context.rb +54 -0
- data/lib/reek/context/method_context.rb +28 -1
- data/lib/reek/context/module_context.rb +55 -1
- data/lib/reek/context/root_context.rb +8 -0
- data/lib/reek/context/singleton_attribute_context.rb +15 -0
- data/lib/reek/context/singleton_method_context.rb +20 -0
- data/lib/reek/context/visibility_tracker.rb +25 -16
- data/lib/reek/context_builder.rb +61 -31
- data/lib/reek/examiner.rb +0 -6
- data/lib/reek/smells/control_parameter.rb +1 -1
- data/lib/reek/smells/nested_iterators.rb +1 -1
- data/lib/reek/smells/nil_check.rb +2 -2
- data/lib/reek/smells/utility_function.rb +1 -2
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +1 -12
- data/spec/reek/ast/node_spec.rb +51 -3
- data/spec/reek/ast/sexp_extensions_spec.rb +16 -3
- data/spec/reek/context/code_context_spec.rb +12 -31
- data/spec/reek/context/ghost_context_spec.rb +60 -0
- data/spec/reek/context/module_context_spec.rb +22 -2
- data/spec/reek/context_builder_spec.rb +225 -2
- data/spec/reek/examiner_spec.rb +1 -1
- data/spec/reek/smells/attribute_spec.rb +35 -0
- data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
- data/spec/reek/tree_dresser_spec.rb +0 -1
- data/spec/samples/checkstyle.xml +2 -2
- metadata +8 -152
- data/lib/reek/ast/sexp_formatter.rb +0 -31
- data/spec/reek/ast/sexp_formatter_spec.rb +0 -35
@@ -3,14 +3,13 @@ module Reek
|
|
3
3
|
module SexpExtensions
|
4
4
|
# Utility methods for :send nodes.
|
5
5
|
module SendNode
|
6
|
-
VISIBILITY_MODIFIERS = [:private, :public, :protected, :module_function]
|
7
6
|
ATTR_DEFN_METHODS = [:attr_writer, :attr_accessor]
|
8
7
|
|
9
8
|
def receiver
|
10
9
|
children.first
|
11
10
|
end
|
12
11
|
|
13
|
-
def
|
12
|
+
def name
|
14
13
|
children[1]
|
15
14
|
end
|
16
15
|
|
@@ -35,22 +34,18 @@ module Reek
|
|
35
34
|
end
|
36
35
|
|
37
36
|
def object_creation_call?
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def visibility_modifier?
|
42
|
-
VISIBILITY_MODIFIERS.include?(method_name)
|
37
|
+
name == :new
|
43
38
|
end
|
44
39
|
|
45
40
|
def attribute_writer?
|
46
|
-
ATTR_DEFN_METHODS.include?(
|
41
|
+
ATTR_DEFN_METHODS.include?(name) ||
|
47
42
|
attr_with_writable_flag?
|
48
43
|
end
|
49
44
|
|
50
45
|
# Handles the case where we create an attribute writer via:
|
51
46
|
# attr :foo, true
|
52
47
|
def attr_with_writable_flag?
|
53
|
-
|
48
|
+
name == :attr && args.any? && args.last.type == :true
|
54
49
|
end
|
55
50
|
end
|
56
51
|
|
@@ -5,8 +5,12 @@ module Reek
|
|
5
5
|
#
|
6
6
|
# A context wrapper for attribute definitions found in a syntax tree.
|
7
7
|
#
|
8
|
+
# :reek:Attribute
|
8
9
|
class AttributeContext < CodeContext
|
10
|
+
attr_accessor :visibility
|
11
|
+
|
9
12
|
def initialize(context, exp, send_expression)
|
13
|
+
@visibility = :public
|
10
14
|
@send_expression = send_expression
|
11
15
|
super context, exp
|
12
16
|
end
|
@@ -15,6 +19,14 @@ module Reek
|
|
15
19
|
send_expression.full_comment || ''
|
16
20
|
end
|
17
21
|
|
22
|
+
def instance_method?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def apply_current_visibility(current_visibility)
|
27
|
+
self.visibility = current_visibility
|
28
|
+
end
|
29
|
+
|
18
30
|
private_attr_reader :send_expression
|
19
31
|
end
|
20
32
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require_relative '../code_comment'
|
2
2
|
require_relative '../ast/object_refs'
|
3
|
-
require_relative 'visibility_tracker'
|
4
3
|
require_relative 'statement_counter'
|
5
4
|
|
6
5
|
require 'forwardable'
|
@@ -20,14 +19,14 @@ module Reek
|
|
20
19
|
extend Forwardable
|
21
20
|
delegate each_node: :exp
|
22
21
|
delegate %i(name type) => :exp
|
23
|
-
delegate %i(visibility visibility= non_public_visibility?) => :visibility_tracker
|
24
22
|
|
25
|
-
attr_reader :children, :
|
23
|
+
attr_reader :children, :parent, :exp, :statement_counter
|
24
|
+
|
26
25
|
private_attr_reader :refs
|
27
26
|
|
28
27
|
# Initializes a new CodeContext.
|
29
28
|
#
|
30
|
-
# @param
|
29
|
+
# @param parent [CodeContext, nil] The parent context
|
31
30
|
# @param exp [Reek::AST::Node] The code described by this context
|
32
31
|
#
|
33
32
|
# For example, given the following code:
|
@@ -40,7 +39,7 @@ module Reek
|
|
40
39
|
#
|
41
40
|
# The {ContextBuilder} object first instantiates a {RootContext}, which has no parent.
|
42
41
|
#
|
43
|
-
# Next, it instantiates a {ModuleContext}, with +
|
42
|
+
# Next, it instantiates a {ModuleContext}, with +parent+ being the
|
44
43
|
# {RootContext} just created, and +exp+ looking like this:
|
45
44
|
#
|
46
45
|
# (class
|
@@ -52,18 +51,16 @@ module Reek
|
|
52
51
|
# (lvar :x))))
|
53
52
|
#
|
54
53
|
# Finally, {ContextBuilder} will instantiate a {MethodContext}. This time,
|
55
|
-
# +
|
54
|
+
# +parent+ is the {ModuleContext} created above, and +exp+ is:
|
56
55
|
#
|
57
56
|
# (def :foo
|
58
57
|
# (args
|
59
58
|
# (arg :x))
|
60
59
|
# (send nil :puts
|
61
60
|
# (lvar :x)))
|
62
|
-
def initialize(
|
63
|
-
@context = context
|
61
|
+
def initialize(_parent, exp)
|
64
62
|
@exp = exp
|
65
63
|
@children = []
|
66
|
-
@visibility_tracker = VisibilityTracker.new
|
67
64
|
@statement_counter = StatementCounter.new
|
68
65
|
@refs = AST::ObjectRefs.new
|
69
66
|
end
|
@@ -93,18 +90,17 @@ module Reek
|
|
93
90
|
end
|
94
91
|
end
|
95
92
|
|
96
|
-
|
93
|
+
def register_with_parent(parent)
|
94
|
+
@parent = parent.append_child_context(self) if parent
|
95
|
+
end
|
97
96
|
|
98
|
-
# Register a
|
99
|
-
#
|
100
|
-
#
|
101
|
-
# This makes the current context responsible for setting the child's
|
102
|
-
# visibility.
|
97
|
+
# Register a context as a child context of this context. This is
|
98
|
+
# generally used by a child context to register itself with its parent.
|
103
99
|
#
|
104
100
|
# @param child [CodeContext] the child context to register
|
105
101
|
def append_child_context(child)
|
106
|
-
visibility_tracker.set_child_visibility(child)
|
107
102
|
children << child
|
103
|
+
self
|
108
104
|
end
|
109
105
|
|
110
106
|
# :reek:TooManyStatements: { max_statements: 6 }
|
@@ -136,24 +132,30 @@ module Reek
|
|
136
132
|
end
|
137
133
|
|
138
134
|
def full_name
|
139
|
-
exp.full_name(
|
135
|
+
exp.full_name(parent ? parent.full_name : '')
|
140
136
|
end
|
141
137
|
|
142
138
|
def config_for(detector_class)
|
143
|
-
|
139
|
+
parent_config_for(detector_class).merge(
|
144
140
|
configuration_via_code_commment[detector_class.smell_type] || {})
|
145
141
|
end
|
146
142
|
|
147
|
-
def track_visibility(visibility, names)
|
148
|
-
visibility_tracker.track_visibility children: children,
|
149
|
-
visibility: visibility,
|
150
|
-
names: names
|
151
|
-
end
|
152
|
-
|
153
143
|
def number_of_statements
|
154
144
|
statement_counter.value
|
155
145
|
end
|
156
146
|
|
147
|
+
def singleton_method?
|
148
|
+
false
|
149
|
+
end
|
150
|
+
|
151
|
+
def instance_method?
|
152
|
+
false
|
153
|
+
end
|
154
|
+
|
155
|
+
def apply_current_visibility(_current_visibility)
|
156
|
+
# Nothing to do by default
|
157
|
+
end
|
158
|
+
|
157
159
|
private
|
158
160
|
|
159
161
|
def configuration_via_code_commment
|
@@ -164,8 +166,8 @@ module Reek
|
|
164
166
|
exp.full_comment || ''
|
165
167
|
end
|
166
168
|
|
167
|
-
def
|
168
|
-
|
169
|
+
def parent_config_for(detector_class)
|
170
|
+
parent ? parent.config_for(detector_class) : {}
|
169
171
|
end
|
170
172
|
end
|
171
173
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'code_context'
|
2
|
+
require_relative 'singleton_method_context'
|
3
|
+
|
4
|
+
module Reek
|
5
|
+
module Context
|
6
|
+
# Semi-transparent context to represent a metaclass while building the
|
7
|
+
# context tree. This context will not be part of the resulting tree, but
|
8
|
+
# will track context and visibility separately while building is in
|
9
|
+
# progress.
|
10
|
+
class GhostContext < ModuleContext
|
11
|
+
attr_reader :children
|
12
|
+
|
13
|
+
def register_with_parent(parent)
|
14
|
+
@parent = parent
|
15
|
+
end
|
16
|
+
|
17
|
+
def append_child_context(child)
|
18
|
+
real_parent = parent.append_child_context(child)
|
19
|
+
super
|
20
|
+
real_parent
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return the correct class for child method contexts (representing nodes
|
24
|
+
# of type `:def`). For GhostContext, this is the class that represents
|
25
|
+
# singleton methods.
|
26
|
+
def method_context_class
|
27
|
+
SingletonMethodContext
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the correct class for child attribute contexts. For
|
31
|
+
# GhostContext, this is the class that represents singleton attributes.
|
32
|
+
def attribute_context_class
|
33
|
+
SingletonAttributeContext
|
34
|
+
end
|
35
|
+
|
36
|
+
def track_visibility(visibility, names)
|
37
|
+
visibility_tracker.track_visibility children: children,
|
38
|
+
visibility: visibility,
|
39
|
+
names: names
|
40
|
+
end
|
41
|
+
|
42
|
+
def track_singleton_visibility(_visibility, _names)
|
43
|
+
end
|
44
|
+
|
45
|
+
def record_use_of_self
|
46
|
+
parent.record_use_of_self
|
47
|
+
end
|
48
|
+
|
49
|
+
def statement_counter
|
50
|
+
parent.statement_counter
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -5,9 +5,16 @@ module Reek
|
|
5
5
|
#
|
6
6
|
# A context wrapper for any method definition found in a syntax tree.
|
7
7
|
#
|
8
|
+
# :reek:Attribute
|
8
9
|
class MethodContext < CodeContext
|
10
|
+
attr_accessor :visibility
|
9
11
|
attr_reader :refs
|
10
12
|
|
13
|
+
def initialize(context, exp)
|
14
|
+
@visibility = :public
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
11
18
|
def references_self?
|
12
19
|
exp.depends_on_instance?
|
13
20
|
end
|
@@ -34,8 +41,28 @@ module Reek
|
|
34
41
|
exp.parameters.select(&:optional_argument?).map(&:children)
|
35
42
|
end
|
36
43
|
|
44
|
+
def method_context_class
|
45
|
+
self.class
|
46
|
+
end
|
47
|
+
|
37
48
|
def singleton_method?
|
38
|
-
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def instance_method?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def apply_current_visibility(current_visibility)
|
57
|
+
self.visibility = current_visibility
|
58
|
+
end
|
59
|
+
|
60
|
+
def module_function?
|
61
|
+
visibility == :module_function
|
62
|
+
end
|
63
|
+
|
64
|
+
def non_public_visibility?
|
65
|
+
visibility != :public
|
39
66
|
end
|
40
67
|
end
|
41
68
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative 'code_context'
|
2
|
+
require_relative 'attribute_context'
|
2
3
|
require_relative 'method_context'
|
3
|
-
require_relative '
|
4
|
+
require_relative 'visibility_tracker'
|
4
5
|
|
5
6
|
module Reek
|
6
7
|
module Context
|
@@ -9,6 +10,39 @@ module Reek
|
|
9
10
|
#
|
10
11
|
# :reek:FeatureEnvy
|
11
12
|
class ModuleContext < CodeContext
|
13
|
+
attr_reader :visibility_tracker
|
14
|
+
|
15
|
+
def initialize(context, exp)
|
16
|
+
super
|
17
|
+
|
18
|
+
@visibility_tracker = VisibilityTracker.new
|
19
|
+
end
|
20
|
+
|
21
|
+
# Register a child context. The child's parent context should be equal to
|
22
|
+
# the current context.
|
23
|
+
#
|
24
|
+
# This makes the current context responsible for setting the child's
|
25
|
+
# visibility.
|
26
|
+
#
|
27
|
+
# @param child [CodeContext] the child context to register
|
28
|
+
def append_child_context(child)
|
29
|
+
visibility_tracker.set_child_visibility(child)
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return the correct class for child method contexts (representing nodes
|
34
|
+
# of type `:def`). For ModuleContext, this is the class that represents
|
35
|
+
# instance methods.
|
36
|
+
def method_context_class
|
37
|
+
MethodContext
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return the correct class for child attribute contexts. For
|
41
|
+
# ModuleContext, this is the class that represents instance attributes.
|
42
|
+
def attribute_context_class
|
43
|
+
AttributeContext
|
44
|
+
end
|
45
|
+
|
12
46
|
def defined_instance_methods(visibility: :public)
|
13
47
|
each.select do |context|
|
14
48
|
context.is_a?(Context::MethodContext) &&
|
@@ -44,6 +78,26 @@ module Reek
|
|
44
78
|
contents = exp.children.last
|
45
79
|
contents && contents.find_nodes([:def, :defs], [:casgn, :class, :module]).empty?
|
46
80
|
end
|
81
|
+
|
82
|
+
def track_visibility(visibility, names)
|
83
|
+
visibility_tracker.track_visibility children: instance_method_children,
|
84
|
+
visibility: visibility,
|
85
|
+
names: names
|
86
|
+
end
|
87
|
+
|
88
|
+
def track_singleton_visibility(visibility, names)
|
89
|
+
visibility_tracker.track_singleton_visibility children: singleton_method_children,
|
90
|
+
visibility: visibility,
|
91
|
+
names: names
|
92
|
+
end
|
93
|
+
|
94
|
+
def instance_method_children
|
95
|
+
children.select(&:instance_method?)
|
96
|
+
end
|
97
|
+
|
98
|
+
def singleton_method_children
|
99
|
+
children.select(&:singleton_method?)
|
100
|
+
end
|
47
101
|
end
|
48
102
|
end
|
49
103
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'code_context'
|
2
|
+
require_relative 'method_context'
|
2
3
|
|
3
4
|
module Reek
|
4
5
|
module Context
|
@@ -17,6 +18,13 @@ module Reek
|
|
17
18
|
def full_name
|
18
19
|
''
|
19
20
|
end
|
21
|
+
|
22
|
+
# Return the correct class for child method contexts (representing nodes
|
23
|
+
# of type `:def`). For RootContext, this is the class that represents
|
24
|
+
# instance methods.
|
25
|
+
def method_context_class
|
26
|
+
MethodContext
|
27
|
+
end
|
20
28
|
end
|
21
29
|
end
|
22
30
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'attribute_context'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
module Context
|
5
|
+
#
|
6
|
+
# A context wrapper for any singleton attribute definition found in a
|
7
|
+
# syntax tree.
|
8
|
+
#
|
9
|
+
class SingletonAttributeContext < AttributeContext
|
10
|
+
def instance_method?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|