rubocop 1.39.0 → 1.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +47 -9
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop/comment_config.rb +5 -0
  6. data/lib/rubocop/config.rb +5 -4
  7. data/lib/rubocop/config_loader.rb +5 -5
  8. data/lib/rubocop/config_loader_resolver.rb +1 -1
  9. data/lib/rubocop/cop/base.rb +2 -9
  10. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
  11. data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
  12. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  13. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  14. data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
  15. data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
  16. data/lib/rubocop/cop/lint/empty_block.rb +1 -5
  17. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  18. data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
  19. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
  20. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +13 -3
  21. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +15 -6
  22. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
  23. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +4 -3
  24. data/lib/rubocop/cop/lint/void.rb +6 -6
  25. data/lib/rubocop/cop/metrics/block_length.rb +9 -4
  26. data/lib/rubocop/cop/metrics/class_length.rb +9 -4
  27. data/lib/rubocop/cop/metrics/method_length.rb +9 -4
  28. data/lib/rubocop/cop/metrics/module_length.rb +9 -4
  29. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -2
  30. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +24 -5
  31. data/lib/rubocop/cop/mixin/statement_modifier.rb +15 -1
  32. data/lib/rubocop/cop/registry.rb +23 -11
  33. data/lib/rubocop/cop/style/array_intersect.rb +111 -0
  34. data/lib/rubocop/cop/style/guard_clause.rb +32 -5
  35. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -1
  36. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  37. data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
  38. data/lib/rubocop/cop/style/redundant_constant_base.rb +72 -0
  39. data/lib/rubocop/cop/style/redundant_return.rb +7 -0
  40. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  41. data/lib/rubocop/cop/style/require_order.rb +88 -0
  42. data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
  43. data/lib/rubocop/cop/style/select_by_regexp.rb +8 -4
  44. data/lib/rubocop/cop/style/string_literals.rb +1 -5
  45. data/lib/rubocop/cop/style/symbol_proc.rb +2 -4
  46. data/lib/rubocop/cop/team.rb +1 -1
  47. data/lib/rubocop/cop/util.rb +1 -1
  48. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  49. data/lib/rubocop/cop/variable_force.rb +20 -29
  50. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  51. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  52. data/lib/rubocop/formatter.rb +3 -1
  53. data/lib/rubocop/optimized_patterns.rb +38 -0
  54. data/lib/rubocop/options.rb +9 -1
  55. data/lib/rubocop/path_util.rb +14 -2
  56. data/lib/rubocop/result_cache.rb +1 -1
  57. data/lib/rubocop/rspec/cop_helper.rb +4 -1
  58. data/lib/rubocop/rspec/support.rb +2 -2
  59. data/lib/rubocop/server/core.rb +1 -1
  60. data/lib/rubocop/target_ruby.rb +1 -1
  61. data/lib/rubocop/version.rb +1 -1
  62. data/lib/rubocop.rb +14 -6
  63. metadata +8 -3
