rubocop 1.66.1 → 1.68.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 +55 -6
- data/config/internal_affairs.yml +11 -0
- data/lib/rubocop/cached_data.rb +12 -4
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
- data/lib/rubocop/cli/command/execute_runner.rb +1 -1
- data/lib/rubocop/cli/command/lsp.rb +2 -2
- data/lib/rubocop/cli/command/version.rb +2 -2
- data/lib/rubocop/config_loader_resolver.rb +3 -3
- data/lib/rubocop/config_validator.rb +2 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +22 -2
- data/lib/rubocop/cop/base.rb +6 -2
- data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
- data/lib/rubocop/cop/cop.rb +8 -0
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
- data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
- data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
- data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
- data/lib/rubocop/cop/internal_affairs.rb +16 -0
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
- data/lib/rubocop/cop/layout/leading_comment_space.rb +56 -1
- data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
- data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
- data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
- data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
- data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +7 -0
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +9 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +107 -41
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
- data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +4 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +10 -0
- data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +3 -1
- data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
- data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +4 -5
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
- data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
- data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +31 -3
- data/lib/rubocop/cop/style/collection_compact.rb +10 -10
- data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/endless_method.rb +1 -14
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +15 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -3
- data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
- data/lib/rubocop/cop/style/lambda.rb +1 -1
- data/lib/rubocop/cop/style/map_into_array.rb +59 -8
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
- data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
- data/lib/rubocop/cop/style/multiple_comparison.rb +28 -39
- data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
- data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
- data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +23 -5
- data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -11
- data/lib/rubocop/cop/style/require_order.rb +1 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
- data/lib/rubocop/cop/style/safe_navigation.rb +104 -50
- data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
- data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
- data/lib/rubocop/cop/style/semicolon.rb +1 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +26 -5
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/team.rb +8 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
- data/lib/rubocop/cop/variable_force/branch.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +5 -1
- data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
- data/lib/rubocop/cops_documentation_generator.rb +81 -40
- data/lib/rubocop/file_finder.rb +9 -4
- data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
- data/lib/rubocop/lsp/runtime.rb +2 -0
- data/lib/rubocop/lsp/server.rb +0 -1
- data/lib/rubocop/rspec/expect_offense.rb +1 -0
- data/lib/rubocop/runner.rb +17 -6
- data/lib/rubocop/server/cache.rb +6 -1
- data/lib/rubocop/server/core.rb +1 -0
- data/lib/rubocop/target_ruby.rb +13 -13
- data/lib/rubocop/version.rb +28 -7
- data/lib/rubocop/yaml_duplication_checker.rb +20 -27
- data/lib/rubocop.rb +10 -0
- metadata +13 -4
@@ -234,11 +234,11 @@ module RuboCop
|
|
234
234
|
end
|
235
235
|
|
236
236
|
def autocorrect_heredoc_argument(corrector, node, heredoc_branch, leave_branch, guard)
|
237
|
+
remove_whole_lines(corrector, node.loc.end)
|
237
238
|
return unless node.else?
|
238
239
|
|
239
240
|
remove_whole_lines(corrector, leave_branch.source_range)
|
240
241
|
remove_whole_lines(corrector, node.loc.else)
|
241
|
-
remove_whole_lines(corrector, node.loc.end)
|
242
242
|
remove_whole_lines(corrector, range_of_branch_to_remove(node, guard))
|
243
243
|
corrector.insert_after(
|
244
244
|
heredoc_branch.last_argument.loc.heredoc_end, "\n#{leave_branch.source}"
|
@@ -283,7 +283,8 @@ module RuboCop
|
|
283
283
|
end
|
284
284
|
|
285
285
|
def accepted_if?(node, ending)
|
286
|
-
return true if node.modifier_form? || node.ternary? || node.elsif_conditional?
|
286
|
+
return true if node.modifier_form? || node.ternary? || node.elsif_conditional? ||
|
287
|
+
assigned_lvar_used_in_if_branch?(node)
|
287
288
|
|
288
289
|
if ending
|
289
290
|
node.else?
|
@@ -292,6 +293,18 @@ module RuboCop
|
|
292
293
|
end
|
293
294
|
end
|
294
295
|
|
296
|
+
def assigned_lvar_used_in_if_branch?(node)
|
297
|
+
return false unless (if_branch = node.if_branch)
|
298
|
+
|
299
|
+
assigned_lvars_in_condition = node.condition.each_descendant(:lvasgn).map do |lvasgn|
|
300
|
+
lvar_name, = *lvasgn
|
301
|
+
lvar_name.to_s
|
302
|
+
end
|
303
|
+
used_lvars_in_branch = if_branch.each_descendant(:lvar).map(&:source) || []
|
304
|
+
|
305
|
+
(assigned_lvars_in_condition & used_lvars_in_branch).any?
|
306
|
+
end
|
307
|
+
|
295
308
|
def remove_whole_lines(corrector, range)
|
296
309
|
corrector.remove(range_by_whole_lines(range, include_final_newline: true))
|
297
310
|
end
|
@@ -57,6 +57,11 @@ module RuboCop
|
|
57
57
|
(call $(call _ ${:keys :values}) :each (block_pass (sym _)))
|
58
58
|
PATTERN
|
59
59
|
|
60
|
+
# @!method hash_mutated?(node, receiver)
|
61
|
+
def_node_matcher :hash_mutated?, <<~PATTERN
|
62
|
+
`(send %1 :[]= ...)
|
63
|
+
PATTERN
|
64
|
+
|
60
65
|
def on_block(node)
|
61
66
|
return unless handleable?(node)
|
62
67
|
|
@@ -103,6 +108,7 @@ module RuboCop
|
|
103
108
|
def handleable?(node)
|
104
109
|
return false if use_array_converter_method_as_preceding?(node)
|
105
110
|
return false unless (root_receiver = root_receiver(node))
|
111
|
+
return false if hash_mutated?(node, root_receiver)
|
106
112
|
|
107
113
|
!root_receiver.literal? || root_receiver.hash_type?
|
108
114
|
end
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
68
68
|
# {a: 1, b: 2}
|
69
69
|
# {:c => 3, 'd' => 4}
|
70
70
|
#
|
71
|
-
# @example EnforcedShorthandSyntax: always
|
71
|
+
# @example EnforcedShorthandSyntax: always
|
72
72
|
#
|
73
73
|
# # bad
|
74
74
|
# {foo: foo, bar: bar}
|
@@ -84,7 +84,7 @@ module RuboCop
|
|
84
84
|
# # good
|
85
85
|
# {foo: foo, bar: bar}
|
86
86
|
#
|
87
|
-
# @example EnforcedShorthandSyntax: either
|
87
|
+
# @example EnforcedShorthandSyntax: either (default)
|
88
88
|
#
|
89
89
|
# # good
|
90
90
|
# {foo: foo, bar: bar}
|
@@ -71,7 +71,7 @@ module RuboCop
|
|
71
71
|
|
72
72
|
else_branch = node.else_branch
|
73
73
|
|
74
|
-
return unless else_branch&.if_type? && else_branch
|
74
|
+
return unless else_branch&.if_type? && else_branch.if?
|
75
75
|
return if allow_if_modifier_in_else_branch?(else_branch)
|
76
76
|
|
77
77
|
add_offense(else_branch.loc.keyword) do |corrector|
|
@@ -81,16 +81,14 @@ module RuboCop
|
|
81
81
|
RUBY
|
82
82
|
end
|
83
83
|
|
84
|
-
# rubocop:disable Metrics/AbcSize
|
85
84
|
def build_expression(expr)
|
86
|
-
return expr.source
|
85
|
+
return expr.source unless require_argument_parentheses?(expr)
|
87
86
|
|
88
87
|
method = expr.source_range.begin.join(expr.loc.selector.end)
|
89
88
|
arguments = expr.first_argument.source_range.begin.join(expr.source_range.end)
|
90
89
|
|
91
90
|
"#{method.source}(#{arguments.source})"
|
92
91
|
end
|
93
|
-
# rubocop:enable Metrics/AbcSize
|
94
92
|
|
95
93
|
def build_else_branch(second_condition)
|
96
94
|
result = <<~RUBY
|
@@ -111,6 +109,12 @@ module RuboCop
|
|
111
109
|
|
112
110
|
result
|
113
111
|
end
|
112
|
+
|
113
|
+
def require_argument_parentheses?(node)
|
114
|
+
return false unless node.call_type?
|
115
|
+
|
116
|
+
!node.parenthesized? && node.arguments.any? && !node.method?(:[]) && !node.method?(:[]=)
|
117
|
+
end
|
114
118
|
end
|
115
119
|
end
|
116
120
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# When passing an existing hash as keyword arguments, provide additional arguments
|
7
|
+
# directly rather than using `merge`.
|
8
|
+
#
|
9
|
+
# Providing arguments directly is more performant, than using `merge`, and
|
10
|
+
# also leads to a shorter and simpler code.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# some_method(**opts.merge(foo: true))
|
15
|
+
# some_method(**opts.merge(other_opts))
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# some_method(**opts, foo: true)
|
19
|
+
# some_method(**opts, **other_opts)
|
20
|
+
#
|
21
|
+
class KeywordArgumentsMerging < Base
|
22
|
+
extend AutoCorrector
|
23
|
+
|
24
|
+
MSG = 'Provide additional arguments directly rather than using `merge`.'
|
25
|
+
|
26
|
+
# @!method merge_kwargs?(node)
|
27
|
+
def_node_matcher :merge_kwargs?, <<~PATTERN
|
28
|
+
(send _ _
|
29
|
+
...
|
30
|
+
(hash
|
31
|
+
(kwsplat
|
32
|
+
$(send $_ :merge $...))
|
33
|
+
...))
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def on_kwsplat(node)
|
37
|
+
return unless (ancestor = node.parent&.parent)
|
38
|
+
|
39
|
+
merge_kwargs?(ancestor) do |merge_node, hash_node, other_hash_node|
|
40
|
+
add_offense(merge_node) do |corrector|
|
41
|
+
autocorrect(corrector, node, hash_node, other_hash_node)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def autocorrect(corrector, kwsplat_node, hash_node, other_hash_node)
|
49
|
+
other_hash_node_replacement =
|
50
|
+
other_hash_node.map do |node|
|
51
|
+
if node.hash_type?
|
52
|
+
if node.braces?
|
53
|
+
node.source[1...-1]
|
54
|
+
else
|
55
|
+
node.source
|
56
|
+
end
|
57
|
+
else
|
58
|
+
"**#{node.source}"
|
59
|
+
end
|
60
|
+
end.join(', ')
|
61
|
+
|
62
|
+
corrector.replace(kwsplat_node, "**#{hash_node.source}, #{other_hash_node_replacement}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
68
68
|
|
69
69
|
return unless offending_selector?(node, selector)
|
70
70
|
|
71
|
-
add_offense(node.send_node
|
71
|
+
add_offense(node.send_node, message: message(node, selector)) do |corrector|
|
72
72
|
if node.send_node.lambda_literal?
|
73
73
|
LambdaLiteralToMethodCorrector.new(node).call(corrector)
|
74
74
|
else
|
@@ -13,8 +13,10 @@ module RuboCop
|
|
13
13
|
# return value of `Enumerable#map` is an `Array`. They are not autocorrected
|
14
14
|
# when a return value could be used because these types differ.
|
15
15
|
#
|
16
|
-
# NOTE: It only detects when the mapping destination is
|
17
|
-
# initialized as an empty array and referred to only by the
|
16
|
+
# NOTE: It only detects when the mapping destination is either:
|
17
|
+
# * a local variable initialized as an empty array and referred to only by the
|
18
|
+
# pushing operation;
|
19
|
+
# * or, if it is the single block argument to a `[].tap` block.
|
18
20
|
# This is because, if not, it's challenging to statically guarantee that the
|
19
21
|
# mapping destination variable remains an empty array:
|
20
22
|
#
|
@@ -42,6 +44,14 @@ module RuboCop
|
|
42
44
|
# # good
|
43
45
|
# dest = src.map { |e| e * 2 }
|
44
46
|
#
|
47
|
+
# # bad
|
48
|
+
# [].tap do |dest|
|
49
|
+
# src.each { |e| dest << e * 2 }
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# # good
|
53
|
+
# dest = src.map { |e| e * 2 }
|
54
|
+
#
|
45
55
|
# # good - contains another operation
|
46
56
|
# dest = []
|
47
57
|
# src.each { |e| dest << e * 2; puts e }
|
@@ -53,12 +63,17 @@ module RuboCop
|
|
53
63
|
|
54
64
|
MSG = 'Use `%<new_method_name>s` instead of `each` to map elements into an array.'
|
55
65
|
|
66
|
+
# @!method suitable_argument_node?(node)
|
67
|
+
def_node_matcher :suitable_argument_node?, <<-PATTERN
|
68
|
+
!{splat forwarded-restarg forwarded-args (hash (forwarded-kwrestarg)) (block-pass nil?)}
|
69
|
+
PATTERN
|
70
|
+
|
56
71
|
# @!method each_block_with_push?(node)
|
57
72
|
def_node_matcher :each_block_with_push?, <<-PATTERN
|
58
73
|
[
|
59
|
-
^({begin kwbegin} ...)
|
74
|
+
^({begin kwbegin block} ...)
|
60
75
|
({block numblock} (send !{nil? self} :each) _
|
61
|
-
(send (lvar _) {:<< :push :append}
|
76
|
+
(send (lvar _) {:<< :push :append} #suitable_argument_node?))
|
62
77
|
]
|
63
78
|
PATTERN
|
64
79
|
|
@@ -74,6 +89,16 @@ module RuboCop
|
|
74
89
|
)
|
75
90
|
PATTERN
|
76
91
|
|
92
|
+
# @!method empty_array_tap(node)
|
93
|
+
def_node_matcher :empty_array_tap, <<~PATTERN
|
94
|
+
^^$(
|
95
|
+
block
|
96
|
+
(send (array) :tap)
|
97
|
+
(args (arg _))
|
98
|
+
...
|
99
|
+
)
|
100
|
+
PATTERN
|
101
|
+
|
77
102
|
# @!method lvar_ref?(node, name)
|
78
103
|
def_node_matcher :lvar_ref?, '(lvar %1)'
|
79
104
|
|
@@ -89,9 +114,14 @@ module RuboCop
|
|
89
114
|
return unless each_block_with_push?(node)
|
90
115
|
|
91
116
|
dest_var = find_dest_var(node)
|
92
|
-
|
93
|
-
|
94
|
-
|
117
|
+
|
118
|
+
if offending_empty_array_tap?(node, dest_var)
|
119
|
+
asgn = dest_var.declaration_node
|
120
|
+
else
|
121
|
+
return unless (asgn = find_closest_assignment(node, dest_var))
|
122
|
+
return unless empty_array_asgn?(asgn)
|
123
|
+
return unless dest_used_only_for_mapping?(node, dest_var, asgn)
|
124
|
+
end
|
95
125
|
|
96
126
|
register_offense(node, dest_var, asgn)
|
97
127
|
end
|
@@ -108,6 +138,15 @@ module RuboCop
|
|
108
138
|
candidates.find { |v| v.references.any? { |n| n.node.equal?(node) } }
|
109
139
|
end
|
110
140
|
|
141
|
+
def offending_empty_array_tap?(node, dest_var)
|
142
|
+
return false unless (tap_block_node = empty_array_tap(dest_var.declaration_node))
|
143
|
+
|
144
|
+
# A `tap` block only offends if the array push is the only thing in it;
|
145
|
+
# otherwise we cannot guarantee that the block variable is still an empty
|
146
|
+
# array when pushed to.
|
147
|
+
tap_block_node.body == node
|
148
|
+
end
|
149
|
+
|
111
150
|
def find_closest_assignment(block, dest_var)
|
112
151
|
dest_var.assignments.reverse_each.lazy.map(&:node).find do |node|
|
113
152
|
node.source_range.end_pos < block.source_range.begin_pos
|
@@ -127,7 +166,13 @@ module RuboCop
|
|
127
166
|
next if return_value_used?(block)
|
128
167
|
|
129
168
|
corrector.replace(block.send_node.selector, new_method_name)
|
130
|
-
|
169
|
+
|
170
|
+
if (tap_block_node = empty_array_tap(dest_var.declaration_node))
|
171
|
+
remove_tap(corrector, block, tap_block_node)
|
172
|
+
else
|
173
|
+
remove_assignment(corrector, asgn)
|
174
|
+
end
|
175
|
+
|
131
176
|
correct_push_node(corrector, block.body)
|
132
177
|
correct_return_value_handling(corrector, block, dest_var)
|
133
178
|
end
|
@@ -159,6 +204,12 @@ module RuboCop
|
|
159
204
|
corrector.remove(range)
|
160
205
|
end
|
161
206
|
|
207
|
+
def remove_tap(corrector, node, block_node)
|
208
|
+
range = range_between(block_node.source_range.begin_pos, node.source_range.begin_pos)
|
209
|
+
corrector.remove(range)
|
210
|
+
corrector.remove(range_with_surrounding_space(block_node.loc.end, side: :left))
|
211
|
+
end
|
212
|
+
|
162
213
|
def correct_push_node(corrector, push_node)
|
163
214
|
range = push_node.source_range
|
164
215
|
arg_range = push_node.first_argument.source_range
|
@@ -7,6 +7,8 @@ module RuboCop
|
|
7
7
|
# Style omit_parentheses
|
8
8
|
# rubocop:disable Metrics/ModuleLength, Metrics/CyclomaticComplexity
|
9
9
|
module OmitParentheses
|
10
|
+
include RangeHelp
|
11
|
+
|
10
12
|
TRAILING_WHITESPACE_REGEX = /\s+\Z/.freeze
|
11
13
|
OMIT_MSG = 'Omit parentheses for method calls with arguments.'
|
12
14
|
private_constant :OMIT_MSG
|
@@ -30,10 +32,13 @@ module RuboCop
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def autocorrect(corrector, node)
|
35
|
+
range = args_begin(node)
|
33
36
|
if parentheses_at_the_end_of_multiline_call?(node)
|
34
|
-
|
37
|
+
# Whitespace after line continuation (`\ `) is a syntax error
|
38
|
+
with_whitespace = range_with_surrounding_space(range, side: :right, newlines: false)
|
39
|
+
corrector.replace(with_whitespace, ' \\')
|
35
40
|
else
|
36
|
-
corrector.replace(
|
41
|
+
corrector.replace(range, ' ')
|
37
42
|
end
|
38
43
|
corrector.remove(node.loc.end)
|
39
44
|
end
|
@@ -47,11 +52,11 @@ module RuboCop
|
|
47
52
|
node.each_ancestor(:def, :defs).any?(&:endless?) && node.arguments.any?
|
48
53
|
end
|
49
54
|
|
50
|
-
def require_parentheses_for_hash_value_omission?(node)
|
55
|
+
def require_parentheses_for_hash_value_omission?(node) # rubocop:disable Metrics/PerceivedComplexity
|
51
56
|
return false unless (last_argument = node.last_argument)
|
52
57
|
return false if !last_argument.hash_type? || !last_argument.pairs.last&.value_omission?
|
53
58
|
|
54
|
-
node.parent&.conditional? || !last_expression?(node)
|
59
|
+
node.parent&.conditional? || node.parent&.single_line? || !last_expression?(node)
|
55
60
|
end
|
56
61
|
|
57
62
|
# Require hash value omission be enclosed in parentheses to prevent the following issue:
|
@@ -127,7 +132,7 @@ module RuboCop
|
|
127
132
|
end
|
128
133
|
|
129
134
|
def call_in_single_line_inheritance?(node)
|
130
|
-
node.parent&.class_type? && node.parent
|
135
|
+
node.parent&.class_type? && node.parent.single_line?
|
131
136
|
end
|
132
137
|
|
133
138
|
def call_with_ambiguous_arguments?(node) # rubocop:disable Metrics/PerceivedComplexity
|
@@ -147,7 +152,7 @@ module RuboCop
|
|
147
152
|
end
|
148
153
|
|
149
154
|
def call_in_argument_with_block?(node)
|
150
|
-
parent = node.parent&.block_type? && node.parent
|
155
|
+
parent = node.parent&.block_type? && node.parent.parent
|
151
156
|
return false unless parent
|
152
157
|
|
153
158
|
parent.call_type? || parent.super_type? || parent.yield_type?
|
@@ -211,7 +216,7 @@ module RuboCop
|
|
211
216
|
|
212
217
|
def unary_literal?(node)
|
213
218
|
(node.numeric_type? && node.sign?) ||
|
214
|
-
(node.parent&.send_type? && node.parent
|
219
|
+
(node.parent&.send_type? && node.parent.unary_operation?)
|
215
220
|
end
|
216
221
|
|
217
222
|
def assigned_before?(node, target)
|
@@ -55,25 +55,19 @@ module RuboCop
|
|
55
55
|
MSG = 'Avoid comparing a variable with multiple items ' \
|
56
56
|
'in a conditional, use `Array#include?` instead.'
|
57
57
|
|
58
|
-
def on_new_investigation
|
59
|
-
reset_comparison
|
60
|
-
end
|
61
|
-
|
62
58
|
def on_or(node)
|
63
59
|
root_of_or_node = root_of_or_node(node)
|
64
|
-
|
65
60
|
return unless node == root_of_or_node
|
66
|
-
return unless
|
67
|
-
|
68
|
-
return
|
61
|
+
return unless nested_comparison?(node)
|
62
|
+
|
63
|
+
return unless (variable, values = find_offending_var(node))
|
64
|
+
return if values.size < comparisons_threshold
|
69
65
|
|
70
66
|
add_offense(node) do |corrector|
|
71
|
-
elements =
|
72
|
-
prefer_method = "[#{elements}].include?(#{
|
67
|
+
elements = values.map(&:source).join(', ')
|
68
|
+
prefer_method = "[#{elements}].include?(#{variable_name(variable)})"
|
73
69
|
|
74
70
|
corrector.replace(node, prefer_method)
|
75
|
-
|
76
|
-
reset_comparison
|
77
71
|
end
|
78
72
|
end
|
79
73
|
|
@@ -92,32 +86,25 @@ module RuboCop
|
|
92
86
|
(send $_ :== $lvar)
|
93
87
|
PATTERN
|
94
88
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
variables_in_node(node).count == 1
|
99
|
-
end
|
100
|
-
|
101
|
-
def variables_in_node(node)
|
89
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
90
|
+
def find_offending_var(node, variables = Set.new, values = [])
|
102
91
|
if node.or_type?
|
103
|
-
node.
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
92
|
+
find_offending_var(node.lhs, variables, values)
|
93
|
+
find_offending_var(node.rhs, variables, values)
|
94
|
+
elsif simple_double_comparison?(node)
|
95
|
+
return
|
96
|
+
elsif (var, obj = simple_comparison?(node))
|
97
|
+
return if allow_method_comparison? && obj.send_type?
|
108
98
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
if (var, obj = simple_comparison_lhs?(node)) || (obj, var = simple_comparison_rhs?(node))
|
114
|
-
@allowed_method_comparison = true if allow_method_comparison? && obj.send_type?
|
115
|
-
@compared_elements << obj.source
|
116
|
-
return [variable_name(var)]
|
99
|
+
variables << var
|
100
|
+
return if variables.size > 1
|
101
|
+
|
102
|
+
values << obj
|
117
103
|
end
|
118
104
|
|
119
|
-
[]
|
105
|
+
[variables.first, values] if variables.any?
|
120
106
|
end
|
107
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
121
108
|
|
122
109
|
def variable_name(node)
|
123
110
|
node.children[0]
|
@@ -132,7 +119,14 @@ module RuboCop
|
|
132
119
|
end
|
133
120
|
|
134
121
|
def comparison?(node)
|
135
|
-
|
122
|
+
simple_comparison?(node) || nested_comparison?(node)
|
123
|
+
end
|
124
|
+
|
125
|
+
def simple_comparison?(node)
|
126
|
+
if (var, obj = simple_comparison_lhs?(node)) ||
|
127
|
+
(obj, var = simple_comparison_rhs?(node))
|
128
|
+
[var, obj]
|
129
|
+
end
|
136
130
|
end
|
137
131
|
|
138
132
|
def root_of_or_node(or_node)
|
@@ -145,11 +139,6 @@ module RuboCop
|
|
145
139
|
end
|
146
140
|
end
|
147
141
|
|
148
|
-
def reset_comparison
|
149
|
-
@compared_elements = []
|
150
|
-
@allowed_method_comparison = false
|
151
|
-
end
|
152
|
-
|
153
142
|
def allow_method_comparison?
|
154
143
|
cop_config.fetch('AllowMethodComparison', true)
|
155
144
|
end
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
next if allowed_omission?(nested)
|
40
40
|
|
41
41
|
message = format(MSG, source: nested.source)
|
42
|
-
add_offense(nested
|
42
|
+
add_offense(nested, message: message) do |corrector|
|
43
43
|
autocorrect(corrector, nested)
|
44
44
|
end
|
45
45
|
end
|
@@ -22,6 +22,9 @@ module RuboCop
|
|
22
22
|
|
23
23
|
MSG = 'Redundant dot detected.'
|
24
24
|
RESTRICT_ON_SEND = %i[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ ! != !~].freeze
|
25
|
+
INVALID_SYNTAX_ARG_TYPES = %i[
|
26
|
+
splat kwsplat forwarded_args forwarded_restarg forwarded_kwrestarg block_pass
|
27
|
+
].freeze
|
25
28
|
|
26
29
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
27
30
|
def on_send(node)
|
@@ -29,14 +32,16 @@ module RuboCop
|
|
29
32
|
return if node.receiver.const_type? || !node.arguments.one?
|
30
33
|
|
31
34
|
_lhs, _op, rhs = *node
|
32
|
-
|
35
|
+
if !rhs || method_call_with_parenthesized_arg?(rhs) || invalid_syntax_argument?(rhs)
|
36
|
+
return
|
37
|
+
end
|
33
38
|
|
34
39
|
add_offense(dot) do |corrector|
|
35
40
|
wrap_in_parentheses_if_chained(corrector, node)
|
36
41
|
corrector.replace(dot, ' ')
|
37
42
|
|
38
43
|
selector = node.loc.selector
|
39
|
-
corrector.insert_after(selector, ' ') if
|
44
|
+
corrector.insert_after(selector, ' ') if insert_space_after?(node)
|
40
45
|
end
|
41
46
|
end
|
42
47
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
@@ -50,11 +55,10 @@ module RuboCop
|
|
50
55
|
argument.children.first && argument.parent.parenthesized?
|
51
56
|
end
|
52
57
|
|
53
|
-
def
|
54
|
-
|
55
|
-
return true if argument.hash_type? && argument.children.first&.forwarded_kwrestarg_type?
|
58
|
+
def invalid_syntax_argument?(argument)
|
59
|
+
type = argument.hash_type? ? argument.children.first&.type : argument.type
|
56
60
|
|
57
|
-
|
61
|
+
INVALID_SYNTAX_ARG_TYPES.include?(type)
|
58
62
|
end
|
59
63
|
|
60
64
|
def wrap_in_parentheses_if_chained(corrector, node)
|
@@ -67,6 +71,21 @@ module RuboCop
|
|
67
71
|
corrector.insert_after(operator, ' ')
|
68
72
|
corrector.wrap(node, '(', ')')
|
69
73
|
end
|
74
|
+
|
75
|
+
def insert_space_after?(node)
|
76
|
+
_lhs, op, rhs = *node
|
77
|
+
selector = node.loc.selector
|
78
|
+
|
79
|
+
return true if selector.end_pos == rhs.source_range.begin_pos
|
80
|
+
return false if node.parent&.call_type? # if chained, a space is already added
|
81
|
+
|
82
|
+
# For `/` operations, if the RHS starts with a `(` without space,
|
83
|
+
# add one to avoid a syntax error.
|
84
|
+
range = selector.end.join(rhs.source_range.begin)
|
85
|
+
return true if op == :/ && range.source == '('
|
86
|
+
|
87
|
+
false
|
88
|
+
end
|
70
89
|
end
|
71
90
|
end
|
72
91
|
end
|
@@ -68,6 +68,10 @@ module RuboCop
|
|
68
68
|
|
69
69
|
MSG = 'Redundant `begin` block detected.'
|
70
70
|
|
71
|
+
def self.autocorrect_incompatible_with
|
72
|
+
[Style::BlockDelimiters]
|
73
|
+
end
|
74
|
+
|
71
75
|
# @!method offensive_kwbegins(node)
|
72
76
|
def_node_search :offensive_kwbegins, <<~PATTERN
|
73
77
|
[(kwbegin ...) !#allowable_kwbegin?]
|