rubocop 1.75.8 → 1.79.1
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 +20 -16
- data/config/default.yml +107 -26
- data/config/obsoletion.yml +6 -3
- data/lib/rubocop/cli.rb +12 -1
- data/lib/rubocop/config_loader.rb +1 -38
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
- data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
- 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/closing_parenthesis_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +99 -0
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +26 -5
- 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/layout/space_before_brackets.rb +2 -9
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
- data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
- data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
- data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
- data/lib/rubocop/cop/lint/literal_as_condition.rb +34 -28
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
- 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/safe_navigation_chain.rb +4 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
- data/lib/rubocop/cop/lint/useless_or.rb +98 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/naming/file_name.rb +2 -2
- data/lib/rubocop/cop/naming/method_name.rb +127 -13
- data/lib/rubocop/cop/naming/predicate_method.rb +306 -0
- data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -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 +53 -23
- data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/case_like_if.rb +1 -1
- data/lib/rubocop/cop/style/collection_querying.rb +167 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +4 -2
- data/lib/rubocop/cop/style/dig_chain.rb +1 -1
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
- data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
- data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
- data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
- data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
- 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 +36 -15
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
- data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
- data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
- data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
- 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_interpolation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +35 -5
- data/lib/rubocop/cop/style/redundant_self.rb +8 -5
- data/lib/rubocop/cop/style/safe_navigation.rb +24 -11
- data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +32 -2
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +18 -7
- data/lib/rubocop/cops_documentation_generator.rb +1 -0
- data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
- data/lib/rubocop/lsp/diagnostic.rb +4 -4
- data/lib/rubocop/lsp/routes.rb +4 -4
- data/lib/rubocop/pending_cops_reporter.rb +56 -0
- data/lib/rubocop/rspec/expect_offense.rb +9 -3
- 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/version.rb +1 -1
- data/lib/rubocop.rb +11 -1
- data/lib/ruby_lsp/rubocop/addon.rb +2 -2
- metadata +21 -8
@@ -5,15 +5,28 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# Checks for blocks with one argument where `it` block parameter can be used.
|
7
7
|
#
|
8
|
-
# It provides
|
8
|
+
# It provides four `EnforcedStyle` options:
|
9
9
|
#
|
10
|
-
# 1. `
|
11
|
-
# 2. `
|
12
|
-
# 3. `
|
10
|
+
# 1. `allow_single_line` (default) ... Always uses the `it` block parameter in a single line.
|
11
|
+
# 2. `only_numbered_parameters` ... Detects only numbered block parameters.
|
12
|
+
# 3. `always` ... Always uses the `it` block parameter.
|
13
|
+
# 4. `disallow` ... Disallows the `it` block parameter.
|
13
14
|
#
|
14
|
-
# A single numbered parameter is detected when `
|
15
|
+
# A single numbered parameter is detected when `allow_single_line`,
|
16
|
+
# `only_numbered_parameters`, or `always`.
|
15
17
|
#
|
16
|
-
# @example EnforcedStyle:
|
18
|
+
# @example EnforcedStyle: allow_single_line (default)
|
19
|
+
# # bad
|
20
|
+
# block do
|
21
|
+
# do_something(it)
|
22
|
+
# end
|
23
|
+
# block { do_something(_1) }
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# block { do_something(it) }
|
27
|
+
# block { |named_param| do_something(named_param) }
|
28
|
+
#
|
29
|
+
# @example EnforcedStyle: only_numbered_parameters
|
17
30
|
# # bad
|
18
31
|
# block { do_something(_1) }
|
19
32
|
#
|
@@ -42,8 +55,9 @@ module RuboCop
|
|
42
55
|
extend TargetRubyVersion
|
43
56
|
extend AutoCorrector
|
44
57
|
|
45
|
-
|
46
|
-
|
58
|
+
MSG_USE_IT_PARAMETER = 'Use `it` block parameter.'
|
59
|
+
MSG_AVOID_IT_PARAMETER = 'Avoid using `it` block parameter.'
|
60
|
+
MSG_AVOID_IT_PARAMETER_MULTILINE = 'Avoid using `it` block parameter for multi-line blocks.'
|
47
61
|
|
48
62
|
minimum_target_ruby_version 3.4
|
49
63
|
|
@@ -57,7 +71,7 @@ module RuboCop
|
|
57
71
|
variables = find_block_variables(node, node.first_argument.source)
|
58
72
|
|
59
73
|
variables.each do |variable|
|
60
|
-
add_offense(variable, message:
|
74
|
+
add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
|
61
75
|
corrector.remove(node.arguments)
|
62
76
|
corrector.replace(variable, 'it')
|
63
77
|
end
|
@@ -71,26 +85,33 @@ module RuboCop
|
|
71
85
|
variables = find_block_variables(node, '_1')
|
72
86
|
|
73
87
|
variables.each do |variable|
|
74
|
-
add_offense(variable, message:
|
88
|
+
add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
|
75
89
|
corrector.replace(variable, 'it')
|
76
90
|
end
|
77
91
|
end
|
78
92
|
end
|
79
93
|
|
80
94
|
def on_itblock(node)
|
81
|
-
|
95
|
+
case style
|
96
|
+
when :allow_single_line
|
97
|
+
return if node.single_line?
|
82
98
|
|
83
|
-
|
99
|
+
add_offense(node, message: MSG_AVOID_IT_PARAMETER_MULTILINE)
|
100
|
+
when :disallow
|
101
|
+
variables = find_block_variables(node, 'it')
|
84
102
|
|
85
|
-
|
86
|
-
|
103
|
+
variables.each do |variable|
|
104
|
+
add_offense(variable, message: MSG_AVOID_IT_PARAMETER)
|
105
|
+
end
|
87
106
|
end
|
88
107
|
end
|
89
108
|
|
90
109
|
private
|
91
110
|
|
92
111
|
def find_block_variables(node, block_argument_name)
|
93
|
-
node.
|
112
|
+
return [] unless node.body
|
113
|
+
|
114
|
+
node.body.each_descendant(:lvar).select do |descendant|
|
94
115
|
descendant.source == block_argument_name
|
95
116
|
end
|
96
117
|
end
|
@@ -167,7 +167,7 @@ module RuboCop
|
|
167
167
|
def call_in_match_pattern?(node)
|
168
168
|
return false unless (parent = node.parent)
|
169
169
|
|
170
|
-
parent.
|
170
|
+
parent.any_match_pattern_type?
|
171
171
|
end
|
172
172
|
|
173
173
|
def hash_literal_in_arguments?(node)
|
@@ -222,11 +222,9 @@ module RuboCop
|
|
222
222
|
end
|
223
223
|
|
224
224
|
def unary_literal?(node)
|
225
|
-
|
226
|
-
return node.source.match?(/\A[+-]/) if node.complex_type?
|
225
|
+
return true if node.numeric_type? && node.sign?
|
227
226
|
|
228
|
-
|
229
|
-
(node.parent&.send_type? && node.parent.unary_operation?)
|
227
|
+
node.parent&.send_type? && node.parent.unary_operation?
|
230
228
|
end
|
231
229
|
|
232
230
|
def assigned_before?(node, target)
|
@@ -251,7 +249,7 @@ module RuboCop
|
|
251
249
|
return false unless (last_argument = node.last_argument)
|
252
250
|
return true if last_argument.forwarded_restarg_type?
|
253
251
|
|
254
|
-
last_argument.hash_type? && last_argument.children.
|
252
|
+
last_argument.hash_type? && last_argument.children.any?(&:forwarded_kwrestarg_type?)
|
255
253
|
end
|
256
254
|
end
|
257
255
|
# rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
|
@@ -132,6 +132,22 @@ module RuboCop
|
|
132
132
|
# bar :baz
|
133
133
|
# end
|
134
134
|
#
|
135
|
+
# @example AllowedMethods: ["puts", "print"]
|
136
|
+
#
|
137
|
+
# # good
|
138
|
+
# puts "Hello world"
|
139
|
+
# print "Hello world"
|
140
|
+
# # still enforces parentheses on other methods
|
141
|
+
# array.delete(e)
|
142
|
+
#
|
143
|
+
# @example AllowedPatterns: ["^assert"]
|
144
|
+
#
|
145
|
+
# # good
|
146
|
+
# assert_equal 'test', x
|
147
|
+
# assert_match(/foo/, bar)
|
148
|
+
# # still enforces parentheses on other methods
|
149
|
+
# array.delete(e)
|
150
|
+
#
|
135
151
|
# @example AllowParenthesesInMultilineCall: false (default)
|
136
152
|
#
|
137
153
|
# # bad
|
@@ -39,13 +39,21 @@ module RuboCop
|
|
39
39
|
include RangeHelp
|
40
40
|
|
41
41
|
MSG = 'Use `%<prefer>s` instead.'
|
42
|
-
|
42
|
+
GREATER_OPERATORS = %i[> >=].freeze
|
43
43
|
LESS_OPERATORS = %i[< <=].freeze
|
44
|
-
COMPARISON_OPERATORS =
|
44
|
+
COMPARISON_OPERATORS = (GREATER_OPERATORS + LESS_OPERATORS).to_set.freeze
|
45
|
+
|
46
|
+
# @!method comparison_condition(node, name)
|
47
|
+
def_node_matcher :comparison_condition, <<~PATTERN
|
48
|
+
{
|
49
|
+
(send $_lhs $COMPARISON_OPERATORS $_rhs)
|
50
|
+
(begin (send $_lhs $COMPARISON_OPERATORS $_rhs))
|
51
|
+
}
|
52
|
+
PATTERN
|
45
53
|
|
46
54
|
def on_if(node)
|
47
|
-
lhs, operator, rhs =
|
48
|
-
return unless
|
55
|
+
lhs, operator, rhs = comparison_condition(node.condition)
|
56
|
+
return unless operator
|
49
57
|
|
50
58
|
if_branch = node.if_branch
|
51
59
|
else_branch = node.else_branch
|
@@ -63,7 +71,7 @@ module RuboCop
|
|
63
71
|
|
64
72
|
def preferred_method(operator, lhs, rhs, if_branch, else_branch)
|
65
73
|
if lhs == if_branch && rhs == else_branch
|
66
|
-
|
74
|
+
GREATER_OPERATORS.include?(operator) ? 'max' : 'min'
|
67
75
|
elsif lhs == else_branch && rhs == if_branch
|
68
76
|
LESS_OPERATORS.include?(operator) ? 'max' : 'min'
|
69
77
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'tsort'
|
4
|
-
|
5
3
|
module RuboCop
|
6
4
|
module Cop
|
7
5
|
module Style
|
@@ -29,6 +27,8 @@ module RuboCop
|
|
29
27
|
MSG = 'Do not use parallel assignment.'
|
30
28
|
|
31
29
|
def on_masgn(node) # rubocop:disable Metrics/AbcSize
|
30
|
+
return if part_of_ignored_node?(node)
|
31
|
+
|
32
32
|
rhs = node.rhs
|
33
33
|
rhs = rhs.body if rhs.rescue_type?
|
34
34
|
rhs_elements = Array(rhs).compact # edge case for one constant
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
41
41
|
add_offense(range) do |corrector|
|
42
42
|
autocorrect(corrector, node, rhs)
|
43
43
|
end
|
44
|
+
ignore_node(node)
|
44
45
|
end
|
45
46
|
|
46
47
|
private
|
@@ -91,15 +92,9 @@ module RuboCop
|
|
91
92
|
def find_valid_order(left_elements, right_elements)
|
92
93
|
# arrange left_elements in an order such that no corresponding right
|
93
94
|
# element refers to a left element earlier in the sequence
|
94
|
-
# this can be done using an algorithm called a "topological sort"
|
95
|
-
# fortunately for us, Ruby's stdlib contains an implementation
|
96
95
|
assignments = left_elements.zip(right_elements)
|
97
96
|
|
98
|
-
|
99
|
-
AssignmentSorter.new(assignments).tsort
|
100
|
-
rescue TSort::Cyclic
|
101
|
-
nil
|
102
|
-
end
|
97
|
+
AssignmentSorter.new(assignments).tsort
|
103
98
|
end
|
104
99
|
|
105
100
|
# Converts (send nil :something) nodes to (send (:self) :something).
|
@@ -114,10 +109,9 @@ module RuboCop
|
|
114
109
|
# @!method implicit_self_getter?(node)
|
115
110
|
def_node_matcher :implicit_self_getter?, '(send nil? $_)'
|
116
111
|
|
117
|
-
#
|
118
|
-
#
|
112
|
+
# Topologically sorts the assignments with Kahn's algorithm.
|
113
|
+
# https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
|
119
114
|
class AssignmentSorter
|
120
|
-
include TSort
|
121
115
|
extend RuboCop::NodePattern::Macros
|
122
116
|
|
123
117
|
# @!method var_name(node)
|
@@ -133,21 +127,39 @@ module RuboCop
|
|
133
127
|
@assignments = assignments
|
134
128
|
end
|
135
129
|
|
136
|
-
def
|
137
|
-
@assignments.
|
130
|
+
def tsort
|
131
|
+
dependencies = @assignments.to_h do |assignment|
|
132
|
+
[assignment, dependencies_for_assignment(assignment)]
|
133
|
+
end
|
134
|
+
result = []
|
135
|
+
|
136
|
+
while (matched_node, = dependencies.find { |_node, edges| edges.empty? })
|
137
|
+
dependencies.delete(matched_node)
|
138
|
+
result.push(matched_node)
|
139
|
+
|
140
|
+
dependencies.each do |node, edges|
|
141
|
+
dependencies[node].delete(matched_node) if edges.include?(matched_node)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
# Cyclic dependency
|
145
|
+
return nil if dependencies.any?
|
146
|
+
|
147
|
+
result
|
138
148
|
end
|
139
149
|
|
140
|
-
|
141
|
-
|
142
|
-
|
150
|
+
# Returns all the assignments which must come after `assignment`
|
151
|
+
# (due to dependencies on the previous value of the assigned var)
|
152
|
+
def dependencies_for_assignment(assignment)
|
143
153
|
my_lhs, _my_rhs = *assignment
|
144
154
|
|
145
|
-
@assignments.
|
146
|
-
|
155
|
+
@assignments.filter_map do |other|
|
156
|
+
# Exclude self, there are no dependencies in cases such as `a, b = a, b`.
|
157
|
+
next if other == assignment
|
147
158
|
|
159
|
+
_other_lhs, other_rhs = *other
|
148
160
|
next unless dependency?(my_lhs, other_rhs)
|
149
161
|
|
150
|
-
|
162
|
+
other
|
151
163
|
end
|
152
164
|
end
|
153
165
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for redundant calls of `Array#flatten`.
|
7
|
+
#
|
8
|
+
# `Array#join` joins nested arrays recursively, so flattening an array
|
9
|
+
# beforehand is redundant.
|
10
|
+
#
|
11
|
+
# @safety
|
12
|
+
# Cop is unsafe because the receiver of `flatten` method might not
|
13
|
+
# be an `Array`, so it's possible it won't respond to `join` method,
|
14
|
+
# or the end result would be different.
|
15
|
+
# Also, if the global variable `$,` is set to a value other than the default `nil`,
|
16
|
+
# false positives may occur.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# # bad
|
20
|
+
# x.flatten.join
|
21
|
+
# x.flatten(1).join
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# x.join
|
25
|
+
#
|
26
|
+
class RedundantArrayFlatten < Base
|
27
|
+
extend AutoCorrector
|
28
|
+
|
29
|
+
MSG = 'Remove the redundant `flatten`.'
|
30
|
+
|
31
|
+
RESTRICT_ON_SEND = %i[flatten].freeze
|
32
|
+
|
33
|
+
# @!method flatten_join?(node)
|
34
|
+
def_node_matcher :flatten_join?, <<~PATTERN
|
35
|
+
(call (call !nil? :flatten _?) :join (nil)?)
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def on_send(node)
|
39
|
+
return unless flatten_join?(node.parent)
|
40
|
+
|
41
|
+
range = node.loc.dot.begin.join(node.source_range.end)
|
42
|
+
add_offense(range) do |corrector|
|
43
|
+
corrector.remove(range)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
alias on_csend on_send
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -49,7 +49,7 @@ module RuboCop
|
|
49
49
|
(block
|
50
50
|
$(call _ :fetch _)
|
51
51
|
(args)
|
52
|
-
${nil?
|
52
|
+
${nil? basic_literal? const_type?})
|
53
53
|
PATTERN
|
54
54
|
|
55
55
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
@@ -71,14 +71,6 @@ module RuboCop
|
|
71
71
|
|
72
72
|
private
|
73
73
|
|
74
|
-
def basic_literal?(node)
|
75
|
-
node&.basic_literal?
|
76
|
-
end
|
77
|
-
|
78
|
-
def const_type?(node)
|
79
|
-
node&.const_type?
|
80
|
-
end
|
81
|
-
|
82
74
|
def should_not_check?(send, body)
|
83
75
|
(body&.const_type? && !check_for_constant?) ||
|
84
76
|
(body&.str_type? && !check_for_string?) ||
|
@@ -49,6 +49,7 @@ module RuboCop
|
|
49
49
|
empty_parentheses?(node) ||
|
50
50
|
first_arg_begins_with_hash_literal?(node) ||
|
51
51
|
rescue?(node) ||
|
52
|
+
in_pattern_matching_in_method_argument?(node) ||
|
52
53
|
allowed_pin_operator?(node) ||
|
53
54
|
allowed_expression?(node)
|
54
55
|
end
|
@@ -122,6 +123,13 @@ module RuboCop
|
|
122
123
|
hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
|
123
124
|
end
|
124
125
|
|
126
|
+
def in_pattern_matching_in_method_argument?(begin_node)
|
127
|
+
return false unless begin_node.parent&.call_type?
|
128
|
+
return false unless (node = begin_node.children.first)
|
129
|
+
|
130
|
+
target_ruby_version <= 2.7 ? node.match_pattern_type? : node.match_pattern_p_type?
|
131
|
+
end
|
132
|
+
|
125
133
|
def method_chain_begins_with_hash_literal(node)
|
126
134
|
return if node.nil?
|
127
135
|
return node if node.hash_type?
|
@@ -134,7 +142,7 @@ module RuboCop
|
|
134
142
|
node = begin_node.children.first
|
135
143
|
|
136
144
|
if (message = find_offense_message(begin_node, node))
|
137
|
-
if node.range_type? && !argument_of_parenthesized_method_call?(begin_node)
|
145
|
+
if node.range_type? && !argument_of_parenthesized_method_call?(begin_node, node)
|
138
146
|
begin_node = begin_node.parent
|
139
147
|
end
|
140
148
|
|
@@ -156,8 +164,12 @@ module RuboCop
|
|
156
164
|
if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
|
157
165
|
return 'an expression'
|
158
166
|
end
|
167
|
+
if disallowed_one_line_pattern_matching?(begin_node, node)
|
168
|
+
return 'a one-line pattern matching'
|
169
|
+
end
|
159
170
|
return 'an interpolated expression' if interpolation?(begin_node)
|
160
|
-
return 'a method argument' if argument_of_parenthesized_method_call?(begin_node)
|
171
|
+
return 'a method argument' if argument_of_parenthesized_method_call?(begin_node, node)
|
172
|
+
return 'a one-line rescue' if oneline_rescue_parentheses_required?(begin_node, node)
|
161
173
|
|
162
174
|
return if begin_node.chained?
|
163
175
|
|
@@ -180,14 +192,23 @@ module RuboCop
|
|
180
192
|
# @!method interpolation?(node)
|
181
193
|
def_node_matcher :interpolation?, '[^begin ^^dstr]'
|
182
194
|
|
183
|
-
def argument_of_parenthesized_method_call?(begin_node)
|
184
|
-
node
|
185
|
-
|
195
|
+
def argument_of_parenthesized_method_call?(begin_node, node)
|
196
|
+
if node.basic_conditional? || node.rescue_type? || method_call_parentheses_required?(node)
|
197
|
+
return false
|
198
|
+
end
|
186
199
|
return false unless (parent = begin_node.parent)
|
187
200
|
|
188
201
|
parent.call_type? && parent.parenthesized? && parent.receiver != begin_node
|
189
202
|
end
|
190
203
|
|
204
|
+
def oneline_rescue_parentheses_required?(begin_node, node)
|
205
|
+
return false unless node.rescue_type?
|
206
|
+
return false unless (parent = begin_node.parent)
|
207
|
+
return false if parent.if_type? && parent.ternary?
|
208
|
+
|
209
|
+
!parent.type?(:call, :array, :pair)
|
210
|
+
end
|
211
|
+
|
191
212
|
def method_call_parentheses_required?(node)
|
192
213
|
return false unless node.call_type?
|
193
214
|
|
@@ -242,6 +263,15 @@ module RuboCop
|
|
242
263
|
end
|
243
264
|
end
|
244
265
|
|
266
|
+
def disallowed_one_line_pattern_matching?(begin_node, node)
|
267
|
+
if (parent = begin_node.parent)
|
268
|
+
return false if parent.any_def_type? && parent.endless?
|
269
|
+
return false if parent.assignment?
|
270
|
+
end
|
271
|
+
|
272
|
+
node.any_match_pattern_type? && node.each_ancestor.none?(&:operator_keyword?)
|
273
|
+
end
|
274
|
+
|
245
275
|
def raised_to_power_negative_numeric?(begin_node, node)
|
246
276
|
return false unless node.numeric_type?
|
247
277
|
|
@@ -67,6 +67,9 @@ module RuboCop
|
|
67
67
|
|
68
68
|
def on_or_asgn(node)
|
69
69
|
allow_self(node.lhs)
|
70
|
+
|
71
|
+
lhs_name = node.lhs.lvasgn_type? ? node.lhs.name : node.lhs
|
72
|
+
add_lhs_to_local_variables_scopes(node.rhs, lhs_name)
|
70
73
|
end
|
71
74
|
alias on_and_asgn on_or_asgn
|
72
75
|
|
@@ -123,11 +126,11 @@ module RuboCop
|
|
123
126
|
def on_if(node)
|
124
127
|
# Allow conditional nodes to use `self` in the condition if that variable
|
125
128
|
# name is used in an `lvasgn` or `masgn` within the `if`.
|
126
|
-
node.
|
127
|
-
if
|
128
|
-
add_lhs_to_local_variables_scopes(node.condition,
|
129
|
-
|
130
|
-
add_masgn_lhs_variables(node.condition,
|
129
|
+
node.each_descendant(:lvasgn, :masgn) do |descendant_node|
|
130
|
+
if descendant_node.lvasgn_type?
|
131
|
+
add_lhs_to_local_variables_scopes(node.condition, descendant_node.lhs)
|
132
|
+
else
|
133
|
+
add_masgn_lhs_variables(node.condition, descendant_node.lhs)
|
131
134
|
end
|
132
135
|
end
|
133
136
|
end
|
@@ -86,6 +86,10 @@ module RuboCop
|
|
86
86
|
# foo.baz = bar if foo
|
87
87
|
# foo.baz + bar if foo
|
88
88
|
# foo.bar > 2 if foo
|
89
|
+
#
|
90
|
+
# foo ? foo[index] : nil # Ignored `foo&.[](index)` due to unclear readability benefit.
|
91
|
+
# foo ? foo[idx] = v : nil # Ignored `foo&.[]=(idx, v)` due to unclear readability benefit.
|
92
|
+
# foo ? foo * 42 : nil # Ignored `foo&.*(42)` due to unclear readability benefit.
|
89
93
|
class SafeNavigation < Base # rubocop:disable Metrics/ClassLength
|
90
94
|
include NilMethods
|
91
95
|
include RangeHelp
|
@@ -146,6 +150,7 @@ module RuboCop
|
|
146
150
|
|
147
151
|
body = extract_if_body(node)
|
148
152
|
method_call = receiver.parent
|
153
|
+
return if dotless_operator_call?(method_call) || method_call.double_colon?
|
149
154
|
|
150
155
|
removal_ranges = [begin_range(node, body), end_range(node, body)]
|
151
156
|
|
@@ -181,6 +186,8 @@ module RuboCop
|
|
181
186
|
end
|
182
187
|
end
|
183
188
|
|
189
|
+
private
|
190
|
+
|
184
191
|
def report_offense(node, rhs, rhs_receiver, *removal_ranges, offense_range: node)
|
185
192
|
add_offense(offense_range) do |corrector|
|
186
193
|
next if ignored_node?(node)
|
@@ -198,8 +205,6 @@ module RuboCop
|
|
198
205
|
end
|
199
206
|
end
|
200
207
|
|
201
|
-
private
|
202
|
-
|
203
208
|
def find_method_chain(node)
|
204
209
|
return node unless node&.parent&.call_type?
|
205
210
|
|
@@ -235,7 +240,7 @@ module RuboCop
|
|
235
240
|
return false if !matching_nodes?(lhs_receiver, rhs_receiver) || rhs_receiver.nil?
|
236
241
|
return false if use_var_only_in_unless_modifier?(node, lhs_receiver)
|
237
242
|
return false if chain_length(rhs, rhs_receiver) > max_chain_length
|
238
|
-
return false if unsafe_method_used?(rhs, rhs_receiver.parent)
|
243
|
+
return false if unsafe_method_used?(node, rhs, rhs_receiver.parent)
|
239
244
|
return false if rhs.send_type? && rhs.method?(:empty?)
|
240
245
|
|
241
246
|
true
|
@@ -253,6 +258,12 @@ module RuboCop
|
|
253
258
|
end
|
254
259
|
end
|
255
260
|
|
261
|
+
def dotless_operator_call?(method_call)
|
262
|
+
return false if method_call.loc.dot
|
263
|
+
|
264
|
+
method_call.method?(:[]) || method_call.method?(:[]=) || method_call.operator_method?
|
265
|
+
end
|
266
|
+
|
256
267
|
def handle_comments(corrector, node, method_call)
|
257
268
|
comments = comments(node)
|
258
269
|
return if comments.empty?
|
@@ -334,21 +345,24 @@ module RuboCop
|
|
334
345
|
end
|
335
346
|
end
|
336
347
|
|
337
|
-
def unsafe_method_used?(method_chain, method)
|
338
|
-
return true if unsafe_method?(method)
|
348
|
+
def unsafe_method_used?(node, method_chain, method)
|
349
|
+
return true if unsafe_method?(node, method)
|
339
350
|
|
340
351
|
method.each_ancestor(:send).any? do |ancestor|
|
341
352
|
break true unless config.cop_enabled?('Lint/SafeNavigationChain')
|
342
353
|
|
343
|
-
break true if unsafe_method?(ancestor)
|
354
|
+
break true if unsafe_method?(node, ancestor)
|
344
355
|
break true if nil_methods.include?(ancestor.method_name)
|
345
356
|
break false if ancestor == method_chain
|
346
357
|
end
|
347
358
|
end
|
348
359
|
|
349
|
-
def unsafe_method?(send_node)
|
350
|
-
negated?(send_node)
|
351
|
-
|
360
|
+
def unsafe_method?(node, send_node)
|
361
|
+
return true if negated?(send_node)
|
362
|
+
|
363
|
+
return false if node.respond_to?(:ternary?) && node.ternary?
|
364
|
+
|
365
|
+
send_node.assignment? ||
|
352
366
|
(!send_node.dot? && !send_node.safe_navigation?)
|
353
367
|
end
|
354
368
|
|
@@ -377,8 +391,7 @@ module RuboCop
|
|
377
391
|
method_chain)
|
378
392
|
start_method.each_ancestor do |ancestor|
|
379
393
|
break unless %i[send block].include?(ancestor.type)
|
380
|
-
next
|
381
|
-
next if ancestor.safe_navigation?
|
394
|
+
next if !ancestor.send_type? || ancestor.operator_method?
|
382
395
|
|
383
396
|
corrector.insert_before(ancestor.loc.dot, '&')
|
384
397
|
|
@@ -65,7 +65,7 @@ module RuboCop
|
|
65
65
|
return false if target_ruby_version < 3.0
|
66
66
|
return false if disallow_endless_method_style?
|
67
67
|
return false unless body_node
|
68
|
-
return false if body_node.parent.assignment_method? ||
|
68
|
+
return false if body_node.basic_conditional? || body_node.parent.assignment_method? ||
|
69
69
|
NOT_SUPPORTED_ENDLESS_METHOD_BODY_TYPES.include?(body_node.type)
|
70
70
|
|
71
71
|
!body_node.type?(:begin, :kwbegin)
|
@@ -86,10 +86,10 @@ module RuboCop
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def correct_to_endless(corrector, node)
|
89
|
-
|
89
|
+
receiver = "#{node.receiver.source}." if node.receiver
|
90
90
|
arguments = node.arguments.any? ? node.arguments.source : '()'
|
91
91
|
body_source = method_body_source(node.body)
|
92
|
-
replacement = "def #{
|
92
|
+
replacement = "def #{receiver}#{node.method_name}#{arguments} = #{body_source}"
|
93
93
|
|
94
94
|
corrector.replace(node, replacement)
|
95
95
|
end
|
@@ -130,7 +130,10 @@ module RuboCop
|
|
130
130
|
end
|
131
131
|
|
132
132
|
def require_parentheses?(method_body)
|
133
|
-
|
133
|
+
return false unless method_body.send_type?
|
134
|
+
return false if method_body.arithmetic_operation?
|
135
|
+
|
136
|
+
!method_body.arguments.empty? && !method_body.comparison_method?
|
134
137
|
end
|
135
138
|
|
136
139
|
def disallow_endless_method_style?
|