rubocop 1.56.2 → 1.57.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +12 -6
  4. data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
  5. data/lib/rubocop/cli.rb +1 -1
  6. data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
  7. data/lib/rubocop/cop/bundler/duplicated_group.rb +1 -1
  8. data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -22
  9. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
  10. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  11. data/lib/rubocop/cop/layout/dot_position.rb +1 -5
  12. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +41 -8
  13. data/lib/rubocop/cop/layout/end_alignment.rb +7 -1
  14. data/lib/rubocop/cop/layout/heredoc_indentation.rb +3 -0
  15. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  16. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
  17. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  18. data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
  19. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  20. data/lib/rubocop/cop/lint/debugger.rb +10 -1
  21. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  22. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  23. data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
  24. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +0 -1
  25. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  26. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +20 -4
  27. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
  28. data/lib/rubocop/cop/lint/useless_assignment.rb +37 -10
  29. data/lib/rubocop/cop/lint/void.rb +29 -11
  30. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  31. data/lib/rubocop/cop/metrics/class_length.rb +8 -3
  32. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  33. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  34. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  35. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  36. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
  37. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +5 -7
  38. data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -6
  39. data/lib/rubocop/cop/style/array_intersect.rb +13 -5
  40. data/lib/rubocop/cop/style/class_equality_comparison.rb +5 -0
  41. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  42. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  43. data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
  44. data/lib/rubocop/cop/style/for.rb +1 -1
  45. data/lib/rubocop/cop/style/format_string.rb +24 -3
  46. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  47. data/lib/rubocop/cop/style/identical_conditional_branches.rb +25 -3
  48. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  49. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  50. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  51. data/lib/rubocop/cop/style/operator_method_call.rb +6 -0
  52. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  53. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
  54. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
  55. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  56. data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
  57. data/lib/rubocop/cop/style/redundant_parentheses.rb +38 -14
  58. data/lib/rubocop/cop/style/return_nil.rb +6 -2
  59. data/lib/rubocop/cop/style/single_argument_dig.rb +2 -1
  60. data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
  61. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  62. data/lib/rubocop/cop/style/symbol_array.rb +10 -11
  63. data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
  64. data/lib/rubocop/file_finder.rb +4 -7
  65. data/lib/rubocop/formatter/html_formatter.rb +4 -2
  66. data/lib/rubocop/magic_comment.rb +12 -10
  67. data/lib/rubocop/rspec/shared_contexts.rb +2 -3
  68. data/lib/rubocop/server/cache.rb +1 -0
  69. data/lib/rubocop/version.rb +1 -1
  70. data/lib/rubocop.rb +1 -0
  71. metadata +6 -19
@@ -4,8 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for redundant safe navigation calls.
7
- # `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`, and `equal?` methods
8
- # are checked by default. These are customizable with `AllowedMethods` option.
7
+ # Use cases where a constant, named in camel case for classes and modules is `nil` are rare,
8
+ # and an offense is not detected when the receiver is a snake case constant.
9
+ #
10
+ # For all receivers, the `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`,
11
+ # and `equal?` methods are checked by default.
12
+ # These are customizable with `AllowedMethods` option.
9
13
  #
10
14
  # The `AllowedMethods` option specifies nil-safe methods,
11
15
  # in other words, it is a method that is allowed to skip safe navigation.
@@ -22,6 +26,9 @@ module RuboCop
22
26
  #
23
27
  # @example
24
28
  # # bad
29
+ # CamelCaseConst&.do_something
30
+ #
31
+ # # bad
25
32
  # do_something if attrs&.respond_to?(:[])
26
33
  #
27
34
  # # good
@@ -33,6 +40,9 @@ module RuboCop
33
40
  # end
34
41
  #
35
42
  # # good
43
+ # CamelCaseConst.do_something
44
+ #
45
+ # # good
36
46
  # while node.is_a?(BeginNode)
37
47
  # node = node.parent
38
48
  # end
@@ -57,18 +67,24 @@ module RuboCop
57
67
 
58
68
  NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
59
69
 
70
+ SNAKE_CASE = /\A[[:digit:][:upper:]_]+\z/.freeze
71
+
60
72
  # @!method respond_to_nil_specific_method?(node)
61
73
  def_node_matcher :respond_to_nil_specific_method?, <<~PATTERN
62
74
  (csend _ :respond_to? (sym %NIL_SPECIFIC_METHODS))
63
75
  PATTERN
64
76
 
77
+ # rubocop:disable Metrics/AbcSize
65
78
  def on_csend(node)
