kevinrutherford-reek 0.3.1.4
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 +92 -0
- data/README.txt +6 -0
- data/Rakefile +7 -0
- data/bin/reek +19 -0
- data/lib/reek/block_context.rb +37 -0
- data/lib/reek/class_context.rb +73 -0
- data/lib/reek/code_context.rb +47 -0
- data/lib/reek/code_parser.rb +204 -0
- data/lib/reek/exceptions.reek +13 -0
- data/lib/reek/if_context.rb +25 -0
- data/lib/reek/method_context.rb +85 -0
- data/lib/reek/module_context.rb +34 -0
- data/lib/reek/name.rb +42 -0
- data/lib/reek/object_refs.rb +53 -0
- data/lib/reek/options.rb +92 -0
- data/lib/reek/rake_task.rb +121 -0
- data/lib/reek/report.rb +42 -0
- data/lib/reek/sexp_formatter.rb +52 -0
- data/lib/reek/singleton_method_context.rb +27 -0
- data/lib/reek/smell_warning.rb +49 -0
- data/lib/reek/smells/control_couple.rb +61 -0
- data/lib/reek/smells/duplication.rb +50 -0
- data/lib/reek/smells/feature_envy.rb +58 -0
- data/lib/reek/smells/large_class.rb +50 -0
- data/lib/reek/smells/long_method.rb +43 -0
- data/lib/reek/smells/long_parameter_list.rb +43 -0
- data/lib/reek/smells/long_yield_list.rb +18 -0
- data/lib/reek/smells/nested_iterators.rb +28 -0
- data/lib/reek/smells/smell_detector.rb +66 -0
- data/lib/reek/smells/smells.rb +85 -0
- data/lib/reek/smells/uncommunicative_name.rb +80 -0
- data/lib/reek/smells/utility_function.rb +34 -0
- data/lib/reek/source.rb +116 -0
- data/lib/reek/spec.rb +130 -0
- data/lib/reek/stop_context.rb +62 -0
- data/lib/reek/yield_call_context.rb +14 -0
- data/lib/reek.rb +8 -0
- data/spec/integration/reek_source_spec.rb +20 -0
- data/spec/integration/script_spec.rb +55 -0
- data/spec/reek/class_context_spec.rb +198 -0
- data/spec/reek/code_context_spec.rb +92 -0
- data/spec/reek/code_parser_spec.rb +44 -0
- data/spec/reek/config_spec.rb +42 -0
- data/spec/reek/module_context_spec.rb +38 -0
- data/spec/reek/object_refs_spec.rb +129 -0
- data/spec/reek/options_spec.rb +13 -0
- data/spec/reek/report_spec.rb +48 -0
- data/spec/reek/sexp_formatter_spec.rb +31 -0
- data/spec/reek/singleton_method_context_spec.rb +17 -0
- data/spec/reek/smells/control_couple_spec.rb +23 -0
- data/spec/reek/smells/duplication_spec.rb +81 -0
- data/spec/reek/smells/feature_envy_spec.rb +129 -0
- data/spec/reek/smells/large_class_spec.rb +86 -0
- data/spec/reek/smells/long_method_spec.rb +59 -0
- data/spec/reek/smells/long_parameter_list_spec.rb +92 -0
- data/spec/reek/smells/nested_iterators_spec.rb +33 -0
- data/spec/reek/smells/smell_spec.rb +24 -0
- data/spec/reek/smells/uncommunicative_name_spec.rb +118 -0
- data/spec/reek/smells/utility_function_spec.rb +96 -0
- data/spec/samples/inline.rb +704 -0
- data/spec/samples/inline_spec.rb +40 -0
- data/spec/samples/optparse.rb +1788 -0
- data/spec/samples/optparse_spec.rb +100 -0
- data/spec/samples/redcloth.rb +1130 -0
- data/spec/samples/redcloth_spec.rb +93 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +15 -0
- data/tasks/reek.rake +20 -0
- data/tasks/rspec.rake +22 -0
- metadata +167 -0
data/History.txt
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
== 0.3.x 2009-??-??
|
2
|
+
|
3
|
+
=== Major enhancements:
|
4
|
+
|
5
|
+
* Place *.reek files in source folder to configure smell detection behaviour
|
6
|
+
* Added -f option to configure report format
|
7
|
+
* --sort_order replaced by -f, -c and -s
|
8
|
+
* Matchers provided for rspec; eg. foo.should_not reek
|
9
|
+
|
10
|
+
=== Minor enhancements:
|
11
|
+
|
12
|
+
* Smells in singleton methods are now analysed
|
13
|
+
* Uncommunicative parameter names in blocks now reported
|
14
|
+
* Modules and blocks now reflected in scope of smell reports
|
15
|
+
|
16
|
+
=== Fixes:
|
17
|
+
|
18
|
+
* Corrected false reports of long arg lists to yield
|
19
|
+
* A method can now be a UtilityFunction only when it includes a call
|
20
|
+
* Removed the website from the gem [#12]
|
21
|
+
|
22
|
+
== 0.3.1 2008-11-17
|
23
|
+
|
24
|
+
* Minor enhancements:
|
25
|
+
* Uncommunicative Name now checks instance variables more thoroughly
|
26
|
+
* Uncommunicative Name now warns about names of the form 'x2'
|
27
|
+
* Added check for duplicated calls within a method
|
28
|
+
* Reduced scope of Feature Envy warnings to cover only overuse of lvars
|
29
|
+
* Added rdoc comments explaining what each smell is about
|
30
|
+
* Chained iterators are no longer mis-reported as nested
|
31
|
+
|
32
|
+
== 0.3.0 2008-11-02
|
33
|
+
|
34
|
+
* Minor enhancements:
|
35
|
+
* New smell: first naive checks for Control Couple
|
36
|
+
* reek now only checks sources passed on the command line
|
37
|
+
* Code snippets can be supplied on the commandline
|
38
|
+
* Added headings and warnings count when smells in multiple files
|
39
|
+
* Added Reek::RakeTask to run reek from rakefiles
|
40
|
+
* Tweaks:
|
41
|
+
* Fixed: Returns exit status 2 when smells are reported
|
42
|
+
* Fixed: no longer claims an empty method is a Utility Function
|
43
|
+
|
44
|
+
== 0.2.3 2008-09-22
|
45
|
+
|
46
|
+
* Minor enhancements:
|
47
|
+
* Only reports Feature Envy when the method isn't a Utility Function
|
48
|
+
* General improvements to assessing Feature Envy
|
49
|
+
* Tweaks:
|
50
|
+
* Fixed: coping with parameterless yield call
|
51
|
+
* Fixed: copes with :self as an expression
|
52
|
+
* Fixed: displaying the receiver of many more kinds of Feature Envy
|
53
|
+
* Fixed: Large Class calculation for Object
|
54
|
+
|
55
|
+
== 0.2.2 2008-09-15
|
56
|
+
|
57
|
+
* Tweaks:
|
58
|
+
* Fixed --version!
|
59
|
+
|
60
|
+
== 0.2.1 2008-09-14
|
61
|
+
|
62
|
+
* Tweaks:
|
63
|
+
* Now works from the source code, instead of requiring each named file
|
64
|
+
* Added integration tests that run reek on a couple of gems
|
65
|
+
|
66
|
+
== 0.2.0 2008-09-10
|
67
|
+
|
68
|
+
* Minor enhancements:
|
69
|
+
* Added --help, --version options
|
70
|
+
* Added --sort option to sort the report by smell or by code location
|
71
|
+
|
72
|
+
== 0.1.1 2008-09-09
|
73
|
+
|
74
|
+
* Some tweaks:
|
75
|
+
* Fixed report printing for Feature Envy when the receiver is a block
|
76
|
+
* Fixed: successive iterators reported as nested
|
77
|
+
* Fixed: Long Method now reports the total length of the method
|
78
|
+
* Fixed: each smell reported only once
|
79
|
+
|
80
|
+
== 0.1.0 2008-09-09
|
81
|
+
|
82
|
+
* 1 minor enhancement:
|
83
|
+
* Added a check for nested iterators within a method
|
84
|
+
* Some tweaks:
|
85
|
+
* Begun adding some rdoc
|
86
|
+
* Split some of the specs into more meaningful chunks
|
87
|
+
* Updated the rakefile so that rcov is no longer the default
|
88
|
+
|
89
|
+
== 0.0.1 2008-09-08
|
90
|
+
|
91
|
+
* 1 major enhancement:
|
92
|
+
* Initial release
|
data/README.txt
ADDED
data/Rakefile
ADDED
data/bin/reek
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Reek examines Ruby source code for smells.
|
4
|
+
# Visit http://reek.rubyforge.org/ for docs etc.
|
5
|
+
#
|
6
|
+
# Author: Kevin Rutherford
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'reek'
|
10
|
+
require 'reek/source'
|
11
|
+
require 'reek/options'
|
12
|
+
|
13
|
+
exitstatus = 0
|
14
|
+
source = Reek::Options.parse(ARGV)
|
15
|
+
if source.smelly?
|
16
|
+
puts source.report
|
17
|
+
exitstatus = 2
|
18
|
+
end
|
19
|
+
exit(exitstatus)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'reek/code_context'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
class BlockContext < CodeContext
|
5
|
+
|
6
|
+
def initialize(outer, exp)
|
7
|
+
super
|
8
|
+
@parameters = []
|
9
|
+
@local_variables = []
|
10
|
+
@name = Name.new('block')
|
11
|
+
end
|
12
|
+
|
13
|
+
def inside_a_block?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_parameter(name)
|
18
|
+
@parameters.include?(name) or @outer.has_parameter(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def nested_block?
|
22
|
+
@outer.inside_a_block?
|
23
|
+
end
|
24
|
+
|
25
|
+
def record_parameter(sym)
|
26
|
+
@parameters << Name.new(sym)
|
27
|
+
end
|
28
|
+
|
29
|
+
def outer_name
|
30
|
+
"#{@outer.outer_name}#{@name}/"
|
31
|
+
end
|
32
|
+
|
33
|
+
def variable_names
|
34
|
+
@parameters + @local_variables
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'reek/code_context'
|
2
|
+
|
3
|
+
class Class
|
4
|
+
def non_inherited_methods
|
5
|
+
instance_methods(false) + private_instance_methods(false)
|
6
|
+
end
|
7
|
+
|
8
|
+
def is_overriding_method?(sym)
|
9
|
+
instance_methods(false).include?(sym) and superclass.instance_methods(true).include?(sym)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Reek
|
14
|
+
class ClassContext < CodeContext
|
15
|
+
|
16
|
+
def ClassContext.create(outer, exp)
|
17
|
+
res = Name.resolve(exp[1], outer)
|
18
|
+
ClassContext.new(res[0], res[1], exp[2])
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(outer, name, superclass = nil)
|
22
|
+
super(outer, nil)
|
23
|
+
@name = name
|
24
|
+
@superclass = superclass
|
25
|
+
@parsed_methods = []
|
26
|
+
@instance_variables = []
|
27
|
+
end
|
28
|
+
|
29
|
+
def myself
|
30
|
+
@myself ||= @outer.find_module(@name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_module(modname)
|
34
|
+
sym = modname.to_s
|
35
|
+
return nil unless myself
|
36
|
+
myself.const_defined?(sym) ? myself.const_get(sym) : nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_overriding_method?(name)
|
40
|
+
return false unless myself
|
41
|
+
myself.is_overriding_method?(name.to_s)
|
42
|
+
end
|
43
|
+
|
44
|
+
def is_struct?
|
45
|
+
@superclass == [:const, :Struct]
|
46
|
+
end
|
47
|
+
|
48
|
+
def num_methods
|
49
|
+
meths = myself ? myself.non_inherited_methods : @parsed_methods
|
50
|
+
meths.length
|
51
|
+
end
|
52
|
+
|
53
|
+
def record_instance_variable(sym)
|
54
|
+
@instance_variables << Name.new(sym)
|
55
|
+
end
|
56
|
+
|
57
|
+
def record_method(name)
|
58
|
+
@parsed_methods << name.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
def outer_name
|
62
|
+
"#{@outer.outer_name}#{@name}#"
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
"#{@outer.outer_name}#{@name}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def variable_names
|
70
|
+
@instance_variables
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Reek
|
2
|
+
|
3
|
+
#
|
4
|
+
# Superclass for all types of source code context. Each instance represents
|
5
|
+
# a code element of some kind, and each provides behaviour relevant to that
|
6
|
+
# code element. CodeContexts form a tree in the same way the code does,
|
7
|
+
# with each context holding a reference to a unique outer context.
|
8
|
+
#
|
9
|
+
class CodeContext
|
10
|
+
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
def initialize(outer, exp)
|
14
|
+
@outer = outer
|
15
|
+
@exp = exp
|
16
|
+
@myself = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def matches?(strings)
|
20
|
+
me = @name.to_s
|
21
|
+
strings.any? do |str|
|
22
|
+
re = /#{str}/
|
23
|
+
re === me or re === self.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Bounces messages up the context tree to the first enclosing context
|
29
|
+
# that knows how to deal with the request.
|
30
|
+
#
|
31
|
+
def method_missing(method, *args)
|
32
|
+
@outer.send(method, *args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def num_methods
|
36
|
+
0
|
37
|
+
end
|
38
|
+
|
39
|
+
def outer_name
|
40
|
+
"#{@name}/"
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
"#{@outer.outer_name}#{@name}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'parse_tree'
|
3
|
+
require 'sexp_processor'
|
4
|
+
require 'reek/block_context'
|
5
|
+
require 'reek/class_context'
|
6
|
+
require 'reek/module_context'
|
7
|
+
require 'reek/stop_context'
|
8
|
+
require 'reek/if_context'
|
9
|
+
require 'reek/method_context'
|
10
|
+
require 'reek/singleton_method_context'
|
11
|
+
require 'reek/yield_call_context'
|
12
|
+
|
13
|
+
module Reek
|
14
|
+
|
15
|
+
class CodeParser < SexpProcessor
|
16
|
+
|
17
|
+
def self.parse_tree_for(code) # :nodoc:
|
18
|
+
ParseTree.new.parse_tree_for_string(code)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Creates a new Ruby code checker. Any smells discovered by
|
22
|
+
# +check_source+ or +check_object+ will be stored in +report+.
|
23
|
+
def initialize(report, smells, ctx = StopContext.new)
|
24
|
+
super()
|
25
|
+
@report = report
|
26
|
+
@smells = smells
|
27
|
+
@element = ctx
|
28
|
+
@unsupported -= [:cfunc]
|
29
|
+
@default_method = :process_default
|
30
|
+
@require_empty = @warn_on_default = false
|
31
|
+
end
|
32
|
+
|
33
|
+
# Analyses the given Ruby source +code+ looking for smells.
|
34
|
+
# Any smells found are saved in the +Report+ object that
|
35
|
+
# was passed to this object's constructor.
|
36
|
+
def check_source(code)
|
37
|
+
check_parse_tree(CodeParser.parse_tree_for(code))
|
38
|
+
end
|
39
|
+
|
40
|
+
# Analyses the given Ruby object +obj+ looking for smells.
|
41
|
+
# Any smells found are saved in the +Report+ object that
|
42
|
+
# was passed to this object's constructor.
|
43
|
+
def check_object(obj)
|
44
|
+
check_parse_tree(ParseTree.new.parse_tree(obj))
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_default(exp)
|
48
|
+
exp[1..-1].each { |sub| process(sub) if Array === sub }
|
49
|
+
s(exp)
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_module(exp)
|
53
|
+
push(ModuleContext.create(@element, exp)) do
|
54
|
+
process_default(exp)
|
55
|
+
check_smells(:module)
|
56
|
+
end
|
57
|
+
s(exp)
|
58
|
+
end
|
59
|
+
|
60
|
+
def process_class(exp)
|
61
|
+
push(ClassContext.create(@element, exp)) do
|
62
|
+
process_default(exp) unless @element.is_struct?
|
63
|
+
check_smells(:class)
|
64
|
+
end
|
65
|
+
s(exp)
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_defn(exp)
|
69
|
+
handle_context(MethodContext, :defn, exp)
|
70
|
+
end
|
71
|
+
|
72
|
+
def process_defs(exp)
|
73
|
+
handle_context(SingletonMethodContext, :defs, exp)
|
74
|
+
end
|
75
|
+
|
76
|
+
def process_args(exp)
|
77
|
+
exp[1..-1].each {|sym| @element.record_parameter(sym) }
|
78
|
+
s(exp)
|
79
|
+
end
|
80
|
+
|
81
|
+
def process_attrset(exp)
|
82
|
+
@element.record_depends_on_self if /^@/ === exp[1].to_s
|
83
|
+
s(exp)
|
84
|
+
end
|
85
|
+
|
86
|
+
def process_lit(exp)
|
87
|
+
val = exp[1]
|
88
|
+
@element.record_depends_on_self if val == :self
|
89
|
+
s(exp)
|
90
|
+
end
|
91
|
+
|
92
|
+
def process_iter(exp)
|
93
|
+
process(exp[1])
|
94
|
+
handle_context(BlockContext, :iter, exp[1..-1])
|
95
|
+
end
|
96
|
+
|
97
|
+
def process_dasgn_curr(exp)
|
98
|
+
@element.record_parameter(exp[1])
|
99
|
+
process_default(exp)
|
100
|
+
end
|
101
|
+
|
102
|
+
def process_block(exp)
|
103
|
+
@element.count_statements(CodeParser.count_statements(exp))
|
104
|
+
process_default(exp)
|
105
|
+
end
|
106
|
+
|
107
|
+
def process_yield(exp)
|
108
|
+
handle_context(YieldCallContext, :yield, exp)
|
109
|
+
end
|
110
|
+
|
111
|
+
def process_call(exp)
|
112
|
+
@element.record_call_to(exp)
|
113
|
+
process_default(exp)
|
114
|
+
end
|
115
|
+
|
116
|
+
def process_fcall(exp)
|
117
|
+
@element.record_depends_on_self
|
118
|
+
@element.refs.record_reference_to_self
|
119
|
+
process_default(exp)
|
120
|
+
end
|
121
|
+
|
122
|
+
def process_cfunc(exp)
|
123
|
+
@element.record_depends_on_self
|
124
|
+
s(exp)
|
125
|
+
end
|
126
|
+
|
127
|
+
def process_vcall(exp)
|
128
|
+
@element.record_depends_on_self
|
129
|
+
@element.refs.record_reference_to_self
|
130
|
+
s(exp)
|
131
|
+
end
|
132
|
+
|
133
|
+
def process_if(exp)
|
134
|
+
handle_context(IfContext, :if, exp)
|
135
|
+
end
|
136
|
+
|
137
|
+
def process_ivar(exp)
|
138
|
+
process_iasgn(exp)
|
139
|
+
end
|
140
|
+
|
141
|
+
def process_lasgn(exp)
|
142
|
+
@element.record_local_variable(exp[1])
|
143
|
+
process(exp[2])
|
144
|
+
s(exp)
|
145
|
+
end
|
146
|
+
|
147
|
+
def process_iasgn(exp)
|
148
|
+
@element.record_instance_variable(exp[1])
|
149
|
+
@element.record_depends_on_self
|
150
|
+
process_default(exp)
|
151
|
+
end
|
152
|
+
|
153
|
+
def process_self(exp)
|
154
|
+
@element.record_depends_on_self
|
155
|
+
s(exp)
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def self.count_statements(exp)
|
161
|
+
stmts = exp[1..-1]
|
162
|
+
ignore = 0
|
163
|
+
ignore = 1 if is_expr?(stmts[0], :args)
|
164
|
+
ignore += 1 if stmts[1] == s(:nil)
|
165
|
+
stmts.length - ignore
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.is_expr?(exp, type)
|
169
|
+
Array === exp and exp[0] == type
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.is_global_variable?(exp)
|
173
|
+
is_expr?(exp, :gvar)
|
174
|
+
end
|
175
|
+
|
176
|
+
def handle_context(klass, type, exp)
|
177
|
+
push(klass.new(@element, exp)) do
|
178
|
+
process_default(exp)
|
179
|
+
check_smells(type)
|
180
|
+
end
|
181
|
+
s(exp)
|
182
|
+
end
|
183
|
+
|
184
|
+
def check_smells(type)
|
185
|
+
@smells[type].each {|smell| smell.examine(@element, @report) }
|
186
|
+
end
|
187
|
+
|
188
|
+
def push(context)
|
189
|
+
orig = @element
|
190
|
+
@element = context
|
191
|
+
yield
|
192
|
+
@element = orig
|
193
|
+
end
|
194
|
+
|
195
|
+
def pop(exp)
|
196
|
+
@element = @element.outer
|
197
|
+
s(exp)
|
198
|
+
end
|
199
|
+
|
200
|
+
def check_parse_tree(sexp) # :nodoc:
|
201
|
+
sexp.each { |exp| process(exp) }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'reek/code_context'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
class IfContext < CodeContext
|
5
|
+
attr_reader :if_expr
|
6
|
+
|
7
|
+
def initialize(outer, exp)
|
8
|
+
@outer = outer
|
9
|
+
@exp = exp
|
10
|
+
@if_expr = exp[1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def tests_a_parameter?
|
14
|
+
@if_expr[0] == :lvar and has_parameter(@if_expr[1])
|
15
|
+
end
|
16
|
+
|
17
|
+
def outer_name
|
18
|
+
@outer.outer_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
@outer.to_s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'reek/name'
|
2
|
+
require 'reek/code_context'
|
3
|
+
require 'reek/object_refs'
|
4
|
+
|
5
|
+
module Reek
|
6
|
+
class MethodContext < CodeContext
|
7
|
+
attr_reader :parameters
|
8
|
+
attr_reader :calls
|
9
|
+
attr_reader :refs
|
10
|
+
attr_reader :num_statements
|
11
|
+
|
12
|
+
def initialize(outer, exp, record = true)
|
13
|
+
super(outer, exp)
|
14
|
+
@parameters = []
|
15
|
+
@local_variables = []
|
16
|
+
@name = Name.new(exp[1])
|
17
|
+
@num_statements = 0
|
18
|
+
@calls = Hash.new(0)
|
19
|
+
@depends_on_self = false
|
20
|
+
@refs = ObjectRefs.new
|
21
|
+
@outer.record_method(@name) # TODO: should be children of outer?
|
22
|
+
end
|
23
|
+
|
24
|
+
def count_statements(num)
|
25
|
+
@num_statements += num
|
26
|
+
end
|
27
|
+
|
28
|
+
def depends_on_instance?
|
29
|
+
@depends_on_self || is_overriding_method?(@name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_parameter(sym)
|
33
|
+
@parameters.include?(sym.to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
def record_call_to(exp)
|
37
|
+
@calls[exp] += 1
|
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
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def record_depends_on_self
|
53
|
+
@depends_on_self = true
|
54
|
+
end
|
55
|
+
|
56
|
+
def record_local_variable(sym)
|
57
|
+
@local_variables << Name.new(sym)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.is_block_arg?(param)
|
61
|
+
Array === param and param[0] == :block
|
62
|
+
end
|
63
|
+
|
64
|
+
def record_parameter(param)
|
65
|
+
@parameters << Name.new(param) unless MethodContext.is_block_arg?(param)
|
66
|
+
end
|
67
|
+
|
68
|
+
def outer_name
|
69
|
+
"#{@outer.outer_name}#{@name}/"
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
"#{@outer.outer_name}#{@name}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def envious_receivers
|
77
|
+
return [] if @refs.self_is_max?
|
78
|
+
@refs.max_keys
|
79
|
+
end
|
80
|
+
|
81
|
+
def variable_names
|
82
|
+
@parameters + @local_variables
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'reek/code_context'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
class ModuleContext < CodeContext
|
5
|
+
|
6
|
+
def ModuleContext.create(outer, exp)
|
7
|
+
res = Name.resolve(exp[1], outer)
|
8
|
+
ModuleContext.new(res[0], res[1])
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(outer, name)
|
12
|
+
super(outer, nil)
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
|
16
|
+
def myself
|
17
|
+
@myself ||= @outer.find_module(@name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_module(modname)
|
21
|
+
return nil unless myself
|
22
|
+
sym = modname.to_s
|
23
|
+
myself.const_defined?(sym) ? myself.const_get(sym) : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def outer_name
|
27
|
+
"#{@outer.outer_name}#{@name}::"
|
28
|
+
end
|
29
|
+
|
30
|
+
def variable_names
|
31
|
+
[]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/reek/name.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Reek
|
2
|
+
class Name
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
def self.resolve(exp, context)
|
6
|
+
return [context, new(exp)] unless Array === exp
|
7
|
+
name = exp[1]
|
8
|
+
case exp[0]
|
9
|
+
when :colon2
|
10
|
+
return [resolve(name, context)[0], new(exp[2])]
|
11
|
+
when :const
|
12
|
+
return [ModuleContext.create(context, exp), new(name)]
|
13
|
+
when :colon3
|
14
|
+
return [StopContext.new, new(name)]
|
15
|
+
else
|
16
|
+
return [context, new(name)]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(sym)
|
21
|
+
@name = sym.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def hash # :nodoc:
|
25
|
+
@name.hash
|
26
|
+
end
|
27
|
+
|
28
|
+
def <=>(other) # :nodoc:
|
29
|
+
@name <=> other.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
alias eql? <=>
|
33
|
+
|
34
|
+
def effective_name
|
35
|
+
@name.gsub(/^@*/, '')
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
@name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|