reek 3.7.1 → 3.8.0

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