reek 4.7.1 → 4.7.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: baf611107a9ba063fb9882a312ae27c556f4a4f6
4
- data.tar.gz: f56386a2775e1a178a1df9af568055c5ac63c80f
3
+ metadata.gz: a57753b0b855d7182054e7551940eac44cf57f60
4
+ data.tar.gz: 245074bd6e150589a1bc7a69830a34e3db600a83
5
5
  SHA512:
6
- metadata.gz: d108db6dd4064eced4dfe277238771246120f73874f89ae3800e7bbef91b101d946861cb1793b7a138ce761bf4b26e854ee05b13ca5d39287c46b7c64027436a
7
- data.tar.gz: 2005c56063bdc5a62f3ec3fe53b73d99158b8b124bdbbb45a3d8ff5ab64ab35e2863afdace05d05b5538855fdc80ed4ca138e073b415ac38f7d9dab95ccbc382
6
+ metadata.gz: 4c422c0a25421d8d0abc337766f7b2defae5f7044137c3e5fb7c34fffea7ee409d165f56922f433565fa8643d716cdbb3e9a3c7cec568bfa50bf15b2eed8b254
7
+ data.tar.gz: d4b77922d2745ab3d7f6f053931193b543f646e605a0b3a7f7a444ddf24d3462df4bf7732a248f14fd49f9253925afbf441d3fda578df736b130bfbf80db5096
@@ -1,5 +1,11 @@
1
1
  # Change log
2
2
 
3
+ ## 4.7.2 (2017-07-24)
4
+
5
+ * (mvz) Also report unused uncommunicative parameter names
6
+ * (mvz) Track visibility correctly when using method definition visibility modifiers
7
+ * (mvz) Handle method comments when using method definition visibility modifiers
8
+
3
9
  ## 4.7.1 (2017-06-12)
4
10
 
5
11
  * (mvz) Improve IrresponsibleModule and fix some bugs along
@@ -41,7 +41,7 @@ Feature: Directory directives
41
41
  And a file named "web_app/app/models/user.rb" with:
42
42
  """
43
43
  class User < ActiveRecord::Base
44
- def logged_in_with_role(r)
44
+ def logged_in_with_role(role)
45
45
  true
46
46
  end
47
47
  end
@@ -53,7 +53,7 @@ Feature: Directory directives
53
53
  [1]:InstanceVariableAssumption: UsersController assumes too much for instance variable '@user' [https://github.com/troessner/reek/blob/master/docs/Instance-Variable-Assumption.md]
54
54
  web_app/app/models/user.rb -- 2 warnings:
55
55
  [1]:IrresponsibleModule: User has no descriptive comment [https://github.com/troessner/reek/blob/master/docs/Irresponsible-Module.md]
56
- [2]:UnusedParameters: User#logged_in_with_role has unused parameter 'r' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
56
+ [2]:UnusedParameters: User#logged_in_with_role has unused parameter 'role' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
57
57
  3 total warnings
58
58
  """
59
59
 
@@ -112,7 +112,7 @@ Feature: Directory directives
112
112
  And a file named "web_app/app/models/user.rb" with:
113
113
  """
114
114
  class User < ActiveRecord::Base
115
- def logged_in_with_role(r)
115
+ def logged_in_with_role(role)
116
116
  true
117
117
  end
118
118
  end
@@ -125,7 +125,7 @@ Feature: Directory directives
125
125
  [1]:IrresponsibleModule: UsersController has no descriptive comment [https://github.com/troessner/reek/blob/master/docs/Irresponsible-Module.md]
126
126
  [4]:NestedIterators: UsersController#show contains iterators nested 2 deep [https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md]
127
127
  web_app/app/models/user.rb -- 1 warning:
128
- [2]:UnusedParameters: User#logged_in_with_role has unused parameter 'r' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
128
+ [2]:UnusedParameters: User#logged_in_with_role has unused parameter 'role' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
129
129
  4 total warnings
130
130
  """
131
131
 
@@ -12,11 +12,8 @@ module Reek
12
12
  # methods to ease the transition from Sexp to AST::Node.
13
13
  #
14
14
  class Node < ::Parser::AST::Node
15
- attr_reader :parent
16
-
17
15
  def initialize(type, children = [], options = {})
18
16
  @comments = options.fetch(:comments, [])
19
- @parent = options.fetch(:parent, nil)
20
17
  super
21
18
  end
22
19
 
@@ -11,10 +11,10 @@ module Reek
11
11
  class AttributeContext < CodeContext
12
12
  attr_accessor :visibility
13
13
 
14
- def initialize(context, exp, send_expression)
14
+ def initialize(exp, send_expression)
15
15
  @visibility = :public
16
16
  @send_expression = send_expression
17
- super context, exp
17
+ super exp
18
18
  end
19
19
 
20
20
  def full_comment
@@ -26,39 +26,8 @@ module Reek
26
26
 
27
27
  # Initializes a new CodeContext.
28
28
  #
29
- # @param _parent [CodeContext, nil] The parent context
30
29
  # @param exp [Reek::AST::Node] The code described by this context
