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