rubocop 0.38.0 → 0.39.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -3
  3. data/config/default.yml +13 -0
  4. data/config/enabled.yml +15 -3
  5. data/lib/rubocop.rb +2 -0
  6. data/lib/rubocop/ast_node/builder.rb +6 -0
  7. data/lib/rubocop/cli.rb +2 -2
  8. data/lib/rubocop/config.rb +13 -13
  9. data/lib/rubocop/config_loader.rb +5 -28
  10. data/lib/rubocop/config_loader_resolver.rb +42 -0
  11. data/lib/rubocop/cop/cop.rb +4 -5
  12. data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -1
  13. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +9 -2
  14. data/lib/rubocop/cop/mixin/statement_modifier.rb +5 -4
  15. data/lib/rubocop/cop/performance/case_when_splat.rb +1 -1
  16. data/lib/rubocop/cop/performance/count.rb +20 -0
  17. data/lib/rubocop/cop/performance/detect.rb +12 -0
  18. data/lib/rubocop/cop/performance/redundant_merge.rb +10 -1
  19. data/lib/rubocop/cop/performance/times_map.rb +14 -0
  20. data/lib/rubocop/cop/style/case_indentation.rb +14 -7
  21. data/lib/rubocop/cop/style/conditional_assignment.rb +221 -20
  22. data/lib/rubocop/cop/style/file_name.rb +31 -17
  23. data/lib/rubocop/cop/style/if_unless_modifier.rb +8 -0
  24. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +45 -0
  25. data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +3 -2
  26. data/lib/rubocop/cop/style/next.rb +6 -1
  27. data/lib/rubocop/cop/style/one_line_conditional.rb +37 -8
  28. data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -0
  29. data/lib/rubocop/cop/style/redundant_self.rb +1 -1
  30. data/lib/rubocop/cop/style/space_around_keyword.rb +10 -1
  31. data/lib/rubocop/cop/style/special_global_vars.rb +23 -14
  32. data/lib/rubocop/cop/style/zero_length_predicate.rb +25 -0
  33. data/lib/rubocop/formatter/html_formatter.rb +3 -3
  34. data/lib/rubocop/result_cache.rb +2 -2
  35. data/lib/rubocop/runner.rb +3 -3
  36. data/lib/rubocop/target_finder.rb +2 -6
  37. data/lib/rubocop/version.rb +1 -1
  38. metadata +6 -4
@@ -18,6 +18,11 @@ module RuboCop
18
18
  # # good
19
19
  # [].detect { |item| true }
20
20
  # [].reverse.detect { |item| true }
21
+ #
22
+ # `ActiveRecord` compatibility:
23
+ # `ActiveRecord` does not implement a `detect` method and `find` has its
24
+ # own meaning. Correcting ActiveRecord methods with this cop should be
25
+ # considered unsafe.
21
26
  class Detect < Cop
22
27
  MSG = 'Use `%s` instead of `%s.%s`.'.freeze
23
28
  REVERSE_MSG = 'Use `reverse.%s` instead of `%s.%s`.'.freeze
@@ -26,6 +31,7 @@ module RuboCop
26
31
  DANGEROUS_METHODS = [:first, :last].freeze
27
32
 
28
33
  def on_send(node)
34
+ return unless should_run?
29
35
  receiver, second_method, *args = *node
30
36
  return unless check_second_call(receiver, second_method, args)
31
37
 
@@ -67,6 +73,12 @@ module RuboCop
67
73
 
68
74
  private
69
75
 
76
+ def should_run?
77
+ !(cop_config['SafeMode'.freeze] ||
78
+ config['Rails'.freeze] &&
79
+ config['Rails'.freeze]['Enabled'.freeze])
80
+ end
81
+
70
82
  def check_second_call(receiver, method, args)
71
83
  receiver &&
72
84
  DANGEROUS_METHODS.include?(method) &&
@@ -17,10 +17,19 @@ module RuboCop
17
17
 
