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,54 @@
1
+ require 'private_attr/everywhere'
2
+
3
+ module Reek
4
+ module Context
5
+ # Responsible for tracking visibilities in regards to CodeContexts.
6
+ # :reek:Attribute
7
+ class VisibilityTracker
8
+ attr_accessor :visibility
9
+ private_attr_accessor :tracked_visibility
10
+
11
+ def initialize(visibility = :public)
12
+ @visibility = visibility
13
+ end
14
+
15
+ # Handle the effects of a visibility modifier.
16
+ #
17
+ # @example Modifying the visibility of existing children
18
+ # track_visibility children, :private, [:hide_me, :implementation_detail]
19
+ #
20
+ # @param children [Array<CodeContext>]
21
+ # @param visibility [Symbol]
22
+ # @param names [Array<Symbol>]
23
+ #
24
+ def track_visibility(children: raise, visibility: raise, names: raise)
25
+ if names.any?
26
+ children.each do |child|
27
+ child.visibility = visibility if names.include?(child.name)
28
+ end
29
+ else
30
+ self.tracked_visibility = visibility
31
+ end
32
+ end
33
+
34
+ # Sets the visibility of a child CodeContext to the tracked visibility.
35
+ #
36
+ # @param child [CodeContext]
37
+ #
38
+ def set_child_visibility(child)
39
+ child.visibility = tracked_visibility
40
+ end
41
+
42
+ # @return [Boolean] If the visibility is public or not.
43
+ def non_public_visibility?
44
+ visibility != :public
45
+ end
46
+
47
+ private
48
+
49
+ def tracked_visibility
50
+ @tracked_visibility ||= :public
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,473 @@
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 'context/send_context'
7
+ require_relative 'context/class_context'
8
+ require_relative 'ast/node'
9
+
10
+ module Reek
11
+ #
12
+ # Traverses an abstract syntax tree and fires events whenever it encounters
13
+ # specific node types.
14
+ #
15
+ # TODO: This class is responsible for statements and reference
16
+ # counting. Ideally `ContextBuilder` would only build up the context tree and leave the
17
+ # statement and reference counting to the contexts.
18
+ #
19
+ # :reek:TooManyMethods: { max_methods: 27 }
20
+ # :reek:UnusedPrivateMethod: { exclude: [ !ruby/regexp /process_/ ] }
21
+ class ContextBuilder
22
+ attr_reader :context_tree
23
+ private_attr_accessor :element
24
+ private_attr_reader :exp
25
+
26
+ def initialize(syntax_tree)
27
+ @exp = syntax_tree
28
+ @element = Context::RootContext.new(exp)
29
+ @context_tree = build(exp)
30
+ end
31
+
32
+ private
33
+
34
+ # Processes the given AST, memoizes it and returns a tree of nested
35
+ # contexts.
36
+ #
37
+ # For example this ruby code:
38
+ #
39
+ # class Car; def drive; end; end
40
+ #
41
+ # would get compiled into this AST:
42
+ #
43
+ # (class
44
+ # (const nil :Car) nil
45
+ # (def :drive
46
+ # (args) nil))
47
+ #
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 build(exp)
57
+ context_processor = "process_#{exp.type}"
58
+ if context_processor_exists?(context_processor)
59
+ send(context_processor, exp)
60
+ else
61
+ process exp
62
+ end
63
+ element
64
+ end
65
+
66
+ # Handles every node for which we have no context_processor.
67
+ #
68
+ def process(exp)
69
+ exp.children.grep(AST::Node).each(&method(:build))
70
+ end
71
+
72
+ # Handles `module` and `class` nodes.
73
+ #
74
+ def process_module(exp)
75
+ inside_new_context(Context::ModuleContext, exp) do
76
+ process(exp)
77
+ end
78
+ end
79
+
80
+ alias_method :process_class, :process_module
81
+
82
+ # Handles `casgn` ("class assign") nodes.
83
+ #
84
+ # An input example that would trigger this method would be:
85
+ #
86
+ # Foo = Class.new Bar
87
+ #
88
+ def process_casgn(exp)
89
+ if exp.defines_module?
90
+ process_module(exp)
91
+ else
92
+ process(exp)
93
+ end
94
+ end
95
+
96
+ # Handles `def` nodes.
97
+ #
98
+ # An input example that would trigger this method would be:
99
+ #
100
+ # def call_me; foo = 2; bar = 5; end
101
+ #
102
+ # Given the above example we would count 2 statements overall.
103
+ #
104
+ def process_def(exp)
105
+ inside_new_context(Context::MethodContext, exp) do
106
+ increase_statement_count_by(exp.body)
107
+ process(exp)
108
+ end
109
+ end
110
+
111
+ # Handles `defs` nodes ("define singleton").
112
+ #
113
+ # An input example that would trigger this method would be:
114
+ #
115
+ # def self.call_me; foo = 2; bar = 5; end
116
+ #
117
+ # Given the above example we would count 2 statements overall.
118
+ #
119
+ def process_defs(exp)
120
+ inside_new_context(Context::SingletonMethodContext, exp) do
121
+ increase_statement_count_by(exp.body)
122
+ process(exp)
123
+ end
124
+ end
125
+
126
+ # Handles `send` nodes a.k.a. method calls.
127
+ #
128
+ # An input example that would trigger this method would be:
129
+ #
130
+ # call_me()
131
+ #
132
+ # Besides checking if it's a visibility modifier or an attribute writer
133
+ # we also record to what the method call is referring to
134
+ # which we later use for smell detectors like FeatureEnvy.
135
+ #
136
+ # :reek:TooManyStatements: { max_statements: 7 }
137
+ # :reek:FeatureEnvy
138
+ def process_send(exp)
139
+ method_name = exp.method_name
140
+ if exp.visibility_modifier?
141
+ element.track_visibility(method_name, exp.arg_names)
142
+ elsif exp.attribute_writer?
143
+ exp.args.each do |arg|
144
+ append_new_context(Context::AttributeContext, arg, exp)
145
+ end
146
+ else
147
+ append_new_context(Context::SendContext, exp, method_name)
148
+ end
149
+ element.record_call_to(exp)
150
+ process(exp)
151
+ end
152
+
153
+ # Handles `op_asgn` nodes a.k.a. Ruby's assignment operators.
154
+ #
155
+ # An input example that would trigger this method would be:
156
+ #
157
+ # x += 5
158
+ #
159
+ # or
160
+ #
161
+ # x *= 3
162
+ #
163
+ # We record one reference to `x` given the example above.
164
+ #
165
+ def process_op_asgn(exp)
166
+ element.record_call_to(exp)
167
+ process(exp)
168
+ end
169
+
170
+ # Handles `ivasgn` and `ivar` nodes a.k.a. nodes related to instance variables.
171
+ #
172
+ # An input example that would trigger this method would be:
173
+ #
174
+ # @item = 5
175
+ #
176
+ # for instance assignments (`ivasgn`) and
177
+ #
178
+ # call_me(@item)
179
+ #
180
+ # for just using instance variables (`ivar`).
181
+ #
182
+ # We record one reference to `self`.
183
+ #
184
+ def process_ivar(exp)
185
+ element.record_use_of_self
186
+ process(exp)
187
+ end
188
+
189
+ alias_method :process_ivasgn, :process_ivar
190
+
191
+ # Handles `self` nodes.
192
+ #
193
+ # An input example that would trigger this method would be:
194
+ #
195
+ # def self.foo; end
196
+ #
197
+ def process_self(_)
198
+ element.record_use_of_self
199
+ end
200
+
201
+ # Handles `zsuper` nodes a.k.a. calls to `super` without any arguments but a block possibly.
202
+ #
203
+ # An input example that would trigger this method would be:
204
+ #
205
+ # def call_me; super; end
206
+ #
207
+ # or
208
+ #
209
+ # def call_me; super do end; end
210
+ #
211
+ # but not
212
+ #
213
+ # def call_me; super(); end
214
+ #
215
+ # We record one reference to `self`.
216
+ #
217
+ def process_zsuper(_)
218
+ element.record_use_of_self
219
+ end
220
+
221
+ # Handles `block` nodes.
222
+ #
223
+ # An input example that would trigger this method would be:
224
+ #
225
+ # list.map { |element| puts element }
226
+ #
227
+ # Counts non-empty blocks as one statement.
228
+ #
229
+ def process_block(exp)
230
+ increase_statement_count_by(exp.block)
231
+ process(exp)
232
+ end
233
+
234
+ # Handles `begin` and `kwbegin` nodes. `begin` nodes are created implicitly
235
+ # e.g. when parsing method bodies (see example below), `kwbegin` nodes are created
236
+ # by explicitly using the `begin` keyword.
237
+ #
238
+ # An input example that would trigger this method would be:
239
+ #
240
+ # def foo; call_me(); @x = 5; end
241
+ #
242
+ # In this case the whole method body would be hanging below the `begin` node.
243
+ #
244
+ # Counts all statements in the method body.
245
+ #
246
+ # At the end we subtract one statement because the surrounding context was already counted
247
+ # as one (e.g. via `process_def`).
248
+ #
249
+ def process_begin(exp)
250
+ increase_statement_count_by(exp.children)
251
+ decrease_statement_count
252
+ process(exp)
253
+ end
254
+
255
+ alias_method :process_kwbegin, :process_begin
256
+
257
+ # Handles `if` nodes.
258
+ #
259
+ # An input example that would trigger this method would be:
260
+ #
261
+ # if a > 5 && b < 3
262
+ # puts 'bingo'
263
+ # else
264
+ # 3
265
+ # end
266
+ #
267
+ # Counts the `if` body as one statement and the `else` body as another statement.
268
+ #
269
+ # At the end we subtract one statement because the surrounding context was already counted
270
+ # as one (e.g. via `process_def`).
271
+ #
272
+ # `children[1]` refers to the `if` body (so `puts 'bingo'` from above) and
273
+ # `children[2]` to the `else` body (so `3` from above), which might be nil.
274
+ #
275
+ def process_if(exp)
276
+ children = exp.children
277
+ increase_statement_count_by(children[1])
278
+ increase_statement_count_by(children[2])
279
+ decrease_statement_count
280
+ process(exp)
281
+ end
282
+
283
+ # Handles `while` and `until` nodes.
284
+ #
285
+ # An input example that would trigger this method would be:
286
+ #
287
+ # while x < 5
288
+ # puts 'bingo'
289
+ # end
290
+ #
291
+ # Counts the `while` body as one statement.
292
+ #
293
+ # At the end we subtract one statement because the surrounding context was already counted
294
+ # as one (e.g. via `process_def`).
295
+ #
296
+ # `children[1]` below refers to the `while` body (so `puts 'bingo'` from above)
297
+ #
298
+ def process_while(exp)
299
+ increase_statement_count_by(exp.children[1])
300
+ decrease_statement_count
301
+ process(exp)
302
+ end
303
+
304
+ alias_method :process_until, :process_while
305
+
306
+ # Handles `for` nodes.
307
+ #
308
+ # An input example that would trigger this method would be:
309
+ #
310
+ # for i in [1,2,3,4]
311
+ # puts i
312
+ # end
313
+ #
314
+ # Counts the `for` body as one statement.
315
+ #
316
+ # At the end we subtract one statement because the surrounding context was already counted
317
+ # as one (e.g. via `process_def`).
318
+ #
319
+ # `children[2]` below refers to the `while` body (so `puts i` from above)
320
+ #
321
+ def process_for(exp)
322
+ increase_statement_count_by(exp.children[2])
323
+ decrease_statement_count
324
+ process(exp)
325
+ end
326
+
327
+ # Handles `rescue` nodes.
328
+ #
329
+ # An input example that would trigger this method would be:
330
+ #
331
+ # def simple
332
+ # raise ArgumentError, 'raising...'
333
+ # rescue => e
334
+ # puts 'rescued!'
335
+ # end
336
+ #
337
+ # Counts everything before the `rescue` body as one statement.
338
+ #
339
+ # At the end we subtract one statement because the surrounding context was already counted
340
+ # as one (e.g. via `process_def`).
341
+ #
342
+ # `exp.children.first` below refers to everything before the actual `rescue`
343
+ # which would be the
344
+ #
345
+ # raise ArgumentError, 'raising...'
346
+ #
347
+ # in the example above.
348
+ # `exp` would be the whole method body wrapped under a `rescue` node.
349
+ # See `process_resbody` for additional reference.
350
+ #
351
+ def process_rescue(exp)
352
+ increase_statement_count_by(exp.children.first)
353
+ decrease_statement_count
354
+ process(exp)
355
+ end
356
+
357
+ # Handles `resbody` nodes.
358
+ #
359
+ # An input example that would trigger this method would be:
360
+ #
361
+ # def simple
362
+ # raise ArgumentError, 'raising...'
363
+ # rescue => e
364
+ # puts 'rescued!'
365
+ # end
366
+ #
367
+ # Counts the exception capturing and every statement related to it.
368
+ #
369
+ # So `exp.children[1..-1]` from the code below would be an array with the following 2 elements:
370
+ # [
371
+ # (lvasgn :e),
372
+ # (send nil :puts (str "rescued!"))
373
+ # ]
374
+ #
375
+ # which thus counts as 2 statements.
376
+ # `exp` would be the whole `rescue` body.
377
+ # See `process_rescue` for additional reference.
378
+ #
379
+ def process_resbody(exp)
380
+ increase_statement_count_by(exp.children[1..-1].compact)
381
+ process(exp)
382
+ end
383
+
384
+ # Handles `case` nodes.
385
+ #
386
+ # An input example that would trigger this method would be:
387
+ #
388
+ # foo = 5
389
+ # case foo
390
+ # when 1..100
391
+ # puts 'In between'
392
+ # else
393
+ # puts 'Not sure what I got here'
394
+ # end
395
+ #
396
+ # Counts the `else` body.
397
+ #
398
+ # At the end we subtract one statement because the surrounding context was already counted
399
+ # as one (e.g. via `process_def`).
400
+ #
401
+ def process_case(exp)
402
+ increase_statement_count_by(exp.else_body)
403
+ decrease_statement_count
404
+ process(exp)
405
+ end
406
+
407
+ # Handles `when` nodes.
408
+ #
409
+ # An input example that would trigger this method would be:
410
+ #
411
+ # foo = 5
412
+ # case foo
413
+ # when (1..100)
414
+ # puts 'In between'
415
+ # else
416
+ # puts 'Not sure what I got here'
417
+ # end
418
+ #
419
+ # Note that input like
420
+ #
421
+ # if foo then :holla else :nope end
422
+ #
423
+ # does not trigger this method.
424
+ #
425
+ # Counts the `when` body.
426
+ #
427
+ def process_when(exp)
428
+ increase_statement_count_by(exp.body)
429
+ process(exp)
430
+ end
431
+
432
+ def context_processor_exists?(name)
433
+ self.class.private_method_defined?(name)
434
+ end
435
+
436
+ # :reek:ControlParameter
437
+ def increase_statement_count_by(sexp)
438
+ element.statement_counter.increase_by sexp
439
+ end
440
+
441
+ def decrease_statement_count
442
+ element.statement_counter.decrease_by 1
443
+ end
444
+
445
+ # Stores a reference to the current context, creates a nested new one,
446
+ # yields to the given block and then restores the previous context.
447
+ #
448
+ # @param klass [Context::*Context] - context class
449
+ # @param exp - current expression
450
+ # @yield block
451
+ #
452
+ def inside_new_context(klass, exp)
453
+ new_context = append_new_context(klass, exp)
454
+
455
+ orig, self.element = element, new_context
456
+ yield
457
+ self.element = orig
458
+ end
459
+
460
+ # Append a new child context to the current element.
461
+ #
462
+ # @param klass [Context::*Context] - context class
463
+ # @param args - arguments for the class initializer
464
+ #
465
+ # @return [Context::*Context] - the context that was appended
466
+ #
467
+ def append_new_context(klass, *args)
468
+ klass.new(element, *args).tap do |new_context|
469
+ element.append_child_context new_context
470
+ end
471
+ end
472
+ end
473
+ end
data/lib/reek/examiner.rb CHANGED
@@ -1,10 +1,10 @@
1
- # NOTE: tree_walker is required first to ensure unparser is required before
1
+ # NOTE: context_builder is required first to ensure unparser is required before
2
2
  # parser. This prevents a potentially incompatible version of parser from being
