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.
Files changed (50) hide show
  1. data/History.txt +42 -22
  2. data/config/defaults.reek +1 -0
  3. data/lib/reek.rb +1 -1
  4. data/lib/reek/block_context.rb +27 -5
  5. data/lib/reek/class_context.rb +5 -4
  6. data/lib/reek/code_parser.rb +26 -11
  7. data/lib/reek/method_context.rb +18 -12
  8. data/lib/reek/module_context.rb +1 -1
  9. data/lib/reek/name.rb +8 -1
  10. data/lib/reek/object_refs.rb +2 -3
  11. data/lib/reek/report.rb +11 -4
  12. data/lib/reek/sexp_formatter.rb +4 -46
  13. data/lib/reek/smells/large_class.rb +26 -7
  14. data/lib/reek/smells/long_parameter_list.rb +1 -1
  15. data/lib/reek/smells/smells.rb +4 -8
  16. data/lib/reek/source.rb +31 -4
  17. data/lib/reek/spec.rb +6 -0
  18. data/lib/reek/stop_context.rb +4 -16
  19. data/lib/reek/yield_call_context.rb +1 -3
  20. data/reek.gemspec +8 -5
  21. data/spec/reek/block_context_spec.rb +40 -0
  22. data/spec/reek/class_context_spec.rb +11 -0
  23. data/spec/reek/code_context_spec.rb +2 -1
  24. data/spec/reek/code_parser_spec.rb +0 -10
  25. data/spec/reek/config_spec.rb +2 -2
  26. data/spec/reek/method_context_spec.rb +14 -0
  27. data/spec/reek/name_spec.rb +13 -0
  28. data/spec/reek/object_refs_spec.rb +11 -9
  29. data/spec/reek/report_spec.rb +1 -1
  30. data/spec/reek/sexp_formatter_spec.rb +5 -4
  31. data/spec/reek/singleton_method_context_spec.rb +1 -1
  32. data/spec/reek/smells/duplication_spec.rb +2 -2
  33. data/spec/reek/smells/feature_envy_spec.rb +132 -36
  34. data/spec/reek/smells/large_class_spec.rb +48 -47
  35. data/spec/reek/smells/long_method_spec.rb +1 -1
  36. data/spec/reek/smells/long_parameter_list_spec.rb +4 -11
  37. data/spec/reek/smells/uncommunicative_name_spec.rb +6 -1
  38. data/spec/reek/smells/utility_function_spec.rb +6 -9
  39. data/spec/{samples → slow}/inline_spec.rb +14 -10
  40. data/spec/{samples → slow}/optparse_spec.rb +20 -12
  41. data/spec/{samples → slow}/redcloth_spec.rb +16 -8
  42. data/spec/{integration → slow}/reek_source_spec.rb +0 -0
  43. data/spec/{samples → slow/samples}/inline.rb +0 -0
  44. data/spec/{samples → slow/samples}/optparse.rb +0 -0
  45. data/spec/{samples → slow/samples}/redcloth.rb +0 -0
  46. data/spec/{integration → slow}/script_spec.rb +0 -0
  47. data/spec/{reek/source_spec.rb → slow/source_list_spec.rb} +3 -3
  48. data/spec/spec_helper.rb +2 -0
  49. data/tasks/rspec.rake +1 -1
  50. metadata +24 -12
data/History.txt CHANGED
@@ -1,44 +1,64 @@
1
- == 1.0.0 2009-04-05
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
- === Major enhancements:
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
- * Minor enhancements:
24
- * Uncommunicative Name now checks instance variables more thoroughly
25
- * Uncommunicative Name now warns about names of the form 'x2'
26
- * Added check for duplicated calls within a method
27
- * Reduced scope of Feature Envy warnings to cover only overuse of lvars
28
- * Added rdoc comments explaining what each smell is about
29
- * Chained iterators are no longer mis-reported as nested
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
- * Minor enhancements:
34
- * New smell: first naive checks for Control Couple
35
- * reek now only checks sources passed on the command line
36
- * Code snippets can be supplied on the commandline
37
- * Added headings and warnings count when smells in multiple files
38
- * Added Reek::RakeTask to run reek from rakefiles
39
- * Tweaks:
40
- * Fixed: Returns exit status 2 when smells are reported
41
- * Fixed: no longer claims an empty method is a Utility Function
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
@@ -7,6 +7,7 @@ LargeClass:
7
7
  - Module
8
8
  - String
9
9
  enabled: true
10
+ max_instance_variables: 9
10
11
  LongParameterList:
11
12
  max_params: 3
12
13
  exclude: []
data/lib/reek.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
 
3
3
  module Reek # :doc:
4
- VERSION = '1.0.1'
4
+ VERSION = '1.1.1'
5
5
  end
@@ -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 record_parameter(sym)
26
- @parameters << Name.new(sym)
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
@@ -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
 
@@ -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
- check_parse_tree(ParseTree.new.parse_tree(obj))
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[1..-1].each { |sub| process(sub) if Array === sub }
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[1..-1])
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.record_depends_on_self
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.record_depends_on_self
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
- sexp.each { |exp| process(exp) }
216
+ process(sexp)
202
217
  end
203
218
  end
204
219
  end
@@ -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
- if receiver.nil?
40
- record_depends_on_self
41
- else
42
- case receiver[0]
43
- when :lvar
44
- @refs.record_ref(receiver) unless meth == :new
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)
@@ -20,7 +20,7 @@ module Reek
20
20
  def find_module(modname)
21
21
  return nil unless myself
22
22
  sym = modname.to_s
23
- myself.const_defined?(sym) ? myself.const_get(sym) : nil
23
+ @myself.const_defined?(sym) ? @myself.const_get(sym) : nil
24
24
  end
25
25
 
26
26
  def outer_name
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
- return [context, new(exp)] unless Array === exp
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
@@ -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; but Hash has 58 methods, and there's currently
38
- # no way to turn off that report; which would therefore make the tests fail
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
- @sources.select {|src| src.smelly? }.map do |src|
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
@@ -1,52 +1,10 @@
1
+ require 'ruby2ruby'
2
+
1
3
  module Reek
2
4
  class SexpFormatter
3
5
  def self.format(sexp)
4
- return sexp.to_s unless Array === sexp
5
- first = sexp[1]
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