18
18
  def_node_matcher :redundant_merge, '(send $_ :merge! (hash $...))'
19
19
  def_node_matcher :modifier_flow_control, '[{if while until} #modifier?]'
20
+ def_node_matcher :each_with_object_node, <<-END
21
+ (block (send _ :each_with_object _) (args _ $_) ...)
22
+ END
20
23
 
21
24
  def on_send(node)
22
25
  redundant_merge(node) do |receiver, pairs|
23
- next if node.value_used?
26
+ if node.value_used?
27
+ parent = node.parent
28
+ grandparent = parent.parent if parent.begin_type?
29
+ second_arg = each_with_object_node(grandparent || parent)
30
+ next if second_arg.nil?
31
+ next unless receiver.loc.name.source == second_arg.loc.name.source
32
+ end
24
33
  next if pairs.size > 1 && !receiver.pure?
25
34
  next if pairs.size > max_key_value_pairs
26
35
 
@@ -42,6 +42,20 @@ module RuboCop
42
42
  {(block (send (send !nil :times) ${:map :collect}) ...)
43
43
  (send (send !nil :times) ${:map :collect} (block_pass ...))}
44
44
  END
45
+
46
+ def autocorrect(node)
47
+ send_node = node.send_type? ? node : node.each_descendant(:send).first
48
+
49
+ receiver, _method_name, *args = *send_node
50
+ count, = *receiver
51
+
52
+ replacement = "Array.new(#{count.source}" \
53
+ "#{args.map { |arg| ", #{arg.source}" }.join})"
54
+
55
+ lambda do |corrector|
56
+ corrector.replace(send_node.loc.expression, replacement)
57
+ end
58
+ end
45
59
  end
46
60
  end
47
61
  end
@@ -64,20 +64,27 @@ module RuboCop
64
64
  end
65
65
 
66
66
  def autocorrect(node)
67
+ whitespace = whitespace_range(node)
68
+ return false unless whitespace.source.strip.empty?
69
+
70
+ ->(corrector) { corrector.replace(whitespace, replacement(node)) }
71
+ end
72
+
73
+ def whitespace_range(node)
67
74
  when_column = node.location.keyword.column
68
- source_buffer = node.source_range.source_buffer
69
75
  begin_pos = node.loc.keyword.begin_pos
70
- whitespace = Parser::Source::Range.new(source_buffer,
71
- begin_pos - when_column,
72
- begin_pos)
73
- return false unless whitespace.source.strip.empty?
74
76
 
77
+ Parser::Source::Range.new(node.source_range.source_buffer,
78
+ begin_pos - when_column,
79
+ begin_pos)
80
+ end
81
+
82
+ def replacement(node)
75
83
  case_node = node.each_ancestor(:case).first
76
84
  base_type = cop_config[parameter_name] == 'end' ? :end : :case
77
85
  column = base_column(case_node, base_type)
78
86
  column += configured_indentation_width if cop_config['IndentOneStep']
79
-
80
- ->(corrector) { corrector.replace(whitespace, ' ' * column) }
87
+ ' ' * column
81
88
  end
82
89
  end
83
90
  end
@@ -28,13 +28,6 @@ module RuboCop
28
28
  when_branches.map { |branch| branch.children[1] }
29
29
  end
30
30
 
31
- def correct_branches(corrector, branches)
32
- branches.each do |branch|
33
- *_, assignment = *branch
34
- corrector.replace(branch.source_range, assignment.source)
35
- end
36
- end
37
-
38
31
  def tail(branch)
39
32
  branch.begin_type? ? [*branch].last : branch
40
33
  end
@@ -89,7 +82,7 @@ module RuboCop
89
82
  indices = node.children[2...-1].map(&:source).join(', ')
90
83
  "#{receiver}[#{indices}] = "
91
84
  elsif node.method_name.to_s.end_with?(EQUAL) &&
92
- ![:!=, :==].include?(node.method_name)
85
+ ![:!=, :==, :===, :>=, :<=].include?(node.method_name)
93
86
  "#{receiver}.#{node.method_name[0...-1]} = "
