rubocop 1.77.0 → 1.80.2
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 -3
- data/config/default.yml +36 -20
- data/exe/rubocop +1 -8
- data/lib/rubocop/cli.rb +17 -1
- data/lib/rubocop/config_loader.rb +1 -38
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +6 -3
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -0
- data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -1
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +5 -4
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- data/lib/rubocop/cop/naming/method_name.rb +127 -13
- data/lib/rubocop/cop/naming/predicate_method.rb +30 -4
- data/lib/rubocop/cop/security/eval.rb +2 -1
- data/lib/rubocop/cop/security/open.rb +1 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
- data/lib/rubocop/cop/style/array_intersect.rb +98 -34
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/dig_chain.rb +1 -1
- data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +8 -9
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
- data/lib/rubocop/cop/style/it_assignment.rb +69 -12
- data/lib/rubocop/cop/style/it_block_parameter.rb +3 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
- data/lib/rubocop/cop/style/map_to_set.rb +1 -3
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
- 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_fetch_block.rb +1 -9
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +28 -11
- data/lib/rubocop/cop/style/safe_navigation.rb +20 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -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/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +25 -8
- data/lib/rubocop/cops_documentation_generator.rb +1 -0
- data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
- data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
- data/lib/rubocop/lsp/routes.rb +35 -6
- data/lib/rubocop/pending_cops_reporter.rb +56 -0
- data/lib/rubocop/result_cache.rb +14 -12
- data/lib/rubocop/runner.rb +6 -4
- data/lib/rubocop/server/cache.rb +4 -2
- data/lib/rubocop/server/client_command/base.rb +10 -0
- data/lib/rubocop/server/client_command/exec.rb +2 -1
- data/lib/rubocop/server/client_command/start.rb +11 -1
- data/lib/rubocop/target_finder.rb +9 -9
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -0
- metadata +9 -6
@@ -6,6 +6,10 @@ module RuboCop
|
|
6
6
|
# Checks for duplicated instance (or singleton) method
|
7
7
|
# definitions.
|
8
8
|
#
|
9
|
+
# NOTE: Aliasing a method to itself is allowed, as it indicates that
|
10
|
+
# the developer intends to suppress Ruby's method redefinition warnings.
|
11
|
+
# See https://bugs.ruby-lang.org/issues/13574.
|
12
|
+
#
|
9
13
|
# @example
|
10
14
|
#
|
11
15
|
# # bad
|
@@ -40,6 +44,18 @@ module RuboCop
|
|
40
44
|
#
|
41
45
|
# alias bar foo
|
42
46
|
#
|
47
|
+
# # good
|
48
|
+
# alias foo foo
|
49
|
+
# def foo
|
50
|
+
# 1
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# # good
|
54
|
+
# alias_method :foo, :foo
|
55
|
+
# def foo
|
56
|
+
# 1
|
57
|
+
# end
|
58
|
+
#
|
43
59
|
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
44
60
|
#
|
45
61
|
# # good
|
@@ -113,11 +129,13 @@ module RuboCop
|
|
113
129
|
|
114
130
|
# @!method method_alias?(node)
|
115
131
|
def_node_matcher :method_alias?, <<~PATTERN
|
116
|
-
(alias (sym $_name) sym)
|
132
|
+
(alias (sym $_name) (sym $_original_name))
|
117
133
|
PATTERN
|
118
134
|
|
119
135
|
def on_alias(node)
|
120
|
-
|
136
|
+
name, original_name = method_alias?(node)
|
137
|
+
return unless name && original_name
|
138
|
+
return if name == original_name
|
121
139
|
return if node.ancestors.any?(&:if_type?)
|
122
140
|
|
123
141
|
found_instance_method(node, name)
|
@@ -125,7 +143,7 @@ module RuboCop
|
|
125
143
|
|
126
144
|
# @!method alias_method?(node)
|
127
145
|
def_node_matcher :alias_method?, <<~PATTERN
|
128
|
-
(send nil? :alias_method (sym $_name)
|
146
|
+
(send nil? :alias_method (sym $_name) (sym $_original_name))
|
129
147
|
PATTERN
|
130
148
|
|
131
149
|
# @!method delegate_method?(node)
|
@@ -140,7 +158,10 @@ module RuboCop
|
|
140
158
|
def_node_matcher :sym_name, '(sym $_name)'
|
141
159
|
|
142
160
|
def on_send(node) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
143
|
-
|
161
|
+
name, original_name = alias_method?(node)
|
162
|
+
|
163
|
+
if name && original_name
|
164
|
+
return if name == original_name
|
144
165
|
return if node.ancestors.any?(&:if_type?)
|
145
166
|
|
146
167
|
found_instance_method(node, name)
|
@@ -24,8 +24,6 @@ module RuboCop
|
|
24
24
|
|
25
25
|
MSG_REPEATED_ELEMENT = 'Duplicate element inside regexp character class'
|
26
26
|
|
27
|
-
OCTAL_DIGITS_AFTER_ESCAPE = 2
|
28
|
-
|
29
27
|
def on_regexp(node)
|
30
28
|
each_repeated_character_class_element_loc(node) do |loc|
|
31
29
|
add_offense(loc, message: MSG_REPEATED_ELEMENT) do |corrector|
|
@@ -40,9 +38,9 @@ module RuboCop
|
|
40
38
|
|
41
39
|
seen = Set.new
|
42
40
|
group_expressions(node, expr.expressions) do |group|
|
43
|
-
group_source = group.
|
41
|
+
group_source = group.to_s
|
44
42
|
|
45
|
-
yield
|
43
|
+
yield group.expression if seen.include?(group_source)
|
46
44
|
|
47
45
|
seen << group_source
|
48
46
|
end
|
@@ -52,40 +50,13 @@ module RuboCop
|
|
52
50
|
private
|
53
51
|
|
54
52
|
def group_expressions(node, expressions)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
until expressions.empty?
|
59
|
-
# With we may need to compose a group of multiple expressions.
|
60
|
-
group = [expressions.shift]
|
61
|
-
next if within_interpolation?(node, group.first)
|
62
|
-
|
63
|
-
# With regexp_parser < 2.7 escaped octal sequences may be up to 3
|
64
|
-
# separate expressions ("\\0", "0", "1").
|
65
|
-
pop_octal_digits(group, expressions) if escaped_octal?(group.first.to_s)
|
66
|
-
|
67
|
-
yield(group)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def pop_octal_digits(current_child, expressions)
|
72
|
-
OCTAL_DIGITS_AFTER_ESCAPE.times do
|
73
|
-
next_child = expressions.first
|
74
|
-
break unless octal?(next_child.to_s)
|
53
|
+
expressions.each do |expression|
|
54
|
+
next if within_interpolation?(node, expression)
|
75
55
|
|
76
|
-
|
56
|
+
yield(expression)
|
77
57
|
end
|
78
58
|
end
|
79
59
|
|
80
|
-
def source_range(children)
|
81
|
-
return children.first.expression if children.size == 1
|
82
|
-
|
83
|
-
range_between(
|
84
|
-
children.first.expression.begin_pos,
|
85
|
-
children.last.expression.begin_pos + children.last.to_s.length
|
86
|
-
)
|
87
|
-
end
|
88
|
-
|
89
60
|
def skip_expression?(expr)
|
90
61
|
expr.type != :set || expr.token == :intersection
|
91
62
|
end
|
@@ -99,14 +70,6 @@ module RuboCop
|
|
99
70
|
interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
|
100
71
|
end
|
101
72
|
|
102
|
-
def escaped_octal?(string)
|
103
|
-
string.length == 2 && string[0] == '\\' && octal?(string[1])
|
104
|
-
end
|
105
|
-
|
106
|
-
def octal?(char)
|
107
|
-
('0'..'7').cover?(char)
|
108
|
-
end
|
109
|
-
|
110
73
|
def interpolation_locs(node)
|
111
74
|
@interpolation_locs ||= {}
|
112
75
|
|
@@ -54,6 +54,18 @@ module RuboCop
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
def on_or(node)
|
58
|
+
return unless node.lhs.falsey_literal?
|
59
|
+
|
60
|
+
add_offense(node.lhs) do |corrector|
|
61
|
+
# Don't autocorrect `'foo' && return` because having `return` as
|
62
|
+
# the leftmost node can lead to a void value expression syntax error.
|
63
|
+
next if node.rhs.type?(:return, :break, :next)
|
64
|
+
|
65
|
+
corrector.replace(node, node.rhs.source)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
57
69
|
def on_if(node)
|
58
70
|
cond = condition(node)
|
59
71
|
|
@@ -123,7 +135,9 @@ module RuboCop
|
|
123
135
|
# rubocop:enable Metrics/AbcSize
|
124
136
|
|
125
137
|
def on_case(case_node)
|
126
|
-
if case_node.condition
|
138
|
+
if (cond = case_node.condition)
|
139
|
+
return if !cond.falsey_literal? && !cond.truthy_literal?
|
140
|
+
|
127
141
|
check_case(case_node)
|
128
142
|
else
|
129
143
|
case_node.when_branches.each do |when_node|
|
@@ -52,10 +52,9 @@ module RuboCop
|
|
52
52
|
each_missing_enable do |cop, line_range|
|
53
53
|
next if acceptable_range?(cop, line_range)
|
54
54
|
|
55
|
-
range = source_range(processed_source.buffer, line_range.min, 0..0)
|
56
55
|
comment = processed_source.comment_at_line(line_range.begin)
|
57
56
|
|
58
|
-
add_offense(
|
57
|
+
add_offense(comment, message: message(cop, comment))
|
59
58
|
end
|
60
59
|
end
|
61
60
|
|
@@ -20,6 +20,15 @@ module RuboCop
|
|
20
20
|
# In the example below, the safe navigation operator (`&.`) is unnecessary
|
21
21
|
# because `NilClass` has methods like `respond_to?` and `is_a?`.
|
22
22
|
#
|
23
|
+
# The `InferNonNilReceiver` option specifies whether to look into previous code
|
24
|
+
# paths to infer if the receiver can't be nil. This check is unsafe because the receiver
|
25
|
+
# can be redefined between the safe navigation call and previous regular method call.
|
26
|
+
# It does the inference only in the current scope, e.g. within the same method definition etc.
|
27
|
+
#
|
28
|
+
# The `AdditionalNilMethods` option specifies additional custom methods which are
|
29
|
+
# defined on `NilClass`. When `InferNonNilReceiver` is set, they are used to determine
|
30
|
+
# whether the receiver can be nil.
|
31
|
+
#
|
23
32
|
# @safety
|
24
33
|
# This cop is unsafe, because autocorrection can change the return type of
|
25
34
|
# the expression. An offending expression that previously could return `nil`
|
@@ -33,6 +42,20 @@ module RuboCop
|
|
33
42
|
# CamelCaseConst.do_something
|
34
43
|
#
|
35
44
|
# # bad
|
45
|
+
# foo.to_s&.strip
|
46
|
+
# foo.to_i&.zero?
|
47
|
+
# foo.to_f&.zero?
|
48
|
+
# foo.to_a&.size
|
49
|
+
# foo.to_h&.size
|
50
|
+
#
|
51
|
+
# # good
|
52
|
+
# foo.to_s.strip
|
53
|
+
# foo.to_i.zero?
|
54
|
+
# foo.to_f.zero?
|
55
|
+
# foo.to_a.size
|
56
|
+
# foo.to_h.size
|
57
|
+
#
|
58
|
+
# # bad
|
36
59
|
# do_something if attrs&.respond_to?(:[])
|
37
60
|
#
|
38
61
|
# # good
|
@@ -81,17 +104,59 @@ module RuboCop
|
|
81
104
|
# do_something if attrs.nil_safe_method(:[])
|
82
105
|
# do_something if attrs&.not_nil_safe_method(:[])
|
83
106
|
#
|
107
|
+
# @example InferNonNilReceiver: false (default)
|
108
|
+
# # good
|
109
|
+
# foo.bar
|
110
|
+
# foo&.baz
|
111
|
+
#
|
112
|
+
# @example InferNonNilReceiver: true
|
113
|
+
# # bad
|
114
|
+
# foo.bar
|
115
|
+
# foo&.baz # would raise on previous line if `foo` is nil
|
116
|
+
#
|
117
|
+
# # good
|
118
|
+
# foo.bar
|
119
|
+
# foo.baz
|
120
|
+
#
|
121
|
+
# # bad
|
122
|
+
# if foo.condition?
|
123
|
+
# foo&.bar
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# # good
|
127
|
+
# if foo.condition?
|
128
|
+
# foo.bar
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# # good (different scopes)
|
132
|
+
# def method1
|
133
|
+
# foo.bar
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# def method2
|
137
|
+
# foo&.bar
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# @example AdditionalNilMethods: [present?]
|
141
|
+
# # good
|
142
|
+
# foo.present?
|
143
|
+
# foo&.bar
|
144
|
+
#
|
84
145
|
class RedundantSafeNavigation < Base
|
85
146
|
include AllowedMethods
|
86
147
|
extend AutoCorrector
|
87
148
|
|
88
149
|
MSG = 'Redundant safe navigation detected, use `.` instead.'
|
89
150
|
MSG_LITERAL = 'Redundant safe navigation with default literal detected.'
|
151
|
+
MSG_NON_NIL = 'Redundant safe navigation on non-nil receiver (detected by analyzing ' \
|
152
|
+
'previous code/method invocations).'
|
90
153
|
|
91
154
|
NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
|
92
155
|
|
93
156
|
SNAKE_CASE = /\A[[:digit:][:upper:]_]+\z/.freeze
|
94
157
|
|
158
|
+
GUARANTEED_INSTANCE_METHODS = %i[to_s to_i to_f to_a to_h].freeze
|
159
|
+
|
95
160
|
# @!method respond_to_nil_specific_method?(node)
|
96
161
|
def_node_matcher :respond_to_nil_specific_method?, <<~PATTERN
|
97
162
|
(csend _ :respond_to? (sym %NIL_SPECIFIC_METHODS))
|
@@ -111,15 +176,27 @@ module RuboCop
|
|
111
176
|
|
112
177
|
# rubocop:disable Metrics/AbcSize
|
113
178
|
def on_csend(node)
|
179
|
+
range = node.loc.dot
|
180
|
+
|
181
|
+
if infer_non_nil_receiver?
|
182
|
+
checker = Lint::Utils::NilReceiverChecker.new(node.receiver, additional_nil_methods)
|
183
|
+
|
184
|
+
if checker.cant_be_nil?
|
185
|
+
add_offense(range, message: MSG_NON_NIL) { |corrector| corrector.replace(range, '.') }
|
186
|
+
return
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
114
190
|
unless assume_receiver_instance_exists?(node.receiver)
|
115
|
-
return
|
191
|
+
return if !guaranteed_instance?(node.receiver) && !check?(node)
|
116
192
|
return if respond_to_nil_specific_method?(node)
|
117
193
|
end
|
118
194
|
|
119
|
-
range = node.loc.dot
|
120
195
|
add_offense(range) { |corrector| corrector.replace(range, '.') }
|
121
196
|
end
|
197
|
+
# rubocop:enable Metrics/AbcSize
|
122
198
|
|
199
|
+
# rubocop:disable Metrics/AbcSize
|
123
200
|
def on_or(node)
|
124
201
|
conversion_with_default?(node) do |send_node|
|
125
202
|
range = send_node.loc.dot.begin.join(node.source_range.end)
|
@@ -142,7 +219,20 @@ module RuboCop
|
|
142
219
|
receiver.self_type? || (receiver.literal? && !receiver.nil_type?)
|
143
220
|
end
|
144
221
|
|
222
|
+
def guaranteed_instance?(node)
|
223
|
+
receiver = if node.any_block_type?
|
224
|
+
node.send_node
|
225
|
+
else
|
226
|
+
node
|
227
|
+
end
|
228
|
+
return false unless receiver.send_type?
|
229
|
+
|
230
|
+
GUARANTEED_INSTANCE_METHODS.include?(receiver.method_name)
|
231
|
+
end
|
232
|
+
|
145
233
|
def check?(node)
|
234
|
+
return false unless allowed_method?(node.method_name)
|
235
|
+
|
146
236
|
parent = node.parent
|
147
237
|
return false unless parent
|
148
238
|
|
@@ -154,6 +244,15 @@ module RuboCop
|
|
154
244
|
def condition?(parent, node)
|
155
245
|
(parent.conditional? || parent.post_condition_loop?) && parent.condition == node
|
156
246
|
end
|
247
|
+
|
248
|
+
def infer_non_nil_receiver?
|
249
|
+
cop_config['InferNonNilReceiver']
|
250
|
+
end
|
251
|
+
|
252
|
+
def additional_nil_methods
|
253
|
+
@additional_nil_methods ||=
|
254
|
+
Array(cop_config.fetch('AdditionalNilMethods', []).map(&:to_sym))
|
255
|
+
end
|
157
256
|
end
|
158
257
|
end
|
159
258
|
end
|
@@ -185,9 +185,9 @@ module RuboCop
|
|
185
185
|
(hash (pair (sym :exception) false))
|
186
186
|
PATTERN
|
187
187
|
|
188
|
-
# rubocop:disable Metrics/AbcSize
|
188
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
189
189
|
def on_send(node)
|
190
|
-
return if hash_or_set_with_block?(node)
|
190
|
+
return if node.arguments.any? || hash_or_set_with_block?(node)
|
191
191
|
|
192
192
|
receiver = find_receiver(node)
|
193
193
|
return unless literal_receiver?(node, receiver) ||
|
@@ -198,10 +198,10 @@ module RuboCop
|
|
198
198
|
message = format(MSG, method: node.method_name)
|
199
199
|
|
200
200
|
add_offense(node.loc.selector, message: message) do |corrector|
|
201
|
-
corrector.remove(node.loc.dot.join(node.loc.selector))
|
201
|
+
corrector.remove(node.loc.dot.join(node.loc.end || node.loc.selector))
|
202
202
|
end
|
203
203
|
end
|
204
|
-
# rubocop:enable Metrics/AbcSize
|
204
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
205
205
|
alias on_csend on_send
|
206
206
|
|
207
207
|
private
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
43
43
|
def on_irange(node)
|
44
44
|
return if node.parent&.begin_type?
|
45
45
|
return unless node.begin && node.end
|
46
|
-
return if same_line?(node.
|
46
|
+
return if same_line?(node.loc.operator, node.end)
|
47
47
|
|
48
48
|
message = format(MSG, range: "#{node.begin.source}#{node.loc.operator.source}")
|
49
49
|
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.receiver)
|
46
46
|
|
47
47
|
if node.method?(:[]=)
|
48
|
-
handle_key_assignment(node)
|
48
|
+
handle_key_assignment(node)
|
49
49
|
elsif node.assignment_method?
|
50
50
|
handle_attribute_assignment(node) if node.arguments.size == 1
|
51
51
|
end
|
@@ -105,12 +105,13 @@ module RuboCop
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def handle_key_assignment(node)
|
108
|
-
value_node = node.
|
108
|
+
value_node = node.last_argument
|
109
|
+
node_arguments = node.arguments[0...-1]
|
109
110
|
|
110
111
|
if value_node.send_type? && value_node.method?(:[]) &&
|
111
112
|
node.receiver == value_node.receiver &&
|
112
|
-
|
113
|
-
|
113
|
+
node_arguments.none?(&:call_type?) &&
|
114
|
+
node_arguments == value_node.arguments
|
114
115
|
add_offense(node)
|
115
116
|
end
|
116
117
|
end
|
@@ -17,6 +17,7 @@ module RuboCop
|
|
17
17
|
#
|
18
18
|
# # good
|
19
19
|
# CGI.escape('http://example.com')
|
20
|
+
# URI.encode_uri_component(uri) # Since Ruby 3.1
|
20
21
|
# URI.encode_www_form([['example', 'param'], ['lang', 'en']])
|
21
22
|
# URI.encode_www_form(page: 10, locale: 'en')
|
22
23
|
# URI.encode_www_form_component('http://example.com')
|
@@ -27,6 +28,7 @@ module RuboCop
|
|
27
28
|
#
|
28
29
|
# # good
|
29
30
|
# CGI.unescape(enc_uri)
|
31
|
+
# URI.decode_uri_component(uri) # Since Ruby 3.1
|
30
32
|
# URI.decode_www_form(enc_uri)
|
31
33
|
# URI.decode_www_form_component(enc_uri)
|
32
34
|
class UriEscapeUnescape < Base
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
module Utils
|
7
|
+
# Utility class that checks if the receiver can't be nil.
|
8
|
+
class NilReceiverChecker
|
9
|
+
NIL_METHODS = (nil.methods + %i[!]).to_set.freeze
|
10
|
+
|
11
|
+
def initialize(receiver, additional_nil_methods)
|
12
|
+
@receiver = receiver
|
13
|
+
@additional_nil_methods = additional_nil_methods
|
14
|
+
@checked_nodes = {}.compare_by_identity
|
15
|
+
end
|
16
|
+
|
17
|
+
def cant_be_nil?
|
18
|
+
sole_condition_of_parent_if?(@receiver) || _cant_be_nil?(@receiver.parent, @receiver)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# rubocop:disable Metrics
|
24
|
+
def _cant_be_nil?(node, receiver)
|
25
|
+
return false unless node
|
26
|
+
|
27
|
+
# For some nodes, we check their parent and then some children for these parents.
|
28
|
+
# This is added to avoid infinite loops.
|
29
|
+
return false if @checked_nodes.key?(node)
|
30
|
+
|
31
|
+
@checked_nodes[node] = true
|
32
|
+
|
33
|
+
case node.type
|
34
|
+
when :def, :class, :module, :sclass
|
35
|
+
return false
|
36
|
+
when :send
|
37
|
+
return non_nil_method?(node.method_name) if node.receiver == receiver
|
38
|
+
|
39
|
+
node.arguments.each do |argument|
|
40
|
+
return true if _cant_be_nil?(argument, receiver)
|
41
|
+
end
|
42
|
+
|
43
|
+
return true if _cant_be_nil?(node.receiver, receiver)
|
44
|
+
when :begin
|
45
|
+
return true if _cant_be_nil?(node.children.first, receiver)
|
46
|
+
when :if, :case
|
47
|
+
return true if _cant_be_nil?(node.condition, receiver)
|
48
|
+
when :and, :or
|
49
|
+
return true if _cant_be_nil?(node.lhs, receiver)
|
50
|
+
when :pair
|
51
|
+
if _cant_be_nil?(node.key, receiver) ||
|
52
|
+
_cant_be_nil?(node.value, receiver)
|
53
|
+
return true
|
54
|
+
end
|
55
|
+
when :when
|
56
|
+
node.each_condition do |condition|
|
57
|
+
return true if _cant_be_nil?(condition, receiver)
|
58
|
+
end
|
59
|
+
when :lvasgn, :ivasgn, :cvasgn, :gvasgn, :casgn
|
60
|
+
return true if _cant_be_nil?(node.expression, receiver)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Due to how `if/else` are implemented (`elsif` is a child of `if` or another `elsif`),
|
64
|
+
# using left_siblings will not work correctly for them.
|
65
|
+
if !else_branch?(node) || (node.if_type? && !node.elsif?)
|
66
|
+
node.left_siblings.reverse_each do |sibling|
|
67
|
+
next unless sibling.is_a?(AST::Node)
|
68
|
+
|
69
|
+
return true if _cant_be_nil?(sibling, receiver)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if node.parent
|
74
|
+
_cant_be_nil?(node.parent, receiver)
|
75
|
+
else
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
# rubocop:enable Metrics
|
80
|
+
|
81
|
+
def non_nil_method?(method_name)
|
82
|
+
!NIL_METHODS.include?(method_name) && !@additional_nil_methods.include?(method_name)
|
83
|
+
end
|
84
|
+
|
85
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
86
|
+
def sole_condition_of_parent_if?(node)
|
87
|
+
parent = node.parent
|
88
|
+
|
89
|
+
while parent
|
90
|
+
if parent.if_type?
|
91
|
+
if parent.condition == node
|
92
|
+
return true
|
93
|
+
elsif parent.elsif?
|
94
|
+
parent = find_top_if(parent)
|
95
|
+
end
|
96
|
+
elsif else_branch?(parent)
|
97
|
+
# Find the top `if` for `else`.
|
98
|
+
parent = parent.parent
|
99
|
+
end
|
100
|
+
|
101
|
+
parent = parent&.parent
|
102
|
+
end
|
103
|
+
|
104
|
+
false
|
105
|
+
end
|
106
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
107
|
+
|
108
|
+
def else_branch?(node)
|
109
|
+
node.parent&.if_type? && node.parent.else_branch == node
|
110
|
+
end
|
111
|
+
|
112
|
+
def find_top_if(node)
|
113
|
+
node = node.parent while node.elsif?
|
114
|
+
|
115
|
+
node
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -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
|