3
3
  # loaded first. This is only relevant when running bin/reek straight from a
4
4
  # checkout directory without using Bundler.
5
5
  #
6
6
  # See also https://github.com/troessner/reek/pull/468
7
- require_relative 'tree_walker'
7
+ require_relative 'context_builder'
8
8
  require_relative 'source/source_code'
9
9
  require_relative 'cli/warning_collector'
10
10
  require_relative 'smells/smell_repository'
@@ -17,6 +17,7 @@ module Reek
17
17
  #
18
18
  # :reek:TooManyInstanceVariables: { max_instance_variables: 6 }
19
19
  class Examiner
20
+ private_attr_reader :collector, :source, :smell_repository
20
21
  #
21
22
  # Creates an Examiner which scans the given +source+ for code smells.
22
23
  #
@@ -25,7 +26,7 @@ module Reek
25
26
  # if it is a File or IO, it is opened and Ruby source code is read from it;
26
27
  #
27
28
  # @param filter_by_smells [Array<String>]
28
- # List of smell types to filter by.
29
+ # List of smell types to filter by, e.g. "DuplicateMethodCall".
29
30
  #
30
31
  # @param configuration [Configuration::AppConfiguration]
31
32
  # The configuration for this Examiner.
@@ -34,11 +35,11 @@ module Reek
34
35
  def initialize(source,
35
36
  filter_by_smells = [],
36
37
  configuration: Configuration::AppConfiguration.default)