94
87
  else
95
88
  "#{receiver} #{node.method_name} "
@@ -102,6 +95,8 @@ module RuboCop
102
95
  # condition can be used instead.
103
96
  #
104
97
  # @example
98
+ # EnforcedStyle: assign_to_condition
99
+ #
105
100
  # # bad
106
101
  # if foo
107
102
  # bar = 1
@@ -145,12 +140,61 @@ module RuboCop
145
140
  # some_other_method
146
141
  # 2
147
142
  # end
143
+ #
144
+ # EnforcedStyle: assign_inside_condition
145
+ # # bad
146
+ # bar = if foo
147
+ # 1
148
+ # else
149
+ # 2
150
+ # end
151
+ #
152
+ # bar += case foo
153
+ # when 'a'
154
+ # 1
155
+ # else
156
+ # 2
157
+ # end
158
+ #
159
+ # bar << if foo
160
+ # some_method
161
+ # 1
162
+ # else
163
+ # some_other_method
164
+ # 2
165
+ # end
166
+ #
167
+ # # good
168
+ # if foo
169
+ # bar = 1
170
+ # else
171
+ # bar = 2
172
+ # end
173
+ #
174
+ # case foo
175
+ # when 'a'
176
+ # bar += 1
177
+ # else
178
+ # bar += 2
179
+ # end
180
+ #
181
+ # if foo
182
+ # some_method
183
+ # bar = 1
184
+ # else
185
+ # some_other_method
186
+ # bar = 2
187
+ # end
148
188
  class ConditionalAssignment < Cop
149
189
  include IfNode
150
190
  include ConditionalAssignmentHelper
191
+ include ConfigurableEnforcedStyle
192
+ include IgnoredNode
151
193
 
152
194
  MSG = 'Use the return of the conditional for variable assignment ' \
153
195
  'and comparison.'.freeze
196
+ ASSIGN_TO_CONDITION_MSG =
197
+ 'Assign variables inside of conditionals'.freeze
154
198
  VARIABLE_ASSIGNMENT_TYPES =
155
199
  [:casgn, :cvasgn, :gvasgn, :ivasgn, :lvasgn].freeze
156
200
  ASSIGNMENT_TYPES =
@@ -164,13 +208,40 @@ module RuboCop
164
208
  SINGLE_LINE_CONDITIONS_ONLY = 'SingleLineConditionsOnly'.freeze
165
209
  WIDTH = 'Width'.freeze
166
210
  METHODS = [:[]=, :<<, :=~, :!~, :<=>].freeze
211
+ CONDITION_TYPES = [:if, :case].freeze
167
212
 
168
- def lhs_all_match?(branches)
169
- first_lhs = lhs(branches.first)
170
- branches.all? { |branch| lhs(branch) == first_lhs }
213
+ ASSIGNMENT_TYPES.each do |type|
214
+ define_method "on_#{type}" do |node|
215
+ return if part_of_ignored_node?(node)
216
+ check_assignment_to_condition(node)
217
+ end
218
+ end
219
+
220
+ def on_send(node)
221
+ return unless assignment_type?(node)
222
+ check_assignment_to_condition(node)
223
+ end
224
+
225
+ def check_assignment_to_condition(node)
226
+ return unless style == :assign_inside_condition
227
+ ignore_node(node)
228
+ *_variable, assignment = *node
229
+ if assignment.respond_to?(:type)
230
+ if assignment.begin_type? && assignment.children.size == 1
231
+ assignment, = *assignment
232
+ end
233
+
234
+ return unless CONDITION_TYPES.include?(assignment.type)
235
+ end
236
+ _condition, *branches, else_branch = *assignment
237
+ return unless else_branch # empty else
238
+ return if single_line_conditions_only? &&
239
+ [*branches, else_branch].any?(&:begin_type?)
240
+ add_offense(node, :expression, ASSIGN_TO_CONDITION_MSG)
171
241
  end
