rubocop 1.6.0 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +3 -2
  4. data/config/default.yml +142 -19
  5. data/lib/rubocop.rb +15 -1
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  7. data/lib/rubocop/comment_config.rb +6 -6
  8. data/lib/rubocop/config.rb +10 -7
  9. data/lib/rubocop/config_loader.rb +11 -14
  10. data/lib/rubocop/config_loader_resolver.rb +21 -4
  11. data/lib/rubocop/config_obsoletion.rb +5 -3
  12. data/lib/rubocop/config_obsoletion/extracted_cop.rb +6 -6
  13. data/lib/rubocop/config_store.rb +12 -1
  14. data/lib/rubocop/cop/base.rb +1 -1
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +3 -2
  16. data/lib/rubocop/cop/generator.rb +1 -3
  17. data/lib/rubocop/cop/internal_affairs.rb +6 -1
  18. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  19. data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
  20. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
  21. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
  22. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +145 -0
  23. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  24. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +19 -3
  25. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +14 -0
  26. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  27. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -10
  28. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +1 -0
  29. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  30. data/lib/rubocop/cop/layout/space_before_brackets.rb +62 -0
  31. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +13 -10
  32. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
  33. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +59 -0
  34. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +7 -2
  35. data/lib/rubocop/cop/lint/deprecated_constants.rb +75 -0
  36. data/lib/rubocop/cop/lint/duplicate_branch.rb +64 -2
  37. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
  38. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
  39. data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
  40. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  41. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  42. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +2 -1
  43. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
  44. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +50 -17
  45. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
  46. data/lib/rubocop/cop/lint/symbol_conversion.rb +102 -0
  47. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  48. data/lib/rubocop/cop/lint/unreachable_loop.rb +17 -0
  49. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  50. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
  51. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  52. data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
  53. data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
  54. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
  55. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +59 -5
  56. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  57. data/lib/rubocop/cop/naming/variable_name.rb +2 -0
  58. data/lib/rubocop/cop/naming/variable_number.rb +2 -9
  59. data/lib/rubocop/cop/registry.rb +10 -0
  60. data/lib/rubocop/cop/severity.rb +3 -3
  61. data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -1
  62. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  63. data/lib/rubocop/cop/style/collection_methods.rb +14 -1
  64. data/lib/rubocop/cop/style/commented_keyword.rb +22 -5
  65. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  66. data/lib/rubocop/cop/style/empty_literal.rb +6 -2
  67. data/lib/rubocop/cop/style/endless_method.rb +102 -0
  68. data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
  69. data/lib/rubocop/cop/style/explicit_block_argument.rb +10 -0
  70. data/lib/rubocop/cop/style/float_division.rb +3 -0
  71. data/lib/rubocop/cop/style/for.rb +2 -0
  72. data/lib/rubocop/cop/style/format_string_token.rb +18 -2
  73. data/lib/rubocop/cop/style/hash_except.rb +95 -0
  74. data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
  75. data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
  76. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +96 -0
  77. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -2
  78. data/lib/rubocop/cop/style/lambda_call.rb +2 -1
  79. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -0
  80. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +16 -6
  81. data/lib/rubocop/cop/style/method_def_parentheses.rb +7 -0
  82. data/lib/rubocop/cop/style/multiline_method_signature.rb +26 -1
  83. data/lib/rubocop/cop/style/multiline_when_then.rb +3 -1
  84. data/lib/rubocop/cop/style/mutable_constant.rb +13 -3
  85. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -0
  86. data/lib/rubocop/cop/style/nil_comparison.rb +1 -0
  87. data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
  88. data/lib/rubocop/cop/style/raise_args.rb +5 -2
  89. data/lib/rubocop/cop/style/redundant_argument.rb +7 -1
  90. data/lib/rubocop/cop/style/redundant_freeze.rb +8 -4
  91. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  92. data/lib/rubocop/cop/style/single_line_methods.rb +34 -2
  93. data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
  94. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  95. data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
  96. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  97. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
  98. data/lib/rubocop/cop/util.rb +3 -1
  99. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  100. data/lib/rubocop/magic_comment.rb +30 -1
  101. data/lib/rubocop/options.rb +10 -10
  102. data/lib/rubocop/rspec/cop_helper.rb +0 -4
  103. data/lib/rubocop/rspec/expect_offense.rb +37 -22
  104. data/lib/rubocop/runner.rb +17 -1
  105. data/lib/rubocop/target_finder.rb +4 -2
  106. data/lib/rubocop/target_ruby.rb +47 -11
  107. data/lib/rubocop/util.rb +16 -0
  108. data/lib/rubocop/version.rb +8 -2
  109. metadata +26 -7
