reek 1.2.7.3 → 1.2.8
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.
- data/History.txt +17 -0
- data/README.md +32 -48
- data/config/defaults.reek +7 -1
- data/features/api.feature +20 -0
- data/features/masking_smells.feature +41 -0
- data/features/options.feature +4 -0
- data/features/rake_task.feature +14 -0
- data/features/yaml.feature +8 -8
- data/lib/reek.rb +1 -1
- data/lib/reek/cli/command_line.rb +9 -2
- data/lib/reek/cli/reek_command.rb +5 -4
- data/lib/reek/cli/yaml_command.rb +2 -2
- data/lib/reek/core/code_context.rb +10 -1
- data/lib/reek/core/method_context.rb +2 -2
- data/lib/reek/core/sniffer.rb +3 -1
- data/lib/reek/core/stop_context.rb +4 -0
- data/lib/reek/examiner.rb +2 -2
- data/lib/reek/rake/task.rb +16 -0
- data/lib/reek/smell_warning.rb +3 -2
- data/lib/reek/smells/attribute.rb +13 -9
- data/lib/reek/smells/boolean_parameter.rb +14 -9
- data/lib/reek/smells/class_variable.rb +16 -5
- data/lib/reek/smells/control_couple.rb +11 -6
- data/lib/reek/smells/data_clump.rb +33 -30
- data/lib/reek/smells/duplication.rb +39 -8
- data/lib/reek/smells/feature_envy.rb +7 -8
- data/lib/reek/smells/irresponsible_module.rb +12 -3
- data/lib/reek/smells/large_class.rb +31 -15
- data/lib/reek/smells/long_method.rb +15 -5
- data/lib/reek/smells/long_parameter_list.rb +14 -7
- data/lib/reek/smells/long_yield_list.rb +12 -9
- data/lib/reek/smells/nested_iterators.rb +46 -11
- data/lib/reek/smells/simulated_polymorphism.rb +16 -8
- data/lib/reek/smells/smell_detector.rb +13 -13
- data/lib/reek/smells/uncommunicative_method_name.rb +12 -20
- data/lib/reek/smells/uncommunicative_module_name.rb +17 -19
- data/lib/reek/smells/uncommunicative_parameter_name.rb +22 -15
- data/lib/reek/smells/uncommunicative_variable_name.rb +24 -18
- data/lib/reek/smells/utility_function.rb +6 -6
- data/lib/reek/source/code_comment.rb +19 -1
- data/lib/reek/source/tree_dresser.rb +40 -22
- data/reek.gemspec +6 -4
- data/spec/matchers/smell_of_matcher.rb +58 -0
- data/spec/reek/core/code_context_spec.rb +4 -2
- data/spec/reek/core/code_parser_spec.rb +2 -1
- data/spec/reek/core/method_context_spec.rb +5 -5
- data/spec/reek/smells/attribute_spec.rb +2 -4
- data/spec/reek/smells/boolean_parameter_spec.rb +32 -42
- data/spec/reek/smells/class_variable_spec.rb +22 -6
- data/spec/reek/smells/control_couple_spec.rb +15 -14
- data/spec/reek/smells/data_clump_spec.rb +29 -111
- data/spec/reek/smells/duplication_spec.rb +79 -49
- data/spec/reek/smells/feature_envy_spec.rb +1 -2
- data/spec/reek/smells/irresponsible_module_spec.rb +43 -22
- data/spec/reek/smells/large_class_spec.rb +34 -59
- data/spec/reek/smells/long_method_spec.rb +15 -10
- data/spec/reek/smells/long_parameter_list_spec.rb +24 -24
- data/spec/reek/smells/long_yield_list_spec.rb +13 -14
- data/spec/reek/smells/nested_iterators_spec.rb +93 -76
- data/spec/reek/smells/smell_detector_shared.rb +4 -2
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +10 -27
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +22 -23
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +36 -26
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +45 -48
- data/spec/reek/smells/utility_function_spec.rb +14 -13
- data/spec/reek/source/code_comment_spec.rb +61 -3
- data/spec/reek/source/tree_dresser_spec.rb +96 -1
- data/spec/samples/config/allow_duplication.reek +3 -0
- data/spec/samples/config/deeper_nested_iterators.reek +3 -0
- data/spec/samples/demo/demo.rb +8 -0
- data/spec/samples/inline_config/dirty.rb +16 -0
- data/spec/samples/inline_config/masked.reek +7 -0
- data/spec/samples/mask_some/dirty.rb +8 -0
- data/spec/samples/mask_some/some.reek +8 -0
- data/spec/spec_helper.rb +2 -0
- metadata +15 -5
data/lib/reek/examiner.rb
CHANGED
@@ -27,7 +27,7 @@ module Reek
|
|
27
27
|
# and if it is an Array, it is assumed to be a list of file paths,
|
28
28
|
# each of which is opened and parsed for source code.
|
29
29
|
#
|
30
|
-
def initialize(source)
|
30
|
+
def initialize(source, config_files = [])
|
31
31
|
sources = case source
|
32
32
|
when Array
|
33
33
|
@description = 'dir'
|
@@ -41,7 +41,7 @@ module Reek
|
|
41
41
|
[src]
|
42
42
|
end
|
43
43
|
collector = Core::WarningCollector.new
|
44
|
-
sources.each { |src| Core::Sniffer.new(src).report_on(collector) }
|
44
|
+
sources.each { |src| Core::Sniffer.new(src, config_files).report_on(collector) }
|
45
45
|
@smells = collector.warnings
|
46
46
|
end
|
47
47
|
|
data/lib/reek/rake/task.rb
CHANGED
@@ -7,6 +7,8 @@ module Reek
|
|
7
7
|
|
8
8
|
#
|
9
9
|
# Defines a task library for running reek.
|
10
|
+
# (Classes here will be configured via the Rakefile, and therefore will
|
11
|
+
# possess a :reek:attribute or two.)
|
10
12
|
#
|
11
13
|
module Rake
|
12
14
|
|
@@ -40,6 +42,11 @@ module Reek
|
|
40
42
|
# Defaults to ['<the absolute path to reek's lib directory>']
|
41
43
|
attr_accessor :libs
|
42
44
|
|
45
|
+
# Glob pattern to match config files.
|
46
|
+
# Setting the REEK_CFG environment variable overrides this.
|
47
|
+
# Defaults to 'config/**/*.reek'.
|
48
|
+
attr_accessor :config_files
|
49
|
+
|
43
50
|
# Glob pattern to match source files.
|
44
51
|
# Setting the REEK_SRC environment variable overrides this.
|
45
52
|
# Defaults to 'lib/**/*.rb'.
|
@@ -65,6 +72,7 @@ module Reek
|
|
65
72
|
def initialize(name = :reek)
|
66
73
|
@name = name
|
67
74
|
@libs = [File.expand_path(File.dirname(__FILE__) + '/../../../lib')]
|
75
|
+
@config_files = nil
|
68
76
|
@source_files = nil
|
69
77
|
@ruby_opts = []
|
70
78
|
@reek_opts = ''
|
@@ -72,6 +80,7 @@ module Reek
|
|
72
80
|
@sort = nil
|
73
81
|
|
74
82
|
yield self if block_given?
|
83
|
+
@config_files ||= 'config/**/*.reek'
|
75
84
|
@source_files ||= 'lib/**/*.rb'
|
76
85
|
define
|
77
86
|
end
|
@@ -104,9 +113,16 @@ module Reek
|
|
104
113
|
ruby_options +
|
105
114
|
[ %Q|"#{Task.reek_script}"| ] +
|
106
115
|
[sort_option] +
|
116
|
+
config_file_list.collect { |fn| ['-c', %["#{fn}"]] }.flatten +
|
107
117
|
source_file_list.collect { |fn| %["#{fn}"] }
|
108
118
|
end
|
109
119
|
|
120
|
+
def config_file_list
|
121
|
+
files = ENV['REEK_CFG'] || @config_files
|
122
|
+
return [] unless files
|
123
|
+
return FileList[files]
|
124
|
+
end
|
125
|
+
|
110
126
|
def ruby_options
|
111
127
|
lib_path = @libs.join(File::PATH_SEPARATOR)
|
112
128
|
@ruby_opts.clone << "-I\"#{lib_path}\""
|
data/lib/reek/smell_warning.rb
CHANGED
@@ -3,6 +3,7 @@ module Reek
|
|
3
3
|
|
4
4
|
#
|
5
5
|
# Reports a warning that a smell has been found.
|
6
|
+
# This object is essentially a DTO, and therefore contains a :reek:attribute or two.
|
6
7
|
#
|
7
8
|
class SmellWarning
|
8
9
|
|
@@ -71,7 +72,7 @@ module Reek
|
|
71
72
|
|
72
73
|
def is_active() @status[ACTIVE_KEY] end
|
73
74
|
|
74
|
-
def hash
|
75
|
+
def hash
|
75
76
|
sort_key.hash
|
76
77
|
end
|
77
78
|
|
@@ -85,7 +86,7 @@ module Reek
|
|
85
86
|
|
86
87
|
def contains_all?(patterns)
|
87
88
|
rpt = sort_key.to_s
|
88
|
-
return patterns.all? {|
|
89
|
+
return patterns.all? {|pattern| pattern === rpt}
|
89
90
|
end
|
90
91
|
|
91
92
|
def matches?(klass, patterns)
|
@@ -21,6 +21,8 @@ module Reek
|
|
21
21
|
class Attribute < SmellDetector
|
22
22
|
|
23
23
|
SMELL_CLASS = self.name.split(/::/)[-1]
|
24
|
+
SMELL_SUBCLASS = SMELL_CLASS
|
25
|
+
|
24
26
|
ATTRIBUTE_KEY = 'attribute'
|
25
27
|
|
26
28
|
def self.contexts # :nodoc:
|
@@ -37,19 +39,21 @@ module Reek
|
|
37
39
|
|
38
40
|
#
|
39
41
|
# Checks whether the given class declares any attributes.
|
40
|
-
# Remembers any smells found.
|
41
42
|
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
# @return [Array<SmellWarning>]
|
44
|
+
#
|
45
|
+
def examine_context(ctx)
|
46
|
+
attributes_in(ctx).map do |attr, line|
|
47
|
+
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [line],
|
48
|
+
"declares the attribute #{attr}",
|
49
|
+
@source, SMELL_SUBCLASS,
|
50
|
+
{ATTRIBUTE_KEY => attr.to_s})
|
51
|
+
smell
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
49
|
-
|
50
|
-
|
51
|
-
# in the given module.
|
52
|
-
#
|
55
|
+
private
|
56
|
+
|
53
57
|
def attributes_in(module_ctx)
|
54
58
|
result = Set.new
|
55
59
|
attr_defn_methods = [:attr, :attr_reader, :attr_writer, :attr_accessor]
|
@@ -14,18 +14,23 @@ module Reek
|
|
14
14
|
#
|
15
15
|
class BooleanParameter < SmellDetector
|
16
16
|
|
17
|
+
SMELL_CLASS = 'ControlCouple'
|
18
|
+
SMELL_SUBCLASS = self.name.split(/::/)[-1]
|
19
|
+
|
20
|
+
PARAMETER_KEY = 'parameter'
|
21
|
+
|
22
|
+
#
|
23
|
+
# Checks whether the given method has any Boolean parameters.
|
17
24
|
#
|
18
|
-
#
|
19
|
-
# Remembers any smells found.
|
25
|
+
# @return [Array<SmellWarning>]
|
20
26
|
#
|
21
27
|
def examine_context(method_ctx)
|
22
|
-
method_ctx.parameters.default_assignments.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
#SMELL: serious duplication
|
28
|
+
method_ctx.parameters.default_assignments.select do |param, value|
|
29
|
+
[:true, :false].include?(value[0])
|
30
|
+
end.map do |param, value|
|
31
|
+
SmellWarning.new(SMELL_CLASS, method_ctx.full_name, [method_ctx.exp.line],
|
32
|
+
"has boolean parameter '#{param.to_s}'",
|
33
|
+
@source, SMELL_SUBCLASS, {PARAMETER_KEY => param.to_s})
|
29
34
|
end
|
30
35
|
end
|
31
36
|
end
|
@@ -15,17 +15,26 @@ module Reek
|
|
15
15
|
#
|
16
16
|
class ClassVariable < SmellDetector
|
17
17
|
|
18
|
+
SMELL_CLASS = self.name.split(/::/)[-1]
|
19
|
+
SMELL_SUBCLASS = SMELL_CLASS
|
20
|
+
VARIABLE_KEY = 'variable'
|
21
|
+
|
18
22
|
def self.contexts # :nodoc:
|
19
23
|
[:class, :module]
|
20
24
|
end
|
21
25
|
|
22
26
|
#
|
23
27
|
# Checks whether the given class or module declares any class variables.
|
24
|
-
#
|
28
|
+
#
|
29
|
+
# @return [Array<SmellWarning>]
|
25
30
|
#
|
26
31
|
def examine_context(ctx)
|
27
|
-
class_variables_in(ctx.exp).
|
28
|
-
|
32
|
+
class_variables_in(ctx.exp).map do |attr_name, lines|
|
33
|
+
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, lines,
|
34
|
+
"declares the class variable #{attr_name.to_s}",
|
35
|
+
@source, SMELL_SUBCLASS,
|
36
|
+
{VARIABLE_KEY => attr_name.to_s})
|
37
|
+
smell
|
29
38
|
end
|
30
39
|
end
|
31
40
|
|
@@ -34,8 +43,10 @@ module Reek
|
|
34
43
|
# in the given module.
|
35
44
|
#
|
36
45
|
def class_variables_in(ast)
|
37
|
-
result =
|
38
|
-
collector = proc
|
46
|
+
result = Hash.new {|hash,key| hash[key] = []}
|
47
|
+
collector = proc do |cvar_node|
|
48
|
+
result[cvar_node.name].push(cvar_node.line)
|
49
|
+
end
|
39
50
|
[:cvar, :cvasgn, :cvdecl].each do |stmt_type|
|
40
51
|
ast.each_node(stmt_type, [:class, :module], &collector)
|
41
52
|
end
|
@@ -45,18 +45,23 @@ module Reek
|
|
45
45
|
|
46
46
|
SMELL_CLASS = self.name.split(/::/)[-1]
|
47
47
|
SMELL_SUBCLASS = 'ControlParameter'
|
48
|
+
PARAMETER_KEY = 'parameter'
|
48
49
|
|
49
50
|
#
|
50
51
|
# Checks whether the given method chooses its execution path
|
51
52
|
# by testing the value of one of its parameters.
|
52
|
-
# Remembers any smells found.
|
53
53
|
#
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
# @return [Array<SmellWarning>]
|
55
|
+
#
|
56
|
+
def examine_context(ctx)
|
57
|
+
control_parameters(ctx).map do |cond, occurs|
|
58
|
+
param = cond.format_ruby
|
57
59
|
lines = occurs.map {|exp| exp.line}
|
58
|
-
|
59
|
-
|
60
|
+
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, lines,
|
61
|
+
"is controlled by argument #{param}",
|
62
|
+
@source, SMELL_SUBCLASS,
|
63
|
+
{PARAMETER_KEY => param})
|
64
|
+
smell
|
60
65
|
end
|
61
66
|
end
|
62
67
|
|
@@ -34,23 +34,32 @@ module Reek
|
|
34
34
|
OCCURRENCES_KEY = 'occurrences'
|
35
35
|
PARAMETERS_KEY = 'parameters'
|
36
36
|
|
37
|
-
|
37
|
+
# @private
|
38
|
+
def self.contexts
|
38
39
|
[:class, :module]
|
39
40
|
end
|
40
41
|
|
42
|
+
#
|
41
43
|
# The name of the config field that sets the maximum allowed
|
42
|
-
# copies of any clump.
|
44
|
+
# copies of any clump. No group of common parameters will be
|
45
|
+
# reported as a DataClump unless there are more than this many
|
46
|
+
# methods containing those parameters.
|
47
|
+
#
|
43
48
|
MAX_COPIES_KEY = 'max_copies'
|
44
|
-
|
45
49
|
DEFAULT_MAX_COPIES = 2
|
46
50
|
|
51
|
+
#
|
52
|
+
# The name of the config field that sets the minimum clump
|
53
|
+
# size. No group of common parameters will be reported as
|
54
|
+
# a DataClump unless it contains at least this many parameters.
|
55
|
+
#
|
47
56
|
MIN_CLUMP_SIZE_KEY = 'min_clump_size'
|
48
57
|
DEFAULT_MIN_CLUMP_SIZE = 2
|
49
58
|
|
50
59
|
def self.default_config
|
51
60
|
super.adopt(
|
52
|
-
|
53
|
-
|
61
|
+
MAX_COPIES_KEY => DEFAULT_MAX_COPIES,
|
62
|
+
MIN_CLUMP_SIZE_KEY => DEFAULT_MIN_CLUMP_SIZE
|
54
63
|
)
|
55
64
|
end
|
56
65
|
|
@@ -60,26 +69,25 @@ module Reek
|
|
60
69
|
|
61
70
|
#
|
62
71
|
# Checks the given class or module for multiple identical parameter sets.
|
63
|
-
#
|
72
|
+
#
|
73
|
+
# @return [Array<SmellWarning>]
|
64
74
|
#
|
65
75
|
def examine_context(ctx)
|
66
|
-
max_copies = value(MAX_COPIES_KEY, ctx, DEFAULT_MAX_COPIES)
|
67
|
-
min_clump_size = value(MIN_CLUMP_SIZE_KEY, ctx, DEFAULT_MIN_CLUMP_SIZE)
|
68
|
-
MethodGroup.new(ctx, min_clump_size, max_copies).clumps.
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
@smells_found << smell
|
78
|
-
#SMELL: serious duplication
|
79
|
-
# SMELL: name.to_s is becoming a nuisance
|
76
|
+
@max_copies = value(MAX_COPIES_KEY, ctx, DEFAULT_MAX_COPIES)
|
77
|
+
@min_clump_size = value(MIN_CLUMP_SIZE_KEY, ctx, DEFAULT_MIN_CLUMP_SIZE)
|
78
|
+
MethodGroup.new(ctx, @min_clump_size, @max_copies).clumps.map do |clump, methods|
|
79
|
+
SmellWarning.new('DataClump', ctx.full_name,
|
80
|
+
methods.map {|meth| meth.line},
|
81
|
+
"takes parameters #{DataClump.print_clump(clump)} to #{methods.length} methods",
|
82
|
+
@source, 'DataClump', {
|
83
|
+
PARAMETERS_KEY => clump.map {|name| name.to_s},
|
84
|
+
OCCURRENCES_KEY => methods.length,
|
85
|
+
METHODS_KEY => methods.map {|meth| meth.name}
|
86
|
+
})
|
80
87
|
end
|
81
88
|
end
|
82
89
|
|
90
|
+
# @private
|
83
91
|
def self.print_clump(clump)
|
84
92
|
"[#{clump.map {|name| name.to_s}.join(', ')}]"
|
85
93
|
end
|
@@ -108,14 +116,12 @@ module Reek
|
|
108
116
|
methods.each do |other_method|
|
109
117
|
clump = [method.arg_names, other_method.arg_names].intersection
|
110
118
|
if clump.length >= @min_clump_size
|
111
|
-
others = methods.select
|
112
|
-
clump - other.arg_names == []
|
113
|
-
end
|
119
|
+
others = methods.select { |other| clump - other.arg_names == [] }
|
114
120
|
results[clump] += [method] + others
|
115
121
|
end
|
116
122
|
end
|
117
123
|
end
|
118
|
-
|
124
|
+
|
119
125
|
def collect_clumps_in(methods, results)
|
120
126
|
return if methods.length <= @max_copies
|
121
127
|
tail = methods[1..-1]
|
@@ -126,9 +132,7 @@ module Reek
|
|
126
132
|
def clumps
|
127
133
|
results = Hash.new([])
|
128
134
|
collect_clumps_in(@candidate_methods, results)
|
129
|
-
results.each_key
|
130
|
-
results[key].uniq!
|
131
|
-
end
|
135
|
+
results.each_key { |key| results[key].uniq! }
|
132
136
|
results
|
133
137
|
end
|
134
138
|
|
@@ -148,13 +152,12 @@ module Reek
|
|
148
152
|
end
|
149
153
|
end
|
150
154
|
|
151
|
-
#
|
152
155
|
# A method definition and a copy of its parameters
|
153
|
-
#
|
156
|
+
# @private
|
154
157
|
class CandidateMethod
|
155
158
|
def initialize(defn_node)
|
156
159
|
@defn = defn_node
|
157
|
-
@params = defn_node.arg_names.clone.sort {|
|
160
|
+
@params = defn_node.arg_names.clone.sort {|first, second| first.to_s <=> second.to_s}
|
158
161
|
end
|
159
162
|
|
160
163
|
def arg_names
|
@@ -19,32 +19,59 @@ module Reek
|
|
19
19
|
#
|
20
20
|
class Duplication < SmellDetector
|
21
21
|
|
22
|
+
SMELL_CLASS = self.name.split(/::/)[-1]
|
23
|
+
SMELL_SUBCLASS = 'DuplicateMethodCall'
|
24
|
+
CALL_KEY = 'call'
|
25
|
+
OCCURRENCES_KEY = 'occurrences'
|
26
|
+
|
22
27
|
# The name of the config field that sets the maximum number of
|
23
28
|
# identical calls to be permitted within any single method.
|
24
29
|
MAX_ALLOWED_CALLS_KEY = 'max_calls'
|
25
30
|
|
26
31
|
DEFAULT_MAX_CALLS = 1
|
27
32
|
|
33
|
+
# The name of the config field that sets the names of any
|
34
|
+
# methods for which identical calls should be to be permitted
|
35
|
+
# within any single method.
|
36
|
+
ALLOW_CALLS_KEY = 'allow_calls'
|
37
|
+
|
38
|
+
DEFAULT_ALLOW_CALLS = []
|
39
|
+
|
28
40
|
def self.default_config
|
29
|
-
super.adopt(
|
41
|
+
super.adopt(
|
42
|
+
MAX_ALLOWED_CALLS_KEY => DEFAULT_MAX_CALLS,
|
43
|
+
ALLOW_CALLS_KEY => DEFAULT_ALLOW_CALLS
|
44
|
+
)
|
30
45
|
end
|
31
46
|
|
32
47
|
def initialize(source, config = Duplication.default_config)
|
33
48
|
super(source, config)
|
34
49
|
end
|
35
50
|
|
36
|
-
|
37
|
-
|
51
|
+
#
|
52
|
+
# Looks for duplicate calls within the body of the method +ctx+.
|
53
|
+
#
|
54
|
+
# @return [Array<SmellWarning>]
|
55
|
+
#
|
56
|
+
def examine_context(ctx)
|
57
|
+
@max_allowed_calls = value(MAX_ALLOWED_CALLS_KEY, ctx, DEFAULT_MAX_CALLS)
|
58
|
+
@allow_calls = value(ALLOW_CALLS_KEY, ctx, DEFAULT_ALLOW_CALLS)
|
59
|
+
calls(ctx).select do |call_exp, copies|
|
60
|
+
copies.length > @max_allowed_calls and not allow_calls?(call_exp.format_ruby)
|
61
|
+
end.map do |call_exp, copies|
|
38
62
|
occurs = copies.length
|
39
|
-
|
40
|
-
call = call_exp.format
|
63
|
+
call = call_exp.format_ruby
|
41
64
|
multiple = occurs == 2 ? 'twice' : "#{occurs} times"
|
42
|
-
|
43
|
-
|
44
|
-
|
65
|
+
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, copies.map {|exp| exp.line},
|
66
|
+
"calls #{call} #{multiple}",
|
67
|
+
@source, SMELL_SUBCLASS,
|
68
|
+
{CALL_KEY => call, OCCURRENCES_KEY => occurs})
|
69
|
+
smell
|
45
70
|
end
|
46
71
|
end
|
47
72
|
|
73
|
+
private
|
74
|
+
|
48
75
|
def calls(method_ctx)
|
49
76
|
result = Hash.new {|hash,key| hash[key] = []}
|
50
77
|
method_ctx.local_nodes(:call) do |call_node|
|
@@ -56,6 +83,10 @@ module Reek
|
|
56
83
|
end
|
57
84
|
result
|
58
85
|
end
|
86
|
+
|
87
|
+
def allow_calls?(method)
|
88
|
+
@allow_calls.any? { |allow| /#{allow}/ === method }
|
89
|
+
end
|
59
90
|
end
|
60
91
|
end
|
61
92
|
end
|
@@ -43,16 +43,15 @@ module Reek
|
|
43
43
|
#
|
44
44
|
# Checks whether the given +context+ includes any code fragment that
|
45
45
|
# might "belong" on another class.
|
46
|
-
#
|
46
|
+
#
|
47
|
+
# @return [Array<SmellWarning>]
|
47
48
|
#
|
48
49
|
def examine_context(method_ctx)
|
49
|
-
method_ctx.envious_receivers.
|
50
|
-
target = ref.
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@smells_found << smell
|
55
|
-
#SMELL: serious duplication
|
50
|
+
method_ctx.envious_receivers.map do |ref, occurs|
|
51
|
+
target = ref.format_ruby
|
52
|
+
SmellWarning.new(SMELL_CLASS, method_ctx.full_name, [method_ctx.exp.line],
|
53
|
+
"refers to #{target} more than self",
|
54
|
+
@source, SMELL_SUBCLASS, {RECEIVER_KEY => target, REFERENCES_KEY => occurs})
|
56
55
|
end
|
57
56
|
end
|
58
57
|
end
|