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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -1
  3. data/CHANGELOG.md +5 -0
  4. data/README.md +1 -1
  5. data/defaults.reek +3 -0
  6. data/docs/Code-Smells.md +1 -0
  7. data/docs/How-reek-works-internally.md +3 -2
  8. data/docs/Unused-Private-Method.md +47 -0
  9. data/features/samples.feature +22 -2
  10. data/features/step_definitions/sample_file_steps.rb +3 -0
  11. data/lib/reek/ast/node.rb +1 -2
  12. data/lib/reek/ast/object_refs.rb +30 -7
  13. data/lib/reek/code_comment.rb +25 -19
  14. data/lib/reek/context/class_context.rb +11 -0
  15. data/lib/reek/context/code_context.rb +58 -78
  16. data/lib/reek/context/module_context.rb +18 -0
  17. data/lib/reek/context/send_context.rb +17 -0
  18. data/lib/reek/context/singleton_method_context.rb +0 -3
  19. data/lib/reek/context/statement_counter.rb +32 -0
  20. data/lib/reek/context/visibility_tracker.rb +54 -0
  21. data/lib/reek/context_builder.rb +473 -0
  22. data/lib/reek/examiner.rb +14 -14
  23. data/lib/reek/smells/feature_envy.rb +3 -3
  24. data/lib/reek/smells/smell_detector.rb +1 -0
  25. data/lib/reek/smells/smell_repository.rb +11 -0
  26. data/lib/reek/smells/too_many_statements.rb +1 -1
  27. data/lib/reek/smells/unused_private_method.rb +82 -0
  28. data/lib/reek/smells/utility_function.rb +1 -1
  29. data/lib/reek/smells.rb +1 -0
  30. data/lib/reek/version.rb +1 -1
  31. data/spec/reek/ast/object_refs_spec.rb +20 -20
  32. data/spec/reek/cli/input_spec.rb +55 -0
  33. data/spec/reek/code_comment_spec.rb +10 -0
  34. data/spec/reek/context/code_context_spec.rb +8 -0
  35. data/spec/reek/context/module_context_spec.rb +10 -8
  36. data/spec/reek/context_builder_spec.rb +221 -0
  37. data/spec/reek/examiner_spec.rb +13 -0
  38. data/spec/reek/smells/boolean_parameter_spec.rb +2 -0
  39. data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
  40. data/spec/reek/smells/feature_envy_spec.rb +1 -1
  41. data/spec/reek/smells/too_many_statements_spec.rb +3 -3
  42. data/spec/reek/smells/unused_private_method_spec.rb +110 -0
  43. data/spec/spec_helper.rb +7 -0
  44. data/tasks/console.rake +5 -0
  45. metadata +13 -5
  46. data/lib/reek/tree_walker.rb +0 -237
  47. data/spec/reek/context/singleton_method_context_spec.rb +0 -16
  48. 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
@@ -0,0 +1,5 @@
1
+ desc 'Starts the interactive console'
2
+ task :console do
3
+ require 'pry'
4
+ Pry.start
5
+ end
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.7.1
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-11-29 00:00:00.000000000 Z
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/context/singleton_method_context_spec.rb
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
@@ -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