rubocop 1.79.2 → 1.81.7
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 +14 -2
- data/exe/rubocop +1 -8
- data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
- data/lib/rubocop/cli.rb +6 -2
- data/lib/rubocop/config_loader.rb +3 -1
- data/lib/rubocop/config_loader_resolver.rb +5 -4
- data/lib/rubocop/config_store.rb +5 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +4 -4
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +7 -4
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +2 -5
- data/lib/rubocop/cop/layout/line_length.rb +9 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +8 -0
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +13 -7
- data/lib/rubocop/cop/lint/debugger.rb +0 -2
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +17 -8
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +6 -5
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/lint/void.rb +7 -0
- data/lib/rubocop/cop/message_annotator.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +4 -2
- data/lib/rubocop/cop/naming/predicate_method.rb +19 -6
- data/lib/rubocop/cop/security/json_load.rb +33 -11
- data/lib/rubocop/cop/style/array_intersect.rb +46 -12
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +7 -3
- data/lib/rubocop/cop/style/constant_visibility.rb +14 -9
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/endless_method.rb +15 -2
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
- data/lib/rubocop/cop/style/float_division.rb +15 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
- data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
- data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +26 -5
- data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +14 -11
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +18 -1
- data/lib/rubocop/cop/style/semicolon.rb +23 -7
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +8 -1
- data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
- data/lib/rubocop/cop/style/unless_else.rb +10 -9
- data/lib/rubocop/cop/utils/format_string.rb +10 -0
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +9 -7
- data/lib/rubocop/cops_documentation_generator.rb +4 -4
- data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
- data/lib/rubocop/lsp/diagnostic.rb +21 -20
- data/lib/rubocop/lsp/routes.rb +62 -6
- data/lib/rubocop/lsp/runtime.rb +2 -2
- data/lib/rubocop/lsp/server.rb +2 -2
- data/lib/rubocop/lsp/stdin_runner.rb +0 -16
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/runner.rb +6 -4
- data/lib/rubocop/target_finder.rb +9 -9
- data/lib/rubocop/target_ruby.rb +10 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +1 -0
- data/lib/ruby_lsp/rubocop/addon.rb +23 -8
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +7 -6
|
@@ -444,7 +444,7 @@ module RuboCop
|
|
|
444
444
|
next if child.parent.dstr_type?
|
|
445
445
|
|
|
446
446
|
white_space = white_space_range(child, column)
|
|
447
|
-
corrector.remove(white_space) if white_space
|
|
447
|
+
corrector.remove(white_space) if white_space
|
|
448
448
|
end
|
|
449
449
|
|
|
450
450
|
if condition.loc.else && !same_line?(condition.else_branch, condition)
|
|
@@ -465,9 +465,13 @@ module RuboCop
|
|
|
465
465
|
|
|
466
466
|
def white_space_range(node, column)
|
|
467
467
|
expression = node.source_range
|
|
468
|
-
|
|
468
|
+
end_pos = expression.begin_pos
|
|
469
|
+
begin_pos = end_pos - (expression.column - column - 2)
|
|
469
470
|
|
|
470
|
-
|
|
471
|
+
return nil if begin_pos > end_pos
|
|
472
|
+
|
|
473
|
+
white_space = Parser::Source::Range.new(expression.source_buffer, begin_pos, end_pos)
|
|
474
|
+
white_space if white_space.source.strip.empty?
|
|
471
475
|
end
|
|
472
476
|
|
|
473
477
|
def assignment(node)
|
|
@@ -48,6 +48,11 @@ module RuboCop
|
|
|
48
48
|
MSG = 'Explicitly make `%<constant_name>s` public or private using ' \
|
|
49
49
|
'either `#public_constant` or `#private_constant`.'
|
|
50
50
|
|
|
51
|
+
# @!method visibility_declaration_for(node)
|
|
52
|
+
def_node_matcher :visibility_declaration_for, <<~PATTERN
|
|
53
|
+
(send nil? {:public_constant :private_constant} $...)
|
|
54
|
+
PATTERN
|
|
55
|
+
|
|
51
56
|
def on_casgn(node)
|
|
52
57
|
return unless class_or_module_scope?(node)
|
|
53
58
|
return if visibility_declaration?(node)
|
|
@@ -77,20 +82,20 @@ module RuboCop
|
|
|
77
82
|
end
|
|
78
83
|
end
|
|
79
84
|
|
|
85
|
+
# rubocop:disable Metrics/AbcSize
|
|
80
86
|
def visibility_declaration?(node)
|
|
81
87
|
node.parent.each_child_node(:send).any? do |child|
|
|
82
|
-
visibility_declaration_for
|
|
83
|
-
end
|
|
84
|
-
end
|
|
88
|
+
next false unless (arguments = visibility_declaration_for(child))
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
arguments = arguments.first.children.first.to_a if arguments.first&.splat_type?
|
|
91
|
+
constant_values = arguments.map do |argument|
|
|
92
|
+
argument.value.to_sym if argument.respond_to?(:value)
|
|
93
|
+
end
|
|
90
94
|
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
constant_values.include?(node.name)
|
|
96
|
+
end
|
|
93
97
|
end
|
|
98
|
+
# rubocop:enable Metrics/AbcSize
|
|
94
99
|
end
|
|
95
100
|
end
|
|
96
101
|
end
|
|
@@ -144,7 +144,7 @@ module RuboCop
|
|
|
144
144
|
MSG_REQUIRE_ALWAYS = 'Use endless method definitions.'
|
|
145
145
|
|
|
146
146
|
def on_def(node)
|
|
147
|
-
return if node.assignment_method?
|
|
147
|
+
return if node.assignment_method? || use_heredoc?(node)
|
|
148
148
|
|
|
149
149
|
case style
|
|
150
150
|
when :allow_single_line, :allow_always
|
|
@@ -198,6 +198,13 @@ module RuboCop
|
|
|
198
198
|
add_offense(node) { |corrector| correct_to_multiline(corrector, node) }
|
|
199
199
|
end
|
|
200
200
|
|
|
201
|
+
def use_heredoc?(node)
|
|
202
|
+
return false unless (body = node.body)
|
|
203
|
+
return true if body.str_type? && body.heredoc?
|
|
204
|
+
|
|
205
|
+
body.each_descendant(:str).any?(&:heredoc?)
|
|
206
|
+
end
|
|
207
|
+
|
|
201
208
|
def correct_to_multiline(corrector, node)
|
|
202
209
|
replacement = <<~RUBY.strip
|
|
203
210
|
def #{node.method_name}#{arguments(node)}
|
|
@@ -225,7 +232,13 @@ module RuboCop
|
|
|
225
232
|
def too_long_when_made_endless?(node)
|
|
226
233
|
return false unless config.cop_enabled?('Layout/LineLength')
|
|
227
234
|
|
|
228
|
-
|
|
235
|
+
offset = modifier_offset(node)
|
|
236
|
+
|
|
237
|
+
endless_replacement(node).length + offset > config.for_cop('Layout/LineLength')['Max']
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def modifier_offset(node)
|
|
241
|
+
same_line?(node.parent, node) ? node.loc.column - node.parent.loc.column : 0
|
|
229
242
|
end
|
|
230
243
|
end
|
|
231
244
|
end
|
|
@@ -7,9 +7,12 @@ module RuboCop
|
|
|
7
7
|
# It is recommended to either always use `fdiv` or coerce one side only.
|
|
8
8
|
# This cop also provides other options for code consistency.
|
|
9
9
|
#
|
|
10
|
+
# For `Regexp.last_match` and nth reference (e.g., `$1`), it assumes that the value
|
|
11
|
+
# is a string matched by a regular expression, and allows conversion with `#to_f`.
|
|
12
|
+
#
|
|
10
13
|
# @safety
|
|
11
14
|
# This cop is unsafe, because if the operand variable is a string object
|
|
12
|
-
# then
|
|
15
|
+
# then `#to_f` will be removed and an error will occur.
|
|
13
16
|
#
|
|
14
17
|
# [source,ruby]
|
|
15
18
|
# ----
|
|
@@ -84,6 +87,14 @@ module RuboCop
|
|
|
84
87
|
(send !nil? :to_f)
|
|
85
88
|
PATTERN
|
|
86
89
|
|
|
90
|
+
# @!method regexp_last_match?(node)
|
|
91
|
+
def_node_matcher :regexp_last_match?, <<~PATTERN
|
|
92
|
+
{
|
|
93
|
+
(send (const {nil? cbase} :Regexp) :last_match int)
|
|
94
|
+
(:nth_ref _)
|
|
95
|
+
}
|
|
96
|
+
PATTERN
|
|
97
|
+
|
|
87
98
|
def on_send(node)
|
|
88
99
|
return unless offense_condition?(node)
|
|
89
100
|
|
|
@@ -104,6 +115,9 @@ module RuboCop
|
|
|
104
115
|
private
|
|
105
116
|
|
|
106
117
|
def offense_condition?(node)
|
|
118
|
+
return false if regexp_last_match?(node.receiver.receiver) ||
|
|
119
|
+
regexp_last_match?(node.first_argument.receiver)
|
|
120
|
+
|
|
107
121
|
case style
|
|
108
122
|
when :left_coerce
|
|
109
123
|
right_coerce?(node)
|
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def autocorrect(corrector, node)
|
|
71
|
-
if node.
|
|
71
|
+
if node.post_condition_loop?
|
|
72
72
|
replace_begin_end_with_modifier(corrector, node)
|
|
73
73
|
elsif node.modifier_form?
|
|
74
74
|
replace_source(corrector, node.source_range, modifier_replacement(node))
|
|
@@ -43,24 +43,26 @@ module RuboCop
|
|
|
43
43
|
# @!method nil_check?(node)
|
|
44
44
|
def_node_matcher :nil_check?, '(send _ :nil?)'
|
|
45
45
|
|
|
46
|
+
# rubocop:disable Metrics/AbcSize
|
|
46
47
|
def on_send(node)
|
|
47
48
|
return unless node.receiver
|
|
48
49
|
|
|
49
50
|
style_check?(node) do
|
|
50
51
|
add_offense(node.loc.selector) do |corrector|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
if prefer_comparison?
|
|
53
|
+
range = node.loc.dot.join(node.loc.selector.end)
|
|
54
|
+
corrector.replace(range, ' == nil')
|
|
55
|
+
else
|
|
56
|
+
range = node.receiver.source_range.end.join(node.source_range.end)
|
|
57
|
+
corrector.replace(range, '.nil?')
|
|
58
|
+
end
|
|
58
59
|
|
|
59
60
|
parent = node.parent
|
|
60
61
|
corrector.wrap(node, '(', ')') if parent.respond_to?(:method?) && parent.method?(:!)
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
end
|
|
65
|
+
# rubocop:enable Metrics/AbcSize
|
|
64
66
|
|
|
65
67
|
private
|
|
66
68
|
|
|
@@ -55,19 +55,21 @@ module RuboCop
|
|
|
55
55
|
include OnNormalIfUnless
|
|
56
56
|
extend AutoCorrector
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
MSG_SUFFIX = 'over single-line `%<keyword>s/then/else/end` constructs.'
|
|
59
|
+
MSG_TERNARY = "Favor the ternary operator (`?:`) #{MSG_SUFFIX}"
|
|
60
|
+
MSG_MULTILINE = "Favor multi-line `%<keyword>s` #{MSG_SUFFIX}"
|
|
60
61
|
|
|
61
62
|
def on_normal_if_unless(node)
|
|
62
63
|
return unless node.single_line?
|
|
63
64
|
return unless node.else_branch
|
|
64
65
|
return if node.elsif? || node.if_branch&.begin_type?
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
multiline = multiline?(node)
|
|
68
|
+
|
|
69
|
+
add_offense(node, message: message(node, multiline)) do |corrector|
|
|
68
70
|
next if part_of_ignored_node?(node)
|
|
69
71
|
|
|
70
|
-
autocorrect(corrector, node)
|
|
72
|
+
autocorrect(corrector, node, multiline)
|
|
71
73
|
|
|
72
74
|
ignore_node(node)
|
|
73
75
|
end
|
|
@@ -75,12 +77,18 @@ module RuboCop
|
|
|
75
77
|
|
|
76
78
|
private
|
|
77
79
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
+
def multiline?(node)
|
|
81
|
+
always_multiline? || cannot_replace_to_ternary?(node)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def message(node, multiline)
|
|
85
|
+
template = multiline ? MSG_MULTILINE : MSG_TERNARY
|
|
86
|
+
|
|
87
|
+
format(template, keyword: node.keyword)
|
|
80
88
|
end
|
|
81
89
|
|
|
82
|
-
def autocorrect(corrector, node)
|
|
83
|
-
if
|
|
90
|
+
def autocorrect(corrector, node, multiline)
|
|
91
|
+
if multiline
|
|
84
92
|
IfThenCorrector.new(node, indentation: configured_indentation_width).call(corrector)
|
|
85
93
|
else
|
|
86
94
|
corrector.replace(node, ternary_correction(node))
|
|
@@ -85,6 +85,29 @@ module RuboCop
|
|
|
85
85
|
end
|
|
86
86
|
alias on_defs on_def
|
|
87
87
|
|
|
88
|
+
def on_if(node)
|
|
89
|
+
return if node.modifier_form?
|
|
90
|
+
|
|
91
|
+
inspect_branches(node)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def on_case(node)
|
|
95
|
+
inspect_branches(node)
|
|
96
|
+
end
|
|
97
|
+
alias on_case_match on_case
|
|
98
|
+
|
|
99
|
+
def on_while(node)
|
|
100
|
+
return if node.modifier_form?
|
|
101
|
+
|
|
102
|
+
body = node.body
|
|
103
|
+
|
|
104
|
+
return unless body&.kwbegin_type?
|
|
105
|
+
return if body.rescue_node || body.ensure_node
|
|
106
|
+
|
|
107
|
+
register_offense(body)
|
|
108
|
+
end
|
|
109
|
+
alias on_until on_while
|
|
110
|
+
|
|
88
111
|
def on_block(node)
|
|
89
112
|
return if target_ruby_version < 2.5
|
|
90
113
|
return if node.send_node.lambda_literal?
|
|
@@ -180,6 +203,8 @@ module RuboCop
|
|
|
180
203
|
end
|
|
181
204
|
|
|
182
205
|
def begin_block_has_multiline_statements?(node)
|
|
206
|
+
return false unless node.parent
|
|
207
|
+
|
|
183
208
|
node.children.count >= 2
|
|
184
209
|
end
|
|
185
210
|
|
|
@@ -199,6 +224,15 @@ module RuboCop
|
|
|
199
224
|
def valid_begin_assignment?(node)
|
|
200
225
|
node.parent&.assignment? && !node.children.one?
|
|
201
226
|
end
|
|
227
|
+
|
|
228
|
+
def inspect_branches(node)
|
|
229
|
+
node.branches.each do |branch|
|
|
230
|
+
next unless branch&.kwbegin_type?
|
|
231
|
+
next if branch.rescue_node || branch.ensure_node
|
|
232
|
+
|
|
233
|
+
register_offense(branch)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
202
236
|
end
|
|
203
237
|
end
|
|
204
238
|
end
|
|
@@ -247,7 +247,7 @@ module RuboCop
|
|
|
247
247
|
"#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
|
|
248
248
|
elsif if_branch.true_type?
|
|
249
249
|
condition = if_branch.parent.condition
|
|
250
|
-
return condition.source if condition.arguments.empty?
|
|
250
|
+
return condition.source if condition.arguments.empty? || condition.parenthesized?
|
|
251
251
|
|
|
252
252
|
wrap_arguments_with_parens(condition)
|
|
253
253
|
else
|
|
@@ -89,7 +89,7 @@ module RuboCop
|
|
|
89
89
|
|
|
90
90
|
def on_send(node)
|
|
91
91
|
format_without_additional_args?(node) do |value|
|
|
92
|
-
replacement = value.source
|
|
92
|
+
replacement = escape_control_chars(value.source)
|
|
93
93
|
|
|
94
94
|
add_offense(node, message: message(node, replacement)) do |corrector|
|
|
95
95
|
corrector.replace(node, replacement)
|
|
@@ -134,6 +134,7 @@ module RuboCop
|
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
137
138
|
def all_fields_literal?(string, arguments)
|
|
138
139
|
count = 0
|
|
139
140
|
sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
|
|
@@ -141,29 +142,44 @@ module RuboCop
|
|
|
141
142
|
|
|
142
143
|
sequences.each do |sequence|
|
|
143
144
|
next if sequence.percent?
|
|
145
|
+
next if unknown_variable_width?(sequence, arguments)
|
|
144
146
|
|
|
145
147
|
hash = arguments.detect(&:hash_type?)
|
|
146
148
|
next unless (argument = find_argument(sequence, arguments, hash))
|
|
147
149
|
next unless matching_argument?(sequence, argument)
|
|
150
|
+
next if (sequence.width || sequence.precision) && argument.dstr_type?
|
|
148
151
|
|
|
149
152
|
count += 1
|
|
150
153
|
end
|
|
151
154
|
|
|
152
155
|
sequences.size == count
|
|
153
156
|
end
|
|
157
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
154
158
|
|
|
159
|
+
# If the sequence has a variable (`*`) width, it cannot be autocorrected
|
|
160
|
+
# if the width is not given as a numeric literal argument
|
|
161
|
+
def unknown_variable_width?(sequence, arguments)
|
|
162
|
+
return false unless sequence.variable_width?
|
|
163
|
+
|
|
164
|
+
argument = arguments[sequence.variable_width_argument_number - 1]
|
|
165
|
+
!numeric?(argument)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# rubocop:disable Metrics/AbcSize
|
|
155
169
|
def find_argument(sequence, arguments, hash)
|
|
156
170
|
if hash && (sequence.annotated? || sequence.template?)
|
|
157
171
|
find_hash_value_node(hash, sequence.name.to_sym).first
|
|
172
|
+
elsif sequence.variable_width?
|
|
173
|
+
# If the specifier contains `*`, the argument for the width can be ignored.
|
|
174
|
+
arguments.delete_at(sequence.variable_width_argument_number - 1)
|
|
175
|
+
arguments.shift
|
|
158
176
|
elsif sequence.arg_number
|
|
159
177
|
arguments[sequence.arg_number.to_i - 1]
|
|
160
178
|
else
|
|
161
|
-
# If the specifier contains `*`, the following arguments will be used
|
|
162
|
-
# to specify the width and can be ignored.
|
|
163
|
-
(sequence.arity - 1).times { arguments.shift }
|
|
164
179
|
arguments.shift
|
|
165
180
|
end
|
|
166
181
|
end
|
|
182
|
+
# rubocop:enable Metrics/AbcSize
|
|
167
183
|
|
|
168
184
|
def matching_argument?(sequence, argument)
|
|
169
185
|
# Template specifiers don't give a type, any acceptable literal type is ok.
|
|
@@ -214,7 +230,12 @@ module RuboCop
|
|
|
214
230
|
end
|
|
215
231
|
end
|
|
216
232
|
|
|
217
|
-
"#{start_delimiter}#{string}#{end_delimiter}"
|
|
233
|
+
"#{start_delimiter}#{escape_control_chars(string)}#{end_delimiter}"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Escape any control characters in the string (eg. `\t` or `\n` become `\\t` or `\\n`)
|
|
237
|
+
def escape_control_chars(string)
|
|
238
|
+
string.gsub(/\p{Cc}/) { |s| s.dump[1..-2] }
|
|
218
239
|
end
|
|
219
240
|
|
|
220
241
|
def argument_values(arguments)
|
|
@@ -49,9 +49,10 @@ module RuboCop
|
|
|
49
49
|
def on_dstr(node)
|
|
50
50
|
return unless single_interpolation?(node)
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
embedded_node = node.children.first
|
|
53
|
+
return if use_match_pattern?(embedded_node)
|
|
54
54
|
|
|
55
|
+
add_offense(node) do |corrector|
|
|
55
56
|
if variable_interpolation?(embedded_node)
|
|
56
57
|
autocorrect_variable_interpolation(corrector, embedded_node, node)
|
|
57
58
|
elsif single_variable_interpolation?(embedded_node)
|
|
@@ -71,6 +72,14 @@ module RuboCop
|
|
|
71
72
|
!embedded_in_percent_array?(node)
|
|
72
73
|
end
|
|
73
74
|
|
|
75
|
+
def use_match_pattern?(node)
|
|
76
|
+
return false if target_ruby_version <= 2.7
|
|
77
|
+
|
|
78
|
+
node.children.any? do |child|
|
|
79
|
+
child.respond_to?(:match_pattern_type?) && child.match_pattern_type?
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
74
83
|
def single_variable_interpolation?(node)
|
|
75
84
|
return false unless node.children.one?
|
|
76
85
|
|
|
@@ -24,9 +24,6 @@ module RuboCop
|
|
|
24
24
|
(send `{(send _recv _msg) str array hash const #variable?} :[] ...)
|
|
25
25
|
PATTERN
|
|
26
26
|
|
|
27
|
-
# @!method method_node_and_args(node)
|
|
28
|
-
def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
|
|
29
|
-
|
|
30
27
|
# @!method rescue?(node)
|
|
31
28
|
def_node_matcher :rescue?, '{^resbody ^^resbody}'
|
|
32
29
|
|
|
@@ -205,6 +202,7 @@ module RuboCop
|
|
|
205
202
|
return false unless node.rescue_type?
|
|
206
203
|
return false unless (parent = begin_node.parent)
|
|
207
204
|
return false if parent.if_type? && parent.ternary?
|
|
205
|
+
return false if parent.conditional? && parent.condition == begin_node
|
|
208
206
|
|
|
209
207
|
!parent.type?(:call, :array, :pair)
|
|
210
208
|
end
|
|
@@ -220,7 +218,7 @@ module RuboCop
|
|
|
220
218
|
end
|
|
221
219
|
|
|
222
220
|
def call_node?(node)
|
|
223
|
-
node.call_type? || (node.any_block_type? && !node.lambda_or_proc?)
|
|
221
|
+
node.call_type? || (node.any_block_type? && node.braces? && !node.lambda_or_proc?)
|
|
224
222
|
end
|
|
225
223
|
|
|
226
224
|
def check_send(begin_node, node)
|
|
@@ -228,7 +226,7 @@ module RuboCop
|
|
|
228
226
|
|
|
229
227
|
return check_unary(begin_node, node) if node.unary_operation?
|
|
230
228
|
|
|
231
|
-
return unless method_call_with_redundant_parentheses?(node)
|
|
229
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
|
232
230
|
return if call_chain_starts_with_int?(begin_node, node) ||
|
|
233
231
|
do_end_block_in_method_chain?(begin_node, node)
|
|
234
232
|
|
|
@@ -239,8 +237,7 @@ module RuboCop
|
|
|
239
237
|
return if begin_node.chained?
|
|
240
238
|
|
|
241
239
|
node = node.children.first while suspect_unary?(node)
|
|
242
|
-
|
|
243
|
-
return if node.send_type? && !method_call_with_redundant_parentheses?(node)
|
|
240
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
|
244
241
|
|
|
245
242
|
offense(begin_node, 'a unary operation')
|
|
246
243
|
end
|
|
@@ -302,13 +299,19 @@ module RuboCop
|
|
|
302
299
|
end
|
|
303
300
|
end
|
|
304
301
|
|
|
305
|
-
def method_call_with_redundant_parentheses?(node)
|
|
306
|
-
return false unless node.
|
|
302
|
+
def method_call_with_redundant_parentheses?(begin_node, node)
|
|
303
|
+
return false unless node.type?(:call, :super, :yield, :defined?)
|
|
307
304
|
return false if node.prefix_not?
|
|
305
|
+
return true if singular_parenthesized_parent?(begin_node)
|
|
306
|
+
|
|
307
|
+
node.arguments.empty? || parentheses?(node) || square_brackets?(node)
|
|
308
|
+
end
|
|
308
309
|
|
|
309
|
-
|
|
310
|
+
def singular_parenthesized_parent?(begin_node)
|
|
311
|
+
return true unless begin_node.parent
|
|
312
|
+
return false if begin_node.parent.type?(:splat, :kwsplat)
|
|
310
313
|
|
|
311
|
-
|
|
314
|
+
parentheses?(begin_node) && begin_node.parent.children.one?
|
|
312
315
|
end
|
|
313
316
|
|
|
314
317
|
def only_begin_arg?(args)
|
|
@@ -66,6 +66,7 @@ module RuboCop
|
|
|
66
66
|
DETERMINISTIC_REGEX.match?(regexp_node.source)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
# rubocop:disable Metrics/MethodLength
|
|
69
70
|
def preferred_argument(regexp_node)
|
|
70
71
|
new_argument = replacement(regexp_node)
|
|
71
72
|
|
|
@@ -73,6 +74,8 @@ module RuboCop
|
|
|
73
74
|
new_argument.gsub!("'", "\\\\'")
|
|
74
75
|
new_argument.gsub!('\"', '"')
|
|
75
76
|
quote = "'"
|
|
77
|
+
elsif new_argument.include?("\\'")
|
|
78
|
+
quote = "'"
|
|
76
79
|
elsif new_argument.include?('\'')
|
|
77
80
|
new_argument.gsub!("'", "\\\\'")
|
|
78
81
|
quote = "'"
|
|
@@ -84,6 +87,7 @@ module RuboCop
|
|
|
84
87
|
|
|
85
88
|
"#{quote}#{new_argument}#{quote}"
|
|
86
89
|
end
|
|
90
|
+
# rubocop:enable Metrics/MethodLength
|
|
87
91
|
|
|
88
92
|
def replacement(regexp_node)
|
|
89
93
|
regexp_content = regexp_node.content
|
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
|
41
41
|
ALLOWED_ALWAYS_ESCAPES = " \n[]^\\#".chars.freeze
|
|
42
42
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES = '-'.chars.freeze
|
|
43
43
|
ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES = '.*+?{}()|$'.chars.freeze
|
|
44
|
+
INTERPOLATION_SIGILS = %w[@ $].freeze
|
|
44
45
|
|
|
45
46
|
def on_regexp(node)
|
|
46
47
|
each_escape(node) do |char, index, within_character_class|
|
|
@@ -64,6 +65,7 @@ module RuboCop
|
|
|
64
65
|
# different versions of Ruby so that e.g. /\i/ != /i/
|
|
65
66
|
return true if /[[:alnum:]]/.match?(char)
|
|
66
67
|
return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
|
|
68
|
+
return true if requires_escape_to_avoid_interpolation?(node.source[index], char)
|
|
67
69
|
|
|
68
70
|
if within_character_class
|
|
69
71
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char) &&
|
|
@@ -95,6 +97,12 @@ module RuboCop
|
|
|
95
97
|
delimiters.include?(char)
|
|
96
98
|
end
|
|
97
99
|
|
|
100
|
+
def requires_escape_to_avoid_interpolation?(char_before_escape, escaped_char)
|
|
101
|
+
# Preserve escapes after '#' that would otherwise trigger interpolation:
|
|
102
|
+
# '#@ivar', '#@@cvar', and '#$gvar'.
|
|
103
|
+
char_before_escape == '#' && INTERPOLATION_SIGILS.include?(escaped_char)
|
|
104
|
+
end
|
|
105
|
+
|
|
98
106
|
def each_escape(node)
|
|
99
107
|
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
|
|
100
108
|
yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
|
|
@@ -142,6 +142,7 @@ module RuboCop
|
|
|
142
142
|
# @!method strip_begin(node)
|
|
143
143
|
def_node_matcher :strip_begin, '{ (begin $!begin) $!(begin) }'
|
|
144
144
|
|
|
145
|
+
# rubocop:disable Metrics/AbcSize
|
|
145
146
|
def on_if(node)
|
|
146
147
|
return if allowed_if_condition?(node)
|
|
147
148
|
|
|
@@ -155,9 +156,11 @@ module RuboCop
|
|
|
155
156
|
removal_ranges = [begin_range(node, body), end_range(node, body)]
|
|
156
157
|
|
|
157
158
|
report_offense(node, method_chain, method_call, *removal_ranges) do |corrector|
|
|
159
|
+
corrector.replace(receiver, checked_variable.source) if checked_variable.csend_type?
|
|
158
160
|
corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
|
|
159
161
|
end
|
|
160
162
|
end
|
|
163
|
+
# rubocop:enable Metrics/AbcSize
|
|
161
164
|
|
|
162
165
|
def on_and(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
163
166
|
collect_and_clauses(node).each do |(lhs, lhs_operator_range), (rhs, _rhs_operator_range)|
|
|
@@ -259,8 +262,14 @@ module RuboCop
|
|
|
259
262
|
end
|
|
260
263
|
|
|
261
264
|
def dotless_operator_call?(method_call)
|
|
265
|
+
return true if dotless_operator_method?(method_call)
|
|
266
|
+
|
|
262
267
|
method_call = method_call.parent while method_call.parent.send_type?
|
|
263
268
|
|
|
269
|
+
dotless_operator_method?(method_call)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def dotless_operator_method?(method_call)
|
|
264
273
|
return false if method_call.loc.dot
|
|
265
274
|
|
|
266
275
|
method_call.method?(:[]) || method_call.method?(:[]=) || method_call.operator_method?
|
|
@@ -335,8 +344,16 @@ module RuboCop
|
|
|
335
344
|
|
|
336
345
|
def matching_call_nodes?(left, right)
|
|
337
346
|
return false unless left && right.respond_to?(:call_type?)
|
|
347
|
+
return false unless left.call_type? && right.call_type?
|
|
348
|
+
|
|
349
|
+
# Compare receiver and method name, but ignore the difference between
|
|
350
|
+
# safe navigation method call (`&.`) and dot method call (`.`).
|
|
351
|
+
left_receiver, left_method, *left_args = left.children
|
|
352
|
+
right_receiver, right_method, *right_args = right.children
|
|
338
353
|
|
|
339
|
-
|
|
354
|
+
left_method == right_method &&
|
|
355
|
+
matching_nodes?(left_receiver, right_receiver) &&
|
|
356
|
+
left_args == right_args
|
|
340
357
|
end
|
|
341
358
|
|
|
342
359
|
def chain_length(method_chain, method)
|