31
- #
32
- # For example, given the following code:
33
- #
34
- # class Omg
35
- # def foo(x)
36
- # puts x
37
- # end
38
- # end
39
- #
40
- # The {ContextBuilder} object first instantiates a {RootContext}, which has no parent.
41
- #
42
- # Next, it instantiates a {ModuleContext}, with +parent+ being the
43
- # {RootContext} just created, and +exp+ looking like this:
44
- #
45
- # (class
46
- # (const nil :Omg) nil
47
- # (def :foo
48
- # (args
49
- # (arg :x))
50
- # (send nil :puts
51
- # (lvar :x))))
52
- #
53
- # Finally, {ContextBuilder} will instantiate a {MethodContext}. This time,
54
- # +parent+ is the {ModuleContext} created above, and +exp+ is:
55
- #
56
- # (def :foo
57
- # (args
58
- # (arg :x))
59
- # (send nil :puts
60
- # (lvar :x)))
61
- def initialize(_parent, exp)
30
+ def initialize(exp)
62
31
  @exp = exp
63
32
  @children = []
64
33
  @statement_counter = StatementCounter.new
@@ -91,6 +60,44 @@ module Reek
91
60
  end
92
61
  end
93
62
 
63
+ # Link the present context to its parent.
64
+ #
65
+ # @param parent [Reek::AST::Node] The parent context of the code described by this context
66
+ #
67
+ # For example, given the following code:
68
+ #
69
+ # class Omg
70
+ # def foo(x)
71
+ # puts x
72
+ # end
73
+ # end
74
+ #
75
+ # The {ContextBuilder} object first instantiates a {RootContext}, which has no parent.
76
+ #
77
+ # Next, it instantiates a {ModuleContext}, with +exp+ looking like this:
78
+ #
79
+ # (class
80
+ # (const nil :Omg) nil
81
+ # (def :foo
82
+ # (args
83
+ # (arg :x))
84
+ # (send nil :puts
85
+ # (lvar :x))))
86
+ #
87
+ # It will then call #register_with_parent on the {ModuleContext}, passing
88
+ # in the parent {RootContext}.
89
+ #
90
+ # Finally, {ContextBuilder} will instantiate a {MethodContext}. This time,
91
+ # +exp+ is:
92
+ #
93
+ # (def :foo
94
+ # (args
95
+ # (arg :x))
96
+ # (send nil :puts
97
+ # (lvar :x)))
98
+ #
99
+ # Then it will call #register_with_parent on the {MethodContext}, passing
100
+ # in the parent {ModuleContext}.
94
101
  def register_with_parent(parent)
95
102
  @parent = parent.append_child_context(self) if parent
96
103
  end
@@ -12,9 +12,10 @@ module Reek
12
12
  attr_accessor :visibility
13
13
  attr_reader :refs
14
14
 
15
- def initialize(context, exp)
15
+ def initialize(exp, parent_exp)
16
+ @parent_exp = parent_exp
16
17
  @visibility = :public
17
- super
18
+ super exp
18
19
  end
19
20
 
20
21
  def references_self?
@@ -36,7 +37,7 @@ module Reek
36
37
  # stop at the `lvasgn` and not detect the contained `lvar`.
37
38
  # Hence we first get all `lvar` nodes followed by all `lvasgn` nodes.
38
39
  #
39
- (local_nodes(:lvar) + local_nodes(:lvasgn)).find { |node| node.var_name == param.to_sym }
40
+ (local_nodes(:lvar) + local_nodes(:lvasgn)).find { |node| node.var_name == param.name }
40
41
  end
41
42
 
42
43
  # :reek:FeatureEnvy
@@ -44,7 +45,7 @@ module Reek
44
45
  exp.arguments.reject do |param|
45
46
  param.anonymous_splat? ||
46
47
  param.marked_unused? ||
47
- uses_param?(param.plain_name)
48
+ uses_param?(param)
48
49
  end
49
50
  end
50
51
 
@@ -80,6 +81,17 @@ module Reek
80
81
  def non_public_visibility?
81
82
  visibility != :public
82
83
  end
84
+
85
+ def full_comment
86
+ own = super
87
+ return own unless own.empty?
88
+ return parent_exp.full_comment if parent_exp
89
+ ''
90
+ end
91
+
92
+ private
93
+
94
+ attr_reader :parent_exp
83
95
  end
84
96
  end
85
97
  end
@@ -13,9 +13,8 @@ module Reek
13
13
  class ModuleContext < CodeContext
14
14
  attr_reader :visibility_tracker
15
15
 
16
- def initialize(context, exp)
16
+ def initialize(exp)
17
17
  super
18
-
19
18
  @visibility_tracker = VisibilityTracker.new
20
19
  end
21
20
 
@@ -9,10 +9,6 @@ module Reek
9
9
  # A context wrapper representing the root of an abstract syntax tree.
10
10
  #
11
11
  class RootContext < CodeContext
12
- def initialize(exp)
13
- super(nil, exp)
14
- end
15
-
16
12
  def type
17
13
  :root
18
14
  end
@@ -10,9 +10,9 @@ module Reek
10
10
  class SendContext < CodeContext
11
11
  attr_reader :name
12
12
 
13
- def initialize(context, exp, name)
13
+ def initialize(exp, name)
14
14
  @name = name
15
- super context, exp
15
+ super exp
16
16
  end
17
17
  end
18
18
  end
@@ -22,6 +22,7 @@ module Reek
22
22
  #
23
23
  # :reek:TooManyMethods: { max_methods: 31 }
24
24
  # :reek:UnusedPrivateMethod: { exclude: [ !ruby/regexp /process_/ ] }
25
+ # :reek:DataClump
25
26
  class ContextBuilder
26
27
  attr_reader :context_tree
27
28
 
@@ -58,10 +59,10 @@ module Reek
58
59
  # RootContext -> children: 1 ModuleContext -> children: 1 MethodContext
59
60
  #
