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