172
242
 
173
243
  def on_if(node)
244
+ return unless style == :assign_to_condition
174
245
  return if elsif?(node)
175
246
 
176
247
  _condition, if_branch, else_branch = *node
@@ -183,6 +254,7 @@ module RuboCop
183
254
  end
184
255
 
185
256
  def on_case(node)
257
+ return unless style == :assign_to_condition
186
258
  _condition, *when_branches, else_branch = *node
187
259
  return unless else_branch # empty else
188
260
 
@@ -193,21 +265,42 @@ module RuboCop
193
265
  end
194
266
 
195
267
  def autocorrect(node)
268
+ if assignment_type?(node)
269
+ move_assignment_inside_condition(node)
270
+ else
271
+ move_assignment_outside_condition(node)
272
+ end
273
+ end
274
+
275
+ private
276
+
277
+ def move_assignment_outside_condition(node)
196
278
  if ternary_op?(node)
197
279
  TernaryCorrector.correct(node)
280
+ elsif node.loc.keyword.is?(IF)
281
+ IfCorrector.correct(self, node)
282
+ elsif node.loc.keyword.is?(UNLESS)
283
+ UnlessCorrector.correct(self, node)
198
284
  else
199
- case node.loc.keyword.source
200
- when IF
201
- IfCorrector.correct(self, node)
202
- when UNLESS
203
- UnlessCorrector.correct(self, node)
204
- else
205
- CaseCorrector.correct(self, node)
206
- end
285
+ CaseCorrector.correct(self, node)
207
286
  end
208
287
  end
209
288
 
210
- private
289
+ def move_assignment_inside_condition(node)
290
+ *_assignment, condition = *node
291
+ if ternary_op?(condition) || ternary_op?(condition.children[0])
292
+ TernaryCorrector.move_assignment_inside_condition(node)
293
+ elsif condition.case_type?
294
+ CaseCorrector.move_assignment_inside_condition(node)
295
+ elsif condition.if_type?
296
+ IfCorrector.move_assignment_inside_condition(node)
297
+ end
298
+ end
299
+
300
+ def lhs_all_match?(branches)
301
+ first_lhs = lhs(branches.first)
302
+ branches.all? { |branch| lhs(branch) == first_lhs }
303
+ end
211
304
 
212
305
  def assignment_types_match?(*nodes)
213
306
  return unless assignment_type?(nodes.first)
@@ -283,10 +376,47 @@ module RuboCop
283
376
  end
284
377
  end
285
378
 
379
+ # Helper module to provide common methods to ConditionalAssignment
380
+ # correctors
381
+ module ConditionalCorrectorHelper
382
+ def remove_whitespace_in_branches(corrector, branch, condition, column)
383
+ branch.each_node do |child|
384
+ child_expression = child.loc.expression
385
+ white_space =
386
+ Parser::Source::Range.new(child_expression.source_buffer,
387
+ child_expression.begin_pos -
388
+ (child.loc.expression.column -
389
+ column - 2),
390
+ child_expression.begin_pos)
391
+
392
+ corrector.remove(white_space) if white_space.source.strip.empty?
393
+ end
394
+
395
+ [condition.loc.else, condition.loc.end].each do |loc|
396
+ corrector.remove_preceding(loc, loc.column - column)
397
+ end
398
+ end
399
+
400
+ def assignment(node)
401
+ *_, condition = *node
402
+ Parser::Source::Range.new(node.loc.expression.source_buffer,
403
+ node.loc.expression.begin_pos,
404
+ condition.loc.expression.begin_pos)
405
+ end
406
+
407
+ def correct_branches(corrector, branches)
408
+ branches.each do |branch|
409
+ *_, assignment = *branch
410
+ corrector.replace(branch.source_range, assignment.source)
411
+ end
412
+ end
413
+ end
414
+
286
415
  # Corrector to correct conditional assignment in ternary conditions.
287
416
  class TernaryCorrector
288
417
  class << self
289
418
  include ConditionalAssignmentHelper