60
61
  # @return [Reek::Context::RootContext] tree of nested contexts
61
- def build(exp)
62
+ def build(exp, parent_exp = nil)
62
63
  context_processor = "process_#{exp.type}"
63
64
  if context_processor_exists?(context_processor)
64
- send(context_processor, exp)
65
+ send(context_processor, exp, parent_exp)
65
66
  else
66
67
  process exp
67
68
  end
@@ -71,12 +72,12 @@ module Reek
71
72
  # Handles every node for which we have no context_processor.
72
73
  #
73
74
  def process(exp)
74
- exp.children.grep(AST::Node).each(&method(:build))
75
+ exp.children.grep(AST::Node).each { |child| build(child, exp) }
75
76
  end
76
77
 
77
78
  # Handles `module` and `class` nodes.
78
79
  #
79
- def process_module(exp)
80
+ def process_module(exp, _parent)
80
81
  inside_new_context(Context::ModuleContext, exp) do
81
82
  process(exp)
82
83
  end
@@ -91,7 +92,7 @@ module Reek
91
92
  # class << self
92
93
  # end
93
94
  #
94
- def process_sclass(exp)
95
+ def process_sclass(exp, _parent)
95
96
  inside_new_context(Context::GhostContext, exp) do
96
97
  process(exp)
97
98
  end
@@ -103,9 +104,9 @@ module Reek
103
104
  #
104
105
  # Foo = Class.new Bar
105
106
  #
106
- def process_casgn(exp)
107
+ def process_casgn(exp, parent)
107
108
  if exp.defines_module?
108
- process_module(exp)
109
+ process_module(exp, parent)
109
110
  else
110
111
  process(exp)
111
112
  end
@@ -119,8 +120,8 @@ module Reek
119
120
  #
120
121
  # Given the above example we would count 2 statements overall.
121
122
  #
122
- def process_def(exp)
123
- inside_new_context(current_context.method_context_class, exp) do
123
+ def process_def(exp, parent)
124
+ inside_new_context(current_context.method_context_class, exp, parent) do
124
125
  increase_statement_count_by(exp.body)
125
126
  process(exp)
126
127
  end
@@ -134,8 +135,8 @@ module Reek
134
135
  #
135
136
  # Given the above example we would count 2 statements overall.
136
137
  #
137
- def process_defs(exp)
138
- inside_new_context(Context::SingletonMethodContext, exp) do
138
+ def process_defs(exp, parent)
139
+ inside_new_context(Context::SingletonMethodContext, exp, parent) do
139
140
  increase_statement_count_by(exp.body)
140
141
  process(exp)
141
142
  end
@@ -151,14 +152,14 @@ module Reek
151
152
  # we also record to what the method call is referring to
152
153
  # which we later use for smell detectors like FeatureEnvy.
153
154
  #
154
- def process_send(exp)
155
+ def process_send(exp, _parent)
156
+ process(exp)
155
157
  case current_context
156
158
  when Context::ModuleContext
157
159
  handle_send_for_modules exp
158
160
  when Context::MethodContext
159
161
  handle_send_for_methods exp
160
162
  end
161
- process(exp)
162
163
  end
163
164
 
164
165
  # Handles `op_asgn` nodes a.k.a. Ruby's assignment operators.
@@ -173,7 +174,7 @@ module Reek
173
174
  #
174
175
  # We record one reference to `x` given the example above.
175
176
  #
176
- def process_op_asgn(exp)
177
+ def process_op_asgn(exp, _parent)
177
178
  current_context.record_call_to(exp)
178
179
  process(exp)
179
180
  end
@@ -192,7 +193,7 @@ module Reek
192
193
  #
193
194
  # We record one reference to `self`.
194
195
  #
195
- def process_ivar(exp)
196
+ def process_ivar(exp, _parent)
196
197
  current_context.record_use_of_self
197
198
  process(exp)
198
199
  end
@@ -205,7 +206,7 @@ module Reek
205
206
  #
206
207
  # def self.foo; end
207
208
  #
208
- def process_self(_)
209
+ def process_self(_, _parent)
209
210
  current_context.record_use_of_self
210
211
  end
211
212
 
@@ -225,7 +226,7 @@ module Reek
225
226
  #
226
227
  # We record one reference to `self`.
227
228
  #
228
- def process_zsuper(_)
229
+ def process_zsuper(_, _parent)
229
230
  current_context.record_use_of_self
230
231
  end
231
232
 
@@ -249,7 +250,7 @@ module Reek
249
250
  #
250
251
  # We record one reference to `self`.
251
252
  #
252
- def process_super(exp)
253
+ def process_super(exp, _parent)
253
254
  current_context.record_use_of_self
254
255
  process(exp)
255
256
  end
@@ -262,7 +263,7 @@ module Reek
262
263
  #
263
264
  # Counts non-empty blocks as one statement.
264
265
  #
265
- def process_block(exp)
266
+ def process_block(exp, _parent)
266
267
  increase_statement_count_by(exp.block)
267
268
  process(exp)
268
269
  end
@@ -282,7 +283,7 @@ module Reek
282
283
  # At the end we subtract one statement because the surrounding context was already counted
283
284
  # as one (e.g. via `process_def`).
284
285
  #
285
- def process_begin(exp)
286
+ def process_begin(exp, _parent)
286
287
  increase_statement_count_by(exp.children)
287
288
  decrease_statement_count
288
289
  process(exp)
@@ -308,7 +309,7 @@ module Reek
308
309
  # `children[1]` refers to the `if` body (so `puts 'bingo'` from above) and
