reek 3.8.3 → 3.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|