rubocop 1.66.1 → 1.68.0
Sign up to get free protection for your applications and to get access to all the features.
- 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?]
|