419
+ include ConditionalCorrectorHelper
290
420
 
291
421
  def correct(node)
292
422
  condition, if_branch, else_branch = *node
@@ -306,6 +436,25 @@ module RuboCop
306
436
  corrector.replace(node.source_range, correction)
307
437
  end
308
438
  end
439
+
440
+ def move_assignment_inside_condition(node)
441
+ *_var, rhs = *node
442
+ condition, = *rhs if rhs.begin_type? && rhs.children.size == 1
443
+ assignment = assignment(node)
444
+ _condition, if_branch, else_branch = *(condition || rhs)
445
+
446
+ lambda do |corrector|
447
+ corrector.remove(assignment)
448
+ if rhs.begin_type?
449
+ corrector.remove(rhs.loc.begin)
450
+ corrector.remove(rhs.loc.end)
451
+ end
452
+ corrector.insert_before(if_branch.loc.expression,
453
+ assignment.source)
454
+ corrector.insert_before(else_branch.loc.expression,
455
+ assignment.source)
456
+ end
457
+ end
309
458
  end
310
459
  end
311
460
 
@@ -313,6 +462,7 @@ module RuboCop
313
462
  class IfCorrector
314
463
  class << self
315
464
  include ConditionalAssignmentHelper
465
+ include ConditionalCorrectorHelper
316
466
 
317
467
  def correct(cop, node)
318
468
  _condition, if_branch, else_branch = *node
@@ -332,6 +482,31 @@ module RuboCop
332
482
  corrector.insert_before(node.loc.end, indent(cop, lhs(if_branch)))
333
483
  end
334
484
  end
485
+
486
+ def move_assignment_inside_condition(node)
487
+ column = node.loc.expression.column
488
+ *_var, condition = *node
489
+ _condition, if_branch, else_branch = *condition
490
+ elsif_branches, else_branch = expand_elses(else_branch)
491
+ assignment = assignment(node)
492
+
493
+ lambda do |corrector|
494
+ corrector.remove(assignment)
495
+
496
+ [if_branch, *elsif_branches, else_branch].each do |branch|
497
+ branch_assignment = tail(branch)
498
+ corrector.insert_before(branch_assignment.loc.expression,
499
+ assignment.source)
500
+
501
+ remove_whitespace_in_branches(corrector, branch,
502
+ condition, column)
503
+
504
+ corrector
505
+ .remove_preceding(branch.parent.loc.else,
506
+ branch.parent.loc.else.column - column)
507
+ end
508
+ end
509
+ end
335
510
  end
336
511
  end
337
512
 
@@ -339,6 +514,7 @@ module RuboCop
339
514
  class CaseCorrector
340
515
  class << self
341
516
  include ConditionalAssignmentHelper
517
+ include ConditionalCorrectorHelper
342
518
 
343
519
  def correct(cop, node)
344
520
  _condition, *when_branches, else_branch = *node
@@ -357,6 +533,31 @@ module RuboCop
357
533
  indent(cop, lhs(else_branch)))
358
534
  end
359
535
  end
536
+
537
+ def move_assignment_inside_condition(node)
538
+ column = node.loc.expression.column
539
+ *_var, condition = *node
540
+ _condition, *when_branches, else_branch = *condition
541
+ when_branches = expand_when_branches(when_branches)
542
+ assignment = assignment(node)
543
+
544
+ lambda do |corrector|
545
+ corrector.remove(assignment)
546
+
547
+ [*when_branches, else_branch].each do |branch|
548
+ branch_assignment = tail(branch)
549
+ corrector.insert_before(branch_assignment.loc.expression,
550
+ assignment.source)
551
+
552
+ remove_whitespace_in_branches(corrector, branch,
553
+ condition, column)
554
+
555
+ corrector
556
+ .remove_preceding(branch.parent.loc.keyword,
557
+ branch.parent.loc.keyword.column - column)
558
+ end
559
+ end
560
+ end
360
561
  end
361
562
  end
362
563