309
310
  # `children[2]` to the `else` body (so `3` from above), which might be nil.
310
311
  #
311
- def process_if(exp)
312
+ def process_if(exp, _parent)
312
313
  children = exp.children
313
314
  increase_statement_count_by(children[1])
314
315
  increase_statement_count_by(children[2])
@@ -331,7 +332,7 @@ module Reek
331
332
  #
332
333
  # `children[1]` below refers to the `while` body (so `puts 'bingo'` from above)
333
334
  #
334
- def process_while(exp)
335
+ def process_while(exp, _parent)
335
336
  increase_statement_count_by(exp.children[1])
336
337
  decrease_statement_count
337
338
  process(exp)
@@ -354,7 +355,7 @@ module Reek
354
355
  #
355
356
  # `children[2]` below refers to the `while` body (so `puts i` from above)
356
357
  #
357
- def process_for(exp)
358
+ def process_for(exp, _parent)
358
359
  increase_statement_count_by(exp.children[2])
359
360
  decrease_statement_count
360
361
  process(exp)
@@ -384,7 +385,7 @@ module Reek
384
385
  # `exp` would be the whole method body wrapped under a `rescue` node.
385
386
  # See `process_resbody` for additional reference.
386
387
  #
387
- def process_rescue(exp)
388
+ def process_rescue(exp, _parent)
388
389
  increase_statement_count_by(exp.children.first)
389
390
  decrease_statement_count
390
391
  process(exp)
@@ -412,7 +413,7 @@ module Reek
412
413
  # `exp` would be the whole `rescue` body.
413
414
  # See `process_rescue` for additional reference.
414
415
  #
415
- def process_resbody(exp)
416
+ def process_resbody(exp, _parent)
416
417
  increase_statement_count_by(exp.children[1..-1].compact)
417
418
  process(exp)
418
419
  end
@@ -434,7 +435,7 @@ module Reek
434
435
  # At the end we subtract one statement because the surrounding context was already counted
435
436
  # as one (e.g. via `process_def`).
436
437
  #
437
- def process_case(exp)
438
+ def process_case(exp, _parent)
438
439
  increase_statement_count_by(exp.else_body)
439
440
  decrease_statement_count
440
441
  process(exp)
@@ -460,7 +461,7 @@ module Reek
460
461
  #
461
462
  # Counts the `when` body.
462
463
  #
463
- def process_when(exp)
464
+ def process_when(exp, _parent)
464
465
  increase_statement_count_by(exp.body)
465
466
  process(exp)
466
467
  end
@@ -482,19 +483,19 @@ module Reek
482
483
  # yields to the given block and then restores the previous context.
483
484
  #
484
485
  # @param klass [Context::*Context] - context class
485
- # @param exp - current expression
486
+ # @param args - arguments for the class initializer
486
487
  # @yield block
487
488
  #
488
- def inside_new_context(klass, exp)
489
- new_context = append_new_context(klass, exp)
489
+ def inside_new_context(klass, *args)
490
+ new_context = append_new_context(klass, *args)
490
491
 
491
492
  orig, self.current_context = current_context, new_context
492
493
  yield
493
494
  self.current_context = orig
494
495
  end
495
496
 
496
- # Append a new child context to the current context but does not change the
497
- # current context.
497
+ # Appends a new child context to the current context but does not change
498
+ # the current context.
498
499
  #
499
500
  # @param klass [Context::*Context] - context class
500
501
  # @param args - arguments for the class initializer
@@ -502,7 +503,7 @@ module Reek
502
503
  # @return [Context::*Context] - the context that was appended
503
504
  #
504
505
  def append_new_context(klass, *args)
505
- klass.new(current_context, *args).tap do |new_context|
506
+ klass.new(*args).tap do |new_context|
506
507
  new_context.register_with_parent(current_context)
507
508
  end
508
509
  end
@@ -35,31 +35,25 @@ module Reek
35
35
  configuration: {})
36
36
  @configuration = configuration
37
37
  @smell_types = smell_types
38
- @detectors = smell_types.map { |klass| klass.new configuration_for(klass) }
39
38
  end
40
39
 
41
40
  def examine(context)
42
- smell_detectors_for(context.type).flat_map do |detector|
43
- detector.run_for(context)
41
+ smell_detectors_for(context.type).flat_map do |klass|
42
+ detector = klass.new configuration: configuration_for(klass), context: context
43
+ detector.run
44
44
  end
45
45
  end
46
46
 
47
47
  private
48
48
 
49
- attr_reader :configuration, :smell_types, :detectors
49
+ attr_reader :configuration, :smell_types
50
50
 
51
51
  def configuration_for(klass)
52
52
  configuration.fetch klass, {}
53
53
  end
54
54
 
55
55
  def smell_detectors_for(type)
56
- enabled_detectors.select do |detector|
57
- detector.contexts.include? type
58
- end
59
- end
60
-
61
- def enabled_detectors
62
- detectors.select { |detector| detector.config.enabled? }
56
+ smell_types.select { |detector| detector.contexts.include? type }
63
57
  end
64
58
  end
65
59
  end
@@ -28,29 +28,22 @@ module Reek
28
28
  # in any configuration file.
29
29
  DEFAULT_EXCLUDE_SET = [].freeze
30
30
 
31
- def initialize(config = {})
32
- @config = SmellConfiguration.new self.class.default_config.merge(config)
31
+ def initialize(configuration: {}, context: nil)
32
+ @config = SmellConfiguration.new self.class.default_config.merge(configuration)
33
+ @context = context
33
34
  end