66
- return unless check?(node) && allowed_method?(node.method_name)
67
- return if respond_to_nil_specific_method?(node)
79
+ unless node.receiver.const_type? && !node.receiver.source.match?(SNAKE_CASE)
80
+ return unless check?(node) && allowed_method?(node.method_name)
81
+ return if respond_to_nil_specific_method?(node)
82
+ end
68
83
 
69
84
  range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
70
85
  add_offense(range) { |corrector| corrector.replace(node.loc.dot, '.') }
71
86
  end
87
+ # rubocop:enable Metrics/AbcSize
72
88
 
73
89
  private
74
90
 
@@ -82,16 +82,23 @@ module RuboCop
82
82
  def autocorrect(corrector, offense_range:, send_node:)
83
83
  corrector.replace(
84
84
  offense_range,
85
- add_safe_navigation_operator(
86
- offense_range: offense_range,
87
- send_node: send_node
88
- )
85
+ add_safe_navigation_operator(offense_range: offense_range, send_node: send_node)
89
86
  )
87
+
88
+ corrector.wrap(send_node, '(', ')') if require_parentheses?(send_node)
90
89
  end
91
90
 
92
91
  def brackets?(send_node)
93
92
  send_node.method?(:[]) || send_node.method?(:[]=)
94
93
  end
94
+
95
+ def require_parentheses?(send_node)
96
+ return false unless send_node.comparison_method?
97
+ return false unless (node = send_node.parent)
98
+
99
+ (node.respond_to?(:logical_operator?) && node.logical_operator?) ||
100
+ (node.respond_to?(:comparison_method?) && node.comparison_method?)
101
+ end
95
102
  end
96
103
  end
97
104
  end
@@ -7,12 +7,18 @@ module RuboCop
7
7
  # scope.
8
8
  # The basic idea for this cop was from the warning of `ruby -cw`:
9
9
  #
10
- # assigned but unused variable - foo
10
+ # [source,console]
11
+ # ----
12
+ # assigned but unused variable - foo
13
+ # ----
11
14
  #
12
15
  # Currently this cop has advanced logic that detects unreferenced
13
16
  # reassignments and properly handles varied cases such as branch, loop,
14
17
  # rescue, ensure, etc.
15
18
  #
19
+ # NOTE: Given the assignment `foo = 1, bar = 2`, removing unused variables
20
+ # can lead to a syntax error, so this case is not autocorrected.
21
+ #
16
22
  # @safety
17
23
  # This cop's autocorrection is unsafe because removing assignment from
18
24
  # operator assignment can cause NameError if this assignment has been used to declare
@@ -51,25 +57,24 @@ module RuboCop
51
57
  scope.variables.each_value { |variable| check_for_unused_assignments(variable) }
52
58
  end
53
59
 
60
+ # rubocop:disable Metrics/AbcSize
54
61
  def check_for_unused_assignments(variable)
55
62
  return if variable.should_be_unused?
56
63
 
57
64
  variable.assignments.each do |assignment|
58
- next if assignment.used?
65
+ next if assignment.used? || part_of_ignored_node?(assignment.node)
59
66
 
60
67
  message = message_for_useless_assignment(assignment)
68
+ range = offense_range(assignment)
61
69
 
62
- location = if assignment.regexp_named_capture?
63
- assignment.node.children.first.source_range
64
- else
65
- assignment.node.loc.name
66
- end
67
-
68
- add_offense(location, message: message) do |corrector|
69
- autocorrect(corrector, assignment)
70
+ add_offense(range, message: message) do |corrector|
71
+ autocorrect(corrector, assignment) unless sequential_assignment?(assignment.node)
70
72
  end
73
+
74
+ ignore_node(assignment.node) if chained_assignment?(assignment.node)
71
75
  end
72
76
  end
77
+ # rubocop:enable Metrics/AbcSize
73
78
 
74
79
  def message_for_useless_assignment(assignment)
75
80
  variable = assignment.variable
@@ -77,6 +82,28 @@ module RuboCop
77
82
  format(MSG, variable: variable.name) + message_specification(assignment, variable).to_s
78
83
  end
79
84
 