@@ -19,6 +19,9 @@ module RuboCop
19
19
  # end
20
20
  #
21
21
  class MultilineMethodSignature < Base
22
+ include RangeHelp
23
+ extend AutoCorrector
24
+
22
25
  MSG = 'Avoid multi-line method signatures.'
23
26
 
24
27
  def on_def(node)
@@ -26,12 +29,34 @@ module RuboCop
26
29
  return if opening_line(node) == closing_line(node)
27
30
  return if correction_exceeds_max_line_length?(node)
28
31
 
29
- add_offense(node)
32
+ add_offense(node) do |corrector|
33
+ autocorrect(corrector, node)
34
+ end
30
35
  end
31
36
  alias on_defs on_def
32
37
 
33
38
  private
34
39
 
40
+ def autocorrect(corrector, node)
41
+ arguments = node.arguments
42
+ joined_arguments = arguments.map(&:source).join(', ')
43
+ last_line_source_of_arguments = processed_source[arguments.last_line - 1].strip
44
+
45
+ if last_line_source_of_arguments.start_with?(')')
46
+ joined_arguments = "#{joined_arguments}#{last_line_source_of_arguments}"
47
+
48
+ corrector.remove(range_by_whole_lines(arguments.loc.end, include_final_newline: true))
49
+ end
50
+
51
+ corrector.replace(arguments_range(node), joined_arguments)
52
+ end
53
+
54
+ def arguments_range(node)
55
+ range_between(
56
+ node.first_argument.source_range.begin_pos, node.last_argument.source_range.end_pos
57
+ )
58
+ end
59
+
35
60
  def opening_line(node)
36
61
  node.first_line
37
62
  end
@@ -58,7 +58,9 @@ module RuboCop
58
58
  private
59
59
 
60
60
  def require_then?(when_node)
61
- return true if when_node.conditions.count >= 2
61
+ unless when_node.conditions.first.first_line == when_node.conditions.last.last_line
62
+ return true
63
+ end
62
64
  return false unless when_node.body
63
65
 
64
66
  when_node.loc.line == when_node.body.loc.line
@@ -14,6 +14,8 @@ module RuboCop
14
14
  # positives. Luckily, there is no harm in freezing an already
15
15
  # frozen object.
16
16
  #
17
+ # NOTE: Regexp and Range literals are frozen objects since Ruby 3.0.
18
+ #
17
19
  # @example EnforcedStyle: literals (default)
18
20
  # # bad
19
21
  # CONST = [1, 2, 3]
@@ -94,7 +96,8 @@ module RuboCop
94
96
  range_enclosed_in_parentheses = range_enclosed_in_parentheses?(value)
95
97
 
96
98
  return unless mutable_literal?(value) ||
97
- range_enclosed_in_parentheses
99
+ target_ruby_version <= 2.7 && range_enclosed_in_parentheses
100
+
98
101
  return if FROZEN_STRING_LITERAL_TYPES.include?(value.type) &&
99
102
  frozen_string_literals_enabled?
100
103
 
@@ -119,11 +122,14 @@ module RuboCop
119
122
  end
120
123
 
121
124
  def mutable_literal?(value)
122
- value&.mutable_literal?
125
+ return false if value.nil?
126
+ return false if frozen_regexp_or_range_literals?(value)
127
+
128
+ value.mutable_literal?
123
129
  end
124
130
 
