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