37
- @source = Source::SourceCode.from(source)
38
- @configuration = configuration
39
- @collector = CLI::WarningCollector.new
40
- @smell_types = Smells::SmellRepository.eligible_smell_types(filter_by_smells)
41
-
38
+ @source = Source::SourceCode.from(source)
39
+ @collector = CLI::WarningCollector.new
40
+ @smell_types = Smells::SmellRepository.eligible_smell_types(filter_by_smells)
41
+ @smell_repository = Smells::SmellRepository.new(smell_types: @smell_types,
42
+ configuration: configuration.directive_for(description))
42
43
  run
43
44
  end
44
45
 
@@ -77,14 +78,13 @@ module Reek
77
78
 
78
79
  private
79
80
 
80
- private_attr_reader :configuration, :collector, :smell_types, :source
81
-
82
81
  def run
83
- smell_repository = Smells::SmellRepository.new(
84
- smell_types: smell_types,
85
- configuration: configuration.directive_for(description))
86
82
  syntax_tree = source.syntax_tree
87
- TreeWalker.new(smell_repository, syntax_tree).walk if syntax_tree
83
+ return unless syntax_tree
84
+ ContextBuilder.new(syntax_tree).context_tree.each do |element|
85
+ smell_repository.examine(element)
86
+ end
87
+
88
88
  smell_repository.report_on(collector)