34
35
 
35
36
  def smell_type
36
37
  self.class.smell_type
37
38
  end
38
39
 
39
- def contexts
40
- self.class.contexts
41
- end
42
-
43
- def run_for(context)
44
- return [] unless enabled_for?(context)
45
- return [] if exception?(context)
40
+ def run
41
+ return [] unless enabled?
42
+ return [] if exception?
46
43
 
47
44
  sniff(context)
48
45
  end
49
46
 
50
- def exception?(context)
51
- context.matches?(value(EXCLUDE_KEY, context))
52
- end
53
-
54
47
  def self.todo_configuration_for(smells)
55
48
  default_exclusions = default_config.fetch 'exclude'
56
49
  exclusions = default_exclusions + smells.map(&:context)
@@ -59,7 +52,13 @@ module Reek
59
52
 
60
53
  private
61
54
 
62
- def enabled_for?(context)
55
+ attr_reader :context
56
+
57
+ def exception?
58
+ context.matches?(value(EXCLUDE_KEY, context))
59
+ end
60
+
61
+ def enabled?
63
62
  config.enabled? && config_for(context)[SmellConfiguration::ENABLED_KEY] != false
64
63
  end
65
64
 
@@ -40,10 +40,12 @@ module Reek
40
40
  #
41
41
  def sniff(context)
42
42
  expression = context.exp
43
- expression.parameter_names.select do |name|
44
- sanitized_name = sanitize name
45
- uncommunicative_parameter_name?(name: sanitized_name, context: context)
46
- end.map do |name|
43
+
44
+ params = expression.parameters.select do |param|
45
+ uncommunicative_parameter?(parameter: param, context: context)
46
+ end
47
+
48
+ params.map(&:name).map do |name|
47
49
  smell_warning(
48
50
  context: context,
49
51
  lines: [expression.line],
@@ -54,11 +56,13 @@ module Reek
54
56
 
55
57
  private
56
58
 
57
- def uncommunicative_parameter_name?(name:, context:)
58
- !acceptable_name?(name: name, context: context) && context.uses_param?(name)
59
+ def uncommunicative_parameter?(parameter:, context:)
60
+ !acceptable_name?(parameter: parameter, context: context) &&
61
+ (context.uses_param?(parameter) || !parameter.marked_unused?)
59
62
  end
60
63
 
61
- def acceptable_name?(name:, context:)
64
+ def acceptable_name?(parameter:, context:)
65
+ name = parameter.plain_name
62
66
  accept_patterns(context).any? { |accept_pattern| name.match accept_pattern } ||
63
67
  reject_patterns(context).none? { |reject_pattern| name.match reject_pattern }
64
68
  end
@@ -70,11 +74,6 @@ module Reek
70
74
  def accept_patterns(context)
71
75
  Array value(ACCEPT_KEY, context)
72
76
  end
73
-
74
- # :reek:UtilityFunction
75
- def sanitize(name)
76
- name.to_s.gsub(/^[@\*\&]*/, '')
77
- end
78
77
  end
79
78
  end
80
79
  end
@@ -75,6 +75,7 @@ module Reek
75
75
 
76
76
  #
77
77
  # @param ctx [Context::ClassContext]
78
+ # @param method [Context::MethodContext]
78
79
  # @return [Boolean]
79
80
  #
80
81
  # :reek:FeatureEnvy
@@ -39,13 +39,13 @@ module Reek
39
39
  #
40
40
  # :reek:FeatureEnvy
41
41
  # :reek:TooManyStatements: { max_statements: 6 }
42
- def dress(sexp, comment_map, parent: nil)
42
+ def dress(sexp, comment_map)
43
43
  return sexp unless sexp.is_a? ::Parser::AST::Node
44
44
  type = sexp.type
45
- children = sexp.children.map { |child| dress(child, comment_map, parent: sexp) }
45
+ children = sexp.children.map { |child| dress(child, comment_map) }
46
46
  comments = comment_map[sexp]
47
47
  klass_map.klass_for(type).new(type, children,
48
- location: sexp.loc, comments: comments, parent: parent)
48
+ location: sexp.loc, comments: comments)
49
49
  end
50
50
 
51
51
  private
@@ -8,6 +8,6 @@ module Reek
8
8
  # @public
9
9
  module Version
10
10
  # @public
11
- STRING = '4.7.1'.freeze
11
+ STRING = '4.7.2'.freeze
12
12
  end
13
13
  end
@@ -4,7 +4,7 @@ require_lib 'reek/context/module_context'
4
4
 
5
5
  RSpec.describe Reek::Context::CodeContext do
6
6
  context 'name recognition' do
7
- let(:ctx) { described_class.new(nil, exp) }
7
+ let(:ctx) { described_class.new(exp) }
8
8
  let(:exp) { instance_double('Reek::AST::SexpExtensions::ModuleNode') }
9
9
  let(:exp_name) { 'random_name' }
10
10
  let(:full_name) { "::::::::::::::::::::#{exp_name}" }
@@ -43,9 +43,9 @@ RSpec.describe Reek::Context::CodeContext do
43
43
  end
44
44
 
45
45
  context 'when there is an outer' do
46
- let(:ctx) { described_class.new(outer, exp) }
46
+ let(:ctx) { described_class.new(exp) }
47
47
  let(:outer_name) { 'another_random sting' }
48
- let(:outer) { described_class.new(nil, instance_double('Reek::AST::Node')) }
48
+ let(:outer) { described_class.new(instance_double('Reek::AST::Node')) }
49
49
 
