reek 3.6.0 → 3.6.1

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