89
89
  end
90
90
  end
@@ -47,12 +47,12 @@ module Reek
47
47
  #
48
48
  def inspect(ctx)
49
49
  return [] unless ctx.references_self?
50
- envious_receivers(ctx).map do |name, refs|
50
+ envious_receivers(ctx).map do |name, lines|
51
51
  smell_warning(
52
52
  context: ctx,
53
- lines: refs.map(&:line),
53
+ lines: lines,
54
54
  message: "refers to #{name} more than self (maybe move it to another class?)",
55
- parameters: { name: name.to_s, count: refs.size })
55
+ parameters: { name: name.to_s, count: lines.size })
56
56
  end
57
57
  end
58
58
 
@@ -14,6 +14,7 @@ module Reek
14
14
  #
15
15
  # :reek:TooManyMethods: { max_methods: 19 }
16
16
  # :reek:TooManyInstanceVariables: { max_instance_variables: 5 }
17
+ # :reek:UnusedPrivateMethod: { exclude: [ inherited, smell_warning ] }
17
18
  class SmellDetector
18
19
  attr_reader :config
19
20
  private_attr_accessor :smells_found
@@ -10,10 +10,21 @@ module Reek
10
10
  class SmellRepository
11
11
  private_attr_reader :configuration, :smell_types, :detectors