85
+ def offense_range(assignment)
86
+ if assignment.regexp_named_capture?
87
+ assignment.node.children.first.source_range
88
+ else
89
+ assignment.node.loc.name
90
+ end
91
+ end
92
+
93
+ def sequential_assignment?(node)
94
+ if node.lvasgn_type? && node.expression&.array_type? &&
95
+ node.each_descendant.any?(&:assignment?)
96
+ return true
97
+ end
98
+ return false unless node.parent
99
+
100
+ sequential_assignment?(node.parent)
101
+ end
102
+
103
+ def chained_assignment?(node)
104
+ node.respond_to?(:expression) && node.expression&.lvasgn_type?
105
+ end
106
+
80
107
  def message_specification(assignment, variable)
81
108
  if assignment.multiple_assignment?
82
109
  multiple_assignment_message(variable.name)
@@ -6,6 +6,16 @@ module RuboCop
6
6
  # Checks for operators, variables, literals, lambda, proc and nonmutating
7
7
  # methods used in void context.
8
8
  #
9
+ # `each` blocks are allowed to prevent false positives.
10
+ # For example, the expression inside the `each` block below.
11
+ # It's not void, especially when the receiver is an `Enumerator`:
12
+ #
13
+ # [source,ruby]
14
+ # ----
15
+ # enumerator = [1, 2, 3].filter
16
+ # enumerator.each { |item| item >= 2 } #=> [2, 3]
17
+ # ----
18
+ #
9
19
  # @example CheckForMethodsWithNoSideEffects: false (default)
10
20
  # # bad
11
21
  # def some_method
@@ -47,6 +57,7 @@ module RuboCop
47
57
 
48
58
  OP_MSG = 'Operator `%<op>s` used in void context.'
49
59
  VAR_MSG = 'Variable `%<var>s` used in void context.'
60
+ CONST_MSG = 'Constant `%<var>s` used in void context.'
50
61
  LIT_MSG = 'Literal `%<lit>s` used in void context.'
51
62
  SELF_MSG = '`self` used in void context.'
52
63
  EXPRESSION_MSG = '`%<expression>s` used in void context.'
@@ -72,6 +83,7 @@ module RuboCop
72
83
  return unless node.body && !node.body.begin_type?
73
84
  return unless in_void_context?(node.body)
74
85
 
86
+ check_void_op(node.body) { node.method?(:each) }
75
87
  check_expression(node.body)
76
88
  end
77
89
 
@@ -87,11 +99,13 @@ module RuboCop
87
99
  def check_begin(node)
88
100
  expressions = *node
89
101
  expressions.pop unless in_void_context?(node)
90
- expressions.each { |expr| check_expression(expr) }
102
+ expressions.each do |expr|
103
+ check_void_op(expr)
104
+ check_expression(expr)
105
+ end
91
106
  end
92
107
 
93
108
  def check_expression(expr)
94
- check_void_op(expr)
95
109
  check_literal(expr)
96
110
  check_var(expr)
97
111
  check_self(expr)
@@ -101,8 +115,9 @@ module RuboCop
101
115
  check_nonmutating(expr)
102
116
  end
103
117
 
104
- def check_void_op(node)
118
+ def check_void_op(node, &block)
105
119
  return unless node.send_type? && OPERATORS.include?(node.method_name)
120
+ return if block && yield(node)
106
121
 
107
122
  add_offense(node.loc.selector,
108
123
  message: format(OP_MSG, op: node.method_name)) do |corrector|
@@ -113,15 +128,18 @@ module RuboCop
113
128
  def check_var(node)
114
129
  return unless node.variable? || node.const_type?
115
130
 
116
- if node.const_type? && node.special_keyword?
117
- add_offense(node, message: format(VAR_MSG, var: node.source)) do |corrector|
118
- autocorrect_void_expression(corrector, node)
119
- end
131
+ if node.const_type?
132
+ template = node.special_keyword? ? VAR_MSG : CONST_MSG
133
+
134
+ offense_range = node
135
+ message = format(template, var: node.source)
120
136
  else
121
- add_offense(node.loc.name,
122
- message: format(VAR_MSG, var: node.loc.name.source)) do |corrector|
123
- autocorrect_void_expression(corrector, node)
124
- end
137
+ offense_range = node.loc.name
138
+ message = format(VAR_MSG, var: node.loc.name.source)
139
+ end
140
+
141
+ add_offense(offense_range, message: message) do |corrector|
142
+ autocorrect_void_expression(corrector, node)
125
143
  end
126
144
  end
127
145
 
@@ -12,6 +12,7 @@ module RuboCop
12
12
  # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
13
13
  # will be counted as one line regardless of its actual size.
14
14
  #
15
+ # NOTE: This cop does not apply for `Struct` definitions.
15
16
  #
16
17
  # NOTE: The `ExcludedMethods` configuration is deprecated and only kept
