kevinrutherford-reek 1.0.1 → 1.1.1
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 +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
|