rubocop 1.4.1 → 1.6.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 +54 -11
- data/config/obsoletion.yml +196 -0
- data/lib/rubocop.rb +14 -0
- data/lib/rubocop/cli.rb +5 -1
- data/lib/rubocop/cli/command/suggest_extensions.rb +80 -0
- data/lib/rubocop/config_loader.rb +1 -1
- data/lib/rubocop/config_loader_resolver.rb +5 -1
- data/lib/rubocop/config_obsoletion.rb +65 -247
- 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 +18 -4
- data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
- 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/generator.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +3 -3
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
- data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +12 -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/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +1 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
- data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +7 -2
- data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
- data/lib/rubocop/cop/metrics/block_length.rb +13 -7
- data/lib/rubocop/cop/metrics/method_length.rb +7 -2
- data/lib/rubocop/cop/metrics/parameter_lists.rb +64 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
- data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
- data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
- data/lib/rubocop/cop/migration/department_name.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -2
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +9 -1
- data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
- data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
- 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/variable_number.rb +3 -1
- data/lib/rubocop/cop/style/and_or.rb +10 -0
- data/lib/rubocop/cop/style/character_literal.rb +10 -11
- data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
- data/lib/rubocop/cop/style/float_division.rb +44 -1
- data/lib/rubocop/cop/style/format_string.rb +8 -3
- data/lib/rubocop/cop/style/if_unless_modifier.rb +4 -0
- data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
- data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
- data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
- data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
- data/lib/rubocop/cop/style/redundant_argument.rb +17 -2
- data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
- 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/sole_nested_conditional.rb +65 -3
- 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 -3
- data/lib/rubocop/core_ext/hash.rb +20 -0
- data/lib/rubocop/ext/regexp_node.rb +29 -12
- data/lib/rubocop/ext/regexp_parser.rb +20 -9
- 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/version.rb +1 -1
- metadata +32 -5
@@ -11,6 +11,12 @@ module RuboCop
|
|
11
11
|
include Metrics::Utils::RepeatedCsendDiscount
|
12
12
|
extend NodePattern::Macros
|
13
13
|
|
14
|
+
# Ensure cops that include `MethodComplexity` have the config
|
15
|
+
# `attr_accessor`s that `ignored_method?` needs.
|
16
|
+
def self.included(base)
|
17
|
+
base.extend(IgnoredMethods::Config)
|
18
|
+
end
|
19
|
+
|
14
20
|
def on_def(node)
|
15
21
|
return if ignored_method?(node.method_name)
|
16
22
|
|
@@ -14,7 +14,10 @@ module RuboCop
|
|
14
14
|
return if part_of_ignored_node?(node)
|
15
15
|
|
16
16
|
if offense?(node)
|
17
|
-
add_offense(node)
|
17
|
+
add_offense(node) do |corrector|
|
18
|
+
opposite_style_detected
|
19
|
+
autocorrect(corrector, node) if respond_to?(:autocorrect, true)
|
20
|
+
end
|
18
21
|
else
|
19
22
|
correct_style_detected
|
20
23
|
end
|
@@ -3,7 +3,13 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Naming
|
6
|
-
# This cop makes sure that accessor methods are named properly.
|
6
|
+
# This cop makes sure that accessor methods are named properly. Applies
|
7
|
+
# to both instance and class methods.
|
8
|
+
#
|
9
|
+
# NOTE: Offenses are only registered for methods with the expected
|
10
|
+
# arity. Getters (`get_attribute`) must have no arguments to be
|
11
|
+
# registered, and setters (`set_attribute(value)`) must have exactly
|
12
|
+
# one.
|
7
13
|
#
|
8
14
|
# @example
|
9
15
|
# # bad
|
@@ -21,6 +27,14 @@ module RuboCop
|
|
21
27
|
# # good
|
22
28
|
# def attribute
|
23
29
|
# end
|
30
|
+
#
|
31
|
+
# # accepted, incorrect arity for getter
|
32
|
+
# def get_value(attr)
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # accepted, incorrect arity for setter
|
36
|
+
# def set_value
|
37
|
+
# end
|
24
38
|
class AccessorMethodName < Base
|
25
39
|
MSG_READER = 'Do not prefix reader method names with `get_`.'
|
26
40
|
MSG_WRITER = 'Do not prefix writer method names with `set_`.'
|
@@ -104,6 +104,8 @@ module RuboCop
|
|
104
104
|
def on_arg(node)
|
105
105
|
@node = node
|
106
106
|
name, = *node
|
107
|
+
return if allowed_identifier?(name)
|
108
|
+
|
107
109
|
check_name(node, name, node.loc.name)
|
108
110
|
end
|
109
111
|
alias on_lvasgn on_arg
|
@@ -139,7 +141,7 @@ module RuboCop
|
|
139
141
|
end
|
140
142
|
|
141
143
|
def allowed_identifier?(name)
|
142
|
-
allowed_identifiers.include?(name.to_s)
|
144
|
+
allowed_identifiers.include?(name.to_s.delete('@'))
|
143
145
|
end
|
144
146
|
|
145
147
|
def allowed_identifiers
|
@@ -72,6 +72,8 @@ module RuboCop
|
|
72
72
|
end
|
73
73
|
|
74
74
|
corrector.replace(node.loc.operator, node.alternate_operator)
|
75
|
+
|
76
|
+
keep_operator_precedence(corrector, node)
|
75
77
|
end
|
76
78
|
end
|
77
79
|
|
@@ -123,6 +125,14 @@ module RuboCop
|
|
123
125
|
corrector.wrap(node, '(', ')')
|
124
126
|
end
|
125
127
|
|
128
|
+
def keep_operator_precedence(corrector, node)
|
129
|
+
if node.or_type? && node.parent&.and_type?
|
130
|
+
corrector.wrap(node, '(', ')')
|
131
|
+
elsif node.and_type? && node.rhs.or_type?
|
132
|
+
corrector.wrap(node.rhs, '(', ')')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
126
136
|
def correctable_send?(node)
|
127
137
|
!node.parenthesized? && node.arguments? && !node.method?(:[])
|
128
138
|
end
|
@@ -14,8 +14,9 @@ module RuboCop
|
|
14
14
|
#
|
15
15
|
# # good
|
16
16
|
# ?\C-\M-d
|
17
|
-
class CharacterLiteral <
|
17
|
+
class CharacterLiteral < Base
|
18
18
|
include StringHelp
|
19
|
+
extend AutoCorrector
|
19
20
|
|
20
21
|
MSG = 'Do not use the character literal - ' \
|
21
22
|
'use string literal instead.'
|
@@ -26,17 +27,15 @@ module RuboCop
|
|
26
27
|
node.source.size.between?(2, 3)
|
27
28
|
end
|
28
29
|
|
29
|
-
def autocorrect(node)
|
30
|
-
|
31
|
-
string = node.source[1..-1]
|
30
|
+
def autocorrect(corrector, node)
|
31
|
+
string = node.source[1..-1]
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
33
|
+
# special character like \n
|
34
|
+
# or ' which needs to use "" or be escaped.
|
35
|
+
if string.length == 2 || string == "'"
|
36
|
+
corrector.replace(node, %("#{string}"))
|
37
|
+
elsif string.length == 1 # normal character
|
38
|
+
corrector.replace(node, "'#{string}'")
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
@@ -55,13 +55,18 @@ module RuboCop
|
|
55
55
|
padding = ((' ' * indent_width) + leading_spaces(node)).to_s
|
56
56
|
padding_for_trailing_end = padding.sub(' ' * node.loc.end.column, '')
|
57
57
|
|
58
|
-
|
58
|
+
replace_namespace_keyword(corrector, node)
|
59
59
|
split_on_double_colon(corrector, node, padding)
|
60
60
|
add_trailing_end(corrector, node, padding_for_trailing_end)
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
64
|
-
|
63
|
+
def replace_namespace_keyword(corrector, node)
|
64
|
+
class_definition = node.left_sibling&.each_node(:class)&.find do |class_node|
|
65
|
+
class_node.identifier == node.identifier.namespace
|
66
|
+
end
|
67
|
+
namespace_keyword = class_definition ? 'class' : 'module'
|
68
|
+
|
69
|
+
corrector.replace(node.loc.keyword, namespace_keyword)
|
65
70
|
end
|
66
71
|
|
67
72
|
def split_on_double_colon(corrector, node, padding)
|
@@ -41,6 +41,8 @@ module RuboCop
|
|
41
41
|
# a.fdiv(b)
|
42
42
|
class FloatDivision < Base
|
43
43
|
include ConfigurableEnforcedStyle
|
44
|
+
extend AutoCorrector
|
45
|
+
|
44
46
|
MESSAGES = {
|
45
47
|
left_coerce: 'Prefer using `.to_f` on the left side.',
|
46
48
|
right_coerce: 'Prefer using `.to_f` on the right side.',
|
@@ -64,7 +66,20 @@ module RuboCop
|
|
64
66
|
PATTERN
|
65
67
|
|
66
68
|
def on_send(node)
|
67
|
-
|
69
|
+
return unless offense_condition?(node)
|
70
|
+
|
71
|
+
add_offense(node) do |corrector|
|
72
|
+
case style
|
73
|
+
when :left_coerce, :single_coerce
|
74
|
+
add_to_f_method(corrector, node.receiver)
|
75
|
+
remove_to_f_method(corrector, node.first_argument)
|
76
|
+
when :right_coerce
|
77
|
+
remove_to_f_method(corrector, node.receiver)
|
78
|
+
add_to_f_method(corrector, node.first_argument)
|
79
|
+
when :fdiv
|
80
|
+
correct_from_slash_to_fdiv(corrector, node, node.receiver, node.first_argument)
|
81
|
+
end
|
82
|
+
end
|
68
83
|
end
|
69
84
|
|
70
85
|
private
|
@@ -87,6 +102,34 @@ module RuboCop
|
|
87
102
|
def message(_node)
|
88
103
|
MESSAGES[style]
|
89
104
|
end
|
105
|
+
|
106
|
+
def add_to_f_method(corrector, node)
|
107
|
+
corrector.insert_after(node, '.to_f') unless node.send_type? && node.method?(:to_f)
|
108
|
+
end
|
109
|
+
|
110
|
+
def remove_to_f_method(corrector, send_node)
|
111
|
+
corrector.remove(send_node.loc.dot)
|
112
|
+
corrector.remove(send_node.loc.selector)
|
113
|
+
end
|
114
|
+
|
115
|
+
def correct_from_slash_to_fdiv(corrector, node, receiver, argument)
|
116
|
+
receiver_source = extract_receiver_source(receiver)
|
117
|
+
argument_source = extract_receiver_source(argument)
|
118
|
+
|
119
|
+
if argument.respond_to?(:parenthesized?) && !argument.parenthesized?
|
120
|
+
argument_source = "(#{argument_source})"
|
121
|
+
end
|
122
|
+
|
123
|
+
corrector.replace(node, "#{receiver_source}.fdiv#{argument_source}")
|
124
|
+
end
|
125
|
+
|
126
|
+
def extract_receiver_source(node)
|
127
|
+
if node.send_type? && node.method?(:to_f)
|
128
|
+
node.receiver.source
|
129
|
+
else
|
130
|
+
node.source
|
131
|
+
end
|
132
|
+
end
|
90
133
|
end
|
91
134
|
end
|
92
135
|
end
|
@@ -111,15 +111,20 @@ module RuboCop
|
|
111
111
|
format = format_arg.source
|
112
112
|
|
113
113
|
args = if param_args.one?
|
114
|
-
|
115
|
-
|
116
|
-
arg.hash_type? ? "{ #{arg.source} }" : arg.source
|
114
|
+
format_single_parameter(param_args.last)
|
117
115
|
else
|
118
116
|
"[#{param_args.map(&:source).join(', ')}]"
|
119
117
|
end
|
120
118
|
|
121
119
|
corrector.replace(node, "#{format} % #{args}")
|
122
120
|
end
|
121
|
+
|
122
|
+
def format_single_parameter(arg)
|
123
|
+
source = arg.source
|
124
|
+
return "{ #{source} }" if arg.hash_type?
|
125
|
+
|
126
|
+
arg.send_type? && arg.operator_method? && !arg.parenthesized? ? "(#{source})" : source
|
127
|
+
end
|
123
128
|
end
|
124
129
|
end
|
125
130
|
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
|
@@ -17,26 +17,61 @@ module RuboCop
|
|
17
17
|
include OnNormalIfUnless
|
18
18
|
extend AutoCorrector
|
19
19
|
|
20
|
-
|
20
|
+
MSG_IF_ELSE = 'Do not use `if %<expr>s;` - use `if/else` instead.'
|
21
|
+
MSG_TERNARY = 'Do not use `if %<expr>s;` - use a ternary operator instead.'
|
21
22
|
|
22
23
|
def on_normal_if_unless(node)
|
23
24
|
return unless node.else_branch
|
25
|
+
return if node.parent&.if_type?
|
24
26
|
|
25
27
|
beginning = node.loc.begin
|
26
28
|
return unless beginning&.is?(';')
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
+
message = node.else_branch.if_type? ? MSG_IF_ELSE : MSG_TERNARY
|
31
|
+
|
32
|
+
add_offense(node, message: format(message, expr: node.condition.source)) do |corrector|
|
33
|
+
corrector.replace(node, autocorrect(node))
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
37
|
private
|
34
38
|
|
35
|
-
def
|
39
|
+
def autocorrect(node)
|
40
|
+
return correct_elsif(node) if node.else_branch.if_type?
|
41
|
+
|
36
42
|
else_code = node.else_branch ? node.else_branch.source : 'nil'
|
37
43
|
|
38
44
|
"#{node.condition.source} ? #{node.if_branch.source} : #{else_code}"
|
39
45
|
end
|
46
|
+
|
47
|
+
def correct_elsif(node)
|
48
|
+
<<~RUBY.chop
|
49
|
+
if #{node.condition.source}
|
50
|
+
#{node.if_branch.source}
|
51
|
+
#{build_else_branch(node.else_branch).chop}
|
52
|
+
end
|
53
|
+
RUBY
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_else_branch(second_condition)
|
57
|
+
result = <<~RUBY
|
58
|
+
elsif #{second_condition.condition.source}
|
59
|
+
#{second_condition.if_branch.source}
|
60
|
+
RUBY
|
61
|
+
|
62
|
+
if second_condition.else_branch
|
63
|
+
result += if second_condition.else_branch.if_type?
|
64
|
+
build_else_branch(second_condition.else_branch)
|
65
|
+
else
|
66
|
+
<<~RUBY
|
67
|
+
else
|
68
|
+
#{second_condition.else_branch.source}
|
69
|
+
RUBY
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
result
|
74
|
+
end
|
40
75
|
end
|
41
76
|
end
|
42
77
|
end
|
@@ -21,21 +21,30 @@ module RuboCop
|
|
21
21
|
def on_send(node)
|
22
22
|
return unless !node.arguments? && node.parenthesized?
|
23
23
|
return if ineligible_node?(node)
|
24
|
+
return if default_argument?(node)
|
24
25
|
return if ignored_method?(node.method_name)
|
25
26
|
return if same_name_assignment?(node)
|
26
27
|
|
28
|
+
register_offense(node)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def register_offense(node)
|
27
34
|
add_offense(offense_range(node)) do |corrector|
|
28
35
|
corrector.remove(node.loc.begin)
|
29
36
|
corrector.remove(node.loc.end)
|
30
37
|
end
|
31
38
|
end
|
32
39
|
|
33
|
-
private
|
34
|
-
|
35
40
|
def ineligible_node?(node)
|
36
41
|
node.camel_case_method? || node.implicit_call? || node.prefix_not?
|
37
42
|
end
|
38
43
|
|
44
|
+
def default_argument?(node)
|
45
|
+
node.parent&.optarg_type?
|
46
|
+
end
|
47
|
+
|
39
48
|
def same_name_assignment?(node)
|
40
49
|
any_assignment?(node) do |asgn_node|
|
41
50
|
next variable_in_mass_assignment?(node.method_name, asgn_node) if asgn_node.masgn_type?
|
@@ -27,12 +27,13 @@ module RuboCop
|
|
27
27
|
# # bad
|
28
28
|
# 10_000_00 # typical representation of $10,000 in cents
|
29
29
|
#
|
30
|
-
class NumericLiterals <
|
30
|
+
class NumericLiterals < Base
|
31
31
|
# The parameter is called MinDigits (meaning the minimum number of
|
32
32
|
# digits for which an offense can be registered), but essentially it's
|
33
33
|
# a Max parameter (the maximum number of something that's allowed).
|
34
34
|
include ConfigurableMax
|
35
35
|
include IntegerNode
|
36
|
+
extend AutoCorrector
|
36
37
|
|
37
38
|
MSG = 'Use underscores(_) as thousands separator and ' \
|
38
39
|
'separate every 3 digits with them.'
|
@@ -46,12 +47,6 @@ module RuboCop
|
|
46
47
|
check(node)
|
47
48
|
end
|
48
49
|
|
49
|
-
def autocorrect(node)
|
50
|
-
lambda do |corrector|
|
51
|
-
corrector.replace(node, format_number(node))
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
50
|
private
|
56
51
|
|
57
52
|
def max_parameter_name
|
@@ -67,11 +62,19 @@ module RuboCop
|
|
67
62
|
|
68
63
|
case int
|
69
64
|
when /^\d+$/
|
70
|
-
|
65
|
+
return unless (self.max = int.size + 1)
|
66
|
+
|
67
|
+
register_offense(node)
|
71
68
|
when /\d{4}/, short_group_regex
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
return unless (self.config_to_allow_offenses = { 'Enabled' => false })
|
70
|
+
|
71
|
+
register_offense(node)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def register_offense(node)
|
76
|
+
add_offense(node) do |corrector|
|
77
|
+
corrector.replace(node, format_number(node))
|
75
78
|
end
|
76
79
|
end
|
77
80
|
|
@@ -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
|