12
12
 
13
+ # @return [Array<Reek::Smells::SmellDetector>] All known SmellDetectors
14
+ # e.g. [Reek::Smells::BooleanParameter, Reek::Smells::ClassVariable].
13
15
  def self.smell_types
14
16
  Reek::Smells::SmellDetector.descendants.sort_by(&:name)
15
17
  end
16
18
 
19
+ # @param filter_by_smells [Array<String>]
20
+ # List of smell types to filter by, e.g. "DuplicateMethodCall".
21
+ # More precisely it should be whatever is returned by `SmellDetector`.smell_type.
22
+ # This means that you can write the "DuplicateMethodCall" from above also like this:
23
+ # Reek::Smells::DuplicateMethodCall.smell_type
24
+ # if you want to make sure you do not fat-finger strings.
25
+ #
26
+ # @return [Array<Reek::Smells::SmellDetector>] All SmellDetectors that we want to filter for
27
+ # e.g. [Reek::Smells::Attribute].
17
28
  def self.eligible_smell_types(filter_by_smells = [])
18
29
  return smell_types if filter_by_smells.empty?
19
30
  smell_types.select do |klass|
@@ -35,7 +35,7 @@ module Reek
35
35
  max_allowed_statements = value(MAX_ALLOWED_STATEMENTS_KEY,
36
36
  ctx,
37
37
  DEFAULT_MAX_STATEMENTS)
38
- count = ctx.num_statements
38
+ count = ctx.number_of_statements
39
39
  return [] if count <= max_allowed_statements
40
40
  [smell_warning(
41
41
  context: ctx,