125
131
  def immutable_literal?(node)
126
- node.nil? || node.immutable_literal?
132
+ node.nil? || frozen_regexp_or_range_literals?(node) || node.immutable_literal?
127
133
  end
128
134
 
129
135
  def frozen_string_literal?(node)
@@ -131,6 +137,10 @@ module RuboCop
131
137
  frozen_string_literals_enabled?
132
138
  end
133
139
 
140
+ def frozen_regexp_or_range_literals?(node)
141
+ target_ruby_version >= 3.0 && (node.regexp_type? || node.range_type?)
142
+ end
143
+
134
144
  def requires_parentheses?(node)
135
145
  node.range_type? ||
136
146
  (node.send_type? && node.loc.dot.nil?)
@@ -19,6 +19,10 @@ module RuboCop
19
19
 
20
20
  MSG = 'Add parentheses to nested method call `%<source>s`.'
21
21
 
22
+ def self.autocorrect_incompatible_with
23
+ [Style::MethodCallWithArgsParentheses]
24
+ end
25
+
22
26
  def on_send(node)
23
27
  return unless node.parenthesized?
24
28
 
@@ -50,6 +50,7 @@ module RuboCop
50
50
  end
51
51
 
52
52
  corrector.replace(node, new_code)
53
+ corrector.wrap(node, '(', ')') if node.parent&.method?(:!)
53
54
  end
54
55
  end
55
56
  end
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # With `IncludeSemanticChanges` set to `false` by default, this cop
9
9
  # does not report offenses for `!x.nil?` and does no changes that might
10
10
  # change behavior.
11
+ # Also `IncludeSemanticChanges` set to `false` with `EnforcedStyle: comparison` of
12
+ # `Style/NilComparison` cop, this cop does not report offenses for `x != nil` and
13
+ # does no changes to `!x.nil?` style.
11
14
  #
12
15
  # With `IncludeSemanticChanges` set to `true`, this cop reports offenses
13
16
  # for `!x.nil?` and autocorrects that and `x != nil` to solely `x`, which
@@ -41,6 +44,9 @@ module RuboCop
41
44
  class NonNilCheck < Base
42
45
  extend AutoCorrector
43
46
 
47
+ MSG_FOR_REPLACEMENT = 'Prefer `%<prefer>s` over `%<current>s`.'
48
+ MSG_FOR_REDUNDANCY = 'Explicit non-nil checks are usually redundant.'
49
+
44
50
  RESTRICT_ON_SEND = %i[!= nil? !].freeze
45
51
 
46
52
  def_node_matcher :not_equal_to_nil?, '(send _ :!= nil)'
@@ -49,11 +55,12 @@ module RuboCop
49
55
  def_node_matcher :not_and_nil_check?, '(send (send _ :nil?) :!)'
50
56
 
51
57
  def on_send(node)
52
- return if ignored_node?(node)
53
- return unless (offense_node = find_offense_node(node))
58
+ return if ignored_node?(node) ||
59
+ !include_semantic_changes? && nil_comparison_style == 'comparison'
60
+ return unless register_offense?(node)
54
61
 
55
62
  message = message(node)
56
- add_offense(offense_node, message: message) do |corrector|
63
+ add_offense(node, message: message) do |corrector|
57
64
  autocorrect(corrector, node)
58
65
  end
59
66
  end
@@ -73,13 +80,9 @@ module RuboCop
73
80
 
74
81
  private
75
82
 
76
- def find_offense_node(node)
77
- if not_equal_to_nil?(node)
78
- node.loc.selector
79
- elsif include_semantic_changes? &&
80
- (not_and_nil_check?(node) || unless_and_nil_check?(node))
81
- node
82
- end
83
+ def register_offense?(node)
84
+ not_equal_to_nil?(node) ||
85
+ include_semantic_changes? && (not_and_nil_check?(node) || unless_and_nil_check?(node))
83
86
  end
84
87
 
85
88
  def autocorrect(corrector, node)
@@ -101,10 +104,11 @@ module RuboCop
101
104
  end
