reek 1.2.2 → 1.2.3
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 +4 -0
- data/Rakefile +0 -1
- data/config/defaults.reek +4 -6
- data/features/masking_smells.feature +9 -9
- data/features/options.feature +2 -2
- data/features/profile.feature +34 -0
- data/features/rake_task.feature +74 -0
- data/features/reports.feature +1 -1
- data/features/samples.feature +4 -4
- data/features/stdin.feature +1 -1
- data/features/step_definitions/reek_steps.rb +11 -7
- data/features/support/env.rb +26 -18
- data/lib/reek.rb +1 -1
- data/lib/reek/adapters/application.rb +9 -2
- data/lib/reek/adapters/command_line.rb +2 -2
- data/lib/reek/adapters/source.rb +4 -1
- data/lib/reek/adapters/spec.rb +1 -3
- data/lib/reek/block_context.rb +14 -8
- data/lib/reek/class_context.rb +6 -66
- data/lib/reek/code_context.rb +10 -0
- data/lib/reek/code_parser.rb +25 -53
- data/lib/reek/configuration.rb +12 -6
- data/lib/reek/if_context.rb +2 -3
- data/lib/reek/method_context.rb +3 -12
- data/lib/reek/module_context.rb +30 -22
- data/lib/reek/name.rb +2 -0
- data/lib/reek/object_refs.rb +0 -3
- data/lib/reek/sexp_formatter.rb +0 -2
- data/lib/reek/smells/class_variable.rb +17 -4
- data/lib/reek/smells/control_couple.rb +3 -10
- data/lib/reek/smells/data_clump.rb +10 -10
- data/lib/reek/smells/feature_envy.rb +1 -8
- data/lib/reek/smells/large_class.rb +3 -3
- data/lib/reek/smells/simulated_polymorphism.rb +17 -3
- data/lib/reek/smells/smell_detector.rb +11 -2
- data/lib/reek/smells/utility_function.rb +1 -1
- data/lib/reek/sniffer.rb +0 -8
- data/lib/reek/stop_context.rb +1 -1
- data/lib/reek/tree_dresser.rb +74 -0
- data/reek.gemspec +3 -3
- data/spec/reek/block_context_spec.rb +6 -6
- data/spec/reek/class_context_spec.rb +2 -23
- data/spec/reek/code_context_spec.rb +149 -67
- data/spec/reek/code_parser_spec.rb +0 -102
- data/spec/reek/method_context_spec.rb +4 -4
- data/spec/reek/singleton_method_context_spec.rb +1 -1
- data/spec/reek/smells/attribute_spec.rb +1 -1
- data/spec/reek/smells/class_variable_spec.rb +96 -7
- data/spec/reek/smells/control_couple_spec.rb +1 -1
- data/spec/reek/smells/data_clump_spec.rb +31 -13
- data/spec/reek/smells/feature_envy_spec.rb +1 -1
- data/spec/reek/smells/large_class_spec.rb +32 -18
- data/spec/reek/smells/simulated_polymorphism_spec.rb +66 -18
- data/spec/reek/sniffer_spec.rb +1 -0
- data/spec/samples/not_quite_masked/dirty.rb +2 -0
- data/spec/spec_helper.rb +1 -1
- data/tasks/reek.rake +1 -1
- metadata +5 -3
- data/lib/reek/exceptions.reek +0 -20
data/lib/reek/adapters/source.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'ruby_parser'
|
1
2
|
require 'reek/adapters/config_file'
|
3
|
+
require 'reek/tree_dresser'
|
2
4
|
|
3
5
|
module Reek
|
4
6
|
|
@@ -17,7 +19,8 @@ module Reek
|
|
17
19
|
def configure(sniffer) end
|
18
20
|
|
19
21
|
def syntax_tree
|
20
|
-
RubyParser.new.parse(@source, @desc) || s()
|
22
|
+
ast = RubyParser.new.parse(@source, @desc) || s()
|
23
|
+
TreeDresser.new.dress(ast)
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
data/lib/reek/adapters/spec.rb
CHANGED
@@ -88,11 +88,9 @@ module Reek
|
|
88
88
|
end
|
89
89
|
|
90
90
|
class ShouldReekOnlyOf < ShouldReekOf # :nodoc:
|
91
|
-
include ReekMatcher
|
92
|
-
|
93
91
|
def matches?(actual)
|
94
92
|
@sniffer = actual.sniff
|
95
|
-
@sniffer.
|
93
|
+
@sniffer.num_smells == 1 and @sniffer.has_smell?(@klass, @patterns)
|
96
94
|
end
|
97
95
|
def failure_message_for_should
|
98
96
|
"Expected #{@sniffer.desc} to reek only of #{@klass}, but got:\n#{report}"
|
data/lib/reek/block_context.rb
CHANGED
@@ -21,15 +21,25 @@ module Reek
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
class
|
24
|
+
class VariableContainer < CodeContext
|
25
|
+
|
26
|
+
def initialize(outer, exp)
|
27
|
+
super
|
28
|
+
@local_variables = Set.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def record_local_variable(sym)
|
32
|
+
@local_variables << Name.new(sym)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class BlockContext < VariableContainer
|
25
37
|
|
26
38
|
def initialize(outer, exp)
|
27
39
|
super
|
28
40
|
@name = Name.new('block')
|
29
|
-
@parameters = exp[
|
30
|
-
@parameters ||= []
|
41
|
+
@parameters = exp[2] || []
|
31
42
|
@parameters.extend(ParameterSet)
|
32
|
-
@local_variables = Set.new
|
33
43
|
end
|
34
44
|
|
35
45
|
def inside_a_block?
|
@@ -43,10 +53,6 @@ module Reek
|
|
43
53
|
def nested_block?
|
44
54
|
@outer.inside_a_block?
|
45
55
|
end
|
46
|
-
|
47
|
-
def record_local_variable(sym)
|
48
|
-
@local_variables << Name.new(sym)
|
49
|
-
end
|
50
56
|
|
51
57
|
def outer_name
|
52
58
|
"#{@outer.outer_name}#{@name}/"
|
data/lib/reek/class_context.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'set'
|
2
|
-
require 'reek/
|
2
|
+
require 'reek/module_context'
|
3
3
|
|
4
4
|
class Class
|
5
5
|
def is_overriding_method?(name)
|
@@ -11,40 +11,14 @@ class Class
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module Reek
|
14
|
-
class ClassContext <
|
14
|
+
class ClassContext < ModuleContext
|
15
15
|
|
16
|
-
|
17
|
-
res = Name.resolve(exp[1], outer)
|
18
|
-
ClassContext.new(res[0], res[1], exp[2])
|
19
|
-
end
|
20
|
-
|
21
|
-
def ClassContext.from_s(src)
|
22
|
-
source = src.to_reek_source
|
23
|
-
sniffer = Sniffer.new(source)
|
24
|
-
CodeParser.new(sniffer).process_class(source.syntax_tree)
|
25
|
-
end
|
16
|
+
attr_reader :parsed_methods
|
26
17
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def initialize(outer, name, superclass = nil)
|
31
|
-
super(outer, nil)
|
32
|
-
@name = name
|
33
|
-
@superclass = superclass
|
34
|
-
@parsed_methods = []
|
18
|
+
def initialize(outer, name, exp)
|
19
|
+
super
|
20
|
+
@superclass = exp[2]
|
35
21
|
@instance_variables = Set.new
|
36
|
-
@conditionals = []
|
37
|
-
@attributes = Set.new
|
38
|
-
@class_variables = Set.new
|
39
|
-
end
|
40
|
-
|
41
|
-
def myself
|
42
|
-
@myself ||= @outer.find_module(@name)
|
43
|
-
end
|
44
|
-
|
45
|
-
def find_module(modname)
|
46
|
-
return nil unless myself
|
47
|
-
@myself.const_or_nil(modname.to_s)
|
48
22
|
end
|
49
23
|
|
50
24
|
def is_overriding_method?(name)
|
@@ -56,50 +30,16 @@ module Reek
|
|
56
30
|
@superclass == [:const, :Struct]
|
57
31
|
end
|
58
32
|
|
59
|
-
def num_methods
|
60
|
-
@parsed_methods.length
|
61
|
-
end
|
62
|
-
|
63
|
-
def check_for_attribute_declaration(exp)
|
64
|
-
if [:attr, :attr_reader, :attr_writer, :attr_accessor].include? exp[2]
|
65
|
-
exp[3][1..-1].each {|arg| record_attribute(arg[1])}
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def record_attribute(attr)
|
70
|
-
@attributes << Name.new(attr)
|
71
|
-
end
|
72
|
-
|
73
|
-
def record_class_variable(cvar)
|
74
|
-
@class_variables << Name.new(cvar)
|
75
|
-
end
|
76
|
-
|
77
33
|
def record_instance_variable(sym)
|
78
34
|
@instance_variables << Name.new(sym)
|
79
35
|
end
|
80
36
|
|
81
|
-
def record_method(meth)
|
82
|
-
@parsed_methods << meth
|
83
|
-
end
|
84
|
-
|
85
37
|
def outer_name
|
86
38
|
"#{@outer.outer_name}#{@name}#"
|
87
39
|
end
|
88
40
|
|
89
|
-
def to_s
|
90
|
-
"#{@outer.outer_name}#{@name}"
|
91
|
-
end
|
92
|
-
|
93
41
|
def variable_names
|
94
42
|
@instance_variables
|
95
43
|
end
|
96
|
-
|
97
|
-
def record_conditional(exp)
|
98
|
-
@conditionals << exp
|
99
|
-
end
|
100
|
-
|
101
|
-
def parameterized_methods(min_clump_size)
|
102
|
-
parsed_methods.select {|meth| meth.parameters.length >= min_clump_size }
|
103
|
-
end
|
104
44
|
end
|
105
45
|
end
|
data/lib/reek/code_context.rb
CHANGED
@@ -23,6 +23,16 @@ module Reek
|
|
23
23
|
@myself = nil
|
24
24
|
end
|
25
25
|
|
26
|
+
def each(type, ignoring, &blk)
|
27
|
+
if block_given?
|
28
|
+
@exp.look_for(type, ignoring, &blk)
|
29
|
+
else
|
30
|
+
result = []
|
31
|
+
@exp.look_for(type, ignoring) {|exp| result << exp}
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
26
36
|
# SMELL: Temporary Field -- @name isn't always initialized
|
27
37
|
def matches?(strings)
|
28
38
|
me = @name.to_s
|
data/lib/reek/code_parser.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'sexp'
|
3
2
|
require 'reek/block_context'
|
4
3
|
require 'reek/class_context'
|
@@ -9,31 +8,12 @@ require 'reek/method_context'
|
|
9
8
|
require 'reek/singleton_method_context'
|
10
9
|
require 'reek/yield_call_context'
|
11
10
|
|
12
|
-
#
|
13
|
-
# Extensions to +Sexp+ to allow +CodeParser+ to navigate the abstract
|
14
|
-
# syntax tree more easily.
|
15
|
-
#
|
16
|
-
class Sexp
|
17
|
-
def children
|
18
|
-
find_all { |item| Sexp === item }
|
19
|
-
end
|
20
|
-
|
21
|
-
def is_language_node?
|
22
|
-
first.class == Symbol
|
23
|
-
end
|
24
|
-
|
25
|
-
def has_type?(type)
|
26
|
-
is_language_node? and first == type
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
11
|
module Reek
|
31
|
-
|
12
|
+
#
|
13
|
+
# Traverses a Sexp abstract syntax tree and fires events whenever
|
14
|
+
# it encounters specific node types.
|
15
|
+
#
|
32
16
|
class CodeParser
|
33
|
-
|
34
|
-
#
|
35
|
-
# Creates a new Ruby code checker.
|
36
|
-
#
|
37
17
|
def initialize(sniffer, ctx = StopContext.new)
|
38
18
|
@sniffer = sniffer
|
39
19
|
@element = ctx
|
@@ -50,42 +30,29 @@ module Reek
|
|
50
30
|
exp[0..-1].each { |sub| process(sub) if Array === sub }
|
51
31
|
end
|
52
32
|
|
53
|
-
def
|
54
|
-
scope =
|
55
|
-
push(scope) do
|
56
|
-
process_default(exp)
|
57
|
-
check_smells(:module)
|
58
|
-
end
|
59
|
-
scope
|
60
|
-
end
|
61
|
-
|
62
|
-
def process_class(exp)
|
63
|
-
scope = ClassContext.create(@element, exp)
|
33
|
+
def do_module_or_class(exp, context_class)
|
34
|
+
scope = context_class.create(@element, exp)
|
64
35
|
push(scope) do
|
65
36
|
process_default(exp) unless @element.is_struct?
|
66
|
-
check_smells(
|
37
|
+
check_smells(exp[0])
|
67
38
|
end
|
68
39
|
scope
|
69
40
|
end
|
70
41
|
|
71
|
-
def
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
def process_cvasgn(exp)
|
76
|
-
process_cvar(exp)
|
42
|
+
def process_module(exp)
|
43
|
+
do_module_or_class(exp, ModuleContext)
|
77
44
|
end
|
78
45
|
|
79
|
-
def
|
80
|
-
|
46
|
+
def process_class(exp)
|
47
|
+
do_module_or_class(exp, ClassContext)
|
81
48
|
end
|
82
49
|
|
83
50
|
def process_defn(exp)
|
84
|
-
handle_context(MethodContext,
|
51
|
+
handle_context(MethodContext, exp[0], exp)
|
85
52
|
end
|
86
53
|
|
87
54
|
def process_defs(exp)
|
88
|
-
handle_context(SingletonMethodContext,
|
55
|
+
handle_context(SingletonMethodContext, exp[0], exp)
|
89
56
|
end
|
90
57
|
|
91
58
|
def process_args(exp) end
|
@@ -105,7 +72,12 @@ module Reek
|
|
105
72
|
|
106
73
|
def process_iter(exp)
|
107
74
|
process(exp[1])
|
108
|
-
|
75
|
+
scope = BlockContext.new(@element, exp)
|
76
|
+
push(scope) do
|
77
|
+
process_default(exp[2..-1])
|
78
|
+
check_smells(exp[0])
|
79
|
+
end
|
80
|
+
scope
|
109
81
|
end
|
110
82
|
|
111
83
|
def process_block(exp)
|
@@ -114,7 +86,7 @@ module Reek
|
|
114
86
|
end
|
115
87
|
|
116
88
|
def process_yield(exp)
|
117
|
-
handle_context(YieldCallContext,
|
89
|
+
handle_context(YieldCallContext, exp[0], exp)
|
118
90
|
end
|
119
91
|
|
120
92
|
def process_call(exp)
|
@@ -136,10 +108,9 @@ module Reek
|
|
136
108
|
end
|
137
109
|
|
138
110
|
def process_if(exp)
|
139
|
-
@element.record_conditional(exp[1])
|
140
111
|
count_clause(exp[2])
|
141
112
|
count_clause(exp[3])
|
142
|
-
handle_context(IfContext,
|
113
|
+
handle_context(IfContext, exp[0], exp)
|
143
114
|
@element.count_statements(-1)
|
144
115
|
end
|
145
116
|
|
@@ -155,12 +126,14 @@ module Reek
|
|
155
126
|
|
156
127
|
def process_for(exp)
|
157
128
|
count_clause(exp[3])
|
158
|
-
|
129
|
+
process_default(exp)
|
130
|
+
@element.count_statements(-1)
|
159
131
|
end
|
160
132
|
|
161
133
|
def process_rescue(exp)
|
162
134
|
count_clause(exp[1])
|
163
|
-
|
135
|
+
process_default(exp)
|
136
|
+
@element.count_statements(-1)
|
164
137
|
end
|
165
138
|
|
166
139
|
def process_resbody(exp)
|
@@ -168,7 +141,6 @@ module Reek
|
|
168
141
|
end
|
169
142
|
|
170
143
|
def process_case(exp)
|
171
|
-
@element.record_conditional(exp[1])
|
172
144
|
process_default(exp)
|
173
145
|
@element.count_statements(-1)
|
174
146
|
end
|
data/lib/reek/configuration.rb
CHANGED
@@ -13,19 +13,25 @@ module Reek
|
|
13
13
|
# for other values in the current smell detector's configuration.
|
14
14
|
OVERRIDES_KEY = 'overrides'
|
15
15
|
|
16
|
-
attr_reader :hash
|
17
|
-
|
18
16
|
def initialize(hash)
|
19
|
-
@
|
17
|
+
@options = hash
|
18
|
+
end
|
19
|
+
|
20
|
+
def adopt!(options)
|
21
|
+
@options.adopt!(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def deep_copy
|
25
|
+
@options.deep_copy # SMELL: Open Secret -- returns a Hash
|
20
26
|
end
|
21
27
|
|
22
28
|
# SMELL: Getter
|
23
29
|
def enabled?
|
24
|
-
@
|
30
|
+
@options[ENABLED_KEY]
|
25
31
|
end
|
26
32
|
|
27
33
|
def overrides_for(context)
|
28
|
-
Overrides.new(@
|
34
|
+
Overrides.new(@options.fetch(OVERRIDES_KEY, {})).for_context(context)
|
29
35
|
end
|
30
36
|
|
31
37
|
# Retrieves the value, if any, for the given +key+.
|
@@ -34,7 +40,7 @@ module Reek
|
|
34
40
|
#
|
35
41
|
def value(key, context, fall_back)
|
36
42
|
overrides_for(context).each { |conf| return conf[key] if conf.has_key?(key) }
|
37
|
-
return @
|
43
|
+
return @options.fetch(key, fall_back)
|
38
44
|
end
|
39
45
|
end
|
40
46
|
|
data/lib/reek/if_context.rb
CHANGED
@@ -5,8 +5,7 @@ module Reek
|
|
5
5
|
attr_reader :if_expr
|
6
6
|
|
7
7
|
def initialize(outer, exp)
|
8
|
-
|
9
|
-
@exp = exp
|
8
|
+
super
|
10
9
|
@if_expr = exp[1]
|
11
10
|
end
|
12
11
|
|
@@ -18,7 +17,7 @@ module Reek
|
|
18
17
|
@outer.outer_name
|
19
18
|
end
|
20
19
|
|
21
|
-
def to_s
|
20
|
+
def to_s # SMELL: should be unnecessary :(
|
22
21
|
@outer.to_s
|
23
22
|
end
|
24
23
|
end
|
data/lib/reek/method_context.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'reek/name'
|
2
|
-
require 'reek/
|
2
|
+
require 'reek/block_context'
|
3
3
|
require 'reek/object_refs'
|
4
4
|
|
5
5
|
class Array
|
@@ -51,7 +51,7 @@ module Reek
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
class MethodContext <
|
54
|
+
class MethodContext < VariableContainer
|
55
55
|
attr_reader :parameters
|
56
56
|
attr_reader :calls
|
57
57
|
attr_reader :refs
|
@@ -62,7 +62,6 @@ module Reek
|
|
62
62
|
@parameters = exp[exp[0] == :defn ? 2 : 3] # SMELL: SimulatedPolymorphism
|
63
63
|
@parameters ||= []
|
64
64
|
@parameters.extend(MethodParameters)
|
65
|
-
@local_variables = []
|
66
65
|
@name = Name.new(exp[1])
|
67
66
|
@num_statements = 0
|
68
67
|
@calls = Hash.new(0)
|
@@ -114,25 +113,17 @@ module Reek
|
|
114
113
|
@depends_on_self = true
|
115
114
|
end
|
116
115
|
|
117
|
-
def record_local_variable(sym)
|
118
|
-
@local_variables << Name.new(sym)
|
119
|
-
end
|
120
|
-
|
121
116
|
def outer_name
|
122
117
|
"#{@outer.outer_name}#{@name}/"
|
123
118
|
end
|
124
119
|
|
125
|
-
def to_s
|
126
|
-
"#{@outer.outer_name}#{@name}"
|
127
|
-
end
|
128
|
-
|
129
120
|
def envious_receivers
|
130
121
|
return [] if @refs.self_is_max?
|
131
122
|
@refs.max_keys
|
132
123
|
end
|
133
124
|
|
134
125
|
def variable_names
|
135
|
-
@parameters.names + @local_variables
|
126
|
+
@parameters.names + @local_variables.to_a
|
136
127
|
end
|
137
128
|
end
|
138
129
|
end
|