17
18
  # for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
@@ -40,7 +41,6 @@ module RuboCop
40
41
  # )
41
42
  # end # 6 points
42
43
  #
43
- # NOTE: This cop does not apply for `Struct` definitions.
44
44
  class BlockLength < Base
45
45
  include CodeLength
46
46
  include AllowedMethods
@@ -11,6 +11,8 @@ module RuboCop
11
11
  # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
12
  # will be counted as one line regardless of its actual size.
13
13
  #
14
+ # NOTE: This cop also applies for `Struct` definitions.
15
+ #
14
16
  # @example CountAsOne: ['array', 'heredoc', 'method_call']
15
17
  #
16
18
  # class Foo
@@ -34,15 +36,18 @@ module RuboCop
34
36
  # )
35
37
  # end # 6 points
36
38
  #
37
- #
38
- # NOTE: This cop also applies for `Struct` definitions.
39
39
  class ClassLength < Base
40
40
  include CodeLength
41
41
 
42
42
  def on_class(node)
43
43
  check_code_length(node)
44
44
  end
45
- alias on_sclass on_class
45
+
46
+ def on_sclass(node)
47
+ return if node.each_ancestor(:class).any?
48
+
49
+ on_class(node)
50
+ end
46
51
 
47
52
  def on_casgn(node)
48
53
  parent = node.parent
@@ -54,7 +54,7 @@ module RuboCop
54
54
  alias on_defs on_def
55
55
 
56
56
  def on_block(node)
57
- return unless node.send_node.method?(:define_method)
57
+ return unless node.method?(:define_method)
58
58
 
59
59
  check_code_length(node)
60
60
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  include Util
11
11
 
12
12
  FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
- CLASSLIKE_TYPES = %i[class module sclass].freeze
13
+ CLASSLIKE_TYPES = %i[class module].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
16
16
  def initialize(node, processed_source, count_comments: false, foldable_types: [])
@@ -145,7 +145,7 @@ module RuboCop
145
145
 
146
146
  def extract_body(node)
147
147
  case node.type
148
- when :class, :module, :block, :numblock, :def, :defs
148
+ when :class, :module, :sclass, :block, :numblock, :def, :defs
149
149
  node.body
150
150
  when :casgn
151
151
  _scope, _name, value = *node
@@ -62,25 +62,29 @@ module RuboCop
62
62
  # Returns the end line of a node, which might be a comment and not part of the AST
63
63
  # End line is considered either the line at which another node starts, or
64
64
  # the line at which the parent node ends.
65
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
65
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
66
66
  def find_end_line(node)
67
- if node.if_type? && node.else?
68
- node.loc.else.line
69
- elsif node.if_type? && node.ternary?
70
- node.else_branch.loc.line
71
- elsif node.if_type? && node.elsif?
72
- node.each_ancestor(:if).find(&:if?).loc.end.line
67
+ if node.if_type?
68
+ if node.else?
69
+ node.loc.else.line
70
+ elsif node.ternary?
71
+ node.else_branch.loc.line
72
+ elsif node.elsif?
73
+ node.each_ancestor(:if).find(&:if?).loc.end.line
74
+ end
73
75
  elsif node.block_type? || node.numblock_type?
74
76
  node.loc.end.line
75
77
  elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node)
76
78
  next_sibling.loc.line
77
79
  elsif (parent = node.parent)
78
- parent.loc.respond_to?(:end) && parent.loc.end ? parent.loc.end.line : parent.loc.line
79
- else
80
- node.loc.end.line
81
- end
80
+ if parent.loc.respond_to?(:end) && parent.loc.end
81
+ parent.loc.end.line
82
+ else
83
+ parent.loc.line
84
+ end
85
+ end || node.loc.end.line
82
86
  end
83
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
87
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
84
88
  end
85
89
  end
86
90
  end
@@ -48,18 +48,21 @@ module RuboCop
48
48
 
49
49
  def register_offense(node, message, replacement) # rubocop:disable Metrics/AbcSize
50
50
  add_offense(node.value, message: message) do |corrector|
51
- if (def_node = def_node_that_require_parentheses(node))
52
- last_argument = def_node.last_argument
53
- if last_argument.nil? || !last_argument.hash_type?
54
- next corrector.replace(node, replacement)
55
- end
56
-
57
- white_spaces = range_between(def_node.selector.end_pos,
58
- def_node.first_argument.source_range.begin_pos)
59
- corrector.replace(white_spaces, '(')
60
- corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last
61
- end
62
51
  corrector.replace(node, replacement)