102
105
 
103
106
  def message(node)
104
- if node.method?(:!=)
105
- 'Prefer `!expression.nil?` over `expression != nil`.'
107
+ if node.method?(:!=) && !include_semantic_changes?
108
+ prefer = "!#{node.receiver.source}.nil?"
109
+ format(MSG_FOR_REPLACEMENT, prefer: prefer, current: node.source)
106
110
  else
107
- 'Explicit non-nil checks are usually redundant.'
111
+ MSG_FOR_REDUNDANCY
108
112
  end
109
113
  end
110
114
 
@@ -138,6 +142,12 @@ module RuboCop
138
142
  corrector.replace(node.parent.loc.keyword, 'if')
139
143
  corrector.replace(node, receiver.source)
140
144
  end
145
+
146
+ def nil_comparison_style
147
+ nil_comparison_conf = config.for_cop('Style/NilComparison')
148
+
149
+ nil_comparison_conf['Enabled'] && nil_comparison_conf['EnforcedStyle']
150
+ end
141
151
  end
142
152
  end
143
153
  end
@@ -81,11 +81,12 @@ module RuboCop
81
81
  return node.source if message_nodes.size > 1
82
82
 
83
83
  argument = message_nodes.first.source
84
+ exception_class = exception_node.const_name || exception_node.receiver.source
84
85
 
85
86
  if node.parent && requires_parens?(node.parent)
86
- "#{node.method_name}(#{exception_node.const_name}.new(#{argument}))"
87
+ "#{node.method_name}(#{exception_class}.new(#{argument}))"
87
88
  else
88
- "#{node.method_name} #{exception_node.const_name}.new(#{argument})"
89
+ "#{node.method_name} #{exception_class}.new(#{argument})"
89
90
  end
90
91
  end
91
92
 
@@ -95,6 +96,7 @@ module RuboCop
95
96
  replacement = correction_exploded_to_compact(node)
96
97
 
97
98
  corrector.replace(node, replacement)
99
+ opposite_style_detected
98
100
  end
99
101
  else
100
102
  correct_style_detected
@@ -115,6 +117,7 @@ module RuboCop
115
117
  replacement = correction_compact_to_exploded(node)
116
118
 
117
119
  corrector.replace(node, replacement)
120
+ opposite_style_detected
118
121
  end
119
122
  end
120
123
 
@@ -12,7 +12,7 @@ module RuboCop
12
12
  # 1. This cop matches for method names only and hence cannot tell apart
13
13
  # methods with same name in different classes.
14
14
  # 2. This cop is limited to methods with single parameter.
15
- # 3. This cop is unsafe if certain special global variables (e.g. `$;`) are set.
15
+ # 3. This cop is unsafe if certain special global variables (e.g. `$;`, `$/`) are set.
16
16
  # That depends on the nature of the target methods, of course.
17
17
  #
18
18
  # Method names and their redundant arguments can be configured like this:
@@ -20,6 +20,8 @@ module RuboCop
20
20
  # Methods:
21
21
  # join: ''
22
22
  # split: ' '
23
+ # chomp: "\n"
24
+ # chomp!: "\n"
23
25
  # foo: 2
24
26
  #
25
27
  # @example
@@ -28,6 +30,8 @@ module RuboCop
28
30
  # [1, 2, 3].join("")
29
31
  # string.split(" ")
30
32
  # "first\nsecond".split(" ")
33
+ # string.chomp("\n")
34
+ # string.chomp!("\n")
31
35
  # A.foo(2)
32
36
  #
33
37
  # # good
@@ -35,6 +39,8 @@ module RuboCop
35
39
  # [1, 2, 3].join
36
40
  # string.split
37
41
  # "first second".split
42
+ # string.chomp
43
+ # string.chomp!
38
44
  # A.foo
39
45
  class RedundantArgument < Base
40
46
  include RangeHelp
@@ -3,7 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # This cop check for uses of Object#freeze on immutable objects.
6
+ # This cop check for uses of `Object#freeze` on immutable objects.
7
+ #
8
+ # NOTE: Regexp and Range literals are frozen objects since Ruby 3.0.
7
9
  #