@@ -48,16 +48,17 @@ module RuboCop
48
48
  (send
49
49
  (const _ _) {:#{SEND_METHODS.join(' :')}}
50
50
  ({sym str} $#mixin_method?)
51
- $(const _ _))
51
+ $(const _ _)+)
52
52
  PATTERN
53
53
 
54
54
  def on_send(node)
55
- send_with_mixin_argument?(node) do |method, module_name|
56
- message = message(method, module_name.source, bad_location(node).source)
55
+ send_with_mixin_argument?(node) do |method, module_names|
56
+ module_names_source = module_names.map(&:source).join(', ')
57
+ message = message(method, module_names_source, bad_location(node).source)
57
58
 
58
59
  bad_location = bad_location(node)
59
60
  add_offense(bad_location, message: message) do |corrector|
60
- corrector.replace(bad_location, "#{method} #{module_name.source}")
61
+ corrector.replace(bad_location, "#{method} #{module_names_source}")
61
62
  end
62
63
  end
63
64
  end
@@ -73,10 +73,11 @@ module RuboCop
73
73
  outer_local_variable_node =
74
74
  find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
75
75
  return true unless outer_local_variable_node
76
+ return false unless outer_local_variable_node.conditional?
77
+ return true if variable_node == outer_local_variable_node
76
78
 
77
- outer_local_variable_node.conditional? &&
78
- (variable_node == outer_local_variable_node ||
79
- variable_node == outer_local_variable_node.else_branch)
79
+ outer_local_variable_node.if_type? &&
80
+ variable_node == outer_local_variable_node.else_branch
80
81
  end
81
82
 
82
83
  def variable_node(variable)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for operators, variables, literals, and nonmutating
6
+ # Checks for operators, variables, literals, lambda, proc and nonmutating
7
7
  # methods used in void context.
8
8
  #
9
9
  # @example CheckForMethodsWithNoSideEffects: false (default)
@@ -45,7 +45,7 @@ module RuboCop
45
45
  VAR_MSG = 'Variable `%<var>s` used in void context.'
46
46
  LIT_MSG = 'Literal `%<lit>s` used in void context.'
47
47
  SELF_MSG = '`self` used in void context.'
48
- DEFINED_MSG = '`%<defined>s` used in void context.'
48
+ EXPRESSION_MSG = '`%<expression>s` used in void context.'
49
49
  NONMUTATING_MSG = 'Method `#%<method>s` used in void context. Did you mean `#%<method>s!`?'
50
50
 
51
51
  BINARY_OPERATORS = %i[* / % + - == === != < > <= >= <=>].freeze
@@ -87,7 +87,7 @@ module RuboCop
87
87
  check_literal(expr)
88
88
  check_var(expr)
89
89
  check_self(expr)
90
- check_defined(expr)
90
+ check_void_expression(expr)
91
91
  return unless cop_config['CheckForMethodsWithNoSideEffects']
92
92
 
93
93
  check_nonmutating(expr)
@@ -117,10 +117,10 @@ module RuboCop
117
117
  add_offense(node, message: SELF_MSG)
118
118
  end
119
119
 
120
- def check_defined(node)
121
- return unless node.defined_type?
120
+ def check_void_expression(node)
121
+ return unless node.defined_type? || node.lambda_or_proc?
122
122
 
123
- add_offense(node, message: format(DEFINED_MSG, defined: node.source))
123
+ add_offense(node, message: format(EXPRESSION_MSG, expression: node.source))
124
124
  end
125
125
 
126
126
  def check_nonmutating(node)
@@ -8,8 +8,8 @@ module RuboCop
8
8
  # The maximum allowed length is configurable.
9
9
  # The cop can be configured to ignore blocks passed to certain methods.
10
10
  #
11
- # You can set literals you want to fold with `CountAsOne`.
12
- # Available are: 'array', 'hash', and 'heredoc'. Each literal
11
+ # You can set constructs you want to fold with `CountAsOne`.
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
15
  #
@@ -17,7 +17,7 @@ module RuboCop
17
17
  # for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
18
18
  # instead. By default, there are no methods to allowed.
19
19
  #
20
- # @example CountAsOne: ['array', 'heredoc']
20
+ # @example CountAsOne: ['array', 'heredoc', 'method_call']
21
21
  #
22
22
  # something do
23
23
  # array = [ # +1
@@ -33,7 +33,12 @@ module RuboCop
33
33
  # Heredoc
34
34
  # content.
35
35
  # HEREDOC
36
- # end # 5 points
36
+ #
37
+ # foo( # +1
38
+ # 1,
39
+ # 2
40
+ # )
41
+ # end # 6 points
37
42
  #
38
43
  # NOTE: This cop does not apply for `Struct` definitions.
39
44
  class BlockLength < Base
@@ -7,11 +7,11 @@ module RuboCop
7
7
  # Comment lines can optionally be ignored.
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
- # You can set literals you want to fold with `CountAsOne`.
11
- # Available are: 'array', 'hash', and 'heredoc'. Each literal
10
+ # You can set constructs you want to fold with `CountAsOne`.
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
- # @example CountAsOne: ['array', 'heredoc']
14
+ # @example CountAsOne: ['array', 'heredoc', 'method_call']
15
15
  #
16
16
  # class Foo
17
17
  # ARRAY = [ # +1
@@ -27,7 +27,12 @@ module RuboCop
27
27
  # Heredoc
28
28
  # content.
29
29
  # HEREDOC
30
- # end # 5 points
30
+ #
31
+ # foo( # +1
32
+ # 1,
33
+ # 2
34
+ # )
35
+ # end # 6 points
31
36
  #
32
37
  #
33
38
  # NOTE: This cop also applies for `Struct` definitions.
@@ -7,8 +7,8 @@ module RuboCop
7
7
  # Comment lines can optionally be allowed.
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
- # You can set literals you want to fold with `CountAsOne`.
11
- # Available are: 'array', 'hash', and 'heredoc'. Each literal
10
+ # You can set constructs you want to fold with `CountAsOne`.
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
14
  # NOTE: The `ExcludedMethods` and `IgnoredMethods` configuration is
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # Please use `AllowedMethods` and `AllowedPatterns` instead.
17
17
  # By default, there are no methods to allowed.
18
18
  #
19
- # @example CountAsOne: ['array', 'heredoc']
19
+ # @example CountAsOne: ['array', 'heredoc', 'method_call']
20
20
  #
21
21
  # def m
22
22
  # array = [ # +1
@@ -32,7 +32,12 @@ module RuboCop
32
32
  # Heredoc
33
33
  # content.
34
34
  # HEREDOC
35
- # end # 5 points
35
+ #
36
+ # foo( # +1
37
+ # 1,
38
+ # 2
39
+ # )
40
+ # end # 6 points
36
41
  #
37
42
  class MethodLength < Base
38
43
  include CodeLength
@@ -7,11 +7,11 @@ module RuboCop
7
7
  # Comment lines can optionally be ignored.
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
- # You can set literals you want to fold with `CountAsOne`.
11
- # Available are: 'array', 'hash', and 'heredoc'. Each literal
10
+ # You can set constructs you want to fold with `CountAsOne`.
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
- # @example CountAsOne: ['array', 'heredoc']
14
+ # @example CountAsOne: ['array', 'heredoc', 'method_call']
15
15
  #
16
16
  # module M
17
17
  # ARRAY = [ # +1
@@ -27,7 +27,12 @@ module RuboCop
27
27
  # Heredoc
28
28
  # content.
29
29
  # HEREDOC
30
- # end # 5 points
30
+ #
31
+ # foo( # +1
32
+ # 1,
33
+ # 2
34
+ # )
35
+ # end # 6 points
31
36
  #
32
37
  class ModuleLength < Base
33
38
  include CodeLength
@@ -9,7 +9,7 @@ module RuboCop
9
9
  extend NodePattern::Macros
10
10
  include Util
11
11
 
12
- FOLDABLE_TYPES = %i[array hash heredoc].freeze
12
+ FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
13
  CLASSLIKE_TYPES = %i[class module].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  private
41
41
 
42
- def build_foldable_checks(types)
42
+ def build_foldable_checks(types) # rubocop:disable Metrics/MethodLength
43
43
  types.map do |type|
44
44
  case type
45
45
  when :array
@@ -48,6 +48,8 @@ module RuboCop
48
48
  ->(node) { node.hash_type? }
49
49
  when :heredoc
50
50
  ->(node) { heredoc_node?(node) }
51
+ when :method_call
52
+ ->(node) { node.call_type? }
51
53
  else
52
54
  raise ArgumentError, "Unknown foldable type: #{type.inspect}. " \
53
55
  "Valid foldable types are: #{FOLDABLE_TYPES.join(', ')}."
@@ -57,6 +59,7 @@ module RuboCop
57
59
 
58
60
  def normalize_foldable_types(types)
59
61
  types.concat(%i[str dstr]) if types.delete(:heredoc)
62
+ types.concat(%i[send csend]) if types.delete(:method_call)
60
63
  types
61
64
  end
62
65
 
@@ -47,6 +47,12 @@ module RuboCop
47
47
 
48
48
  def register_offense(node, message, replacement)
49
49
  add_offense(node.value, message: message) do |corrector|
50
+ if (def_node = def_node_that_require_parentheses(node))
51
+ white_spaces = range_between(def_node.loc.selector.end_pos,
52
+ def_node.first_argument.source_range.begin_pos)
53
+ corrector.replace(white_spaces, '(')
54
+ corrector.insert_after(def_node.arguments.last, ')')
55
+ end
50
56
  corrector.replace(node, replacement)
51
57
  end
52
58
  end
@@ -76,12 +82,25 @@ module RuboCop
76
82
  end
77
83
 
78
84
  def require_hash_value_for_around_hash_literal?(node)
79
- return false unless (ancestor = node.parent.parent)
80
- return false if ancestor.send_type? && ancestor.method?(:[])
85
+ return false unless (send_node = find_ancestor_send_node(node))
86
+
87
+ !node.parent.braces? && !use_element_of_hash_literal_as_receiver?(send_node, node.parent) &&
88
+ use_modifier_form_without_parenthesized_method_call?(send_node)
89
+ end
90
+
91
+ def def_node_that_require_parentheses(node)
92
+ return unless (send_node = find_ancestor_send_node(node))
93
+ return unless without_parentheses_call_expr_follows?(send_node)
94
+
95
+ def_node = node.each_ancestor(:send, :csend).first
96
+
97
+ def_node unless def_node && def_node.arguments.empty?
98
+ end
99
+
100
+ def find_ancestor_send_node(node)
101
+ ancestor = node.parent.parent
81
102
 
82
- !node.parent.braces? && !use_element_of_hash_literal_as_receiver?(ancestor, node.parent) &&
83
- (use_modifier_form_without_parenthesized_method_call?(ancestor) ||
84
- without_parentheses_call_expr_follows?(ancestor))
103
+ ancestor if ancestor&.call_type? && !ancestor&.method?(:[])
85
104
  end
86
105
 
87
106
  def use_element_of_hash_literal_as_receiver?(ancestor, parent)
@@ -48,11 +48,25 @@ module RuboCop
48
48
  end
49
49
 
50
50
  def to_modifier_form(node)
51
- expression = [node.body.source, node.keyword, node.condition.source].compact.join(' ')
51
+ body = if_body_source(node.body)
52
+ expression = [body, node.keyword, node.condition.source].compact.join(' ')
52
53
  parenthesized = parenthesize?(node) ? "(#{expression})" : expression
53
54
  [parenthesized, first_line_comment(node)].compact.join(' ')
54
55
  end
55
56
 
57
+ def if_body_source(if_body)
58
+ if if_body.call_type? &&
59
+ if_body.last_argument&.hash_type? && if_body.last_argument.pairs.last.value_omission?
60
+ "#{method_source(if_body)}(#{if_body.arguments.map(&:source).join(', ')})"
61
+ else
62
+ if_body.source
63
+ end
64
+ end
65
+
66
+ def method_source(if_body)
67
+ range_between(if_body.loc.expression.begin_pos, if_body.loc.selector.end_pos).source
68
+ end
69
+
56
70
  def first_line_comment(node)
57
71
  comment = processed_source.find_comment { |c| same_line?(c, node) }
58
72
  return unless comment
@@ -72,27 +72,27 @@ module RuboCop
72
72
  #
73
73
  # @example gives back a correctly qualified cop name
74
74
  #
75
- # cops = RuboCop::Cop::Cop.all
76
- # cops.
77
- # qualified_cop_name('Layout/EndOfLine') # => 'Layout/EndOfLine'
75
+ # registry = RuboCop::Cop::Registry
76
+ # registry.qualified_cop_name('Layout/EndOfLine', '') # => 'Layout/EndOfLine'
78
77
  #
79
78
  # @example fixes incorrect namespaces
80
79
  #
81
- # cops = RuboCop::Cop::Cop.all
82
- # cops.qualified_cop_name('Lint/EndOfLine') # => 'Layout/EndOfLine'
80
+ # registry = RuboCop::Cop::Registry
81
+ # registry.qualified_cop_name('Lint/EndOfLine', '') # => 'Layout/EndOfLine'
83
82
  #
84
83
  # @example namespaces bare cop identifiers
85
84
  #
86
- # cops = RuboCop::Cop::Cop.all
87
- # cops.qualified_cop_name('EndOfLine') # => 'Layout/EndOfLine'
85
+ # registry = RuboCop::Cop::Registry
86
+ # registry.qualified_cop_name('EndOfLine', '') # => 'Layout/EndOfLine'
88
87
  #
89
88
  # @example passes back unrecognized cop names
90
89
  #
91
- # cops = RuboCop::Cop::Cop.all
92
- # cops.qualified_cop_name('NotACop') # => 'NotACop'
90
+ # registry = RuboCop::Cop::Registry
91
+ # registry.qualified_cop_name('NotACop', '') # => 'NotACop'
93
92
  #
94
93
  # @param name [String] Cop name extracted from config
95
94
  # @param path [String, nil] Path of file that `name` was extracted from
95
+ # @param warn [Boolean] Print a warning if no department given for `name`
96
96
  #
97
97
  # @raise [AmbiguousCopName]
98
98
  # if a bare identifier with two possible namespaces is provided
@@ -158,7 +158,7 @@ module RuboCop
158
158
  end
159
159
 
160
160
  def enabled?(cop, config)
161
- return true if options.fetch(:only, []).include?(cop.cop_name)
161
+ return true if options[:only]&.include?(cop.cop_name)
162
162
 
163
163
  cfg = config.for_cop(cop)
164
164
 
@@ -182,8 +182,12 @@ module RuboCop
182
182
  cops.map(&:cop_name)
183
183
  end
184
184
 
185
+ def cops_for_department(department)
186
+ cops.select { |cop| cop.department == department.to_sym }
187
+ end
188
+
185
189
  def names_for_department(department)
186
- cops.select { |cop| cop.department == department.to_sym }.map(&:cop_name)
190
+ cops_for_department(department).map(&:cop_name)
187
191
  end
188
192
 
189
193
  def ==(other)
@@ -211,6 +215,14 @@ module RuboCop
211
215
  to_h[cop_name].first
212
216
  end
213
217
 
218
+ # When a cop name is given returns a single-element array with the cop class.
219
+ # When a department name is given returns an array with all the cop classes
220
+ # for that department.
221
+ def find_cops_by_directive(directive)
222
+ cop = find_by_cop_name(directive)
223
+ cop ? [cop] : cops_for_department(directive)
224
+ end
225
+
214
226
  def freeze
215
227
  clear_enrollment_queue
216
228
  unqualified_cop_names # build cache
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # In Ruby 3.1, `Array#intersect?` has been added.
7
+ #
8
+ # This cop identifies places where `(array1 & array2).any?`
9
+ # can be replaced by `array1.intersect?(array2)`.
10
+ #
11
+ # The `array1.intersect?(array2)` method is faster than
12
+ # `(array1 & array2).any?` and is more readable.
13
+ #
14
+ # @safety
15
+ # This cop cannot guarantee that array1 and array2 are
16
+ # actually arrays while method `intersect?` is for arrays only.
17
+ #
18
+ # @example
19
+ # # bad
20
+ # (array1 & array2).any?
21
+ # (array1 & array2).empty?
22
+ #
23
+ # # good
24
+ # array1.intersect?(array2)
25
+ # !array1.intersect?(array2)
26
+ #
27
+ # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
28
+ # # good
29
+ # (array1 & array2).present?
30
+ # (array1 & array2).blank?
31
+ #
32
+ # @example AllCops:ActiveSupportExtensionsEnabled: true
33
+ # # bad
34
+ # (array1 & array2).present?
35
+ # (array1 & array2).blank?
36
+ #
37
+ # # good
38
+ # array1.intersect?(array2)
39
+ # !array1.intersect?(array2)
40
+ class ArrayIntersect < Base
41
+ extend AutoCorrector
42
+ extend TargetRubyVersion
43
+
44
+ minimum_target_ruby_version 3.1
45
+
46
+ # @!method regular_bad_intersection_check?(node)
47
+ def_node_matcher :regular_bad_intersection_check?, <<~PATTERN
48
+ (send
49
+ (begin
50
+ (send $(...) :& $(...))
51
+ ) ${:any? :empty?}
52
+ )
53
+ PATTERN
54
+
55
+ # @!method active_support_bad_intersection_check?(node)
56
+ def_node_matcher :active_support_bad_intersection_check?, <<~PATTERN
57
+ (send
58
+ (begin
59
+ (send $(...) :& $(...))
60
+ ) ${:present? :any? :blank? :empty?}
61
+ )
62
+ PATTERN
63
+
64
+ MSG = 'Use `%<negated>s%<receiver>s.intersect?(%<argument>s)` ' \
65
+ 'instead of `(%<receiver>s & %<argument>s).%<method_name>s`.'
66
+ STRAIGHT_METHODS = %i[present? any?].freeze
67
+ NEGATED_METHODS = %i[blank? empty?].freeze
68
+ RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
69
+
70
+ def on_send(node)
71
+ return unless (receiver, argument, method_name = bad_intersection_check?(node))
72
+
73
+ message = message(receiver.source, argument.source, method_name)
74
+
75
+ 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
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def bad_intersection_check?(node)
87
+ if active_support_extensions_enabled?
88
+ active_support_bad_intersection_check?(node)
89
+ else
90
+ regular_bad_intersection_check?(node)
91
+ end
92
+ end
93
+
94
+ def straight?(method_name)
95
+ STRAIGHT_METHODS.include?(method_name.to_sym)
96
+ end
97
+
98
+ def message(receiver, argument, method_name)
99
+ negated = straight?(method_name) ? '' : '!'
100
+ format(
101
+ MSG,
102
+ negated: negated,
103
+ receiver: receiver,
104
+ argument: argument,
105
+ method_name: method_name
106
+ )
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -94,6 +94,7 @@ module RuboCop
94
94
  #
95
95
  class GuardClause < Base
96
96
  extend AutoCorrector
97
+ include RangeHelp
97
98
  include MinBodyLength
98
99
  include StatementModifier
99
100
 
@@ -179,13 +180,35 @@ module RuboCop
179
180
  end
180
181
  end
181
182
 
183
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
182
184
  def autocorrect(corrector, node, condition, replacement, guard)
183
185
  corrector.replace(node.loc.keyword.join(condition.loc.expression), replacement)
184
- corrector.remove(node.loc.end)
185
- return unless node.else?
186
186
 
187
- corrector.remove(node.loc.else)
188
- corrector.remove(branch_to_remove(node, guard))
187
+ if_branch = node.if_branch
188
+ else_branch = node.else_branch
189
+
190
+ if if_branch&.send_type? && if_branch.last_argument&.heredoc?
191
+ autocorrect_heredoc_argument(corrector, node, if_branch, else_branch, guard)
192
+ elsif else_branch&.send_type? && else_branch.last_argument&.heredoc?
193
+ autocorrect_heredoc_argument(corrector, node, else_branch, if_branch, guard)
194
+ else
195
+ corrector.remove(node.loc.end)
196
+ return unless node.else?
197
+
198
+ corrector.remove(node.loc.else)
199
+ corrector.remove(branch_to_remove(node, guard))
200
+ end
201
+ end
202
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
203
+
204
+ def autocorrect_heredoc_argument(corrector, node, heredoc_branch, leave_branch, guard)
205
+ remove_whole_lines(corrector, leave_branch.source_range)
206
+ remove_whole_lines(corrector, node.loc.else)
207
+ remove_whole_lines(corrector, node.loc.end)
208
+ remove_whole_lines(corrector, branch_to_remove(node, guard).source_range)
209
+ corrector.insert_after(
210
+ heredoc_branch.last_argument.loc.heredoc_end, "\n#{leave_branch.source}"
211
+ )
189
212
  end
190
213
 
191
214
  def branch_to_remove(node, guard)
@@ -222,7 +245,7 @@ module RuboCop
222
245
  end
223
246
 
224
247
  def accepted_if?(node, ending)
225
- return true if node.modifier_form? || node.ternary?
248
+ return true if node.modifier_form? || node.ternary? || node.elsif_conditional?
226
249
 
227
250
  if ending
228
251
  node.else?
@@ -231,6 +254,10 @@ module RuboCop
231
254
  end
232
255
  end
233
256
 
257
+ def remove_whole_lines(corrector, range)
258
+ corrector.remove(range_by_whole_lines(range, include_final_newline: true))
259
+ end
260
+
234
261
  def allowed_consecutive_conditionals?
235
262
  cop_config.fetch('AllowConsecutiveConditionals', false)
236
263
  end
@@ -39,9 +39,10 @@ module RuboCop
39
39
  def autocorrect(node)
40
40
  return correct_elsif(node) if node.else_branch.if_type?
41
41
 
42
+ then_code = node.if_branch ? node.if_branch.source : 'nil'
42
43
  else_code = node.else_branch ? node.else_branch.source : 'nil'
43
44
 
44
- "#{node.condition.source} ? #{node.if_branch.source} : #{else_code}"
45
+ "#{node.condition.source} ? #{then_code} : #{else_code}"
45
46
  end
46
47
 
47
48
  def correct_elsif(node)
@@ -44,7 +44,7 @@ module RuboCop
44
44
  PATTERN
45
45
 
46
46
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
47
- return unless node.lambda? || node.proc?
47
+ return unless node.lambda_or_proc?
48
48
  return unless nil_return?(node.body)
49
49
 
50
50
  message = format(MSG, type: node.lambda? ? 'lambda' : 'proc')
@@ -13,6 +13,7 @@ module RuboCop
13
13
  # ----
14
14
  # Methods:
15
15
  # join: ''
16
+ # sum: 0
16
17
  # split: ' '
17
18
  # chomp: "\n"
18
19
  # chomp!: "\n"
@@ -33,6 +34,7 @@ module RuboCop
33
34
  # # bad
34
35
  # array.join('')
35
36
  # [1, 2, 3].join("")
37
+ # array.sum(0)
36
38
  # string.split(" ")
37
39
  # "first\nsecond".split(" ")
38
40
  # string.chomp("\n")
@@ -42,6 +44,7 @@ module RuboCop
42
44
  # # good
43
45
  # array.join
44
46
  # [1, 2, 3].join
47
+ # array.sum
45
48
  # string.split
46
49
  # "first second".split
47
50
  # string.chomp