reek 3.7.1 → 3.8.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -1
- data/CHANGELOG.md +5 -0
- data/README.md +1 -1
- data/defaults.reek +3 -0
- data/docs/Code-Smells.md +1 -0
- data/docs/How-reek-works-internally.md +3 -2
- data/docs/Unused-Private-Method.md +47 -0
- data/features/samples.feature +22 -2
- data/features/step_definitions/sample_file_steps.rb +3 -0
- data/lib/reek/ast/node.rb +1 -2
- data/lib/reek/ast/object_refs.rb +30 -7
- data/lib/reek/code_comment.rb +25 -19
- data/lib/reek/context/class_context.rb +11 -0
- data/lib/reek/context/code_context.rb +58 -78
- data/lib/reek/context/module_context.rb +18 -0
- data/lib/reek/context/send_context.rb +17 -0
- data/lib/reek/context/singleton_method_context.rb +0 -3
- data/lib/reek/context/statement_counter.rb +32 -0
- data/lib/reek/context/visibility_tracker.rb +54 -0
- data/lib/reek/context_builder.rb +473 -0
- data/lib/reek/examiner.rb +14 -14
- data/lib/reek/smells/feature_envy.rb +3 -3
- data/lib/reek/smells/smell_detector.rb +1 -0
- data/lib/reek/smells/smell_repository.rb +11 -0
- data/lib/reek/smells/too_many_statements.rb +1 -1
- data/lib/reek/smells/unused_private_method.rb +82 -0
- data/lib/reek/smells/utility_function.rb +1 -1
- data/lib/reek/smells.rb +1 -0
- data/lib/reek/version.rb +1 -1
- data/spec/reek/ast/object_refs_spec.rb +20 -20
- data/spec/reek/cli/input_spec.rb +55 -0
- data/spec/reek/code_comment_spec.rb +10 -0
- data/spec/reek/context/code_context_spec.rb +8 -0
- data/spec/reek/context/module_context_spec.rb +10 -8
- data/spec/reek/context_builder_spec.rb +221 -0
- data/spec/reek/examiner_spec.rb +13 -0
- data/spec/reek/smells/boolean_parameter_spec.rb +2 -0
- data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
- data/spec/reek/smells/feature_envy_spec.rb +1 -1
- data/spec/reek/smells/too_many_statements_spec.rb +3 -3
- data/spec/reek/smells/unused_private_method_spec.rb +110 -0
- data/spec/spec_helper.rb +7 -0
- data/tasks/console.rake +5 -0
- metadata +13 -5
- data/lib/reek/tree_walker.rb +0 -237
- data/spec/reek/context/singleton_method_context_spec.rb +0 -16
- data/spec/reek/tree_walker_spec.rb +0 -237
@@ -0,0 +1,110 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_lib 'reek/smells/unused_private_method'
|
3
|
+
require_lib 'reek/examiner'
|
4
|
+
require_relative 'smell_detector_shared'
|
5
|
+
|
6
|
+
RSpec.describe Reek::Smells::UnusedPrivateMethod do
|
7
|
+
let(:detector) { build(:smell_detector, smell_type: :UnusedPrivateMethod) }
|
8
|
+
|
9
|
+
it_should_behave_like 'SmellDetector'
|
10
|
+
|
11
|
+
context 'unused private methods' do
|
12
|
+
it 'reports instance methods' do
|
13
|
+
source = <<-EOF
|
14
|
+
class Car
|
15
|
+
private
|
16
|
+
def start; end
|
17
|
+
def drive; end
|
18
|
+
end
|
19
|
+
EOF
|
20
|
+
|
21
|
+
expect(source).to reek_of(:UnusedPrivateMethod, name: :start)
|
22
|
+
expect(source).to reek_of(:UnusedPrivateMethod, name: :drive)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'creates warnings correctly' do
|
26
|
+
source = <<-EOF
|
27
|
+
class Car
|
28
|
+
private
|
29
|
+
def start; end
|
30
|
+
def drive; end
|
31
|
+
end
|
32
|
+
EOF
|
33
|
+
|
34
|
+
examiner = Reek::Examiner.new(source, 'UnusedPrivateMethod')
|
35
|
+
|
36
|
+
first_warning = examiner.smells.first
|
37
|
+
expect(first_warning.smell_category).to eq(Reek::Smells::UnusedPrivateMethod.smell_category)
|
38
|
+
expect(first_warning.smell_type).to eq(Reek::Smells::UnusedPrivateMethod.smell_type)
|
39
|
+
expect(first_warning.parameters[:name]).to eq(:drive)
|
40
|
+
expect(first_warning.lines).to eq([4])
|
41
|
+
|
42
|
+
second_warning = examiner.smells.last
|
43
|
+
expect(second_warning.smell_category).to eq(Reek::Smells::UnusedPrivateMethod.smell_category)
|
44
|
+
expect(second_warning.smell_type).to eq(Reek::Smells::UnusedPrivateMethod.smell_type)
|
45
|
+
expect(second_warning.parameters[:name]).to eq(:start)
|
46
|
+
expect(second_warning.lines).to eq([3])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'configuring the detector via source code comment' do
|
51
|
+
it 'does not report methods we excluded' do
|
52
|
+
source = <<-EOF
|
53
|
+
# :reek:UnusedPrivateMethod: { exclude: [ start ] }
|
54
|
+
class Car
|
55
|
+
private
|
56
|
+
def start; end
|
57
|
+
def drive; end
|
58
|
+
end
|
59
|
+
EOF
|
60
|
+
|
61
|
+
examiner = Reek::Examiner.new(source, 'UnusedPrivateMethod')
|
62
|
+
|
63
|
+
expect(examiner.smells.size).to eq(1)
|
64
|
+
warning_for_drive = examiner.smells.first
|
65
|
+
expect(warning_for_drive.parameters[:name]).to eq(:drive)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'used private methods' do
|
70
|
+
it 'are not reported' do
|
71
|
+
source = <<-EOF
|
72
|
+
class Car
|
73
|
+
def drive
|
74
|
+
start
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def start; end
|
79
|
+
end
|
80
|
+
EOF
|
81
|
+
|
82
|
+
expect(source).not_to reek_of(:UnusedPrivateMethod)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'unused protected methods' do
|
87
|
+
it 'are not reported' do
|
88
|
+
source = <<-EOF
|
89
|
+
class Car
|
90
|
+
protected
|
91
|
+
def start; end
|
92
|
+
end
|
93
|
+
EOF
|
94
|
+
|
95
|
+
expect(source).not_to reek_of(:UnusedPrivateMethod)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'unused public methods' do
|
100
|
+
it 'are not reported' do
|
101
|
+
source = <<-EOF
|
102
|
+
class Car
|
103
|
+
def start; end
|
104
|
+
end
|
105
|
+
EOF
|
106
|
+
|
107
|
+
expect(source).not_to reek_of(:UnusedPrivateMethod)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -33,6 +33,13 @@ module Helpers
|
|
33
33
|
configuration
|
34
34
|
end
|
35
35
|
|
36
|
+
# @param code [String] The given code.
|
37
|
+
#
|
38
|
+
# @return syntax_tree [Reek::AST::Node]
|
39
|
+
def syntax_tree(code)
|
40
|
+
Reek::Source::SourceCode.from(code).syntax_tree
|
41
|
+
end
|
42
|
+
|
36
43
|
# :reek:UncommunicativeMethodName
|
37
44
|
def sexp(type, *children)
|
38
45
|
@klass_map ||= Reek::AST::ASTNodeClassMap.new
|
data/tasks/console.rake
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reek
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Rutherford
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2015-
|
14
|
+
date: 2015-12-27 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: codeclimate-engine-rb
|
@@ -276,6 +276,7 @@ files:
|
|
276
276
|
- docs/Uncommunicative-Parameter-Name.md
|
277
277
|
- docs/Uncommunicative-Variable-Name.md
|
278
278
|
- docs/Unused-Parameters.md
|
279
|
+
- docs/Unused-Private-Method.md
|
279
280
|
- docs/Utility-Function.md
|
280
281
|
- docs/Versioning-Policy.md
|
281
282
|
- docs/YAML-Reports.md
|
@@ -342,11 +343,16 @@ files:
|
|
342
343
|
- lib/reek/configuration/directory_directives.rb
|
343
344
|
- lib/reek/configuration/excluded_paths.rb
|
344
345
|
- lib/reek/context/attribute_context.rb
|
346
|
+
- lib/reek/context/class_context.rb
|
345
347
|
- lib/reek/context/code_context.rb
|
346
348
|
- lib/reek/context/method_context.rb
|
347
349
|
- lib/reek/context/module_context.rb
|
348
350
|
- lib/reek/context/root_context.rb
|
351
|
+
- lib/reek/context/send_context.rb
|
349
352
|
- lib/reek/context/singleton_method_context.rb
|
353
|
+
- lib/reek/context/statement_counter.rb
|
354
|
+
- lib/reek/context/visibility_tracker.rb
|
355
|
+
- lib/reek/context_builder.rb
|
350
356
|
- lib/reek/examiner.rb
|
351
357
|
- lib/reek/rake/task.rb
|
352
358
|
- lib/reek/report.rb
|
@@ -384,6 +390,7 @@ files:
|
|
384
390
|
- lib/reek/smells/uncommunicative_parameter_name.rb
|
385
391
|
- lib/reek/smells/uncommunicative_variable_name.rb
|
386
392
|
- lib/reek/smells/unused_parameters.rb
|
393
|
+
- lib/reek/smells/unused_private_method.rb
|
387
394
|
- lib/reek/smells/utility_function.rb
|
388
395
|
- lib/reek/source/source_code.rb
|
389
396
|
- lib/reek/source/source_locator.rb
|
@@ -393,7 +400,6 @@ files:
|
|
393
400
|
- lib/reek/spec/should_reek_only_of.rb
|
394
401
|
- lib/reek/spec/smell_matcher.rb
|
395
402
|
- lib/reek/tree_dresser.rb
|
396
|
-
- lib/reek/tree_walker.rb
|
397
403
|
- lib/reek/version.rb
|
398
404
|
- logo/reek.bw.png
|
399
405
|
- logo/reek.bw.svg
|
@@ -409,6 +415,7 @@ files:
|
|
409
415
|
- spec/reek/ast/reference_collector_spec.rb
|
410
416
|
- spec/reek/ast/sexp_extensions_spec.rb
|
411
417
|
- spec/reek/ast/sexp_formatter_spec.rb
|
418
|
+
- spec/reek/cli/input_spec.rb
|
412
419
|
- spec/reek/cli/option_interpreter_spec.rb
|
413
420
|
- spec/reek/cli/options_spec.rb
|
414
421
|
- spec/reek/cli/warning_collector_spec.rb
|
@@ -422,7 +429,7 @@ files:
|
|
422
429
|
- spec/reek/context/method_context_spec.rb
|
423
430
|
- spec/reek/context/module_context_spec.rb
|
424
431
|
- spec/reek/context/root_context_spec.rb
|
425
|
-
- spec/reek/
|
432
|
+
- spec/reek/context_builder_spec.rb
|
426
433
|
- spec/reek/examiner_spec.rb
|
427
434
|
- spec/reek/report/code_climate_formatter_spec.rb
|
428
435
|
- spec/reek/report/code_climate_report_spec.rb
|
@@ -459,6 +466,7 @@ files:
|
|
459
466
|
- spec/reek/smells/uncommunicative_parameter_name_spec.rb
|
460
467
|
- spec/reek/smells/uncommunicative_variable_name_spec.rb
|
461
468
|
- spec/reek/smells/unused_parameters_spec.rb
|
469
|
+
- spec/reek/smells/unused_private_method_spec.rb
|
462
470
|
- spec/reek/smells/utility_function_spec.rb
|
463
471
|
- spec/reek/source/source_code_spec.rb
|
464
472
|
- spec/reek/source/source_locator_spec.rb
|
@@ -467,7 +475,6 @@ files:
|
|
467
475
|
- spec/reek/spec/should_reek_spec.rb
|
468
476
|
- spec/reek/spec/smell_matcher_spec.rb
|
469
477
|
- spec/reek/tree_dresser_spec.rb
|
470
|
-
- spec/reek/tree_walker_spec.rb
|
471
478
|
- spec/samples/all_but_one_masked/clean_one.rb
|
472
479
|
- spec/samples/all_but_one_masked/dirty.rb
|
473
480
|
- spec/samples/all_but_one_masked/masked.reek
|
@@ -503,6 +510,7 @@ files:
|
|
503
510
|
- spec/samples/two_smelly_files/dirty_two.rb
|
504
511
|
- spec/spec_helper.rb
|
505
512
|
- tasks/configuration.rake
|
513
|
+
- tasks/console.rake
|
506
514
|
- tasks/reek.rake
|
507
515
|
- tasks/rubocop.rake
|
508
516
|
- tasks/test.rake
|
data/lib/reek/tree_walker.rb
DELETED
@@ -1,237 +0,0 @@
|
|
1
|
-
require_relative 'context/method_context'
|
2
|
-
require_relative 'context/module_context'
|
3
|
-
require_relative 'context/root_context'
|
4
|
-
require_relative 'context/singleton_method_context'
|
5
|
-
require_relative 'context/attribute_context'
|
6
|
-
require_relative 'ast/node'
|
7
|
-
|
8
|
-
module Reek
|
9
|
-
#
|
10
|
-
# Traverses an abstract syntax tree and fires events whenever it encounters
|
11
|
-
# specific node types.
|
12
|
-
#
|
13
|
-
# SMELL: This class is responsible for counting statements and for feeding
|
14
|
-
# each context to the smell repository.
|
15
|
-
#
|
16
|
-
# TODO: Make TreeWalker responsible only for creating Context objects, and
|
17
|
-
# loop over the created set of contexts elsewhere.
|
18
|
-
#
|
19
|
-
# :reek:TooManyMethods: { max_methods: 29 }
|
20
|
-
class TreeWalker
|
21
|
-
def initialize(smell_repository, exp)
|
22
|
-
@smell_repository = smell_repository
|
23
|
-
@exp = exp
|
24
|
-
@element = Context::RootContext.new(exp)
|
25
|
-
end
|
26
|
-
|
27
|
-
def walk
|
28
|
-
context_tree.each do |element|
|
29
|
-
smell_repository.examine(element)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
private_attr_accessor :element
|
36
|
-
private_attr_reader :exp, :smell_repository
|
37
|
-
|
38
|
-
# Processes the given AST, memoizes it and returns a tree of nested
|
39
|
-
# contexts.
|
40
|
-
#
|
41
|
-
# For example this ruby code:
|
42
|
-
# class Car; def drive; end; end
|
43
|
-
# would get compiled into this AST:
|
44
|
-
# (class
|
45
|
-
# (const nil :Car) nil
|
46
|
-
# (def :drive
|
47
|
-
# (args) nil))
|
48
|
-
# Processing this AST would result in a context tree where each node
|
49
|
-
# contains the outer context, the AST and the child contexts. The top
|
50
|
-
# node is always Reek::Context::RootContext. Using the example above,
|
51
|
-
# the tree would look like this:
|
52
|
-
#
|
53
|
-
# RootContext -> children: 1 ModuleContext -> children: 1 MethodContext
|
54
|
-
#
|
55
|
-
# @return [Reek::Context::RootContext] tree of nested contexts
|
56
|
-
def context_tree
|
57
|
-
@context_tree ||= process(exp)
|
58
|
-
end
|
59
|
-
|
60
|
-
def process(exp)
|
61
|
-
context_processor = "process_#{exp.type}"
|
62
|
-
if context_processor_exists?(context_processor)
|
63
|
-
send(context_processor, exp)
|
64
|
-
else
|
65
|
-
process_default exp
|
66
|
-
end
|
67
|
-
element
|
68
|
-
end
|
69
|
-
|
70
|
-
def process_module(exp)
|
71
|
-
inside_new_context(Context::ModuleContext, exp) do
|
72
|
-
process_default(exp)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
alias_method :process_class, :process_module
|
77
|
-
|
78
|
-
def process_casgn(exp)
|
79
|
-
if exp.defines_module?
|
80
|
-
process_module(exp)
|
81
|
-
else
|
82
|
-
process_default(exp)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def process_def(exp)
|
87
|
-
inside_new_context(Context::MethodContext, exp) do
|
88
|
-
count_clause(exp.body)
|
89
|
-
process_default(exp)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def process_defs(exp)
|
94
|
-
inside_new_context(Context::SingletonMethodContext, exp) do
|
95
|
-
count_clause(exp.body)
|
96
|
-
process_default(exp)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def process_default(exp)
|
101
|
-
exp.children.each do |child|
|
102
|
-
process(child) if child.is_a? AST::Node
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def process_args(_) end
|
107
|
-
|
108
|
-
# :reek:TooManyStatements: { max_statements: 6 }
|
109
|
-
# :reek:FeatureEnvy
|
110
|
-
def process_send(exp)
|
111
|
-
if exp.visibility_modifier?
|
112
|
-
element.track_visibility(exp.method_name, exp.arg_names)
|
113
|
-
end
|
114
|
-
if exp.attribute_writer?
|
115
|
-
exp.args.each do |arg|
|
116
|
-
next unless arg.type == :sym
|
117
|
-
new_context(Context::AttributeContext, arg, exp)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
element.record_call_to(exp)
|
121
|
-
process_default(exp)
|
122
|
-
end
|
123
|
-
|
124
|
-
def process_attrasgn(exp)
|
125
|
-
element.record_call_to(exp)
|
126
|
-
process_default(exp)
|
127
|
-
end
|
128
|
-
|
129
|
-
alias_method :process_op_asgn, :process_attrasgn
|
130
|
-
|
131
|
-
def process_ivar(exp)
|
132
|
-
element.record_use_of_self
|
133
|
-
process_default(exp)
|
134
|
-
end
|
135
|
-
|
136
|
-
alias_method :process_ivasgn, :process_ivar
|
137
|
-
|
138
|
-
def process_self(_)
|
139
|
-
element.record_use_of_self
|
140
|
-
end
|
141
|
-
|
142
|
-
alias_method :process_zsuper, :process_self
|
143
|
-
|
144
|
-
#
|
145
|
-
# Statement counting
|
146
|
-
#
|
147
|
-
|
148
|
-
def process_block(exp)
|
149
|
-
count_clause(exp.block)
|
150
|
-
process_default(exp)
|
151
|
-
end
|
152
|
-
|
153
|
-
def process_begin(exp)
|
154
|
-
count_statement_list(exp.children)
|
155
|
-
element.count_statements(-1)
|
156
|
-
process_default(exp)
|
157
|
-
end
|
158
|
-
|
159
|
-
alias_method :process_kwbegin, :process_begin
|
160
|
-
|
161
|
-
def process_if(exp)
|
162
|
-
children = exp.children
|
163
|
-
count_clause(children[1])
|
164
|
-
count_clause(children[2])
|
165
|
-
element.count_statements(-1)
|
166
|
-
process_default(exp)
|
167
|
-
end
|
168
|
-
|
169
|
-
def process_while(exp)
|
170
|
-
count_clause(exp.children[1])
|
171
|
-
element.count_statements(-1)
|
172
|
-
process_default(exp)
|
173
|
-
end
|
174
|
-
|
175
|
-
alias_method :process_until, :process_while
|
176
|
-
|
177
|
-
def process_for(exp)
|
178
|
-
count_clause(exp.children[2])
|
179
|
-
element.count_statements(-1)
|
180
|
-
process_default(exp)
|
181
|
-
end
|
182
|
-
|
183
|
-
def process_rescue(exp)
|
184
|
-
count_clause(exp.children.first)
|
185
|
-
element.count_statements(-1)
|
186
|
-
process_default(exp)
|
187
|
-
end
|
188
|
-
|
189
|
-
def process_resbody(exp)
|
190
|
-
count_statement_list(exp.children[1..-1].compact)
|
191
|
-
process_default(exp)
|
192
|
-
end
|
193
|
-
|
194
|
-
def process_case(exp)
|
195
|
-
count_clause(exp.else_body)
|
196
|
-
element.count_statements(-1)
|
197
|
-
process_default(exp)
|
198
|
-
end
|
199
|
-
|
200
|
-
def process_when(exp)
|
201
|
-
count_clause(exp.body)
|
202
|
-
process_default(exp)
|
203
|
-
end
|
204
|
-
|
205
|
-
def context_processor_exists?(name)
|
206
|
-
self.class.private_method_defined?(name)
|
207
|
-
end
|
208
|
-
|
209
|
-
# :reek:ControlParameter
|
210
|
-
def count_clause(sexp)
|
211
|
-
element.count_statements(1) if sexp
|
212
|
-
end
|
213
|
-
|
214
|
-
def count_statement_list(statement_list)
|
215
|
-
element.count_statements statement_list.length
|
216
|
-
end
|
217
|
-
|
218
|
-
def inside_new_context(klass, exp)
|
219
|
-
push(new_context(klass, exp)) do
|
220
|
-
yield
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
def new_context(klass, *args)
|
225
|
-
klass.new(element, *args).tap do |scope|
|
226
|
-
element.append_child_context(scope)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
def push(scope)
|
231
|
-
orig = element
|
232
|
-
self.element = scope
|
233
|
-
yield
|
234
|
-
self.element = orig
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
require_relative '../../spec_helper'
|
2
|
-
require_lib 'reek/context/singleton_method_context'
|
3
|
-
|
4
|
-
RSpec.describe Reek::Context::SingletonMethodContext do
|
5
|
-
let(:smc) do
|
6
|
-
sexp = sexp(:def, :foo, sexp(:args, sexp(:arg, :bar)), nil)
|
7
|
-
Reek::Context::SingletonMethodContext.new(nil, sexp)
|
8
|
-
end
|
9
|
-
|
10
|
-
describe '#envious_receivers' do
|
11
|
-
it 'should not record envious calls' do
|
12
|
-
smc.record_call_to sexp(:send, sexp(:lvar, :bar), :baz)
|
13
|
-
expect(smc.envious_receivers).to be_empty
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|