kevinrutherford-reek 1.0.1 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +42 -22
- data/config/defaults.reek +1 -0
- data/lib/reek.rb +1 -1
- data/lib/reek/block_context.rb +27 -5
- data/lib/reek/class_context.rb +5 -4
- data/lib/reek/code_parser.rb +26 -11
- data/lib/reek/method_context.rb +18 -12
- data/lib/reek/module_context.rb +1 -1
- data/lib/reek/name.rb +8 -1
- data/lib/reek/object_refs.rb +2 -3
- data/lib/reek/report.rb +11 -4
- data/lib/reek/sexp_formatter.rb +4 -46
- data/lib/reek/smells/large_class.rb +26 -7
- data/lib/reek/smells/long_parameter_list.rb +1 -1
- data/lib/reek/smells/smells.rb +4 -8
- data/lib/reek/source.rb +31 -4
- data/lib/reek/spec.rb +6 -0
- data/lib/reek/stop_context.rb +4 -16
- data/lib/reek/yield_call_context.rb +1 -3
- data/reek.gemspec +8 -5
- data/spec/reek/block_context_spec.rb +40 -0
- data/spec/reek/class_context_spec.rb +11 -0
- data/spec/reek/code_context_spec.rb +2 -1
- data/spec/reek/code_parser_spec.rb +0 -10
- data/spec/reek/config_spec.rb +2 -2
- data/spec/reek/method_context_spec.rb +14 -0
- data/spec/reek/name_spec.rb +13 -0
- data/spec/reek/object_refs_spec.rb +11 -9
- data/spec/reek/report_spec.rb +1 -1
- data/spec/reek/sexp_formatter_spec.rb +5 -4
- data/spec/reek/singleton_method_context_spec.rb +1 -1
- data/spec/reek/smells/duplication_spec.rb +2 -2
- data/spec/reek/smells/feature_envy_spec.rb +132 -36
- data/spec/reek/smells/large_class_spec.rb +48 -47
- data/spec/reek/smells/long_method_spec.rb +1 -1
- data/spec/reek/smells/long_parameter_list_spec.rb +4 -11
- data/spec/reek/smells/uncommunicative_name_spec.rb +6 -1
- data/spec/reek/smells/utility_function_spec.rb +6 -9
- data/spec/{samples → slow}/inline_spec.rb +14 -10
- data/spec/{samples → slow}/optparse_spec.rb +20 -12
- data/spec/{samples → slow}/redcloth_spec.rb +16 -8
- data/spec/{integration → slow}/reek_source_spec.rb +0 -0
- data/spec/{samples → slow/samples}/inline.rb +0 -0
- data/spec/{samples → slow/samples}/optparse.rb +0 -0
- data/spec/{samples → slow/samples}/redcloth.rb +0 -0
- data/spec/{integration → slow}/script_spec.rb +0 -0
- data/spec/{reek/source_spec.rb → slow/source_list_spec.rb} +3 -3
- data/spec/spec_helper.rb +2 -0
- data/tasks/rspec.rake +1 -1
- metadata +24 -12
data/History.txt
CHANGED
@@ -1,44 +1,64 @@
|
|
1
|
-
== 1.
|
1
|
+
== 1.1.1 2009-05-08
|
2
|
+
|
3
|
+
=== Minor enhancements
|
4
|
+
* LargeClass now also warns about any class with > 9 instance variables (#6)
|
5
|
+
* Now depends on ruby2ruby, to display code better
|
6
|
+
* Duplication notices more repeated method calls
|
7
|
+
* Smells within blocks are now reported better
|
8
|
+
|
9
|
+
== 1.1.0 2009-04-10
|
10
|
+
|
11
|
+
=== Minor enhancements
|
12
|
+
* Now possible to write 'MyClass.should_not reek' (#33)
|
13
|
+
|
14
|
+
=== Fixes
|
15
|
+
* Now counts attr assignments ([]= etc) in feature envy calculations
|
16
|
+
* Doesn't attempt to find *.reek files when reading code from stdin
|
17
|
+
|
18
|
+
== 1.0.1 2009-04-06
|
19
|
+
|
20
|
+
=== Fixes
|
21
|
+
* Dir[...].to_source now creates a Report that can be browsed (#36)
|
2
22
|
|
3
|
-
|
23
|
+
== 1.0.0 2009-04-05
|
4
24
|
|
25
|
+
=== Major enhancements
|
5
26
|
* Use *.reek files in source tree to configure Reek's behaviour
|
6
27
|
* Added -f option to configure report format
|
7
28
|
* --sort_order replaced by -f, -c and -s
|
8
29
|
* Matchers provided for rspec; eg. foo.should_not reek
|
9
30
|
|
10
|
-
=== Minor enhancements
|
11
|
-
|
31
|
+
=== Minor enhancements
|
12
32
|
* Smells in singleton methods are now analysed
|
13
33
|
* Uncommunicative parameter names in blocks now reported
|
14
34
|
* Modules and blocks now reflected in scope of smell reports
|
15
35
|
|
16
|
-
=== Fixes
|
17
|
-
|
36
|
+
=== Fixes
|
18
37
|
* Corrected false reports of long arg lists to yield
|
19
38
|
* A method can now be a UtilityFunction only when it includes a call
|
20
39
|
|
21
40
|
== 0.3.1 2008-11-17
|
22
41
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
42
|
+
=== Minor enhancements
|
43
|
+
* Uncommunicative Name now checks instance variables more thoroughly
|
44
|
+
* Uncommunicative Name now warns about names of the form 'x2'
|
45
|
+
* Added check for duplicated calls within a method
|
46
|
+
* Reduced scope of Feature Envy warnings to cover only overuse of lvars
|
47
|
+
* Added rdoc comments explaining what each smell is about
|
48
|
+
* Chained iterators are no longer mis-reported as nested
|
30
49
|
|
31
50
|
== 0.3.0 2008-11-02
|
32
51
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
52
|
+
=== Minor enhancements
|
53
|
+
* New smell: first naive checks for Control Couple
|
54
|
+
* reek now only checks sources passed on the command line
|
55
|
+
* Code snippets can be supplied on the commandline
|
56
|
+
* Added headings and warnings count when smells in multiple files
|
57
|
+
* Added Reek::RakeTask to run reek from rakefiles
|
58
|
+
|
59
|
+
=== Fixes
|
60
|
+
* Fixed: Returns exit status 2 when smells are reported
|
61
|
+
* Fixed: no longer claims an empty method is a Utility Function
|
42
62
|
|
43
63
|
== 0.2.3 2008-09-22
|
44
64
|
|
data/config/defaults.reek
CHANGED
data/lib/reek.rb
CHANGED
data/lib/reek/block_context.rb
CHANGED
@@ -1,13 +1,35 @@
|
|
1
|
+
require 'set'
|
1
2
|
require 'reek/code_context'
|
2
3
|
|
3
4
|
module Reek
|
5
|
+
|
6
|
+
module ParameterSet
|
7
|
+
def names
|
8
|
+
return @names if @names
|
9
|
+
return (@names = []) if empty?
|
10
|
+
arg = slice(1)
|
11
|
+
case slice(0)
|
12
|
+
when :masgn
|
13
|
+
@names = arg[1..-1].map {|lasgn| Name.new(lasgn[1]) }
|
14
|
+
when :lasgn
|
15
|
+
@names = [Name.new(arg)]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def include?(name)
|
20
|
+
names.include?(name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
4
24
|
class BlockContext < CodeContext
|
5
25
|
|
6
26
|
def initialize(outer, exp)
|
7
27
|
super
|
8
|
-
@parameters = []
|
9
|
-
@local_variables = []
|
10
28
|
@name = Name.new('block')
|
29
|
+
@parameters = exp[0] if exp
|
30
|
+
@parameters ||= []
|
31
|
+
@parameters.extend(ParameterSet)
|
32
|
+
@local_variables = Set.new
|
11
33
|
end
|
12
34
|
|
13
35
|
def inside_a_block?
|
@@ -22,8 +44,8 @@ module Reek
|
|
22
44
|
@outer.inside_a_block?
|
23
45
|
end
|
24
46
|
|
25
|
-
def
|
26
|
-
@
|
47
|
+
def record_local_variable(sym)
|
48
|
+
@local_variables << Name.new(sym)
|
27
49
|
end
|
28
50
|
|
29
51
|
def outer_name
|
@@ -31,7 +53,7 @@ module Reek
|
|
31
53
|
end
|
32
54
|
|
33
55
|
def variable_names
|
34
|
-
@parameters + @local_variables
|
56
|
+
@parameters.names + @local_variables.to_a
|
35
57
|
end
|
36
58
|
end
|
37
59
|
end
|
data/lib/reek/class_context.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'set'
|
1
2
|
require 'reek/code_context'
|
2
3
|
|
3
4
|
class Class
|
@@ -23,7 +24,7 @@ module Reek
|
|
23
24
|
@name = name
|
24
25
|
@superclass = superclass
|
25
26
|
@parsed_methods = []
|
26
|
-
@instance_variables =
|
27
|
+
@instance_variables = Set.new
|
27
28
|
end
|
28
29
|
|
29
30
|
def myself
|
@@ -33,12 +34,12 @@ module Reek
|
|
33
34
|
def find_module(modname)
|
34
35
|
sym = modname.to_s
|
35
36
|
return nil unless myself
|
36
|
-
myself.const_defined?(sym) ? myself.const_get(sym) : nil
|
37
|
+
@myself.const_defined?(sym) ? @myself.const_get(sym) : nil
|
37
38
|
end
|
38
39
|
|
39
40
|
def is_overriding_method?(name)
|
40
41
|
return false unless myself
|
41
|
-
myself.is_overriding_method?(name.to_s)
|
42
|
+
@myself.is_overriding_method?(name.to_s)
|
42
43
|
end
|
43
44
|
|
44
45
|
def is_struct?
|
@@ -46,7 +47,7 @@ module Reek
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def num_methods
|
49
|
-
meths = myself ? myself.non_inherited_methods : @parsed_methods
|
50
|
+
meths = myself ? @myself.non_inherited_methods : @parsed_methods
|
50
51
|
meths.length
|
51
52
|
end
|
52
53
|
|
data/lib/reek/code_parser.rb
CHANGED
@@ -14,8 +14,16 @@ module Reek
|
|
14
14
|
|
15
15
|
class CodeParser < SexpProcessor
|
16
16
|
|
17
|
+
def self.unify(sexp) # :nodoc:
|
18
|
+
unifier = Unifier.new
|
19
|
+
unifier.processors.each do |proc|
|
20
|
+
proc.unsupported.delete :cfunc # HACK
|
21
|
+
end
|
22
|
+
return unifier.process(sexp[0])
|
23
|
+
end
|
24
|
+
|
17
25
|
def self.parse_tree_for(code) # :nodoc:
|
18
|
-
ParseTree.new.parse_tree_for_string(code)
|
26
|
+
unify(ParseTree.new.parse_tree_for_string(code))
|
19
27
|
end
|
20
28
|
|
21
29
|
# Creates a new Ruby code checker. Any smells discovered by
|
@@ -41,11 +49,12 @@ module Reek
|
|
41
49
|
# Any smells found are saved in the +Report+ object that
|
42
50
|
# was passed to this object's constructor.
|
43
51
|
def check_object(obj)
|
44
|
-
|
52
|
+
sexp = CodeParser.unify(ParseTree.new.parse_tree(obj))
|
53
|
+
check_parse_tree(sexp)
|
45
54
|
end
|
46
55
|
|
47
56
|
def process_default(exp)
|
48
|
-
exp[
|
57
|
+
exp[0..-1].each { |sub| process(sub) if Array === sub }
|
49
58
|
s(exp)
|
50
59
|
end
|
51
60
|
|
@@ -91,7 +100,7 @@ module Reek
|
|
91
100
|
|
92
101
|
def process_iter(exp)
|
93
102
|
process(exp[1])
|
94
|
-
handle_context(BlockContext, :iter, exp[
|
103
|
+
handle_context(BlockContext, :iter, exp[2..-1])
|
95
104
|
end
|
96
105
|
|
97
106
|
def process_dasgn_curr(exp)
|
@@ -114,8 +123,7 @@ module Reek
|
|
114
123
|
end
|
115
124
|
|
116
125
|
def process_fcall(exp)
|
117
|
-
@element.
|
118
|
-
@element.refs.record_reference_to_self
|
126
|
+
@element.record_use_of_self
|
119
127
|
process_default(exp)
|
120
128
|
end
|
121
129
|
|
@@ -125,11 +133,18 @@ module Reek
|
|
125
133
|
end
|
126
134
|
|
127
135
|
def process_vcall(exp)
|
128
|
-
@element.
|
129
|
-
@element.refs.record_reference_to_self
|
136
|
+
@element.record_use_of_self
|
130
137
|
s(exp)
|
131
138
|
end
|
132
139
|
|
140
|
+
def process_attrasgn(exp)
|
141
|
+
process_call(exp)
|
142
|
+
end
|
143
|
+
|
144
|
+
def process_op_asgn1(exp)
|
145
|
+
process_call(exp)
|
146
|
+
end
|
147
|
+
|
133
148
|
def process_if(exp)
|
134
149
|
handle_context(IfContext, :if, exp)
|
135
150
|
end
|
@@ -155,8 +170,6 @@ module Reek
|
|
155
170
|
s(exp)
|
156
171
|
end
|
157
172
|
|
158
|
-
private
|
159
|
-
|
160
173
|
def self.count_statements(exp)
|
161
174
|
stmts = exp[1..-1]
|
162
175
|
ignore = 0
|
@@ -165,6 +178,8 @@ module Reek
|
|
165
178
|
stmts.length - ignore
|
166
179
|
end
|
167
180
|
|
181
|
+
private
|
182
|
+
|
168
183
|
def self.is_expr?(exp, type)
|
169
184
|
Array === exp and exp[0] == type
|
170
185
|
end
|
@@ -198,7 +213,7 @@ module Reek
|
|
198
213
|
end
|
199
214
|
|
200
215
|
def check_parse_tree(sexp) # :nodoc:
|
201
|
-
|
216
|
+
process(sexp)
|
202
217
|
end
|
203
218
|
end
|
204
219
|
end
|
data/lib/reek/method_context.rb
CHANGED
@@ -32,22 +32,28 @@ module Reek
|
|
32
32
|
def has_parameter(sym)
|
33
33
|
@parameters.include?(sym.to_s)
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def record_call_to(exp)
|
37
37
|
@calls[exp] += 1
|
38
38
|
receiver, meth = exp[1..2]
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
when :ivar
|
46
|
-
record_depends_on_self
|
47
|
-
@refs.record_reference_to_self
|
48
|
-
end
|
39
|
+
receiver ||= [:self]
|
40
|
+
case receiver[0]
|
41
|
+
when :lvar
|
42
|
+
@refs.record_ref(receiver) unless meth == :new
|
43
|
+
when :self
|
44
|
+
record_use_of_self
|
49
45
|
end
|
50
46
|
end
|
47
|
+
|
48
|
+
def record_use_of_self
|
49
|
+
record_depends_on_self
|
50
|
+
@refs.record_reference_to_self
|
51
|
+
end
|
52
|
+
|
53
|
+
def record_instance_variable(sym)
|
54
|
+
record_use_of_self
|
55
|
+
@outer.record_instance_variable(sym)
|
56
|
+
end
|
51
57
|
|
52
58
|
def record_depends_on_self
|
53
59
|
@depends_on_self = true
|
@@ -58,7 +64,7 @@ module Reek
|
|
58
64
|
end
|
59
65
|
|
60
66
|
def self.is_block_arg?(param)
|
61
|
-
Array === param and param[0] == :block
|
67
|
+
(Array === param and param[0] == :block) or (param.to_s =~ /^\&/)
|
62
68
|
end
|
63
69
|
|
64
70
|
def record_parameter(param)
|
data/lib/reek/module_context.rb
CHANGED
data/lib/reek/name.rb
CHANGED
@@ -3,7 +3,9 @@ module Reek
|
|
3
3
|
include Comparable
|
4
4
|
|
5
5
|
def self.resolve(exp, context)
|
6
|
-
|
6
|
+
unless Array === exp
|
7
|
+
return resolve_string(exp.to_s, context)
|
8
|
+
end
|
7
9
|
name = exp[1]
|
8
10
|
case exp[0]
|
9
11
|
when :colon2
|
@@ -17,6 +19,11 @@ module Reek
|
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
22
|
+
def self.resolve_string(str, context)
|
23
|
+
return [context, new(str)] unless str =~ /::/
|
24
|
+
resolve(ParseTree.new.parse_tree_for_string(str)[0], context)
|
25
|
+
end
|
26
|
+
|
20
27
|
def initialize(sym)
|
21
28
|
@name = sym.to_s
|
22
29
|
end
|
data/lib/reek/object_refs.rb
CHANGED
@@ -6,7 +6,6 @@ module Reek
|
|
6
6
|
class ObjectRefs # :nodoc:
|
7
7
|
def initialize
|
8
8
|
@refs = Hash.new(0)
|
9
|
-
record_reference_to_self
|
10
9
|
end
|
11
10
|
|
12
11
|
def record_reference_to_self
|
@@ -34,8 +33,8 @@ module Reek
|
|
34
33
|
end
|
35
34
|
|
36
35
|
# TODO
|
37
|
-
# Should be moved to Hash
|
38
|
-
#
|
36
|
+
# Should be moved to Hash
|
37
|
+
#
|
39
38
|
def max_keys
|
40
39
|
max = max_refs
|
41
40
|
@refs.reject {|key,val| val != max}.keys
|
data/lib/reek/report.rb
CHANGED
@@ -35,6 +35,12 @@ module Reek
|
|
35
35
|
@report.to_a[index]
|
36
36
|
end
|
37
37
|
|
38
|
+
# Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
|
39
|
+
# this report, with a heading.
|
40
|
+
def full_report(desc)
|
41
|
+
"\"#{desc}\" -- #{length} warnings:\n#{to_s}\n"
|
42
|
+
end
|
43
|
+
|
38
44
|
# Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
|
39
45
|
# this report.
|
40
46
|
def to_s
|
@@ -64,11 +70,12 @@ module Reek
|
|
64
70
|
@sources.inject(0) {|sum, src| sum + src.report.length }
|
65
71
|
end
|
66
72
|
|
73
|
+
def smelly_sources
|
74
|
+
@sources.select {|src| src.smelly? }
|
75
|
+
end
|
76
|
+
|
67
77
|
def to_s
|
68
|
-
|
69
|
-
warnings = src.report
|
70
|
-
"\"#{src}\" -- #{warnings.length} warnings:\n#{warnings.to_s}\n"
|
71
|
-
end.join("\n")
|
78
|
+
smelly_sources.map { |src| src.full_report }.join("\n")
|
72
79
|
end
|
73
80
|
end
|
74
81
|
end
|
data/lib/reek/sexp_formatter.rb
CHANGED
@@ -1,52 +1,10 @@
|
|
1
|
+
require 'ruby2ruby'
|
2
|
+
|
1
3
|
module Reek
|
2
4
|
class SexpFormatter
|
3
5
|
def self.format(sexp)
|
4
|
-
|
5
|
-
|
6
|
-
case sexp[0]
|
7
|
-
when :array
|
8
|
-
format_all(sexp, ', ')
|
9
|
-
when :call
|
10
|
-
meth, args = sexp[2..3]
|
11
|
-
result = format(first)
|
12
|
-
if meth.to_s == '[]'
|
13
|
-
result += (args.nil? ? '[]' : "[#{format(args)}]")
|
14
|
-
else
|
15
|
-
result += ".#{meth}" + (args ? "(#{format(args)})" : '')
|
16
|
-
end
|
17
|
-
result
|
18
|
-
when :colon2
|
19
|
-
format_all(sexp, '::')
|
20
|
-
when :const, :cvar, :dvar
|
21
|
-
format(first)
|
22
|
-
when :dot2
|
23
|
-
format_all(sexp, '..')
|
24
|
-
when :dstr
|
25
|
-
'"' + format_all(sexp, '') + '"'
|
26
|
-
when :evstr
|
27
|
-
"\#\{#{format(first)}\}"
|
28
|
-
when :fcall, :vcall
|
29
|
-
args = sexp[2]
|
30
|
-
result = first.to_s
|
31
|
-
result += "(#{format(args)})" if args
|
32
|
-
result
|
33
|
-
when :iter
|
34
|
-
'block'
|
35
|
-
when :lasgn
|
36
|
-
format_all(sexp, '=')
|
37
|
-
when :nth_ref
|
38
|
-
"$#{first}"
|
39
|
-
when :str
|
40
|
-
first
|
41
|
-
when :xstr
|
42
|
-
"`#{first}`"
|
43
|
-
else
|
44
|
-
sexp[-1].to_s
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.format_all(sexp, glue)
|
49
|
-
sexp[1..-1].map {|arg| format(arg)}.join(glue)
|
6
|
+
sexp = YAML::load(YAML::dump(sexp))
|
7
|
+
Ruby2Ruby.new.process(sexp)
|
50
8
|
end
|
51
9
|
end
|
52
10
|
end
|