52
+
53
+ next unless (def_node = def_node_that_require_parentheses(node))
54
+
55
+ last_argument = def_node.last_argument
56
+ if last_argument.nil? || !last_argument.hash_type?
57
+ next corrector.replace(node, replacement)
58
+ end
59
+
60
+ white_spaces = range_between(def_node.selector.end_pos,
61
+ def_node.first_argument.source_range.begin_pos)
62
+ next if node.parent.braces?
63
+
64
+ corrector.replace(white_spaces, '(')
65
+ corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last
63
66
  end
64
67
  end
65
68
 
@@ -20,16 +20,17 @@ module RuboCop
20
20
  range = offending_range(node, lhs, rhs, style)
21
21
  check(range, node, lhs, rhs)
22
22
  end
23
+ alias on_csend on_send
23
24
 
24
25
  private
25
26
 
26
- # In a chain of method calls, we regard the top send node as the base
27
+ # In a chain of method calls, we regard the top call node as the base
27
28
  # for indentation of all lines following the first. For example:
28
29
  # a.
29
30
  # b c { block }. <-- b is indented relative to a
30
31
  # d <-- d is indented relative to a
31
32
  def left_hand_side(lhs)
32
- while lhs.parent&.send_type? && lhs.parent.loc.dot && !lhs.parent.assignment_method?
33
+ while lhs.parent&.call_type? && lhs.parent.loc.dot && !lhs.parent.assignment_method?
33
34
  lhs = lhs.parent
34
35
  end
35
36
  lhs
@@ -75,7 +75,7 @@ module RuboCop
75
75
  end
76
76
 
77
77
  def aligned_token?(range, line)
78
- aligned_words?(range, line) || aligned_dot?(range, line) || aligned_assignment?(range, line)
78
+ aligned_words?(range, line) || aligned_assignment?(range, line)
79
79
  end
80
80
 
81
81
  def aligned_operator?(range, line)
@@ -83,13 +83,11 @@ module RuboCop
83
83
  end
84
84
 
85
85
  def aligned_words?(range, line)
86
- /\s\S/.match?(line[range.column - 1, 2])
87
- end
88
-
89
- def aligned_dot?(range, line)
90
- char = line[range.column]
86
+ left_edge = range.column
87
+ return true if /\s\S/.match?(line[left_edge - 1, 2])
91
88
 
92
- char == '.' && char == range.source[0]
89
+ token = range.source
90
+ token == line[left_edge, token.length]
93
91
  end
94
92
 
95
93
  def aligned_assignment?(range, line)
@@ -116,11 +116,11 @@ module RuboCop
116
116
  end
117
117
 
118
118
  def only_forwards_all?(send_classifications)
119
- send_classifications.each_value.all? { |c, _, _| c == :all }
119
+ send_classifications.all? { |_, c, _, _| c == :all }
120
120
  end
121
121
 
122
122
  def add_forward_all_offenses(node, send_classifications, forwardable_args)
123
- send_classifications.each do |send_node, (_c, forward_rest, _forward_kwrest)|
123
+ send_classifications.each do |send_node, _c, forward_rest, _forward_kwrest|
124
124
  register_forward_all_offense(send_node, send_node, forward_rest)
125
125
  end
126
126
 
@@ -133,7 +133,7 @@ module RuboCop
133
133
 
134
134
  rest_arg, kwrest_arg, _block_arg = *forwardable_args
135
135
 
136
- send_classifications.each do |send_node, (_c, forward_rest, forward_kwrest)|
136
+ send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
137
137
  if forward_rest
138
138
  register_forward_args_offense(def_node.arguments, rest_arg)
139
139
  register_forward_args_offense(send_node, forward_rest)
@@ -157,7 +157,7 @@ module RuboCop
157
157
  end
158
158
 
159
159
  def classify_send_nodes(def_node, send_nodes, referenced_lvars, forwardable_args)
160
- send_nodes.to_h do |send_node|
160
+ send_nodes.filter_map do |send_node|
161
161
  classification_and_forwards = classification_and_forwards(
162
162
  def_node,
163
163
  send_node,
@@ -165,8 +165,10 @@ module RuboCop
165
165
  forwardable_args
166
166
  )
167
167
 
168
- [send_node, classification_and_forwards]
169
- end.compact
168
+ next unless classification_and_forwards
169
+
170
+ [send_node, *classification_and_forwards]
171
+ end
170
172
  end
171
173
 
172
174
  def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