50
50
  before do
51
51
  ctx.register_with_parent outer
@@ -71,7 +71,7 @@ RSpec.describe Reek::Context::CodeContext do
71
71
  let(:ctx) do
72
72
  src = 'module Emptiness; end'
73
73
  ast = Reek::Source::SourceCode.from(src).syntax_tree
74
- described_class.new(nil, ast)
74
+ described_class.new(ast)
75
75
  end
76
76
 
77
77
  it 'yields no calls' do
@@ -99,7 +99,7 @@ RSpec.describe Reek::Context::CodeContext do
99
99
  let(:ctx) do
100
100
  src = "module Loneliness; def calloo; puts('hello') end; end"
101
101
  ast = Reek::Source::SourceCode.from(src).syntax_tree
102
- described_class.new(nil, ast)
102
+ described_class.new(ast)
103
103
  end
104
104
 
105
105
  it 'yields no ifs' do
@@ -149,7 +149,7 @@ RSpec.describe Reek::Context::CodeContext do
149
149
  end
150
150
  EOS
151
151
  ast = Reek::Source::SourceCode.from(src).syntax_tree
152
- ctx = described_class.new(nil, ast)
152
+ ctx = described_class.new(ast)
153
153
  expect(ctx.each_node(:if, []).length).to eq(3)
154
154
  end
155
155
  end
@@ -166,7 +166,7 @@ RSpec.describe Reek::Context::CodeContext do
166
166
  end
167
167
  let(:expression) { Reek::Source::SourceCode.from(src).syntax_tree }
168
168
  let(:outer) { nil }
169
- let(:context) { described_class.new(outer, expression) }
169
+ let(:context) { described_class.new(expression) }
170
170
  let(:sniffer) { class_double('Reek::SmellDetectors::BaseDetector') }
171
171
 
172
172
  before do
@@ -181,7 +181,7 @@ RSpec.describe Reek::Context::CodeContext do
181
181
  end
182
182
 
183
183
  context 'when there is an outer context' do
184
- let(:outer) { described_class.new(nil, instance_double('Reek::AST::Node')) }
184
+ let(:outer) { described_class.new(instance_double('Reek::AST::Node')) }
185
185
 
186
186
  before do
