rubocop 1.5.0 → 1.7.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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +52 -7
- data/config/obsoletion.yml +196 -0
- data/lib/rubocop.rb +14 -0
- data/lib/rubocop/cli/command/suggest_extensions.rb +21 -8
- data/lib/rubocop/config.rb +8 -5
- data/lib/rubocop/config_loader.rb +10 -6
- data/lib/rubocop/config_loader_resolver.rb +21 -4
- data/lib/rubocop/config_obsoletion.rb +64 -262
- data/lib/rubocop/config_obsoletion/changed_enforced_styles.rb +33 -0
- data/lib/rubocop/config_obsoletion/changed_parameter.rb +21 -0
- data/lib/rubocop/config_obsoletion/cop_rule.rb +34 -0
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +44 -0
- data/lib/rubocop/config_obsoletion/parameter_rule.rb +44 -0
- data/lib/rubocop/config_obsoletion/removed_cop.rb +41 -0
- data/lib/rubocop/config_obsoletion/renamed_cop.rb +34 -0
- data/lib/rubocop/config_obsoletion/rule.rb +41 -0
- data/lib/rubocop/config_obsoletion/split_cop.rb +27 -0
- data/lib/rubocop/config_validator.rb +11 -4
- data/lib/rubocop/cop/base.rb +17 -15
- data/lib/rubocop/cop/cop.rb +2 -2
- data/lib/rubocop/cop/correctors/string_literal_corrector.rb +6 -8
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +3 -2
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +145 -0
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +19 -3
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +26 -0
- data/lib/rubocop/cop/layout/line_length.rb +6 -16
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +7 -3
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -10
- data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +1 -0
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
- data/lib/rubocop/cop/layout/space_before_brackets.rb +64 -0
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +13 -10
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +59 -0
- data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +7 -2
- data/lib/rubocop/cop/lint/duplicate_branch.rb +64 -2
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +50 -17
- data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
- data/lib/rubocop/cop/lint/unreachable_loop.rb +17 -0
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/migration/department_name.rb +1 -1
- data/lib/rubocop/cop/mixin/string_help.rb +4 -1
- data/lib/rubocop/cop/naming/accessor_method_name.rb +15 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +59 -5
- data/lib/rubocop/cop/naming/variable_number.rb +3 -1
- data/lib/rubocop/cop/registry.rb +10 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -1
- data/lib/rubocop/cop/style/character_literal.rb +10 -11
- data/lib/rubocop/cop/style/collection_methods.rb +14 -1
- data/lib/rubocop/cop/style/commented_keyword.rb +22 -5
- data/lib/rubocop/cop/style/float_division.rb +44 -1
- data/lib/rubocop/cop/style/for.rb +2 -0
- data/lib/rubocop/cop/style/hash_except.rb +95 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +4 -0
- data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -2
- data/lib/rubocop/cop/style/lambda_call.rb +2 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +16 -6
- data/lib/rubocop/cop/style/method_def_parentheses.rb +7 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +26 -1
- data/lib/rubocop/cop/style/multiline_when_then.rb +3 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +13 -3
- data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
- data/lib/rubocop/cop/style/raise_args.rb +2 -0
- data/lib/rubocop/cop/style/redundant_argument.rb +21 -2
- data/lib/rubocop/cop/style/redundant_freeze.rb +8 -4
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +24 -8
- data/lib/rubocop/cop/style/single_line_block_params.rb +30 -7
- data/lib/rubocop/cop/style/single_line_methods.rb +4 -0
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +24 -8
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -13
- data/lib/rubocop/cop/style/string_concatenation.rb +26 -1
- data/lib/rubocop/cop/style/string_literals.rb +14 -8
- data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
- data/lib/rubocop/cop/util.rb +3 -1
- data/lib/rubocop/ext/regexp_node.rb +31 -9
- data/lib/rubocop/ext/regexp_parser.rb +21 -3
- data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -0
- data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
- data/lib/rubocop/formatter/tap_formatter.rb +2 -0
- data/lib/rubocop/lockfile.rb +40 -0
- data/lib/rubocop/options.rb +9 -9
- data/lib/rubocop/rspec/cop_helper.rb +0 -4
- data/lib/rubocop/rspec/expect_offense.rb +34 -22
- data/lib/rubocop/runner.rb +16 -1
- data/lib/rubocop/target_finder.rb +4 -2
- data/lib/rubocop/util.rb +16 -0
- data/lib/rubocop/version.rb +8 -2
- metadata +33 -5
@@ -51,6 +51,7 @@ module RuboCop
|
|
51
51
|
if style == :each
|
52
52
|
add_offense(node, message: PREFER_EACH) do |corrector|
|
53
53
|
ForToEachCorrector.new(node).call(corrector)
|
54
|
+
opposite_style_detected
|
54
55
|
end
|
55
56
|
else
|
56
57
|
correct_style_detected
|
@@ -63,6 +64,7 @@ module RuboCop
|
|
63
64
|
if style == :for
|
64
65
|
add_offense(node, message: PREFER_FOR) do |corrector|
|
65
66
|
EachToForCorrector.new(node).call(corrector)
|
67
|
+
opposite_style_detected
|
66
68
|
end
|
67
69
|
else
|
68
70
|
correct_style_detected
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for usages of `Hash#reject`, `Hash#select`, and `Hash#filter` methods
|
7
|
+
# that can be replaced with `Hash#except` method.
|
8
|
+
#
|
9
|
+
# This cop should only be enabled on Ruby version 3.0 or higher.
|
10
|
+
# (`Hash#except` was added in Ruby 3.0.)
|
11
|
+
#
|
12
|
+
# For safe detection, it is limited to commonly used string and symbol comparisons
|
13
|
+
# when used `==`.
|
14
|
+
# And do not check `Hash#delete_if` and `Hash#keep_if` to change receiver object.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
#
|
18
|
+
# # bad
|
19
|
+
# {foo: 1, bar: 2, baz: 3}.reject {|k, v| k == :bar }
|
20
|
+
# {foo: 1, bar: 2, baz: 3}.select {|k, v| k != :bar }
|
21
|
+
# {foo: 1, bar: 2, baz: 3}.filter {|k, v| k != :bar }
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# {foo: 1, bar: 2, baz: 3}.except(:bar)
|
25
|
+
#
|
26
|
+
class HashExcept < Base
|
27
|
+
include RangeHelp
|
28
|
+
extend TargetRubyVersion
|
29
|
+
extend AutoCorrector
|
30
|
+
|
31
|
+
minimum_target_ruby_version 3.0
|
32
|
+
|
33
|
+
MSG = 'Use `%<prefer>s` instead.'
|
34
|
+
RESTRICT_ON_SEND = %i[reject select filter].freeze
|
35
|
+
|
36
|
+
def_node_matcher :bad_method?, <<~PATTERN
|
37
|
+
(block
|
38
|
+
(send _ _)
|
39
|
+
(args
|
40
|
+
(arg _)
|
41
|
+
(arg _))
|
42
|
+
(send
|
43
|
+
_ {:== :!= :eql?} _))
|
44
|
+
PATTERN
|
45
|
+
|
46
|
+
def on_send(node)
|
47
|
+
block = node.parent
|
48
|
+
return unless bad_method?(block) && semantically_except_method?(node, block)
|
49
|
+
|
50
|
+
except_key = except_key(block)
|
51
|
+
return unless safe_to_register_offense?(block, except_key)
|
52
|
+
|
53
|
+
range = offense_range(node)
|
54
|
+
preferred_method = "except(#{except_key.source})"
|
55
|
+
|
56
|
+
add_offense(range, message: format(MSG, prefer: preferred_method)) do |corrector|
|
57
|
+
corrector.replace(range, preferred_method)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def semantically_except_method?(send, block)
|
64
|
+
body = block.body
|
65
|
+
|
66
|
+
case send.method_name
|
67
|
+
when :reject
|
68
|
+
body.method?('==') || body.method?('eql?')
|
69
|
+
when :select, :filter
|
70
|
+
body.method?('!=')
|
71
|
+
else
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def safe_to_register_offense?(block, except_key)
|
77
|
+
return true if block.body.method?('eql?')
|
78
|
+
|
79
|
+
except_key.sym_type? || except_key.str_type?
|
80
|
+
end
|
81
|
+
|
82
|
+
def except_key(node)
|
83
|
+
key_argument = node.argument_list.first
|
84
|
+
lhs, _method_name, rhs = *node.body
|
85
|
+
|
86
|
+
[lhs, rhs].find { |operand| operand.source != key_argument.source }
|
87
|
+
end
|
88
|
+
|
89
|
+
def offense_range(node)
|
90
|
+
range_between(node.loc.selector.begin_pos, node.parent.loc.end.end_pos)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -46,6 +46,10 @@ module RuboCop
|
|
46
46
|
MSG_USE_NORMAL =
|
47
47
|
'Modifier form of `%<keyword>s` makes the line too long.'
|
48
48
|
|
49
|
+
def self.autocorrect_incompatible_with
|
50
|
+
[Style::SoleNestedConditional]
|
51
|
+
end
|
52
|
+
|
49
53
|
def on_if(node)
|
50
54
|
msg = if single_line_as_modifier?(node) && !named_capture_in_condition?(node)
|
51
55
|
MSG_USE_MODIFIER
|
@@ -21,6 +21,16 @@ module RuboCop
|
|
21
21
|
# # body omitted
|
22
22
|
# end
|
23
23
|
#
|
24
|
+
# # bad
|
25
|
+
# do_something do |first: false, second:, third: 10|
|
26
|
+
# # body omitted
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# do_something do |second:, first: false, third: 10|
|
31
|
+
# # body omitted
|
32
|
+
# end
|
33
|
+
#
|
24
34
|
class KeywordParametersOrder < Base
|
25
35
|
include RangeHelp
|
26
36
|
extend AutoCorrector
|
@@ -35,7 +45,7 @@ module RuboCop
|
|
35
45
|
if node.parent.find(&:kwoptarg_type?) == node
|
36
46
|
corrector.insert_before(node, "#{kwarg_nodes.map(&:source).join(', ')}, ")
|
37
47
|
|
38
|
-
arguments = node.each_ancestor(:def, :defs).first.arguments
|
48
|
+
arguments = node.each_ancestor(:def, :defs, :block).first.arguments
|
39
49
|
append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments)
|
40
50
|
|
41
51
|
remove_kwargs(kwarg_nodes, corrector)
|
@@ -50,7 +60,7 @@ module RuboCop
|
|
50
60
|
return if last_argument.kwrestarg_type? || last_argument.blockarg_type?
|
51
61
|
|
52
62
|
last_kwoptarg = arguments.reverse.find(&:kwoptarg_type?)
|
53
|
-
corrector.insert_after(last_kwoptarg, "\n")
|
63
|
+
corrector.insert_after(last_kwoptarg, "\n") unless arguments.parent.block_type?
|
54
64
|
end
|
55
65
|
|
56
66
|
def remove_kwargs(kwarg_nodes, corrector)
|
@@ -40,6 +40,9 @@ module RuboCop
|
|
40
40
|
# to `true` allows the presence of parentheses in such a method call
|
41
41
|
# even with arguments.
|
42
42
|
#
|
43
|
+
# NOTE: Parens are required around a method with arguments when inside an
|
44
|
+
# endless method definition (>= Ruby 3.0).
|
45
|
+
#
|
43
46
|
# @example EnforcedStyle: require_parentheses (default)
|
44
47
|
#
|
45
48
|
# # bad
|
@@ -14,25 +14,35 @@ module RuboCop
|
|
14
14
|
|
15
15
|
def omit_parentheses(node)
|
16
16
|
return unless node.parenthesized?
|
17
|
+
return if inside_endless_method_def?(node)
|
17
18
|
return if node.implicit_call?
|
18
19
|
return if super_call_without_arguments?(node)
|
19
20
|
return if allowed_camel_case_method_call?(node)
|
20
21
|
return if legitimate_call_with_parentheses?(node)
|
21
22
|
|
22
23
|
add_offense(offense_range(node), message: OMIT_MSG) do |corrector|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
auto_correct(corrector, node)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def auto_correct(corrector, node)
|
29
|
+
if parentheses_at_the_end_of_multiline_call?(node)
|
30
|
+
corrector.replace(args_begin(node), ' \\')
|
31
|
+
else
|
32
|
+
corrector.replace(args_begin(node), ' ')
|
29
33
|
end
|
34
|
+
corrector.remove(node.loc.end)
|
30
35
|
end
|
31
36
|
|
32
37
|
def offense_range(node)
|
33
38
|
node.loc.begin.join(node.loc.end)
|
34
39
|
end
|
35
40
|
|
41
|
+
def inside_endless_method_def?(node)
|
42
|
+
# parens are required around arguments inside an endless method
|
43
|
+
node.each_ancestor(:def).any?(&:endless?) && node.arguments.any?
|
44
|
+
end
|
45
|
+
|
36
46
|
def super_call_without_arguments?(node)
|
37
47
|
node.super_type? && node.arguments.none?
|
38
48
|
end
|
@@ -6,6 +6,9 @@ module RuboCop
|
|
6
6
|
# This cop checks for parentheses around the arguments in method
|
7
7
|
# definitions. Both instance and class/singleton methods are checked.
|
8
8
|
#
|
9
|
+
# This cop does not consider endless methods, since parentheses are
|
10
|
+
# always required for them.
|
11
|
+
#
|
9
12
|
# @example EnforcedStyle: require_parentheses (default)
|
10
13
|
# # The `require_parentheses` style requires method definitions
|
11
14
|
# # to always use parentheses
|
@@ -94,6 +97,8 @@ module RuboCop
|
|
94
97
|
'parameters.'
|
95
98
|
|
96
99
|
def on_def(node)
|
100
|
+
return if node.endless?
|
101
|
+
|
97
102
|
args = node.arguments
|
98
103
|
|
99
104
|
if require_parentheses?(args)
|
@@ -142,6 +147,7 @@ module RuboCop
|
|
142
147
|
|
143
148
|
add_offense(location, message: MSG_MISSING) do |corrector|
|
144
149
|
correct_definition(node, corrector)
|
150
|
+
unexpected_style_detected 'require_no_parentheses'
|
145
151
|
end
|
146
152
|
end
|
147
153
|
|
@@ -149,6 +155,7 @@ module RuboCop
|
|
149
155
|
add_offense(args, message: MSG_PRESENT) do |corrector|
|
150
156
|
# offense is registered on args node when parentheses are unwanted
|
151
157
|
correct_arguments(args, corrector)
|
158
|
+
unexpected_style_detected 'require_parentheses'
|
152
159
|
end
|
153
160
|
end
|
154
161
|
end
|
@@ -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
|
-
|
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
|
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?)
|
@@ -4,7 +4,8 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
6
|
# This cop looks for uses of Perl-style regexp match
|
7
|
-
# backreferences
|
7
|
+
# backreferences and their English versions like
|
8
|
+
# $1, $2, $&, &+, $MATCH, $PREMATCH, etc.
|
8
9
|
#
|
9
10
|
# @example
|
10
11
|
# # bad
|
@@ -15,19 +16,95 @@ module RuboCop
|
|
15
16
|
class PerlBackrefs < Base
|
16
17
|
extend AutoCorrector
|
17
18
|
|
18
|
-
|
19
|
+
MESSAGE_FORMAT = 'Prefer `%<preferred_expression>s` over `%<original_expression>s`.'
|
20
|
+
|
21
|
+
def on_back_ref(node)
|
22
|
+
on_back_ref_or_gvar_or_nth_ref(node)
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_gvar(node)
|
26
|
+
on_back_ref_or_gvar_or_nth_ref(node)
|
27
|
+
end
|
19
28
|
|
20
29
|
def on_nth_ref(node)
|
21
|
-
|
22
|
-
|
23
|
-
parent_type = node.parent ? node.parent.type : nil
|
30
|
+
on_back_ref_or_gvar_or_nth_ref(node)
|
31
|
+
end
|
24
32
|
|
25
|
-
|
26
|
-
|
33
|
+
private
|
34
|
+
|
35
|
+
# @private
|
36
|
+
# @param [RuboCop::AST::Node] node
|
37
|
+
# @return [Boolean]
|
38
|
+
def derived_from_braceless_interpolation?(node)
|
39
|
+
%i[
|
40
|
+
dstr
|
41
|
+
regexp
|
42
|
+
xstr
|
43
|
+
].include?(node.parent&.type)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @private
|
47
|
+
# @param [RuboCop::AST::Node] node
|
48
|
+
# @param [String] preferred_expression
|
49
|
+
# @return [String]
|
50
|
+
def format_message(node:, preferred_expression:)
|
51
|
+
original_expression = original_expression_of(node)
|
52
|
+
format(
|
53
|
+
MESSAGE_FORMAT,
|
54
|
+
original_expression: original_expression,
|
55
|
+
preferred_expression: preferred_expression
|
56
|
+
)
|
57
|
+
end
|
27
58
|
|
28
|
-
|
29
|
-
|
59
|
+
# @private
|
60
|
+
# @param [RuboCop::AST::Node] node
|
61
|
+
# @return [String]
|
62
|
+
def original_expression_of(node)
|
63
|
+
first = node.to_a.first
|
64
|
+
if first.is_a?(::Integer)
|
65
|
+
"$#{first}"
|
66
|
+
else
|
67
|
+
first.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# @private
|
72
|
+
# @param [RuboCop::AST::Node] node
|
73
|
+
# @return [String, nil]
|
74
|
+
def preferred_expression_to(node)
|
75
|
+
first = node.to_a.first
|
76
|
+
case first
|
77
|
+
when ::Integer
|
78
|
+
"Regexp.last_match(#{first})"
|
79
|
+
when :$&, :$MATCH
|
80
|
+
'Regexp.last_match(0)'
|
81
|
+
when :$`, :$PREMATCH
|
82
|
+
'Regexp.last_match.pre_match'
|
83
|
+
when :$', :$POSTMATCH
|
84
|
+
'Regexp.last_match.post_match'
|
85
|
+
when :$+, :$LAST_PAREN_MATCH
|
86
|
+
'Regexp.last_match(-1)'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @private
|
91
|
+
# @param [RuboCop::AST::Node] node
|
92
|
+
def on_back_ref_or_gvar_or_nth_ref(node)
|
93
|
+
preferred_expression = preferred_expression_to(node)
|
94
|
+
return unless preferred_expression
|
95
|
+
|
96
|
+
add_offense(
|
97
|
+
node,
|
98
|
+
message: format_message(
|
99
|
+
node: node,
|
100
|
+
preferred_expression: preferred_expression
|
101
|
+
)
|
102
|
+
) do |corrector|
|
103
|
+
if derived_from_braceless_interpolation?(node)
|
104
|
+
preferred_expression = "{#{preferred_expression}}"
|
30
105
|
end
|
106
|
+
|
107
|
+
corrector.replace(node, preferred_expression)
|
31
108
|
end
|
32
109
|
end
|
33
110
|
end
|