@@ -11,6 +11,15 @@ module RuboCop
11
11
  # The `array1.intersect?(array2)` method is faster than
12
12
  # `(array1 & array2).any?` and is more readable.
13
13
  #
14
+ # In cases like the following, compatibility is not ensured,
15
+ # so it will not be detected when using block argument.
16
+ #
17
+ # [source,ruby]
18
+ # ----
19
+ # ([1] & [1,2]).any? { |x| false } # => false
20
+ # [1].intersect?([1,2]) { |x| false } # => true
21
+ # ----
22
+ #
14
23
  # @safety
15
24
  # This cop cannot guarantee that `array1` and `array2` are
16
25
  # actually arrays while method `intersect?` is for arrays only.
@@ -68,16 +77,15 @@ module RuboCop
68
77
  RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
69
78
 
70
79
  def on_send(node)
80
+ return if (parent = node.parent) && (parent.block_type? || parent.numblock_type?)
71
81
  return unless (receiver, argument, method_name = bad_intersection_check?(node))
72
82
 
73
83
  message = message(receiver.source, argument.source, method_name)
74
84
 
75
85
  add_offense(node, message: message) do |corrector|
76
- if straight?(method_name)
77
- corrector.replace(node, "#{receiver.source}.intersect?(#{argument.source})")
78
- else
79
- corrector.replace(node, "!#{receiver.source}.intersect?(#{argument.source})")
80
- end
86
+ bang = straight?(method_name) ? '' : '!'
87
+
88
+ corrector.replace(node, "#{bang}#{receiver.source}.intersect?(#{argument.source})")
81
89
  end
82
90
  end
83
91
 
@@ -8,6 +8,11 @@ module RuboCop
8
8
  # `==`, `equal?`, and `eql?` custom method definitions are allowed by default.
9
9
  # These are customizable with `AllowedMethods` option.
10
10
  #
11
+ # @safety
12
+ # This cop's autocorrection is unsafe because there is no guarantee that
13
+ # the constant `Foo` exists when autocorrecting `var.class.name == 'Foo'` to
14
+ # `var.instance_of?(Foo)`.
15
+ #
11
16
  # @example
12
17
  # # bad
13
18
  # var.class == Date
@@ -25,6 +25,7 @@ module RuboCop
25
25
  # # bad
26
26
  # items.collect
27
27
  # items.collect!
28
+ # items.collect_concat
28
29
  # items.inject
29
30
  # items.detect
30
31
  # items.find_all
@@ -33,6 +34,7 @@ module RuboCop
33
34
  # # good
34
35
  # items.map
35
36
  # items.map!
37
+ # items.flat_map
36
38
  # items.reduce
37
39
  # items.find
38
40
  # items.select
@@ -93,8 +93,9 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def same_collection_looping_block?(node, sibling)
96
- (sibling&.block_type? || sibling&.numblock_type?) &&
97
- sibling.send_node.method?(node.method_name) &&
96
+ return false if sibling.nil? || (!sibling.block_type? && !sibling.numblock_type?)
97
+
98
+ sibling.method?(node.method_name) &&
98
99
  sibling.receiver == node.receiver &&
99
100
  sibling.send_node.arguments == node.send_node.arguments
100
101
  end
@@ -40,9 +40,13 @@ module RuboCop
40
40
  extend AutoCorrector
41
41
 
42
42
  MSG = 'Do not use empty `case` condition, instead use an `if` expression.'
43
+ NOT_SUPPORTED_PARENT_TYPES = %i[return break next send csend].freeze
43
44
 
45
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
44
46
  def on_case(case_node)
45
- return if case_node.condition
47
+ if case_node.condition || NOT_SUPPORTED_PARENT_TYPES.include?(case_node.parent&.type)
48
+ return
49
+ end
46
50
 
47
51
  branch_bodies = [*case_node.when_branches.map(&:body), case_node.else_branch].compact
48
52
 
@@ -52,6 +56,7 @@ module RuboCop
52
56
 
53
57
  add_offense(case_node.loc.keyword) { |corrector| autocorrect(corrector, case_node) }
54
58
  end
59
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
55
60
 
56
61
  private
57
62
 
@@ -80,7 +80,7 @@ module RuboCop
80
80
  private
81
81
 
82
82
  def suspect_enumerable?(node)
83
- node.multiline? && node.send_node.method?(:each) && !node.send_node.arguments?
83
+ node.multiline? && node.method?(:each) && !node.send_node.arguments?
84
84
  end
85
85
  end
86
86
  end