187
187
  allow(outer).to receive(:config_for).with(sniffer).and_return(
@@ -196,9 +196,9 @@ RSpec.describe Reek::Context::CodeContext do
196
196
  end
197
197
 
198
198
  describe '#register_with_parent' do
199
- let(:context) { described_class.new(nil, instance_double('Reek::AST::Node')) }
200
- let(:first_child) { described_class.new(context, instance_double('Reek::AST::Node')) }
201
- let(:second_child) { described_class.new(context, instance_double('Reek::AST::Node')) }
199
+ let(:context) { described_class.new(instance_double('Reek::AST::Node')) }
200
+ let(:first_child) { described_class.new(instance_double('Reek::AST::Node')) }
201
+ let(:second_child) { described_class.new(instance_double('Reek::AST::Node')) }
202
202
 
203
203
  it "appends the element to the parent context's list of children" do
204
204
  first_child.register_with_parent context
@@ -209,9 +209,9 @@ RSpec.describe Reek::Context::CodeContext do
209
209
  end
210
210
 
211
211
  describe '#each' do
212
- let(:context) { described_class.new(nil, instance_double('Reek::AST::Node')) }
213
- let(:first_child) { described_class.new(context, instance_double('Reek::AST::Node')) }
214
- let(:second_child) { described_class.new(context, instance_double('Reek::AST::Node')) }
212
+ let(:context) { described_class.new(instance_double('Reek::AST::Node')) }
213
+ let(:first_child) { described_class.new(instance_double('Reek::AST::Node')) }
214
+ let(:second_child) { described_class.new(instance_double('Reek::AST::Node')) }
215
215
 
216
216
  it 'yields each child' do
217
217
  first_child.register_with_parent context
@@ -4,53 +4,53 @@ require_lib 'reek/context/ghost_context'
4
4
 
5
5
  RSpec.describe Reek::Context::GhostContext do
6
6
  let(:exp) { instance_double('Reek::AST::Node') }
7
- let(:parent) { Reek::Context::CodeContext.new(nil, exp) }
7
+ let(:parent) { Reek::Context::CodeContext.new(exp) }
8
8
 
9
9
  describe '#register_with_parent' do
10
10
  it 'does not append itself to its parent' do
11
- ghost = described_class.new(parent, nil)
11
+ ghost = described_class.new(nil)
12
12
  ghost.register_with_parent(parent)
13
13
  expect(parent.children).not_to include ghost
14
14
  end
15
15
  end
16
16
 
17
17
  describe '#append_child_context' do
18
- let(:ghost) { described_class.new(parent, nil) }
18
+ let(:ghost) { described_class.new(nil) }
19
19
 
20
20
  before do
21
21
  ghost.register_with_parent(parent)
22
22
  end
23
23
 
24
24
  it 'appends the child to the grandparent context' do
25
- child = Reek::Context::CodeContext.new(ghost, sexp(:foo))
25
+ child = Reek::Context::CodeContext.new(sexp(:foo))
26
26
  child.register_with_parent(ghost)
27
27
 
28
28
  expect(parent.children).to include child
29
29
  end
30
30
 
31
31
  it "sets the child's parent to the grandparent context" do
32
- child = Reek::Context::CodeContext.new(ghost, sexp(:foo))
32
+ child = Reek::Context::CodeContext.new(sexp(:foo))
33
33
  child.register_with_parent(ghost)
34
34
 
35
35
  expect(child.parent).to eq parent
36
36
  end
37
37
 
38
38
  it 'appends the child to the list of children' do
39
- child = Reek::Context::CodeContext.new(ghost, sexp(:foo))
39
+ child = Reek::Context::CodeContext.new(sexp(:foo))
40
40
  child.register_with_parent(ghost)
41
41
 
42
42
  expect(ghost.children).to include child
43
43
  end
44
44
 
45
45
  context 'if the grandparent is also a ghost' do
46
- let(:child_ghost) { described_class.new(ghost, nil) }
46
+ let(:child_ghost) { described_class.new(nil) }
47
47
 
48
48
  before do
49
49
  child_ghost.register_with_parent(ghost)
50
50
  end
51
51
 
52
52
  it 'sets the childs parent to its remote ancestor' do
53
- child = Reek::Context::CodeContext.new(child_ghost, sexp(:foo))
53
+ child = Reek::Context::CodeContext.new(sexp(:foo))
54
54
  child.register_with_parent(child_ghost)
55
55
 
56
56
  expect(child.parent).to eq parent
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
2
2
  require_lib 'reek/context/method_context'
3
3
 
4
4
  RSpec.describe Reek::Context::MethodContext do
5
- let(:method_context) { described_class.new(nil, exp) }
5
+ let(:method_context) { described_class.new(exp, nil) }
6
6
 
7
7
  describe '#matches?' do
8
8
  let(:exp) { instance_double('Reek::AST::SexpExtensions::ModuleNode').as_null_object }
@@ -31,7 +31,7 @@ RSpec.describe Reek::Context::MethodContext do
31
31
  describe '#default_assignments' do
32
32
  def assignments_from(src)
33
33
  exp = Reek::Source::SourceCode.from(src).syntax_tree
34
- ctx = Reek::Context::MethodContext.new(nil, exp)
34
+ ctx = Reek::Context::MethodContext.new(exp, nil)
35
35
  ctx.default_assignments
36
36
  end
37
37
 
@@ -32,9 +32,9 @@ RSpec.describe Reek::Context::ModuleContext do
32
32
  let(:first_def) { instance_double('Reek::AST::SexpExtensions::DefNode', name: :foo) }
33
33
  let(:second_def) { instance_double('Reek::AST::SexpExtensions::DefNode') }
34
34
 
35
- let(:context) { described_class.new(nil, main_exp) }
36
- let(:first_child) { Reek::Context::MethodContext.new(context, first_def) }
37
- let(:second_child) { Reek::Context::MethodContext.new(context, second_def) }
35
+ let(:context) { described_class.new(main_exp) }
36
+ let(:first_child) { Reek::Context::MethodContext.new(first_def, main_exp) }
37
+ let(:second_child) { Reek::Context::MethodContext.new(second_def, main_exp) }
38
38
 
39
39
  it 'sets visibility on subsequent child contexts' do
40
40
  context.append_child_context first_child
@@ -220,6 +220,26 @@ RSpec.describe Reek::ContextBuilder do
220
220
  described_class.new(syntax_tree(code)).context_tree
221
221
  end
222
222
 
223
+ it 'marks instance methods when using a def modifier' do
224
+ code = <<-EOS
225
+ class Foo
226
+ private def bar
227
+ end
228
+
229
+ def baz
230
+ end
231
+ end
232
+ EOS
233
+
234
+ root = context_tree_for(code)
235
+ module_context = root.children.first
236
+ method_contexts = module_context.children
237
+ aggregate_failures do
238
+ expect(method_contexts[0].visibility).to eq :private
239
+ expect(method_contexts[1].visibility).to eq :public
240
+ end
241
+ end
242
+
223
243
  it 'does not mark class methods with instance visibility' do
224
244
  code = <<-EOS
225
245
  class Foo
@@ -49,17 +49,6 @@ RSpec.describe Reek::SmellDetectors::IrresponsibleModule do
49
49
  expect(src).not_to reek_of(:IrresponsibleModule)
50
50
  end
51
51
 
52
- it "does not report re-opened #{scope} in the same file" do
53
- src = <<-EOS
54
- # This comment describes Alfa
55
- #{scope} Alfa; end
56
-
57
- #{scope} Alfa; def bravo; end; end
58
- EOS
59
-
60
- expect(src).not_to reek_of(:IrresponsibleModule)
61
- end
62
-
63
52
  it "reports a #{scope} with an empty comment" do
64
53
  src = <<-EOS
65
54
  #
@@ -32,15 +32,6 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeParameterName do
32
32
  { 'alfa.' => 'with a receiver',
33
33
  '' => 'without a receiver' }.each do |host, description|
34
34
  context "in a method definition #{description}" do
35
- it 'does not recognise *' do
36
- expect("def #{host}bravo(*); end").not_to reek_of(:UncommunicativeParameterName)
37
- end
38
-
39
- it 'does not report unused parameters' do
40
- src = "def #{host}bravo(x); charlie; end"
41
- expect(src).not_to reek_of(:UncommunicativeParameterName)
42
- end
43
-
44
35
  it 'does not report two-letter parameter names' do
45
36
  src = "def #{host}bravo(ab); charlie(ab); end"
46
37
  expect(src).not_to reek_of(:UncommunicativeParameterName)
@@ -56,6 +47,26 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeParameterName do
56
47
  expect(src).to reek_of(:UncommunicativeParameterName, name: 'param2')
57
48
  end
58
49
 
50
+ it 'reports unused parameters' do
51
+ src = "def #{host}bravo(x); charlie; end"
52
+ expect(src).to reek_of(:UncommunicativeParameterName)
53
+ end
54
+
55
+ it 'reports splat parameters' do
56
+ expect("def #{host}bravo(*a); charlie(a); end").
57
+ to reek_of(:UncommunicativeParameterName, name: 'a')
58
+ end
59
+
60
+ it 'reports double splat parameters' do
61
+ expect("def #{host}bravo(**a); charlie(a); end").
62
+ to reek_of(:UncommunicativeParameterName, name: 'a')
63
+ end
64
+
65
+ it 'reports block parameters' do
66
+ expect("def #{host}bravo(&a); charlie(a); end").
67
+ to reek_of(:UncommunicativeParameterName, name: 'a')
68
+ end
69
+
59
70
  it 'does not report unused anonymous parameter' do
60
71
  src = "def #{host}bravo(_); charlie; end"
61
72
  expect(src).not_to reek_of(:UncommunicativeParameterName)
@@ -63,12 +74,20 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeParameterName do
63
74
 
64
75
  it 'reports used anonymous parameter' do
65
76
  src = "def #{host}bravo(_); charlie(_) end"
66
- expect(src).to reek_of(:UncommunicativeParameterName)
77
+ expect(src).to reek_of(:UncommunicativeParameterName, name: '_')
67
78
  end
68
79
 
69
80
  it 'reports used parameters marked as unused' do
70
81
  src = "def #{host}bravo(_unused) charlie(_unused) end"
71
- expect(src).to reek_of(:UncommunicativeParameterName)
82
+ expect(src).to reek_of(:UncommunicativeParameterName, name: '_unused')
83
+ end
84
+
85
+ it 'does not report anonymous splat' do
86
+ expect("def #{host}bravo(*); end").not_to reek_of(:UncommunicativeParameterName)
87
+ end
88
+
89
+ it 'does not report anonymous double splat' do
90
+ expect("def #{host}bravo(**); end").not_to reek_of(:UncommunicativeParameterName)
72
91
  end
73
92
 
74
93
  it 'reports names inside array decomposition' do
@@ -103,6 +103,17 @@ RSpec.describe Reek::SmellDetectors::UnusedPrivateMethod do
103
103
  to reek_of(:UnusedPrivateMethod, name: 'bravo', lines: [3]).
104
104
  and reek_of(:UnusedPrivateMethod, name: 'charlie', lines: [4])
105
105
  end
106
+
107
+ it 'reports instance methods defined as private with a modifier' do
108
+ source = <<-EOF
109
+ class Alfa
110
+ private def bravo; end
111
+ end
112
+ EOF
113
+
114
+ expect(source).
115
+ to reek_of(:UnusedPrivateMethod, name: 'bravo')
116
+ end
106
117
  end
107
118
 
108
119
  describe 'configuring the detector via source code comment' do
@@ -221,6 +221,18 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
221
221
 
222
222
  expect(src).not_to reek_of(:UtilityFunction).with_config(config)
223
223
  end
224
+
225
+ it 'does not report UtilityFunction when private is used as a def modifier' do
226
+ src = <<-EOS
227
+ class Alfa
228
+ private def bravo(charlie)
229
+ charlie.delta.echo
230
+ end
231
+ end
232
+ EOS
233
+
234
+ expect(src).not_to reek_of(:UtilityFunction).with_config(config)
235
+ end
224
236
  end
225
237
 
226
238
  context 'protected methods' do
@@ -236,6 +248,46 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
236
248
 
237
249
  expect(src).not_to reek_of(:UtilityFunction).with_config(config)
238
250
  end
251
+
252
+ it 'does not report UtilityFunction when protected is used as a def modifier' do
253
+ src = <<-EOS
254
+ class Alfa
255
+ protected def bravo(charlie)
256
+ charlie.delta.echo
257
+ end
258
+ end
259
+ EOS
260
+
261
+ expect(src).not_to reek_of(:UtilityFunction).with_config(config)
262
+ end
263
+ end
264
+ end
265
+
266
+ describe 'disabling with a comment' do
267
+ it 'disables the method following the comment' do
268
+ src = <<-EOS
269
+ class Alfa
270
+ # :reek:UtilityFunction
271
+ def bravo(charlie)
272
+ charlie.delta.echo
273
+ end
274
+ end
275
+ EOS
276
+
277
+ expect(src).not_to reek_of(:UtilityFunction)
278
+ end
279
+
280
+ it 'disables a method when it has a visibility modifier' do
281
+ src = <<-EOS
282
+ class Alfa
283
+ # :reek:UtilityFunction
284
+ private def bravo(charlie)
285
+ charlie.delta.echo
286
+ end
287
+ end
288
+ EOS
289
+
290
+ expect(src).not_to reek_of(:UtilityFunction)
239
291
  end
240
292
  end
241
293
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reek
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.7.1
4
+ version: 4.7.2
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: 2017-06-12 00:00:00.000000000 Z
14
+ date: 2017-07-24 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: codeclimate-engine-rb
@@ -443,7 +443,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
443
443
  version: '0'
444
444
  requirements: []
445
445
  rubyforge_project:
446
- rubygems_version: 2.5.1
446
+ rubygems_version: 2.6.12
447
447
  signing_key:
448
448
  specification_version: 4
449
449
  summary: Code smell detector for Ruby