reek 3.6.0 → 3.6.1

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -1
  3. data/README.md +6 -1
  4. data/docs/API.md +27 -0
  5. data/lib/reek/ast/node.rb +1 -1
  6. data/lib/reek/ast/reference_collector.rb +1 -1
  7. data/lib/reek/context/code_context.rb +1 -0
  8. data/lib/reek/smells/attribute.rb +1 -1
  9. data/lib/reek/smells/boolean_parameter.rb +1 -1
  10. data/lib/reek/smells/class_variable.rb +1 -1
  11. data/lib/reek/smells/control_parameter.rb +1 -1
  12. data/lib/reek/smells/data_clump.rb +1 -1
  13. data/lib/reek/smells/duplicate_method_call.rb +1 -1
  14. data/lib/reek/smells/feature_envy.rb +1 -1
  15. data/lib/reek/smells/irresponsible_module.rb +1 -1
  16. data/lib/reek/smells/long_parameter_list.rb +1 -1
  17. data/lib/reek/smells/long_yield_list.rb +1 -1
  18. data/lib/reek/smells/module_initialize.rb +1 -1
  19. data/lib/reek/smells/nested_iterators.rb +1 -1
  20. data/lib/reek/smells/nil_check.rb +1 -1
  21. data/lib/reek/smells/prima_donna_method.rb +1 -1
  22. data/lib/reek/smells/repeated_conditional.rb +1 -1
  23. data/lib/reek/smells/smell_detector.rb +45 -61
  24. data/lib/reek/smells/smell_repository.rb +16 -18
  25. data/lib/reek/smells/too_many_instance_variables.rb +1 -1
  26. data/lib/reek/smells/too_many_methods.rb +2 -2
  27. data/lib/reek/smells/too_many_statements.rb +1 -1
  28. data/lib/reek/smells/uncommunicative_method_name.rb +1 -1
  29. data/lib/reek/smells/uncommunicative_module_name.rb +1 -1
  30. data/lib/reek/smells/uncommunicative_parameter_name.rb +1 -1
  31. data/lib/reek/smells/uncommunicative_variable_name.rb +1 -1
  32. data/lib/reek/smells/unused_parameters.rb +1 -1
  33. data/lib/reek/smells/utility_function.rb +1 -1
  34. data/lib/reek/tree_walker.rb +21 -3
  35. data/lib/reek/version.rb +1 -1
  36. data/reek.gemspec +1 -1
  37. data/spec/reek/ast/reference_collector_spec.rb +4 -0
  38. data/spec/reek/smells/boolean_parameter_spec.rb +1 -1
  39. data/spec/reek/smells/class_variable_spec.rb +4 -4
  40. data/spec/reek/smells/control_parameter_spec.rb +1 -1
  41. data/spec/reek/smells/data_clump_spec.rb +1 -1
  42. data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
  43. data/spec/reek/smells/long_parameter_list_spec.rb +1 -1
  44. data/spec/reek/smells/long_yield_list_spec.rb +1 -1
  45. data/spec/reek/smells/nested_iterators_spec.rb +5 -5
  46. data/spec/reek/smells/nil_check_spec.rb +1 -1
  47. data/spec/reek/smells/prima_donna_method_spec.rb +1 -1
  48. data/spec/reek/smells/smell_detector_shared.rb +2 -0
  49. data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
  50. data/spec/reek/smells/too_many_methods_spec.rb +4 -4
  51. data/spec/reek/smells/too_many_statements_spec.rb +1 -1
  52. data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -1
  53. data/spec/reek/smells/uncommunicative_module_name_spec.rb +2 -2
  54. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +2 -2
  55. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +3 -3
  56. data/spec/reek/smells/utility_function_spec.rb +4 -0
  57. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4dcff37dc4ddba77bf578314779a49fab1fcd0e7
4
- data.tar.gz: d3a82f168438222a7a5bbab73a491b877cbd8144
3
+ metadata.gz: 6656a7be5a8741a3d09652e3d4fffa05f06d8e8e
4
+ data.tar.gz: fe908f1791d3ca5ff1dddee324569dbc52293f6c
5
5
  SHA512:
6
- metadata.gz: ccc8992f22bda5b7be9d68ee269fb454db74f179dd2ebe05c42d78b4b0c779af0202e511f43c337507187e411c03be58ab740a8383b79844c04f64d11c4e7ffc
7
- data.tar.gz: b11c75c4d4ebf103cb97355b3688049ba275165783fba752b5c9cefe5c94cbeaa6f2a38dd0b1c5f6ab2faf0368c4e7548da42b56685acbf1f7f71a7cf9b8cf1d
6
+ metadata.gz: b90ac67fe60415c01a8b9330acdabbfa61ba82627e2a22f5e899b8bb2e7683d055b05ddfbf0643a3dffa551a1e39823d76df08a5496b70ddbb15a9928475236f
7
+ data.tar.gz: 8adff3362ad6e24ac4a2f91b8e8325fb4432f890a66a10fc3ced5fd997ab9029642283eec0f2550d3c84c8777ae53d00eefa83be3cc95dae1e15e1d10ca8434a
data/CHANGELOG.md CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- ## 3.6 (2015-10-30)
5
+ ## 3.6.1 (2015-11-13)
6
+
7
+ * (mvz) Make UtilityFunction not report methods that call `super` with
8
+ arguments.
9
+
10
+ ## 3.6.0 (2015-10-30)
6
11
 
