rubocop 1.79.2 → 1.81.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 +10 -0
- 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_store.rb +5 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +2 -2
- 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/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/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
- data/lib/rubocop/cop/lint/self_assignment.rb +5 -4
- 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 +1 -1
- data/lib/rubocop/cop/naming/predicate_method.rb +15 -2
- data/lib/rubocop/cop/style/array_intersect.rb +45 -11
- 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/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -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/it_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
- data/lib/rubocop/cop/style/numbered_parameters.rb +1 -1
- 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 +18 -3
- 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/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/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 +11 -7
@@ -19,8 +19,7 @@ module RuboCop
|
|
19
19
|
def check_end_kw_alignment(node, align_ranges)
|
20
20
|
return if ignored_node?(node)
|
21
21
|
|
22
|
-
end_loc = node.loc.end
|
23
|
-
return if accept_end_kw_alignment?(end_loc)
|
22
|
+
return unless (end_loc = node.loc.end)
|
24
23
|
|
25
24
|
matching = matching_ranges(end_loc, align_ranges)
|
26
25
|
|
@@ -57,11 +56,6 @@ module RuboCop
|
|
57
56
|
add_offense(end_loc, message: msg) { |corrector| autocorrect(corrector, node) }
|
58
57
|
end
|
59
58
|
|
60
|
-
def accept_end_kw_alignment?(end_loc)
|
61
|
-
end_loc.nil? || # Discard modifier forms of if/while/until.
|
62
|
-
!/\A[ \t]*end/.match?(processed_source.lines[end_loc.line - 1])
|
63
|
-
end
|
64
|
-
|
65
59
|
def style_parameter_name
|
66
60
|
'EnforcedStyleAlignWith'
|
67
61
|
end
|
@@ -140,7 +140,7 @@ module RuboCop
|
|
140
140
|
end
|
141
141
|
|
142
142
|
def last_item_precedes_newline?(node)
|
143
|
-
after_last_item = node.children.last.source_range.end.join(node.
|
143
|
+
after_last_item = node.children.last.source_range.end.join(node.source_range.end)
|
144
144
|
|
145
145
|
after_last_item.source.start_with?(/,?\s*(#.*)?\n/)
|
146
146
|
end
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
14
14
|
# method calls are assumed to return boolean values. The cop does not make an assessment
|
15
15
|
# if the return type is unknown (non-predicate method calls, variables, etc.).
|
16
16
|
#
|
17
|
-
# NOTE:
|
17
|
+
# NOTE: The `initialize` method and operator methods (`def ==`, etc.) are ignored.
|
18
18
|
#
|
19
19
|
# By default, the cop runs in `conservative` mode, which allows a method to be named
|
20
20
|
# with a question mark as long as at least one return value is boolean. In `aggressive`
|
@@ -113,6 +113,18 @@ module RuboCop
|
|
113
113
|
# true
|
114
114
|
# end
|
115
115
|
#
|
116
|
+
# @example AllowedMethods: [call] (default)
|
117
|
+
# # good
|
118
|
+
# def call
|
119
|
+
# foo == bar
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# @example AllowedPatterns: [\Afoo]
|
123
|
+
# # good
|
124
|
+
# def foo?
|
125
|
+
# 'foo'
|
126
|
+
# end
|
127
|
+
#
|
116
128
|
# @example AllowBangMethods: false (default)
|
117
129
|
# # bad
|
118
130
|
# def save!
|
@@ -149,7 +161,8 @@ module RuboCop
|
|
149
161
|
private
|
150
162
|
|
151
163
|
def allowed?(node)
|
152
|
-
|
164
|
+
node.method?(:initialize) ||
|
165
|
+
allowed_method?(node.method_name) ||
|
153
166
|
matches_allowed_pattern?(node.method_name) ||
|
154
167
|
allowed_bang_method?(node) ||
|
155
168
|
node.operator_method? ||
|
@@ -10,6 +10,8 @@ module RuboCop
|
|
10
10
|
# * `(array1 & array2).any?`
|
11
11
|
# * `(array1.intersection(array2)).any?`
|
12
12
|
# * `array1.any? { |elem| array2.member?(elem) }`
|
13
|
+
# * `(array1 & array2).count > 0`
|
14
|
+
# * `(array1 & array2).size > 0`
|
13
15
|
#
|
14
16
|
# can be replaced with `array1.intersect?(array2)`.
|
15
17
|
#
|
@@ -51,6 +53,19 @@ module RuboCop
|
|
51
53
|
# array1.intersect?(array2)
|
52
54
|
# !array1.intersect?(array2)
|
53
55
|
#
|
56
|
+
# # bad
|
57
|
+
# (array1 & array2).count > 0
|
58
|
+
# (array1 & array2).count.positive?
|
59
|
+
# (array1 & array2).count != 0
|
60
|
+
#
|
61
|
+
# (array1 & array2).count == 0
|
62
|
+
# (array1 & array2).count.zero?
|
63
|
+
#
|
64
|
+
# # good
|
65
|
+
# array1.intersect?(array2)
|
66
|
+
#
|
67
|
+
# !array1.intersect?(array2)
|
68
|
+
#
|
54
69
|
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
55
70
|
# # good
|
56
71
|
# (array1 & array2).present?
|
@@ -73,9 +88,11 @@ module RuboCop
|
|
73
88
|
PREDICATES = %i[any? empty? none?].to_set.freeze
|
74
89
|
ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
|
75
90
|
|
91
|
+
ARRAY_SIZE_METHODS = %i[count length size].to_set.freeze
|
92
|
+
|
76
93
|
# @!method bad_intersection_check?(node, predicates)
|
77
94
|
def_node_matcher :bad_intersection_check?, <<~PATTERN
|
78
|
-
(call
|
95
|
+
$(call
|
79
96
|
{
|
80
97
|
(begin (send $_ :& $_))
|
81
98
|
(call $_ :intersection $_)
|
@@ -84,6 +101,20 @@ module RuboCop
|
|
84
101
|
)
|
85
102
|
PATTERN
|
86
103
|
|
104
|
+
# @!method intersection_size_check?(node, predicates)
|
105
|
+
def_node_matcher :intersection_size_check?, <<~PATTERN
|
106
|
+
(call
|
107
|
+
$(call
|
108
|
+
{
|
109
|
+
(begin (send $_ :& $_))
|
110
|
+
(call $_ :intersection $_)
|
111
|
+
}
|
112
|
+
%ARRAY_SIZE_METHODS
|
113
|
+
)
|
114
|
+
{$:> (int 0) | $:positive? | $:!= (int 0) | $:== (int 0) | $:zero?}
|
115
|
+
)
|
116
|
+
PATTERN
|
117
|
+
|
87
118
|
# @!method any_none_block_intersection(node)
|
88
119
|
def_node_matcher :any_none_block_intersection, <<~PATTERN
|
89
120
|
{
|
@@ -104,15 +135,15 @@ module RuboCop
|
|
104
135
|
PATTERN
|
105
136
|
|
106
137
|
MSG = 'Use `%<replacement>s` instead of `%<existing>s`.'
|
107
|
-
STRAIGHT_METHODS = %i[present? any?].freeze
|
108
|
-
NEGATED_METHODS = %i[blank? empty? none?].freeze
|
138
|
+
STRAIGHT_METHODS = %i[present? any? > positive? !=].freeze
|
139
|
+
NEGATED_METHODS = %i[blank? empty? none? == zero?].freeze
|
109
140
|
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
110
141
|
|
111
142
|
def on_send(node)
|
112
143
|
return if node.block_literal?
|
113
|
-
return unless (receiver, argument, method_name = bad_intersection?(node))
|
144
|
+
return unless (dot_node, receiver, argument, method_name = bad_intersection?(node))
|
114
145
|
|
115
|
-
dot =
|
146
|
+
dot = dot_node.loc.dot.source
|
116
147
|
bang = straight?(method_name) ? '' : '!'
|
117
148
|
replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
|
118
149
|
|
@@ -135,13 +166,16 @@ module RuboCop
|
|
135
166
|
private
|
136
167
|
|
137
168
|
def bad_intersection?(node)
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
PREDICATES
|
142
|
-
end
|
169
|
+
bad_intersection_check?(node, bad_intersection_predicates) ||
|
170
|
+
intersection_size_check?(node)
|
171
|
+
end
|
143
172
|
|
144
|
-
|
173
|
+
def bad_intersection_predicates
|
174
|
+
if active_support_extensions_enabled?
|
175
|
+
ACTIVE_SUPPORT_PREDICATES
|
176
|
+
else
|
177
|
+
PREDICATES
|
178
|
+
end
|
145
179
|
end
|
146
180
|
|
147
181
|
def straight?(method_name)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Use `include?(element)` instead of `intersect?([element])`.
|
7
|
+
#
|
8
|
+
# @safety
|
9
|
+
# The receiver might not be an array.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # bad
|
13
|
+
# array.intersect?([element])
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# array.include?(element)
|
17
|
+
class ArrayIntersectWithSingleElement < Base
|
18
|
+
extend AutoCorrector
|
19
|
+
|
20
|
+
MSG = 'Use `include?(element)` instead of `intersect?([element])`.'
|
21
|
+
|
22
|
+
RESTRICT_ON_SEND = %i[intersect?].freeze
|
23
|
+
|
24
|
+
# @!method single_element(node)
|
25
|
+
def_node_matcher :single_element, <<~PATTERN
|
26
|
+
(send _ _ $(array $_))
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
array, element = single_element(node)
|
31
|
+
return unless array
|
32
|
+
|
33
|
+
add_offense(
|
34
|
+
node.source_range.with(begin_pos: node.loc.selector.begin_pos)
|
35
|
+
) do |corrector|
|
36
|
+
corrector.replace(node.loc.selector, 'include?')
|
37
|
+
corrector.replace(
|
38
|
+
array,
|
39
|
+
array.percent_literal? ? element.value.inspect : element.source
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
alias on_csend on_send
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -70,18 +70,25 @@ module RuboCop
|
|
70
70
|
(send _ :& _))
|
71
71
|
PATTERN
|
72
72
|
|
73
|
+
# rubocop:disable Metrics/AbcSize
|
73
74
|
def on_send(node)
|
74
75
|
return unless node.receiver&.begin_type?
|
75
76
|
return unless (preferred_method = preferred_method(node))
|
76
77
|
|
77
78
|
bit_operation = node.receiver.children.first
|
78
79
|
lhs, _operator, rhs = *bit_operation
|
79
|
-
|
80
|
+
|
81
|
+
preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source
|
82
|
+
"#{rhs.source}.allbits?(#{lhs.source})"
|
83
|
+
else
|
84
|
+
"#{lhs.source}.#{preferred_method}(#{rhs.source})"
|
85
|
+
end
|
80
86
|
|
81
87
|
add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
|
82
88
|
corrector.replace(node, preferred)
|
83
89
|
end
|
84
90
|
end
|
91
|
+
# rubocop:enable Metrics/AbcSize
|
85
92
|
|
86
93
|
private
|
87
94
|
|
@@ -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))
|
@@ -94,7 +94,7 @@ module RuboCop
|
|
94
94
|
def on_itblock(node)
|
95
95
|
case style
|
96
96
|
when :allow_single_line
|
97
|
-
return if node.
|
97
|
+
return if same_line?(node.source_range.begin, node.source_range.end)
|
98
98
|
|
99
99
|
add_offense(node, message: MSG_AVOID_IT_PARAMETER_MULTILINE)
|
100
100
|
when :disallow
|
@@ -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
|
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
36
36
|
def on_numblock(node)
|
37
37
|
if style == :disallow
|
38
38
|
add_offense(node, message: MSG_DISALLOW)
|
39
|
-
elsif node.
|
39
|
+
elsif !same_line?(node.source_range.begin, node.source_range.end)
|
40
40
|
add_offense(node, message: MSG_MULTI_LINE)
|
41
41
|
end
|
42
42
|
end
|
@@ -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
|
@@ -134,6 +134,7 @@ module RuboCop
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
137
138
|
def all_fields_literal?(string, arguments)
|
138
139
|
count = 0
|
139
140
|
sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
|
@@ -141,6 +142,7 @@ 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))
|
@@ -151,19 +153,32 @@ module RuboCop
|
|
151
153
|
|
152
154
|
sequences.size == count
|
153
155
|
end
|
156
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
154
157
|
|
158
|
+
# If the sequence has a variable (`*`) width, it cannot be autocorrected
|
159
|
+
# if the width is not given as a numeric literal argument
|
160
|
+
def unknown_variable_width?(sequence, arguments)
|
161
|
+
return false unless sequence.variable_width?
|
162
|
+
|
163
|
+
argument = arguments[sequence.variable_width_argument_number - 1]
|
164
|
+
!numeric?(argument)
|
165
|
+
end
|
166
|
+
|
167
|
+
# rubocop:disable Metrics/AbcSize
|
155
168
|
def find_argument(sequence, arguments, hash)
|
156
169
|
if hash && (sequence.annotated? || sequence.template?)
|
157
170
|
find_hash_value_node(hash, sequence.name.to_sym).first
|
171
|
+
elsif sequence.variable_width?
|
172
|
+
# If the specifier contains `*`, the argument for the width can be ignored.
|
173
|
+
arguments.delete_at(sequence.variable_width_argument_number - 1)
|
174
|
+
arguments.shift
|
158
175
|
elsif sequence.arg_number
|
159
176
|
arguments[sequence.arg_number.to_i - 1]
|
160
177
|
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
178
|
arguments.shift
|
165
179
|
end
|
166
180
|
end
|
181
|
+
# rubocop:enable Metrics/AbcSize
|
167
182
|
|
168
183
|
def matching_argument?(sequence, argument)
|
169
184
|
# Template specifiers don't give a type, any acceptable literal type is ok.
|
@@ -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)
|