8
10
  # @example
9
11
  # # bad
@@ -37,8 +39,10 @@ module RuboCop
37
39
 
38
40
  return true if node.immutable_literal?
39
41
 
40
- FROZEN_STRING_LITERAL_TYPES.include?(node.type) &&
41
- frozen_string_literals_enabled?
42
+ return true if FROZEN_STRING_LITERAL_TYPES.include?(node.type) &&
43
+ frozen_string_literals_enabled?
44
+
45
+ target_ruby_version >= 3.0 && (node.regexp_type? || node.range_type?)
42
46
  end
43
47
 
44
48
  def strip_parenthesis(node)
@@ -52,7 +56,7 @@ module RuboCop
52
56
  def_node_matcher :operation_produces_immutable_object?, <<~PATTERN
53
57
  {
54
58
  (begin (send {float int} {:+ :- :* :** :/ :% :<<} _))
55
- (begin (send !(str _) {:+ :- :* :** :/ :%} {float int}))
59
+ (begin (send !{(str _) array} {:+ :- :* :** :/ :%} {float int}))
56
60
  (begin (send _ {:== :=== :!= :<= :>= :< :>} _))
57
61
  (send (const {nil? cbase} :ENV) :[] _)
58
62
  (send _ {:count :length :size} ...)
@@ -66,7 +66,7 @@ module RuboCop
66
66
  end
67
67
 
68
68
  def correct_with_arguments(return_node, corrector)
69
- if return_node.arguments.size > 1
69
+ if return_node.children.size > 1
70
70
  add_brackets(corrector, return_node)
71
71
  elsif hash_without_braces?(return_node.first_argument)
72
72
  add_braces(corrector, return_node.first_argument)
@@ -6,6 +6,12 @@ module RuboCop
6
6
  # This cop checks for single-line method definitions that contain a body.
7
7
  # It will accept single-line methods with no body.
8
8
  #
9
+ # Endless methods added in Ruby 3.0 are also accepted by this cop.
10
+ #
11
+ # If `Style/EndlessMethod` is enabled with `EnforcedStyle: allow_single_line` or
12
+ # `allow_always`, single-line methods will be auto-corrected to endless
13
+ # methods if there is only one statement in the body.
14
+ #
9
15
  # @example
10
16
  # # bad
11
17
  # def some_method; body end
@@ -15,6 +21,7 @@ module RuboCop
15
21
  # # good
16
22
  # def self.resource_class=(klass); end
17
23
  # def @table.columns; end
24
+ # def some_method() = body
18
25
  #
19
26
  # @example AllowIfMethodIsEmpty: true (default)
20
27
  # # good
@@ -32,6 +39,7 @@ module RuboCop
32
39
 
33
40
  def on_def(node)
34
41
  return unless node.single_line?
42
+ return if node.endless?
35
43
  return if allow_empty? && !node.body
36
44
 
37
45
  add_offense(node) do |corrector|
@@ -43,6 +51,28 @@ module RuboCop
43
51
  private
44
52
 
45
53
  def autocorrect(corrector, node)
54
+ if correct_to_endless?(node.body)
55
+ correct_to_endless(corrector, node)
56
+ else
57
+ correct_to_multiline(corrector, node)
58
+ end
59
+ end
60
+
61
+ def allow_empty?
62
+ cop_config['AllowIfMethodIsEmpty']
63
+ end
64
+
65
+ def correct_to_endless?(body_node)
66
+ endless_method_config = config.for_cop('Style/EndlessMethod')
67
+
68
+ return false unless endless_method_config['Enabled']
69
+ return false if endless_method_config['EnforcedStyle'] == 'disallow'
70
+ return false unless body_node
71
+
72
+ !(body_node.begin_type? || body_node.kwbegin_type?)
73
+ end
74
+
75
+ def correct_to_multiline(corrector, node)
46
76
  each_part(node.body) do |part|
47
77
  LineBreakCorrector.break_line_before(
48
78
  range: part, node: node, corrector: corrector,
@@ -58,8 +88,10 @@ module RuboCop
58
88
  move_comment(node, corrector)
59
89
  end
60
90
 
61
- def allow_empty?
62
- cop_config['AllowIfMethodIsEmpty']
91
+ def correct_to_endless(corrector, node)
92
+ arguments = node.arguments.any? ? node.arguments.source : '()'
93
+ replacement = "def #{node.method_name}#{arguments} = #{node.body.source}"
94
+ corrector.replace(node, replacement)
63
95
  end
64
96
 
65
97
  def each_part(body)
@@ -63,13 +63,13 @@ module RuboCop
63
63
  end
64
64
 
65
65
  def autocorrect(corrector, node, if_branch)
66
+ corrector.wrap(node.condition, '(', ')') if node.condition.or_type?
67
+
66
68
  if node.unless?
67
69
  corrector.replace(node.loc.keyword, 'if')
68
70
  corrector.insert_before(node.condition, '!')
69
71
  end
70
72
 
71
- corrector.wrap(node.condition, '(', ')') if node.condition.or_type?
72
-
73
73
  and_operator = if_branch.unless? ? ' && !' : ' && '
74
74
  if if_branch.modifier_form?
75
75
  correct_for_guard_condition_style(corrector, node, if_branch, and_operator)
@@ -80,8 +80,11 @@ module RuboCop
80
80
  end
81
81
 
82
82
  def correct_for_guard_condition_style(corrector, node, if_branch, and_operator)
83
+ outer_condition = node.condition
84
+ correct_outer_condition(corrector, outer_condition)
85
+
83
86
  condition = if_branch.condition
84
- corrector.insert_after(node.condition, replacement_condition(and_operator, condition))
87
+ corrector.insert_after(outer_condition, replacement_condition(and_operator, condition))
85
88
 
86
89
  range = range_between(if_branch.loc.keyword.begin_pos, condition.source_range.end_pos)
87
90
  corrector.remove(range_with_surrounding_space(range: range, newlines: false))
@@ -100,14 +103,35 @@ module RuboCop
100
103
  def correct_for_comment(corrector, node, if_branch)
101
104
  return if config.for_cop('Style/IfUnlessModifier')['Enabled']
102
105
 
103
- comments = processed_source.comments_before_line(if_branch.source_range.line)
106
+ comments = processed_source.ast_with_comments[if_branch]
104
107
  comment_text = comments.map(&:text).join("\n") << "\n"
105
108
 
106
109
  corrector.insert_before(node.loc.keyword, comment_text) unless comments.empty?
107
110
  end
108
111
 
112
+ def correct_outer_condition(corrector, condition)
113
+ return unless requrie_parentheses?(condition)
114
+
115
+ range = range_between(
116
+ condition.loc.selector.end_pos, condition.first_argument.source_range.begin_pos
117
+ )
118
+
119
+ corrector.replace(range, '(')
120
+ corrector.insert_after(condition.last_argument.source_range, ')')
121
+ end
122
+
123
+ def requrie_parentheses?(condition)
124
+ condition.send_type? && !condition.arguments.empty? && !condition.parenthesized?
125
+ end
126
+
127
+ def arguments_range(node)
128
+ range_between(
129
+ node.first_argument.source_range.begin_pos, node.last_argument.source_range.end_pos
130
+ )
131
+ end
132
+
109
133
  def wrap_condition?(node)
110
- node.or_type? ||
134
+ node.and_type? || node.or_type? ||
111
135
  (node.send_type? && node.arguments.any? && !node.parenthesized?)
112
136
  end
113
137
 
@@ -116,7 +116,7 @@ module RuboCop
116
116
  parts.map do |part|
117
117
  if part.str_type?
118
118
  if single_quoted?(part)
119
- part.value.gsub('\\') { '\\\\' }
119
+ part.value.gsub(/(\\|")/, '\\\\\&')
120
120
  else
121
121
  part.value.inspect[1..-2]
122
122
  end