7
12
  * (mvz) Make Attribute respect suppressing comments
8
13
  * (chastell) Adjust parser dependency to allow versions 2.2.3+ (and even 2.3+)
data/README.md CHANGED
@@ -14,7 +14,12 @@
14
14
 
15
15
  Reek is a tool that examines Ruby classes, modules and methods and reports any
16
16
  [Code Smells](docs/Code-Smells.md) it finds.
17
- Install it like this:
17
+
18
+ For an excellent introduction to
19
+ [Code Smells](docs/Code-Smells.md) and `Reek` check out [this blog post](https://blog.codeship.com/how-to-find-ruby-code-smells-with-reek/)
20
+ or [this talk](https://www.youtube.com/watch?v=ZzqOuHI5MkA).
21
+
22
+ Install it via rubygems:
18
23
 
19
24
  ```Bash
20
25
  gem install reek
data/docs/API.md CHANGED
@@ -50,6 +50,33 @@ string -- 5 warnings:
50
50
 
51
51
  Note that `Reek::Examiner.new` can take `source` as `String`, `Pathname`, `File` or `IO`.
52
52
 
53
+ ## API stability
54
+
55
+ Everything that is mentioned in this document can be considered stable in the
56
+ sense that it will only change across major versions.
57
+
58
+ `Reek 3` was the first major version with a stable API. As soon as `Reek 4`
59
+ is released we will mark the differences between `3` and `4`.
60
+
61
+ There is one thing in this API documentation you can't and shouldn't rely on:
62
+ The `SmellWarning` messages itself.
63
+
64
+ Something like this
65
+
66
+ ```
67
+ Dirty#m has the parameter name 'a' (UncommunicativeParameterName)
68
+ ```
69
+
70
+ might change even across minor versions.
71
+
72
+ You should not need to be specific about those messages anyways.
73
+ In case you'd like to be specific about `SmellWarnings` please have a look at
74
+ [accessing the smell warnings directly](#accessing-the-smell-warnings-directly).
75
+
76
+ Additionally you can use one of our structured [outputs formats](#choosing-your-output-format)
77
+ like JSON or YAML if you need a more fine-grained access to our
78
+ `SmellWarnings`.
79
+
53
80
  ## Choosing your output format
54
81
 
55
82
  Besides normal text output, Reek can generate output in YAML,
data/lib/reek/ast/node.rb CHANGED
@@ -43,7 +43,7 @@ module Reek
43
43
  #
44
44
  # target_type - the type to look for, e.g. :send, :block
45
45
  # ignoring - types to ignore, e.g. [:casgn, :class, :module]
46
- # blk - block to execute for every hit
46
+ # blk - block to execute for every hit. Gets passed in the matching element itself.
47
47
  #
48
48
  # Examples:
49
49
  # context.each_node(:send, [:mlhs]) do |call_node| .... end
@@ -22,7 +22,7 @@ module Reek
22
22
  private_attr_reader :ast
23
23
 
24
24
  def explicit_self_calls
25
- [:self, :zsuper, :ivar, :ivasgn].flat_map do |node_type|
25
+ [:self, :super, :zsuper, :ivar, :ivasgn].flat_map do |node_type|
26
26
  ast.each_node(node_type, STOP_NODES)
27
27
  end
28
28
  end
@@ -105,6 +105,7 @@ module Reek
105
105
  each_node(type, [:casgn, :class, :module], &blk)
106
106
  end
107
107
 
108
+ # See Reek::AST::Node for details.
108
109
  def each_node(type, ignoring, &blk)
109
110
  exp.each_node(type, ignoring, &blk)
110
111
  end
@@ -29,7 +29,7 @@ module Reek
29
29
  #
30
30
  # @return [Array<SmellWarning>]
31
31
  #
32
- def examine_context(ctx)
32
+ def inspect(ctx)
33
33
  attributes_in(ctx).map do |attribute, line|
34
34
  smell_warning(
35
35
  context: ctx,
@@ -23,7 +23,7 @@ module Reek
23
23
  # @return [Array<SmellWarning>]
24
24
  #
25
25
  # :reek:FeatureEnvy
26
- def examine_context(ctx)
26
+ def inspect(ctx)
27
27
  ctx.default_assignments.select do |_param, value|
28
28
  [:true, :false].include?(value.type)
29
29
  end.map do |parameter, _value|
@@ -23,7 +23,7 @@ module Reek
23
23
  #
24
24
  # @return [Array<SmellWarning>]
25
25
  #
26
- def examine_context(ctx)
26
+ def inspect(ctx)
27
27
  class_variables_in(ctx.exp).map do |variable, lines|
28
28
  smell_warning(
29
29
  context: ctx,
@@ -53,7 +53,7 @@ module Reek
53
53
  # @return [Array<SmellWarning>]
54
54
  #
55
55
  # :reek:FeatureEnvy
56
- def examine_context(ctx)
56
+ def inspect(ctx)
57
57
  ControlParameterCollector.new(ctx).control_parameters.map do |control_parameter|
58
58
  name = control_parameter.name.to_s
59
59
  smell_warning(
@@ -51,7 +51,7 @@ module Reek
51
51
  # @return [Array<SmellWarning>]
52
52
  #
53
53
  # :reek:FeatureEnvy
54
- def examine_context(ctx)
54
+ def inspect(ctx)
55
55
  max_copies = value(MAX_COPIES_KEY, ctx, DEFAULT_MAX_COPIES)
56
56
  min_clump_size = value(MIN_CLUMP_SIZE_KEY, ctx, DEFAULT_MIN_CLUMP_SIZE)
57
57
  MethodGroup.new(ctx, min_clump_size, max_copies).clumps.map do |clump, methods|
@@ -47,7 +47,7 @@ module Reek
47
47
  #
48
48
  # :reek:FeatureEnvy
49
49
  # :reek:DuplicateMethodCall: { max_calls: 2 }
50
- def examine_context(ctx)
50
+ def inspect(ctx)
51
51
  max_allowed_calls = value(MAX_ALLOWED_CALLS_KEY, ctx, DEFAULT_MAX_CALLS)
52
52
  allow_calls = value(ALLOW_CALLS_KEY, ctx, DEFAULT_ALLOW_CALLS)
53
53
 
@@ -45,7 +45,7 @@ module Reek
45
45
  #
46
46
  # @return [Array<SmellWarning>]
47
47
  #
48
- def examine_context(ctx)
48
+ def inspect(ctx)
49
49
  return [] unless ctx.references_self?
50
50
  envious_receivers(ctx).map do |name, refs|
51
51
  smell_warning(
@@ -18,7 +18,7 @@ module Reek
18
18
  #
19
19
  # @return [Array<SmellWarning>]
20
20
  #
21
- def examine_context(ctx)
21
+ def inspect(ctx)
22
22
  return [] if descriptive?(ctx) || ctx.namespace_module?
23
23
  expression = ctx.exp
24
24
  [smell_warning(
@@ -33,7 +33,7 @@ module Reek
33
33
  #
34
34
  # @return [Array<SmellWarning>]
35
35
  #
36
- def examine_context(ctx)
36
+ def inspect(ctx)
37
37
  max_allowed_params = value(MAX_ALLOWED_PARAMS_KEY, ctx, DEFAULT_MAX_ALLOWED_PARAMS)
38
38
  exp = ctx.exp
39
39
  count = exp.arg_names.length
@@ -29,7 +29,7 @@ module Reek
29
29
  #
30
30
  # :reek:FeatureEnvy
31
31
  # :reek:DuplicateMethodCall: { max_calls: 2 }
32
- def examine_context(ctx)
32
+ def inspect(ctx)
33
33
  max_allowed_params = value(MAX_ALLOWED_PARAMS_KEY,
34
34
  ctx,
35
35
  DEFAULT_MAX_ALLOWED_PARAMS)
@@ -20,7 +20,7 @@ module Reek
20
20
  # @return [Array<SmellWarning>]
21
21
  #
22
22
  # :reek:FeatureEnvy
23
- def examine_context(ctx)
23
+ def inspect(ctx)
24
24
  ctx.local_nodes(:def) do |node| # FIXME: also search for :defs?
25
25
  if node.name.to_s == 'initialize'
26
26
  return [
@@ -44,7 +44,7 @@ module Reek
44
44
  # @return [Array<SmellWarning>]
45
45
  #
46
46
  # :reek:TooManyStatements: { max_statements: 6 }
47
- def examine_context(ctx)
47
+ def inspect(ctx)
48
48
  configure_ignore_iterators(ctx)
49
49
  deepest_iterator = find_deepest_iterator ctx
50
50
  return [] unless deepest_iterator
@@ -12,7 +12,7 @@ module Reek
12
12
  'SimulatedPolymorphism'
13
13
  end
14
14
 
15
- def examine_context(ctx)
15
+ def inspect(ctx)
16
16
  call_node_finder = NodeFinder.new(ctx, :send, NilCallNodeDetector)
17
17
  case_node_finder = NodeFinder.new(ctx, :when, NilWhenNodeDetector)
18
18
  smelly_nodes = call_node_finder.smelly_nodes + case_node_finder.smelly_nodes
@@ -28,7 +28,7 @@ module Reek
28
28
  [:class]
29
29
  end
30
30
 
31
- def examine_context(ctx)
31
+ def inspect(ctx)
32
32
  ctx.node_instance_methods.map do |method_sexp|
33
33
  check_for_smells(method_sexp, ctx)
34
34
  end.compact
@@ -51,7 +51,7 @@ module Reek
51
51
  #
52
52
  # :reek:TooManyStatements: { max_statements: 6 }
53
53
  # :reek:DuplicateMethodCall: { max_calls: 2 }
54
- def examine_context(ctx)
54
+ def inspect(ctx)
55
55
  max_identical_ifs = value(MAX_IDENTICAL_IFS_KEY, ctx, DEFAULT_MAX_IFS)
56
56
  conditional_counts(ctx).select do |_key, lines|
57
57
  lines.length > max_identical_ifs
@@ -15,6 +15,8 @@ module Reek
15
15
  # :reek:TooManyMethods: { max_methods: 19 }
16
16
  # :reek:TooManyInstanceVariables: { max_instance_variables: 5 }
17
17
  class SmellDetector
18
+ attr_reader :config
19
+ private_attr_accessor :smells_found
18
20
  # The name of the config field that lists the names of code contexts
19
21
  # that should not be checked. Add this field to the config for each
20
22
  # smell that should ignore this code element.
@@ -24,32 +26,9 @@ module Reek
24
26
  # in any configuration file.
25
27
  DEFAULT_EXCLUDE_SET = []
26
28
 
27
- class << self
28
- def contexts
29
- [:def, :defs]
30
- end
31
-
32
- # :reek:UtilityFunction
33
- def default_config
34
- {
35
- SmellConfiguration::ENABLED_KEY => true,
36
- EXCLUDE_KEY => DEFAULT_EXCLUDE_SET.dup
37
- }
38
- end
39
-
40
- def inherited(subclass)
41
- subclasses << subclass
42
- end
43
-
44
- def descendants
45
- subclasses
46
- end
47
-
48
- private
49
-
50
- def subclasses
51
- @subclasses ||= []
52
- end
29
+ def initialize(config = {})
30
+ @config = SmellConfiguration.new self.class.default_config.merge(config)
31
+ @smells_found = []
53
32
  end
54
33
 
55
34
  def smell_category
@@ -60,52 +39,25 @@ module Reek
60
39
  self.class.smell_type
61
40
  end
62
41
 
63
- class << self
64
- def smell_category
65
- @smell_category ||= default_smell_category
66
- end
67
-
68
- def smell_type
69
- @smell_type ||= default_smell_category
70
- end
71
-
72
- def default_smell_category
73
- name.split(/::/)[-1]
74
- end
75
- end
76
-
77
- def initialize(config = {})
78
- config = self.class.default_config.merge(config)
79
- @config = SmellConfiguration.new(config)
80
- @smells_found = []
42
+ def contexts
43
+ self.class.contexts
81
44
  end
82
45
 
83
- def register(hooks)
84
- return unless config.enabled?
85
- self.class.contexts.each { |ctx| hooks[ctx] << self }
86
- end
87
-
88
- def examine(context)
89
- return unless enabled_for? context
46
+ def run_for(context)
47
+ return unless enabled_for?(context)
90
48
  return if exception?(context)
91
49
 
92
- sm = examine_context(context)
93
- self.smells_found += sm
50
+ self.smells_found = smells_found + inspect(context)
94
51
  end
95
52
 
96
- def report_on(report)
97
- smells_found.each { |smell| smell.report_on(report) }
53
+ def report_on(collector)
54
+ smells_found.each { |smell| smell.report_on(collector) }
98
55
  end
99
56
 
100
57
  def exception?(context)
101
58
  context.matches?(value(EXCLUDE_KEY, context, DEFAULT_EXCLUDE_SET))
102
59
  end
103
60
 
104
- protected
105
-
106
- # NOTE: Needs to be protected so += works for Ruby < 2.2
107
- attr_accessor :smells_found
108
-
109
61
  private
110
62
 
111
63
  def enabled_for?(context)
@@ -133,7 +85,39 @@ module Reek
133
85
  parameters: options.fetch(:parameters, {}))
134
86
  end
135
87
 
136
- private_attr_reader :config
88
+ class << self
89
+ def smell_category
90
+ @smell_category ||= default_smell_category
91
+ end
92
+
93
+ def smell_type
94
+ @smell_type ||= default_smell_category
95
+ end
96
+
97
+ def default_smell_category
98
+ name.split(/::/)[-1]
99
+ end
100
+
101
+ def contexts
102
+ [:def, :defs]
103
+ end
104
+
105
+ # :reek:UtilityFunction
106
+ def default_config
107
+ {
108
+ SmellConfiguration::ENABLED_KEY => true,
109
+ EXCLUDE_KEY => DEFAULT_EXCLUDE_SET.dup
110
+ }
111
+ end
112
+
113
+ def inherited(subclass)
114
+ descendants << subclass
115
+ end
116
+
117
+ def descendants
118
+ @descendants ||= []
119
+ end
120
+ end
137
121
  end
138
122
  end
139
123
  end
@@ -8,6 +8,8 @@ module Reek
8
8
  # Contains all the existing smells and exposes operations on them.
9
9
  #
10
10
  class SmellRepository
11
+ private_attr_reader :configuration, :smell_types, :detectors
12
+
11
13
  def self.smell_types
12
14
  Reek::Smells::SmellDetector.descendants.sort_by(&:name)
13
15
  end
@@ -23,38 +25,34 @@ module Reek
23
25
  configuration: {})
24
26
  @configuration = configuration
25
27
  @smell_types = smell_types
28
+ @detectors = smell_types.map { |klass| klass.new configuration_for(klass) }
26
29
  end
27
30
 
28
- def report_on(listener)
29
- detectors.each_value { |detector| detector.report_on(listener) }
31
+ def report_on(collector)
32
+ detectors.each { |detector| detector.report_on(collector) }
30
33
  end
31
34
 
32
35
  def examine(context)
33
- smell_listeners[context.type].each do |detector|
34
- detector.examine(context)
36
+ smell_detectors_for(context.type).each do |detector|
37
+ detector.run_for(context)
35
38
  end
36
39
  end
37
40
 
38
- def detectors
39
- @initialized_detectors ||= smell_types.map do |klass|
40
- { klass => klass.new(source_configuration_for(klass)) }
41
- end.reduce({}, :merge)
42
- end
43
-
44
41
  private
45
42
 
46
- private_attr_reader :configuration, :smell_types
47
-
48
- def source_configuration_for(klass)
49
- configuration[klass] || {}
43
+ def configuration_for(klass)
44
+ configuration.fetch klass, {}
50
45
  end
51
46
 
52
- # TODO: Make a method smell_detectors_for(scope)
53
- def smell_listeners
54
- @smell_listeners ||= Hash.new { |hash, key| hash[key] = [] }.tap do |listeners|
55
- detectors.each_value { |detector| detector.register(listeners) }
47
+ def smell_detectors_for(type)
48
+ enabled_detectors.select do |detector|
49
+ detector.contexts.include? type
56
50
  end
57
51
  end
52
+
53
+ def enabled_detectors
54
+ detectors.select { |detector| detector.config.enabled? }
55
+ end
58
56
  end
59
57
  end
60
58
  end
@@ -37,7 +37,7 @@ module Reek
37
37
  #
38
38
  # @return [Array<SmellWarning>]
39
39
  #
40
- def examine_context(ctx)
40
+ def inspect(ctx)
41
41
  max_allowed_ivars = value(MAX_ALLOWED_IVARS_KEY, ctx, DEFAULT_MAX_IVARS)
42
42
  count = ctx.local_nodes(:ivasgn).map { |ivasgn| ivasgn.children.first }.uniq.length
43
43
  return [] if count <= max_allowed_ivars
@@ -23,7 +23,7 @@ module Reek
23
23
  'LargeClass'
24
24
  end
25
25
 
26
- def self.contexts # :nodoc:
26
+ def self.contexts
27
27
  [:class]
28
28
  end
29
29
 
@@ -39,7 +39,7 @@ module Reek
39
39
  #
40
40
  # @return [Array<SmellWarning>]
41
41
  #
42
- def examine_context(ctx)
42
+ def inspect(ctx)
43
43
  max_allowed_methods = value(MAX_ALLOWED_METHODS_KEY, ctx, DEFAULT_MAX_METHODS)
44
44
  # TODO: Only checks instance methods!
45
45
  actual = ctx.node_instance_methods.length
@@ -31,7 +31,7 @@ module Reek
31
31
  #
32
32
  # @return [Array<SmellWarning>]
33
33
  #
34
- def examine_context(ctx)
34
+ def inspect(ctx)
35
35
  max_allowed_statements = value(MAX_ALLOWED_STATEMENTS_KEY,
36
36
  ctx,
37
37
  DEFAULT_MAX_STATEMENTS)
@@ -39,7 +39,7 @@ module Reek
39
39
  #
40
40
  # @return [Array<SmellWarning>]
41
41
  #
42
- def examine_context(context)
42
+ def inspect(context)
43
43
  name = context.name.to_s
44
44
  return [] if acceptable_name?(name: name, context: context)
45
45
 
@@ -49,7 +49,7 @@ module Reek
49
49
  #
50
50
  # @return [Array<SmellWarning>]
51
51
  #
52
- def examine_context(context)
52
+ def inspect(context)
53
53
  fully_qualified_name = context.full_name
54
54
  exp = context.exp
55
55
  module_name = exp.simple_name
@@ -40,7 +40,7 @@ module Reek
40
40
  #
41
41
  # @return [Array<SmellWarning>]
42
42
  #
43
- def examine_context(context)
43
+ def inspect(context)
44
44
  expression = context.exp
45
45
  expression.parameter_names.select do |name|
46
46
  sanitized_name = sanitize name
@@ -51,7 +51,7 @@ module Reek
51
51
  #
52
52
  # @return [Array<SmellWarning>]
53
53
  #
54
- def examine_context(ctx)
54
+ def inspect(ctx)
55
55
  self.reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
56
56
  self.accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
57
57
  variable_names(ctx.exp).select do |name, _lines|
@@ -18,7 +18,7 @@ module Reek
18
18
  # @return [Array<SmellWarning>]
19
19
  #
20
20
  # :reek:FeatureEnvy
21
- def examine_context(ctx)
21
+ def inspect(ctx)
22
22
  return [] if ctx.uses_super_with_implicit_arguments?
23
23
  ctx.unused_params.map do |param|
24
24
  name = param.name.to_s
@@ -57,7 +57,7 @@ module Reek
57
57
  #
58
58
  # :reek:FeatureEnvy
59
59
  # :reek:TooManyStatements: { max_statements: 6 }
60
- def examine_context(ctx)
60
+ def inspect(ctx)
61
61
  return [] if ctx.singleton_method?
62
62
  return [] if ctx.num_statements == 0
63
63
  return [] if ctx.references_self?
@@ -25,7 +25,7 @@ module Reek
25
25
  end
26
26
 
27
27
  def walk
28
- result.each do |element|
28
+ context_tree.each do |element|
29
29
  smell_repository.examine(element)
30
30
  end
31
31
  end
@@ -35,8 +35,26 @@ module Reek
35
35
  private_attr_accessor :element
36
36
  private_attr_reader :exp, :smell_repository
37
37
 
38
- def result
39
- @result ||= process(exp)
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)
40
58
  end
41
59
 
42
60
  def process(exp)
data/lib/reek/version.rb CHANGED
@@ -6,6 +6,6 @@ module Reek
6
6
  # @public
7
7
  module Version
8
8
  # @public
9
- STRING = '3.6.0'
9
+ STRING = '3.6.1'
10
10
  end
11
11
  end
data/reek.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.add_runtime_dependency 'unparser', '~> 0.2.2'
29
29
 
30
30
  s.add_development_dependency 'activesupport', '~> 4.2'
31
- s.add_development_dependency 'aruba', '~> 0.9.0'
31
+ s.add_development_dependency 'aruba', '~> 0.10.0'
32
32
  s.add_development_dependency 'ataru', '~> 0.2.0'
33
33
  s.add_development_dependency 'bundler', '~> 1.1'
34
34
  s.add_development_dependency 'cucumber', '~> 2.0'
@@ -16,6 +16,10 @@ RSpec.describe Reek::AST::ReferenceCollector do
16
16
  expect(refs_to_self('def simple() super; end')).to eq(1)
17
17
  end
18
18
 
19
+ it 'counts a call to super with arguments' do
20
+ expect(refs_to_self('def simple() super(); end')).to eq(1)
21
+ end
22
+
19
23
  it 'counts a local call' do
20
24
  expect(refs_to_self('def simple() to_s; end')).to eq(1)
21
25
  end
@@ -74,7 +74,7 @@ RSpec.describe Reek::Smells::BooleanParameter do
74
74
  let(:warning) do
75
75
  src = 'def cc(arga = true) end'
76
76
  ctx = Reek::Context::MethodContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
77
- detector.examine_context(ctx).first
77
+ detector.inspect(ctx).first
78
78
  end
79
79
 
80
80
  it_should_behave_like 'common fields set correctly'
@@ -12,19 +12,19 @@ RSpec.describe Reek::Smells::ClassVariable do
12
12
  context 'with no class variables' do
13
13
  it 'records nothing in the class' do
14
14
  exp = sexp(:class, :Fred)
15
- expect(detector.examine_context(Reek::Context::CodeContext.new(nil, exp))).to be_empty
15
+ expect(detector.inspect(Reek::Context::CodeContext.new(nil, exp))).to be_empty
16
16
  end
17
17
 
18
18
  it 'records nothing in the module' do
19
19
  exp = sexp(:module, :Fred)
20
- expect(detector.examine_context(Reek::Context::CodeContext.new(nil, exp))).to be_empty
20
+ expect(detector.inspect(Reek::Context::CodeContext.new(nil, exp))).to be_empty
21
21
  end
22
22
  end
23
23
 
24
24
  context 'with one class variable' do
25
25
  shared_examples_for 'one variable found' do
26
26
  let(:ast) { Reek::Source::SourceCode.from(src).syntax_tree }
27
- let(:smells) { detector.examine_context(Reek::Context::CodeContext.new(nil, ast)) }
27
+ let(:smells) { detector.inspect(Reek::Context::CodeContext.new(nil, ast)) }
28
28
 
29
29
  it 'records only that class variable' do
30
30
  expect(smells.length).to eq(1)
@@ -85,7 +85,7 @@ RSpec.describe Reek::Smells::ClassVariable do
85
85
  end
86
86
  EOS
87
87
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
88
- detector.examine_context(ctx).first
88
+ detector.inspect(ctx).first
89
89
  end
90
90
 
91
91
  it_should_behave_like 'common fields set correctly'
@@ -270,7 +270,7 @@ RSpec.describe Reek::Smells::ControlParameter do
270
270
  end
271
271
  EOS
272
272
  ctx = Reek::Context::MethodContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
273
- smells = detector.examine(ctx)
273
+ smells = detector.run_for(ctx)
274
274
  expect(smells.length).to eq(1)
275
275
  smells.first
276
276
  end
@@ -26,7 +26,7 @@ RSpec.shared_examples_for 'a data clump detector' do
26
26
  end
27
27
  EOS
28
28
  ctx = Reek::Context::ModuleContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
29
- build(:smell_detector, smell_type: :DataClump).examine_context(ctx)
29
+ build(:smell_detector, smell_type: :DataClump).inspect(ctx)
30
30
  end
31
31
 
32
32
  it 'records only the one smell' do
@@ -17,7 +17,7 @@ RSpec.describe Reek::Smells::DuplicateMethodCall do
17
17
  end
18
18
  EOS
19
19
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
20
- smells = detector.examine_context(ctx)
20
+ smells = detector.inspect(ctx)
21
21
  expect(smells.length).to eq(1)
22
22
  smells.first
23
23
  end
@@ -87,7 +87,7 @@ RSpec.describe Reek::Smells::LongParameterList do
87
87
  end
88
88
  EOS
89
89
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
90
- detector.examine_context(ctx).first
90
+ detector.inspect(ctx).first
91
91
  end
92
92
 
93
93
  it_should_behave_like 'common fields set correctly'
@@ -36,7 +36,7 @@ RSpec.describe Reek::Smells::LongYieldList do
36
36
  end
37
37
  EOS
38
38
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
39
- detector.examine_context(ctx).first
39
+ detector.inspect(ctx).first
40
40
  end
41
41
 
42
42
  it_should_behave_like 'common fields set correctly'
@@ -109,7 +109,7 @@ RSpec.describe Reek::Smells::NestedIterators do
109
109
  expect(src).to reek_of(:NestedIterators)
110
110
  end
111
111
 
112
- describe 'examine_context / warnings' do
112
+ describe 'inspect / warnings' do
113
113
  let(:detector) { build(:smell_detector, smell_type: :NestedIterators) }
114
114
 
115
115
  it 'reports correctly' do
@@ -120,7 +120,7 @@ RSpec.describe Reek::Smells::NestedIterators do
120
120
  end
121
121
  end
122
122
  EOS
123
- warnings = detector.examine_context(build(:method_context, source: source))
123
+ warnings = detector.inspect(build(:method_context, source: source))
124
124
  warning = warnings.first
125
125
 
126
126
  expect(warning.smell_category).to eq(Reek::Smells::NestedIterators.smell_category)
@@ -137,7 +137,7 @@ RSpec.describe Reek::Smells::NestedIterators do
137
137
  end
138
138
  EOS
139
139
 
140
- warnings = detector.examine_context(build(:method_context, source: source))
140
+ warnings = detector.inspect(build(:method_context, source: source))
141
141
  expect(warnings.size).to eq(1)
142
142
  warning = warnings.first
143
143
  expect(warning.parameters[:name]).to eq('bad')
@@ -150,7 +150,7 @@ RSpec.describe Reek::Smells::NestedIterators do
150
150
  @jim.each {|ting| ting.each {|piece| piece.each {|atom| atom.foo } } }
151
151
  end
152
152
  EOS
153
- warnings = detector.examine_context(build(:method_context, source: source))
153
+ warnings = detector.inspect(build(:method_context, source: source))
154
154
  expect(warnings.size).to eq(1)
155
155
  warning = warnings.first
156
156
 
@@ -273,7 +273,7 @@ RSpec.describe Reek::Smells::NestedIterators do
273
273
  end
274
274
  EOS
275
275
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
276
- detector.examine_context(ctx).first
276
+ detector.inspect(ctx).first
277
277
  end
278
278
 
279
279
  it_should_behave_like 'common fields set correctly'
@@ -13,7 +13,7 @@ RSpec.describe Reek::Smells::NilCheck do
13
13
  EOS
14
14
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
15
15
  detector = build(:smell_detector, smell_type: :NilCheck)
16
- smells = detector.examine_context(ctx)
16
+ smells = detector.inspect(ctx)
17
17
  expect(smells[0].lines).to eq [2]
18
18
  end
19
19
 
@@ -20,7 +20,7 @@ RSpec.describe Reek::Smells::PrimaDonnaMethod do
20
20
  end
21
21
 
22
22
  it 'should be reported' do
23
- smells = detector.examine_context(ctx)
23
+ smells = detector.inspect(ctx)
24
24
  warning = smells[0]
25
25
 
26
26
  expect(warning.smell_category).to eq('PrimaDonnaMethod')
@@ -22,9 +22,11 @@ RSpec.shared_examples_for 'common fields set correctly' do
22
22
  it 'reports the source' do
23
23
  expect(warning.source).to eq('string')
24
24
  end
25
+
25
26
  it 'reports the smell class' do
26
27
  expect(warning.smell_category).to eq(detector.smell_category)
27
28
  end
29
+
28
30
  it 'reports the smell sub class' do
29
31
  expect(warning.smell_type).to eq(detector.smell_type)
30
32
  end
@@ -85,7 +85,7 @@ RSpec.describe Reek::Smells::TooManyInstanceVariables do
85
85
  end
86
86
  EOS
87
87
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
88
- detector.examine_context(ctx).first
88
+ detector.inspect(ctx).first
89
89
  end
90
90
 
91
91
  it_should_behave_like 'common fields set correctly'
@@ -18,7 +18,7 @@ RSpec.describe Reek::Smells::TooManyMethods do
18
18
  EOS
19
19
  syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
20
20
  ctx = Reek::Context::ModuleContext.new(nil, syntax_tree)
21
- expect(detector.examine_context(ctx)).to be_empty
21
+ expect(detector.inspect(ctx)).to be_empty
22
22
  end
23
23
 
24
24
  it 'should report if we exceed max_methods' do
@@ -31,7 +31,7 @@ RSpec.describe Reek::Smells::TooManyMethods do
31
31
  EOS
32
32
  syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
33
33
  ctx = Reek::Context::ModuleContext.new(nil, syntax_tree)
34
- smells = detector.examine_context(ctx)
34
+ smells = detector.inspect(ctx)
35
35
  expect(smells.length).to eq(1)
36
36
  expect(smells[0].smell_type).to eq(described_class.smell_type)
37
37
  expect(smells[0].parameters[:count]).to eq(3)
@@ -54,7 +54,7 @@ RSpec.describe Reek::Smells::TooManyMethods do
54
54
  EOS
55
55
  syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
56
56
  ctx = Reek::Context::ModuleContext.new(nil, syntax_tree)
57
- expect(detector.examine_context(ctx)).to be_empty
57
+ expect(detector.inspect(ctx)).to be_empty
58
58
  end
59
59
  end
60
60
 
@@ -69,7 +69,7 @@ RSpec.describe Reek::Smells::TooManyMethods do
69
69
 
70
70
  syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
71
71
  ctx = Reek::Context::ModuleContext.new(nil, syntax_tree)
72
- warning = detector.examine_context(ctx)[0]
72
+ warning = detector.inspect(ctx)[0]
73
73
  expect(warning.source).to eq(source_name)
74
74
  expect(warning.smell_category).to eq(described_class.smell_category)
75
75
  expect(warning.smell_type).to eq(described_class.smell_type)
@@ -51,7 +51,7 @@ RSpec.describe Reek::Smells::TooManyStatements do
51
51
  ctx = double('method_context').as_null_object
52
52
  expect(ctx).to receive(:num_statements).and_return(num_statements)
53
53
  expect(ctx).to receive(:config_for).with(described_class).and_return({})
54
- detector.examine_context(ctx)
54
+ detector.inspect(ctx)
55
55
  end
56
56
 
57
57
  it 'reports only 1 smell' do
@@ -19,7 +19,7 @@ RSpec.describe Reek::Smells::UncommunicativeMethodName do
19
19
  let(:warning) do
20
20
  src = "def #{method_name}; end"
21
21
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
22
- detector.examine_context(ctx).first
22
+ detector.inspect(ctx).first
23
23
  end
24
24
 
25
25
  it_should_behave_like 'common fields set correctly'
@@ -28,7 +28,7 @@ RSpec.describe Reek::Smells::UncommunicativeModuleName do
28
28
  it 'reports a bad scoped name' do
29
29
  src = "#{type} Foo::X; end"
30
30
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
31
- smells = detector.examine_context(ctx)
31
+ smells = detector.inspect(ctx)
32
32
  expect(smells.length).to eq(1)
33
33
  expect(smells[0].smell_category).to eq(Reek::Smells::UncommunicativeModuleName.smell_category)
34
34
  expect(smells[0].smell_type).to eq(Reek::Smells::UncommunicativeModuleName.smell_type)
@@ -66,7 +66,7 @@ RSpec.describe Reek::Smells::UncommunicativeModuleName do
66
66
  let(:warning) do
67
67
  src = 'module Printer2; end'
68
68
  ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
69
- detector.examine_context(ctx).first
69
+ detector.inspect(ctx).first
70
70
  end
71
71
 
72
72
  it_should_behave_like 'common fields set correctly'
@@ -8,7 +8,7 @@ RSpec.describe Reek::Smells::UncommunicativeParameterName do
8
8
 
9
9
  it_should_behave_like 'SmellDetector'
10
10
 
11
- { 'obj.' => 'with a receiveer',
11
+ { 'obj.' => 'with a receiver',
12
12
  '' => 'without a receiver' }.each do |host, description|
13
13
  context "in a method definition #{description}" do
14
14
  it 'does not recognise *' do
@@ -77,7 +77,7 @@ RSpec.describe Reek::Smells::UncommunicativeParameterName do
77
77
  let(:warning) do
78
78
  src = 'def bad(good, bad2, good_again); basics(good, bad2, good_again); end'
79
79
  ctx = Reek::Context::MethodContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
80
- detector.examine_context(ctx).first
80
+ detector.inspect(ctx).first
81
81
  end
82
82
 
83
83
  it_should_behave_like 'common fields set correctly'
@@ -51,7 +51,7 @@ RSpec.describe Reek::Smells::UncommunicativeVariableName do
51
51
  src = 'def simple(fred) x = jim(45); x = y end'
52
52
  syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
53
53
  ctx = Reek::Context::CodeContext.new(nil, syntax_tree)
54
- smells = detector.examine_context(ctx)
54
+ smells = detector.inspect(ctx)
55
55
  expect(smells.length).to eq(1)
56
56
  expect(smells[0].smell_type).to eq(described_class.smell_type)
57
57
  expect(smells[0].parameters[:name]).to eq('x')
@@ -164,7 +164,7 @@ RSpec.describe Reek::Smells::UncommunicativeVariableName do
164
164
  EOS
165
165
  syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
166
166
  ctx = Reek::Context::CodeContext.new(nil, syntax_tree)
167
- detector.examine_context(ctx).first
167
+ detector.inspect(ctx).first
168
168
  end
169
169
 
170
170
  it_should_behave_like 'common fields set correctly'
@@ -180,7 +180,7 @@ RSpec.describe Reek::Smells::UncommunicativeVariableName do
180
180
  src = 'def self.bad() x2 = 4; end'
181
181
  syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
182
182
  ctx = Reek::Context::CodeContext.new(nil, syntax_tree)
183
- detector.examine_context(ctx).first
183
+ detector.inspect(ctx).first
184
184
  end
185
185
 
186
186
  it_should_behave_like 'common fields set correctly'
@@ -197,6 +197,10 @@ RSpec.describe Reek::Smells::UtilityFunction do
197
197
  expect('def child(arg) super; arg.to_s; end').not_to reek_of(:UtilityFunction)
198
198
  end
199
199
 
200
+ it 'does not report a method that calls super with arguments' do
201
+ expect('def child(arg) super(arg * 2); arg.to_s; end').not_to reek_of(:UtilityFunction)
202
+ end
203
+
200
204
  it 'should recognise a deep call' do
201
205
  src = <<-EOS
202
206
  class Red
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.6.0
4
+ version: 3.6.1
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-10-30 00:00:00.000000000 Z
14
+ date: 2015-11-13 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: parser
@@ -95,14 +95,14 @@ dependencies:
95
95
  requirements:
96
96
  - - "~>"
97
97
  - !ruby/object:Gem::Version
98
- version: 0.9.0
98
+ version: 0.10.0
99
99
  type: :development
100
100
  prerelease: false
101
101
  version_requirements: !ruby/object:Gem::Requirement
102
102
  requirements:
103
103
  - - "~>"
104
104
  - !ruby/object:Gem::Version
105
- version: 0.9.0
105
+ version: 0.10.0
106
106
  - !ruby/object:Gem::Dependency
107
107
  name: ataru
108
108
  requirement: !ruby/object:Gem::Requirement