reek 1.2.7.1 → 1.2.7.2
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 +13 -1
- data/config/defaults.reek +4 -1
- data/features/masking_smells.feature +7 -7
- data/features/rake_task.feature +2 -2
- data/features/reports.feature +3 -3
- data/features/samples.feature +5 -2
- data/features/yaml.feature +0 -39
- data/lib/reek.rb +1 -1
- data/lib/reek/cli/command_line.rb +3 -3
- data/lib/reek/cli/reek_command.rb +5 -6
- data/lib/reek/cli/report.rb +9 -20
- data/lib/reek/cli/yaml_command.rb +1 -1
- data/lib/reek/core/class_context.rb +1 -2
- data/lib/reek/core/code_context.rb +10 -27
- data/lib/reek/core/code_parser.rb +1 -18
- data/lib/reek/core/detector_stack.rb +4 -0
- data/lib/reek/core/masking_collection.rb +6 -0
- data/lib/reek/core/method_context.rb +8 -56
- data/lib/reek/core/module_context.rb +6 -32
- data/lib/reek/core/object_refs.rb +36 -36
- data/lib/reek/core/singleton_method_context.rb +10 -21
- data/lib/reek/core/sniffer.rb +3 -2
- data/lib/reek/examiner.rb +39 -31
- data/lib/reek/smell_warning.rb +8 -0
- data/lib/reek/smells/attribute.rb +4 -2
- data/lib/reek/smells/class_variable.rb +3 -3
- data/lib/reek/smells/control_couple.rb +1 -2
- data/lib/reek/smells/data_clump.rb +86 -9
- data/lib/reek/smells/duplication.rb +2 -3
- data/lib/reek/smells/feature_envy.rb +9 -4
- data/lib/reek/smells/simulated_polymorphism.rb +1 -2
- data/lib/reek/smells/smell_detector.rb +0 -6
- data/lib/reek/smells/uncommunicative_method_name.rb +8 -2
- data/lib/reek/smells/uncommunicative_parameter_name.rb +1 -1
- data/lib/reek/smells/uncommunicative_variable_name.rb +1 -1
- data/lib/reek/smells/utility_function.rb +17 -5
- data/lib/reek/source/reference_collector.rb +21 -0
- data/lib/reek/source/sexp_formatter.rb +1 -0
- data/lib/reek/source/tree_dresser.rb +67 -9
- data/lib/reek/spec/should_reek.rb +1 -1
- data/lib/reek/spec/should_reek_of.rb +1 -1
- data/lib/reek/spec/should_reek_only_of.rb +1 -1
- data/reek.gemspec +3 -3
- data/spec/reek/cli/reek_command_spec.rb +3 -2
- data/spec/reek/cli/report_spec.rb +2 -2
- data/spec/reek/cli/yaml_command_spec.rb +2 -2
- data/spec/reek/core/code_context_spec.rb +39 -54
- data/spec/reek/core/method_context_spec.rb +7 -26
- data/spec/reek/core/module_context_spec.rb +0 -15
- data/spec/reek/core/singleton_method_context_spec.rb +0 -6
- data/spec/reek/examiner_spec.rb +6 -6
- data/spec/reek/smells/attribute_spec.rb +30 -32
- data/spec/reek/smells/class_variable_spec.rb +15 -18
- data/spec/reek/smells/data_clump_spec.rb +22 -6
- data/spec/reek/smells/duplication_spec.rb +33 -19
- data/spec/reek/smells/feature_envy_spec.rb +82 -88
- data/spec/reek/smells/large_class_spec.rb +1 -1
- data/spec/reek/smells/smell_detector_shared.rb +1 -1
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +37 -35
- data/spec/reek/smells/utility_function_spec.rb +7 -0
- data/spec/reek/source/reference_collector_spec.rb +53 -0
- data/spec/reek/source/tree_dresser_spec.rb +10 -0
- data/spec/reek/spec/should_reek_only_of_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -0
- metadata +4 -5
- data/features/profile.feature +0 -34
- data/lib/reek/core/block_context.rb +0 -18
- data/spec/reek/core/block_context_spec.rb +0 -26
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'sexp'
|
2
|
-
require File.join(File.dirname(File.expand_path(__FILE__)), 'block_context')
|
3
2
|
require File.join(File.dirname(File.expand_path(__FILE__)), 'class_context')
|
4
3
|
require File.join(File.dirname(File.expand_path(__FILE__)), 'method_context')
|
5
4
|
require File.join(File.dirname(File.expand_path(__FILE__)), 'module_context')
|
@@ -61,21 +60,6 @@ module Reek
|
|
61
60
|
@element.record_use_of_self
|
62
61
|
end
|
63
62
|
|
64
|
-
def process_lit(exp)
|
65
|
-
val = exp[1]
|
66
|
-
@element.record_depends_on_self if val == :self
|
67
|
-
end
|
68
|
-
|
69
|
-
def process_iter(exp)
|
70
|
-
process(exp[1])
|
71
|
-
scope = BlockContext.new(@element, exp)
|
72
|
-
push(scope) do
|
73
|
-
process_default(exp[2..-1])
|
74
|
-
check_smells(exp[0])
|
75
|
-
end
|
76
|
-
scope
|
77
|
-
end
|
78
|
-
|
79
63
|
def process_block(exp)
|
80
64
|
@element.count_statements(CodeParser.count_statements(exp))
|
81
65
|
process_default(exp)
|
@@ -144,8 +128,7 @@ module Reek
|
|
144
128
|
end
|
145
129
|
|
146
130
|
def process_iasgn(exp)
|
147
|
-
@element.
|
148
|
-
@element.record_depends_on_self
|
131
|
+
@element.record_use_of_self
|
149
132
|
process_default(exp)
|
150
133
|
end
|
151
134
|
|
@@ -12,6 +12,12 @@ module Reek
|
|
12
12
|
@visible_items = SortedSet.new
|
13
13
|
@masked_items = SortedSet.new
|
14
14
|
end
|
15
|
+
|
16
|
+
def collect_from(sources, config)
|
17
|
+
sources.each { |src| Core::Sniffer.new(src, config).report_on(self) }
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
15
21
|
def all_items
|
16
22
|
all = SortedSet.new(@visible_items)
|
17
23
|
all.merge(@masked_items)
|
@@ -1,31 +1,5 @@
|
|
1
1
|
require File.join(File.dirname(File.expand_path(__FILE__)), 'object_refs')
|
2
2
|
|
3
|
-
#
|
4
|
-
# Extensions to +Array+ needed by Reek.
|
5
|
-
#
|
6
|
-
class Array
|
7
|
-
def power_set
|
8
|
-
self.inject([[]]) { |cum, element| cum.cross(element) }
|
9
|
-
end
|
10
|
-
|
11
|
-
def bounded_power_set(lower_bound)
|
12
|
-
power_set.select {|ps| ps.length > lower_bound}
|
13
|
-
end
|
14
|
-
|
15
|
-
def cross(element)
|
16
|
-
result = []
|
17
|
-
self.each do |set|
|
18
|
-
result << set
|
19
|
-
result << (set + [element])
|
20
|
-
end
|
21
|
-
result
|
22
|
-
end
|
23
|
-
|
24
|
-
def intersection
|
25
|
-
self.inject { |res, elem| elem & res }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
3
|
module Reek
|
30
4
|
module Core
|
31
5
|
|
@@ -77,10 +51,7 @@ module Reek
|
|
77
51
|
@parameters = exp[exp[0] == :defn ? 2 : 3] # SMELL: SimulatedPolymorphism
|
78
52
|
@parameters ||= []
|
79
53
|
@parameters.extend(MethodParameters)
|
80
|
-
@name = exp[1].to_s
|
81
|
-
@scope_connector = '#'
|
82
54
|
@num_statements = 0
|
83
|
-
@depends_on_self = false
|
84
55
|
@refs = ObjectRefs.new
|
85
56
|
@outer.record_method(self) # SMELL: these could be found by tree walking
|
86
57
|
end
|
@@ -89,44 +60,25 @@ module Reek
|
|
89
60
|
@num_statements += num
|
90
61
|
end
|
91
62
|
|
92
|
-
def depends_on_instance?
|
93
|
-
@depends_on_self
|
94
|
-
end
|
95
|
-
|
96
63
|
def record_call_to(exp)
|
97
|
-
|
64
|
+
receiver, meth = exp[1..2]
|
65
|
+
receiver ||= [:self]
|
66
|
+
case receiver[0]
|
67
|
+
when :lvar
|
68
|
+
@refs.record_ref(receiver) unless meth == :new
|
69
|
+
when :self
|
70
|
+
record_use_of_self
|
71
|
+
end
|
98
72
|
end
|
99
73
|
|
100
74
|
def record_use_of_self
|
101
|
-
record_depends_on_self
|
102
75
|
@refs.record_reference_to_self
|
103
76
|
end
|
104
77
|
|
105
|
-
def record_instance_variable(sym)
|
106
|
-
record_use_of_self
|
107
|
-
end
|
108
|
-
|
109
|
-
def record_depends_on_self
|
110
|
-
@depends_on_self = true
|
111
|
-
end
|
112
|
-
|
113
78
|
def envious_receivers
|
114
79
|
return [] if @refs.self_is_max?
|
115
80
|
@refs.max_keys
|
116
81
|
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
def record_receiver(exp)
|
121
|
-
receiver, meth = exp[1..2]
|
122
|
-
receiver ||= [:self]
|
123
|
-
case receiver[0]
|
124
|
-
when :lvar
|
125
|
-
@refs.record_ref(receiver) unless meth == :new
|
126
|
-
when :self
|
127
|
-
record_use_of_self
|
128
|
-
end
|
129
|
-
end
|
130
82
|
end
|
131
83
|
end
|
132
84
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
require File.join(
|
2
|
-
require File.join(
|
3
|
-
require File.join(
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'code_context')
|
2
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'code_parser')
|
3
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'sniffer')
|
4
|
+
require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'source', 'sexp_formatter')
|
4
5
|
|
5
6
|
module Reek
|
6
7
|
module Core
|
@@ -12,8 +13,8 @@ module Reek
|
|
12
13
|
|
13
14
|
class << self
|
14
15
|
def create(outer, exp)
|
15
|
-
res =
|
16
|
-
new(
|
16
|
+
res = Source::SexpFormatter.format(exp[1])
|
17
|
+
new(outer, res, exp)
|
17
18
|
end
|
18
19
|
|
19
20
|
def from_s(src)
|
@@ -21,41 +22,14 @@ module Reek
|
|
21
22
|
sniffer = Sniffer.new(source)
|
22
23
|
CodeParser.new(sniffer).do_module_or_class(source.syntax_tree, self)
|
23
24
|
end
|
24
|
-
|
25
|
-
def resolve(exp, context)
|
26
|
-
unless Array === exp
|
27
|
-
return resolve_string(exp.to_s, context)
|
28
|
-
end
|
29
|
-
name = exp[1]
|
30
|
-
case exp[0]
|
31
|
-
when :colon2
|
32
|
-
return [resolve(name, context)[0], exp[2].to_s]
|
33
|
-
when :const
|
34
|
-
return [ModuleContext.create(context, exp), name.to_s]
|
35
|
-
when :colon3
|
36
|
-
return [StopContext.new, name.to_s]
|
37
|
-
else
|
38
|
-
return [context, name.to_s]
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def resolve_string(str, context)
|
43
|
-
return [context, str.to_s] unless str =~ /::/
|
44
|
-
resolve(RubyParser.new.parse(str), context)
|
45
|
-
end
|
46
25
|
end
|
47
26
|
|
48
27
|
def initialize(outer, name, exp)
|
49
28
|
super(outer, exp)
|
50
29
|
@name = name
|
51
|
-
@scope_connector = '::'
|
52
30
|
@parsed_methods = []
|
53
31
|
end
|
54
32
|
|
55
|
-
def parameterized_methods(min_clump_size)
|
56
|
-
@parsed_methods.select {|meth| meth.parameters.length >= min_clump_size }
|
57
|
-
end
|
58
|
-
|
59
33
|
def record_method(meth)
|
60
34
|
@parsed_methods << meth
|
61
35
|
end
|
@@ -1,51 +1,51 @@
|
|
1
1
|
module Reek
|
2
2
|
module Core
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
#
|
5
|
+
# Manages and counts the references out of a method to other objects.
|
6
|
+
#
|
7
|
+
class ObjectRefs # :nodoc:
|
8
|
+
def initialize
|
9
|
+
@refs = Hash.new(0)
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
def record_reference_to_self
|
13
|
+
record_ref(SELF_REF)
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
def record_ref(exp)
|
17
|
+
type = exp[0]
|
18
|
+
case type
|
19
|
+
when :gvar
|
20
|
+
return
|
21
|
+
when :self
|
22
|
+
record_reference_to_self
|
23
|
+
else
|
24
|
+
@refs[exp] += 1
|
25
|
+
end
|
25
26
|
end
|
26
|
-
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
def refs_to_self
|
29
|
+
@refs[SELF_REF]
|
30
|
+
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
def max_refs
|
33
|
+
@refs.values.max or 0
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
def max_keys
|
37
|
+
max = max_refs
|
38
|
+
@refs.reject {|key,val| val != max}
|
39
|
+
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
def self_is_max?
|
42
|
+
max_keys.length == 0 || @refs[SELF_REF] == max_refs
|
43
|
+
end
|
44
44
|
|
45
|
-
|
45
|
+
private
|
46
46
|
|
47
|
-
|
47
|
+
SELF_REF = Sexp.from_array([:lit, :self])
|
48
48
|
|
49
|
-
|
49
|
+
end
|
50
50
|
end
|
51
51
|
end
|
@@ -1,31 +1,20 @@
|
|
1
1
|
require File.join(File.dirname(File.expand_path(__FILE__)), 'method_context')
|
2
|
-
require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'source')
|
3
2
|
|
4
3
|
module Reek
|
5
4
|
module Core
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
#
|
7
|
+
# A context wrapper for any singleton method definition found in a syntax tree.
|
8
|
+
#
|
9
|
+
class SingletonMethodContext < MethodContext
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@receiver = Source::SexpFormatter.format(exp[1])
|
16
|
-
@scope_connector = ""
|
17
|
-
record_depends_on_self
|
18
|
-
end
|
19
|
-
|
20
|
-
def envious_receivers
|
21
|
-
[]
|
22
|
-
end
|
11
|
+
def initialize(outer, exp)
|
12
|
+
super(outer, exp)
|
13
|
+
end
|
23
14
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
"#{prefix}#{@receiver}.#{@name}"
|
15
|
+
def envious_receivers
|
16
|
+
[]
|
17
|
+
end
|
28
18
|
end
|
29
19
|
end
|
30
|
-
end
|
31
20
|
end
|
data/lib/reek/core/sniffer.rb
CHANGED
@@ -65,7 +65,8 @@ module Reek
|
|
65
65
|
]
|
66
66
|
end
|
67
67
|
|
68
|
-
def initialize(src)
|
68
|
+
def initialize(src, config_strategy = ActiveSmellsOnly.new) # SMELL: open secret -- need a Strategy
|
69
|
+
@config_strategy = config_strategy
|
69
70
|
@already_checked_for_smells = false
|
70
71
|
@typed_detectors = nil
|
71
72
|
@detectors = Hash.new
|
@@ -83,7 +84,7 @@ module Reek
|
|
83
84
|
end
|
84
85
|
|
85
86
|
def configure(klass, config)
|
86
|
-
@detectors[klass]
|
87
|
+
@config_strategy.configure(@detectors[klass], config)
|
87
88
|
end
|
88
89
|
|
89
90
|
def report_on(report)
|
data/lib/reek/examiner.rb
CHANGED
@@ -4,8 +4,28 @@ require File.join(File.dirname(File.expand_path(__FILE__)), 'source')
|
|
4
4
|
|
5
5
|
module Reek
|
6
6
|
|
7
|
+
class ActiveSmellsOnly
|
8
|
+
def configure(detectors, config)
|
9
|
+
detectors.adopt(config)
|
10
|
+
end
|
11
|
+
|
12
|
+
def smells_in(sources)
|
13
|
+
Core::MaskingCollection.new.collect_from(sources, self).all_active_items.to_a
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class ActiveAndMaskedSmells
|
18
|
+
def configure(detectors, config)
|
19
|
+
detectors.push(config)
|
20
|
+
end
|
21
|
+
|
22
|
+
def smells_in(sources)
|
23
|
+
Core::MaskingCollection.new.collect_from(sources, self).all_items
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
7
27
|
#
|
8
|
-
# Finds the code smells in Ruby source code.
|
28
|
+
# Finds the active code smells in Ruby source code.
|
9
29
|
#
|
10
30
|
class Examiner
|
11
31
|
|
@@ -27,62 +47,50 @@ module Reek
|
|
27
47
|
# and if it is an Array, it is assumed to be a list of file paths,
|
28
48
|
# each of which is opened and parsed for source code.
|
29
49
|
#
|
30
|
-
|
31
|
-
|
50
|
+
# @param [#smells_in]
|
51
|
+
# The +collector+ will be asked to examine the sources and report
|
52
|
+
# an array of SmellWarning objects. The default collector is an
|
53
|
+
# instance of ActiveSmellsOnly, which completely ignores all smells
|
54
|
+
# that have been masked by configuration options.
|
55
|
+
#
|
56
|
+
def initialize(source, collector = ActiveSmellsOnly.new)
|
57
|
+
sources = case source
|
32
58
|
when Array
|
33
59
|
@description = 'dir'
|
34
|
-
Source::SourceLocator.new(source).all_sources
|
60
|
+
Source::SourceLocator.new(source).all_sources
|
35
61
|
when Source::SourceCode
|
36
62
|
@description = source.desc
|
37
|
-
[
|
63
|
+
[source]
|
38
64
|
else
|
39
65
|
src = source.to_reek_source
|
40
66
|
@description = src.desc
|
41
|
-
[
|
67
|
+
[src]
|
42
68
|
end
|
43
|
-
@
|
44
|
-
sniffers.each {|sniffer| sniffer.report_on(@warnings)}
|
69
|
+
@smells = collector.smells_in(sources)
|
45
70
|
end
|
46
71
|
|
47
72
|
#
|
48
|
-
# Returns an Array of SmellWarning objects, one for each
|
73
|
+
# Returns an Array of SmellWarning objects, one for each active smell
|
49
74
|
# in the source.
|
50
75
|
#
|
51
76
|
# @return [Array<SmellWarning>]
|
52
77
|
#
|
53
|
-
def
|
54
|
-
@
|
55
|
-
end
|
56
|
-
|
57
|
-
#
|
58
|
-
# Returns an Array of SmellWarning objects, one for each smell
|
59
|
-
# in the source; includes active smells and masked smells.
|
60
|
-
#
|
61
|
-
# @return [Array<SmellWarning>]
|
62
|
-
#
|
63
|
-
def all_smells
|
64
|
-
@warnings.all_items
|
78
|
+
def smells
|
79
|
+
@smells
|
65
80
|
end
|
66
81
|
|
67
82
|
#
|
68
83
|
# Returns the number of non-masked smells in the source.
|
69
84
|
#
|
70
|
-
def
|
71
|
-
@
|
72
|
-
end
|
73
|
-
|
74
|
-
#
|
75
|
-
# Returns the number of masked smells in the source.
|
76
|
-
#
|
77
|
-
def num_masked_smells
|
78
|
-
@warnings.num_masked_items
|
85
|
+
def num_smells
|
86
|
+
@smells.length
|
79
87
|
end
|
80
88
|
|
81
89
|
#
|
82
90
|
# True if and only if there are non-masked code smells in the given source.
|
83
91
|
#
|
84
92
|
def smelly?
|
85
|
-
not
|
93
|
+
not @smells.empty?
|
86
94
|
end
|
87
95
|
end
|
88
96
|
end
|