rubocop 1.56.4 → 1.60.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/LICENSE.txt +1 -1
- data/README.md +4 -4
- data/config/default.yml +57 -3
- data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/config.rb +0 -2
- data/lib/rubocop/config_loader.rb +0 -1
- data/lib/rubocop/config_obsoletion.rb +11 -8
- data/lib/rubocop/config_validator.rb +0 -2
- data/lib/rubocop/cop/base.rb +6 -0
- data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
- data/lib/rubocop/cop/exclude_limit.rb +1 -1
- data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
- data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
- data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +12 -2
- data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +16 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
- data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
- data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/debugger.rb +11 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
- data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
- data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +0 -1
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
- data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +63 -4
- data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
- data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
- data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
- data/lib/rubocop/cop/lint/syntax.rb +6 -3
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
- data/lib/rubocop/cop/lint/useless_times.rb +1 -1
- data/lib/rubocop/cop/lint/void.rb +43 -12
- data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/metrics/class_length.rb +8 -3
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
- data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
- data/lib/rubocop/cop/naming/constant_name.rb +1 -2
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/security/open.rb +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +120 -17
- data/lib/rubocop/cop/style/array_first_last.rb +64 -0
- data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
- data/lib/rubocop/cop/style/case_like_if.rb +4 -4
- data/lib/rubocop/cop/style/class_check.rb +1 -0
- data/lib/rubocop/cop/style/class_equality_comparison.rb +5 -0
- data/lib/rubocop/cop/style/collection_compact.rb +18 -8
- data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
- data/lib/rubocop/cop/style/date_time.rb +5 -4
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
- data/lib/rubocop/cop/style/each_with_object.rb +2 -2
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
- data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
- data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
- data/lib/rubocop/cop/style/format_string.rb +24 -3
- data/lib/rubocop/cop/style/guard_clause.rb +26 -0
- data/lib/rubocop/cop/style/hash_each_methods.rb +83 -10
- data/lib/rubocop/cop/style/hash_except.rb +2 -1
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +28 -3
- data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
- data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
- data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
- data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
- data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -3
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
- data/lib/rubocop/cop/style/next.rb +1 -1
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
- data/lib/rubocop/cop/style/redundant_argument.rb +3 -2
- data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
- data/lib/rubocop/cop/style/redundant_each.rb +7 -4
- data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +10 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +68 -21
- data/lib/rubocop/cop/style/redundant_return.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self.rb +17 -2
- data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
- data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
- data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
- data/lib/rubocop/cop/style/sample.rb +2 -1
- data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
- data/lib/rubocop/cop/style/self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +8 -0
- data/lib/rubocop/cop/style/single_argument_dig.rb +7 -3
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
- data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
- data/lib/rubocop/cop/style/string_chars.rb +1 -0
- data/lib/rubocop/cop/style/strip.rb +7 -4
- data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
- data/lib/rubocop/cop/style/unpack_first.rb +11 -14
- data/lib/rubocop/cops_documentation_generator.rb +11 -1
- data/lib/rubocop/ext/regexp_node.rb +9 -4
- data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
- data/lib/rubocop/formatter/html_formatter.rb +5 -4
- data/lib/rubocop/formatter/json_formatter.rb +0 -1
- data/lib/rubocop/formatter.rb +1 -1
- data/lib/rubocop/lsp/routes.rb +1 -1
- data/lib/rubocop/options.rb +0 -8
- data/lib/rubocop/result_cache.rb +0 -1
- data/lib/rubocop/rspec/shared_contexts.rb +6 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/runner.rb +1 -1
- data/lib/rubocop/server/cache.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +5 -0
- metadata +16 -24
- /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -5,17 +5,21 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# Checks for RuntimeError as the argument of raise/fail.
|
7
7
|
#
|
8
|
-
# It checks for code like this:
|
9
|
-
#
|
10
8
|
# @example
|
11
|
-
# #
|
9
|
+
# # bad
|
12
10
|
# raise RuntimeError, 'message'
|
13
|
-
#
|
14
|
-
# # Bad
|
15
11
|
# raise RuntimeError.new('message')
|
16
12
|
#
|
17
|
-
# #
|
13
|
+
# # good
|
18
14
|
# raise 'message'
|
15
|
+
#
|
16
|
+
# # bad - message is not a string
|
17
|
+
# raise RuntimeError, Object.new
|
18
|
+
# raise RuntimeError.new(Object.new)
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# raise Object.new.to_s
|
22
|
+
#
|
19
23
|
class RedundantException < Base
|
20
24
|
extend AutoCorrector
|
21
25
|
|
@@ -30,26 +34,42 @@ module RuboCop
|
|
30
34
|
fix_exploded(node) || fix_compact(node)
|
31
35
|
end
|
32
36
|
|
37
|
+
private
|
38
|
+
|
33
39
|
def fix_exploded(node)
|
34
40
|
exploded?(node) do |command, message|
|
35
41
|
add_offense(node, message: MSG_1) do |corrector|
|
36
|
-
|
37
|
-
corrector.replace(node, "#{command}(#{message.source})")
|
38
|
-
else
|
39
|
-
corrector.replace(node, "#{command} #{message.source}")
|
40
|
-
end
|
42
|
+
corrector.replace(node, replaced_exploded(node, command, message))
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
47
|
+
def replaced_exploded(node, command, message)
|
48
|
+
arg = string_message?(message) ? message.source : "#{message.source}.to_s"
|
49
|
+
arg = node.parenthesized? ? "(#{arg})" : " #{arg}"
|
50
|
+
"#{command}#{arg}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def string_message?(message)
|
54
|
+
message.str_type? || message.dstr_type? || message.xstr_type?
|
55
|
+
end
|
56
|
+
|
45
57
|
def fix_compact(node)
|
46
58
|
compact?(node) do |new_call, message|
|
47
59
|
add_offense(node, message: MSG_2) do |corrector|
|
48
|
-
corrector.replace(new_call, message
|
60
|
+
corrector.replace(new_call, replaced_compact(message))
|
49
61
|
end
|
50
62
|
end
|
51
63
|
end
|
52
64
|
|
65
|
+
def replaced_compact(message)
|
66
|
+
if string_message?(message)
|
67
|
+
message.source
|
68
|
+
else
|
69
|
+
"#{message.source}.to_s"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
53
73
|
# @!method exploded?(node)
|
54
74
|
def_node_matcher :exploded?, <<~PATTERN
|
55
75
|
(send nil? ${:raise :fail} (const {nil? cbase} :RuntimeError) $_)
|
@@ -47,7 +47,7 @@ module RuboCop
|
|
47
47
|
# @!method redundant_fetch_block_candidate?(node)
|
48
48
|
def_node_matcher :redundant_fetch_block_candidate?, <<~PATTERN
|
49
49
|
(block
|
50
|
-
$(
|
50
|
+
$(call _ :fetch _)
|
51
51
|
(args)
|
52
52
|
${nil? #basic_literal? #const_type?})
|
53
53
|
PATTERN
|
@@ -61,10 +61,10 @@ module RuboCop
|
|
61
61
|
bad = build_bad_method(send, body)
|
62
62
|
|
63
63
|
add_offense(range, message: format(MSG, good: good, bad: bad)) do |corrector|
|
64
|
-
|
64
|
+
_, _, key = send.children
|
65
65
|
default_value = body ? body.source : 'nil'
|
66
66
|
|
67
|
-
corrector.replace(
|
67
|
+
corrector.replace(range, "fetch(#{key.source}, #{default_value})")
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
@@ -6,6 +6,12 @@ module RuboCop
|
|
6
6
|
# Identifies usages of `any?`, `empty?` or `none?` predicate methods
|
7
7
|
# chained to `select`/`filter`/`find_all` and change them to use predicate method instead.
|
8
8
|
#
|
9
|
+
# @safety
|
10
|
+
# This cop's autocorrection is unsafe because `array.select.any?` evaluates all elements
|
11
|
+
# through the `select` method, while `array.any?` uses short-circuit evaluation.
|
12
|
+
# In other words, `array.select.any?` guarantees the evaluation of every element,
|
13
|
+
# but `array.any?` does not necessarily evaluate all of them.
|
14
|
+
#
|
9
15
|
# @example
|
10
16
|
# # bad
|
11
17
|
# arr.select { |x| x > 1 }.any?
|
@@ -28,6 +34,9 @@ module RuboCop
|
|
28
34
|
# # good
|
29
35
|
# arr.select { |x| x > 1 }.many?
|
30
36
|
#
|
37
|
+
# # good
|
38
|
+
# arr.select { |x| x > 1 }.present?
|
39
|
+
#
|
31
40
|
# @example AllCops:ActiveSupportExtensionsEnabled: true
|
32
41
|
# # bad
|
33
42
|
# arr.select { |x| x > 1 }.many?
|
@@ -35,20 +44,26 @@ module RuboCop
|
|
35
44
|
# # good
|
36
45
|
# arr.many? { |x| x > 1 }
|
37
46
|
#
|
47
|
+
# # bad
|
48
|
+
# arr.select { |x| x > 1 }.present?
|
49
|
+
#
|
50
|
+
# # good
|
51
|
+
# arr.any? { |x| x > 1 }
|
52
|
+
#
|
38
53
|
class RedundantFilterChain < Base
|
39
54
|
extend AutoCorrector
|
40
55
|
|
41
56
|
MSG = 'Use `%<prefer>s` instead of `%<first_method>s.%<second_method>s`.'
|
42
57
|
|
43
|
-
RAILS_METHODS = %i[many?].freeze
|
58
|
+
RAILS_METHODS = %i[many? present?].freeze
|
44
59
|
RESTRICT_ON_SEND = (%i[any? empty? none? one?] + RAILS_METHODS).freeze
|
45
60
|
|
46
61
|
# @!method select_predicate?(node)
|
47
62
|
def_node_matcher :select_predicate?, <<~PATTERN
|
48
|
-
(
|
63
|
+
(call
|
49
64
|
{
|
50
|
-
(block $(
|
51
|
-
$(
|
65
|
+
(block $(call _ {:select :filter :find_all}) ...)
|
66
|
+
$(call _ {:select :filter :find_all} block_pass_type?)
|
52
67
|
}
|
53
68
|
${:#{RESTRICT_ON_SEND.join(' :')}})
|
54
69
|
PATTERN
|
@@ -58,7 +73,8 @@ module RuboCop
|
|
58
73
|
empty?: :none?,
|
59
74
|
none?: :none?,
|
60
75
|
one?: :one?,
|
61
|
-
many?: :many
|
76
|
+
many?: :many?,
|
77
|
+
present?: :any?
|
62
78
|
}.freeze
|
63
79
|
private_constant :REPLACEMENT_METHODS
|
64
80
|
|
@@ -71,6 +87,7 @@ module RuboCop
|
|
71
87
|
register_offense(select_node, node)
|
72
88
|
end
|
73
89
|
end
|
90
|
+
alias on_csend on_send
|
74
91
|
|
75
92
|
private
|
76
93
|
|
@@ -94,7 +94,8 @@ module RuboCop
|
|
94
94
|
!ends_with_backslash_without_comment?(range.source_line) ||
|
95
95
|
string_concatenation?(range.source_line) ||
|
96
96
|
start_with_arithmetic_operator?(processed_source[range.line]) ||
|
97
|
-
inside_string_literal_or_method_with_argument?(range)
|
97
|
+
inside_string_literal_or_method_with_argument?(range) ||
|
98
|
+
leading_dot_method_chain_with_blank_line?(range)
|
98
99
|
end
|
99
100
|
|
100
101
|
def ends_with_backslash_without_comment?(source_line)
|
@@ -107,10 +108,18 @@ module RuboCop
|
|
107
108
|
|
108
109
|
def inside_string_literal_or_method_with_argument?(range)
|
109
110
|
processed_source.tokens.each_cons(2).any? do |token, next_token|
|
111
|
+
next if token.line == next_token.line
|
112
|
+
|
110
113
|
inside_string_literal?(range, token) || method_with_argument?(token, next_token)
|
111
114
|
end
|
112
115
|
end
|
113
116
|
|
117
|
+
def leading_dot_method_chain_with_blank_line?(range)
|
118
|
+
return false unless range.source_line.strip.start_with?('.', '&.')
|
119
|
+
|
120
|
+
processed_source[range.line].strip.empty?
|
121
|
+
end
|
122
|
+
|
114
123
|
def redundant_line_continuation?(range)
|
115
124
|
return true unless (node = find_node_for_line(range.line))
|
116
125
|
return false if argument_newline?(node)
|
@@ -17,12 +17,11 @@ module RuboCop
|
|
17
17
|
include Parentheses
|
18
18
|
extend AutoCorrector
|
19
19
|
|
20
|
+
ALLOWED_NODE_TYPES = %i[and or send splat kwsplat].freeze
|
21
|
+
|
20
22
|
# @!method square_brackets?(node)
|
21
23
|
def_node_matcher :square_brackets?, '(send {(send _recv _msg) str array hash} :[] ...)'
|
22
24
|
|
23
|
-
# @!method range_end?(node)
|
24
|
-
def_node_matcher :range_end?, '^^{irange erange}'
|
25
|
-
|
26
25
|
# @!method method_node_and_args(node)
|
27
26
|
def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
|
28
27
|
|
@@ -54,15 +53,16 @@ module RuboCop
|
|
54
53
|
def ignore_syntax?(node)
|
55
54
|
return false unless (parent = node.parent)
|
56
55
|
|
57
|
-
parent.while_post_type? || parent.until_post_type? ||
|
58
|
-
like_method_argument_parentheses?(parent)
|
56
|
+
parent.while_post_type? || parent.until_post_type? || parent.match_with_lvasgn_type? ||
|
57
|
+
like_method_argument_parentheses?(parent) || multiline_control_flow_statements?(node)
|
59
58
|
end
|
60
59
|
|
61
60
|
def allowed_expression?(node)
|
62
61
|
allowed_ancestor?(node) ||
|
63
62
|
allowed_method_call?(node) ||
|
64
63
|
allowed_multiple_expression?(node) ||
|
65
|
-
allowed_ternary?(node)
|
64
|
+
allowed_ternary?(node) ||
|
65
|
+
node.parent&.range_type?
|
66
66
|
end
|
67
67
|
|
68
68
|
def allowed_ancestor?(node)
|
@@ -104,6 +104,13 @@ module RuboCop
|
|
104
104
|
!node.arithmetic_operation? && node.first_argument.begin_type?
|
105
105
|
end
|
106
106
|
|
107
|
+
def multiline_control_flow_statements?(node)
|
108
|
+
return false unless (parent = node.parent)
|
109
|
+
return false if parent.single_line?
|
110
|
+
|
111
|
+
parent.return_type? || parent.next_type? || parent.break_type?
|
112
|
+
end
|
113
|
+
|
107
114
|
def empty_parentheses?(node)
|
108
115
|
# Don't flag `()`
|
109
116
|
node.children.empty?
|
@@ -111,34 +118,69 @@ module RuboCop
|
|
111
118
|
|
112
119
|
def first_arg_begins_with_hash_literal?(node)
|
113
120
|
# Don't flag `method ({key: value})` or `method ({key: value}.method)`
|
114
|
-
method_chain_begins_with_hash_literal
|
115
|
-
|
116
|
-
|
121
|
+
hash_literal = method_chain_begins_with_hash_literal(node.children.first)
|
122
|
+
if (root_method = node.each_ancestor(:send).to_a.last)
|
123
|
+
parenthesized = root_method.parenthesized_call?
|
124
|
+
end
|
125
|
+
hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
|
117
126
|
end
|
118
127
|
|
119
|
-
def method_chain_begins_with_hash_literal
|
120
|
-
return
|
121
|
-
return
|
122
|
-
return
|
128
|
+
def method_chain_begins_with_hash_literal(node)
|
129
|
+
return if node.nil?
|
130
|
+
return node if node.hash_type?
|
131
|
+
return unless node.send_type?
|
123
132
|
|
124
|
-
method_chain_begins_with_hash_literal
|
133
|
+
method_chain_begins_with_hash_literal(node.children.first)
|
125
134
|
end
|
126
135
|
|
127
136
|
def check(begin_node)
|
128
137
|
node = begin_node.children.first
|
129
|
-
return offense(begin_node, 'a keyword') if keyword_with_redundant_parentheses?(node)
|
130
|
-
return offense(begin_node, 'a literal') if disallowed_literal?(begin_node, node)
|
131
|
-
return offense(begin_node, 'a variable') if node.variable?
|
132
|
-
return offense(begin_node, 'a constant') if node.const_type?
|
133
138
|
|
134
|
-
|
139
|
+
if (message = find_offense_message(begin_node, node))
|
140
|
+
return offense(begin_node, message)
|
141
|
+
end
|
135
142
|
|
136
143
|
check_send(begin_node, node) if node.call_type?
|
137
144
|
end
|
138
145
|
|
146
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
147
|
+
def find_offense_message(begin_node, node)
|
148
|
+
return 'a keyword' if keyword_with_redundant_parentheses?(node)
|
149
|
+
return 'a literal' if disallowed_literal?(begin_node, node)
|
150
|
+
return 'a variable' if node.variable?
|
151
|
+
return 'a constant' if node.const_type?
|
152
|
+
if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
|
153
|
+
return 'an expression'
|
154
|
+
end
|
155
|
+
return 'an interpolated expression' if interpolation?(begin_node)
|
156
|
+
|
157
|
+
return if begin_node.chained?
|
158
|
+
|
159
|
+
if node.and_type? || node.or_type?
|
160
|
+
return if node.semantic_operator? && begin_node.parent
|
161
|
+
return if node.multiline? && allow_in_multiline_conditions?
|
162
|
+
return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
|
163
|
+
return if begin_node.parent&.if_type? && begin_node.parent&.ternary?
|
164
|
+
|
165
|
+
'a logical expression'
|
166
|
+
elsif node.respond_to?(:comparison_method?) && node.comparison_method?
|
167
|
+
return unless begin_node.parent.nil?
|
168
|
+
|
169
|
+
'a comparison expression'
|
170
|
+
end
|
171
|
+
end
|
172
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
173
|
+
|
139
174
|
# @!method interpolation?(node)
|
140
175
|
def_node_matcher :interpolation?, '[^begin ^^dstr]'
|
141
176
|
|
177
|
+
def allow_in_multiline_conditions?
|
178
|
+
parentheses_around_condition_config = config.for_cop('Style/ParenthesesAroundCondition')
|
179
|
+
return false unless parentheses_around_condition_config['Enabled']
|
180
|
+
|
181
|
+
!!parentheses_around_condition_config['AllowInMultilineConditions']
|
182
|
+
end
|
183
|
+
|
142
184
|
def check_send(begin_node, node)
|
143
185
|
return check_unary(begin_node, node) if node.unary_operation?
|
144
186
|
|
@@ -203,7 +245,6 @@ module RuboCop
|
|
203
245
|
def method_call_with_redundant_parentheses?(node)
|
204
246
|
return false unless node.call_type?
|
205
247
|
return false if node.prefix_not?
|
206
|
-
return false if range_end?(node)
|
207
248
|
|
208
249
|
send_node, args = method_node_and_args(node)
|
209
250
|
|
@@ -215,7 +256,13 @@ module RuboCop
|
|
215
256
|
end
|
216
257
|
|
217
258
|
def first_argument?(node)
|
218
|
-
first_send_argument?(node) ||
|
259
|
+
if first_send_argument?(node) ||
|
260
|
+
first_super_argument?(node) ||
|
261
|
+
first_yield_argument?(node)
|
262
|
+
return true
|
263
|
+
end
|
264
|
+
|
265
|
+
node.each_ancestor.any? { |ancestor| first_argument?(ancestor) }
|
219
266
|
end
|
220
267
|
|
221
268
|
# @!method first_send_argument?(node)
|
@@ -58,7 +58,7 @@ module RuboCop
|
|
58
58
|
|
59
59
|
MSG = 'Redundant `return` detected.'
|
60
60
|
MULTI_RETURN_MSG = 'To return multiple values, use an array.'
|
61
|
-
RESTRICT_ON_SEND = %i[define_method define_singleton_method].freeze
|
61
|
+
RESTRICT_ON_SEND = %i[define_method define_singleton_method lambda].freeze
|
62
62
|
|
63
63
|
def on_send(node)
|
64
64
|
return unless (parent = node.parent) && parent.block_type?
|
@@ -17,7 +17,8 @@ module RuboCop
|
|
17
17
|
# protected scope, you cannot send private messages this way.
|
18
18
|
#
|
19
19
|
# Note we allow uses of `self` with operators because it would be awkward
|
20
|
-
# otherwise.
|
20
|
+
# otherwise. Also allows the use of `self.it` without arguments in blocks,
|
21
|
+
# as in `0.times { self.it }`, following `Lint/ItWithoutArgumentsInBlock` cop.
|
21
22
|
#
|
22
23
|
# @example
|
23
24
|
#
|
@@ -107,8 +108,8 @@ module RuboCop
|
|
107
108
|
def on_send(node)
|
108
109
|
return unless node.self_receiver? && regular_method_call?(node)
|
109
110
|
return if node.parent&.mlhs_type?
|
110
|
-
|
111
111
|
return if allowed_send_node?(node)
|
112
|
+
return if it_method_in_block?(node)
|
112
113
|
|
113
114
|
add_offense(node.receiver) do |corrector|
|
114
115
|
corrector.remove(node.receiver)
|
@@ -155,6 +156,20 @@ module RuboCop
|
|
155
156
|
KERNEL_METHODS.include?(node.method_name)
|
156
157
|
end
|
157
158
|
|
159
|
+
# Respects `Lint/ItWithoutArgumentsInBlock` cop and the following Ruby 3.3's warning:
|
160
|
+
#
|
161
|
+
# $ ruby -e '0.times { begin; it; end }'
|
162
|
+
# -e:1: warning: `it` calls without arguments will refer to the first block param in
|
163
|
+
# Ruby 3.4; use it() or self.it
|
164
|
+
#
|
165
|
+
def it_method_in_block?(node)
|
166
|
+
return false unless node.method?(:it)
|
167
|
+
return false unless (block_node = node.each_ancestor(:block).first)
|
168
|
+
return false unless block_node.arguments.empty_and_without_delimiters?
|
169
|
+
|
170
|
+
node.arguments.empty? && !node.block_literal?
|
171
|
+
end
|
172
|
+
|
158
173
|
def regular_method_call?(node)
|
159
174
|
!(node.operator_method? ||
|
160
175
|
KEYWORDS.include?(node.method_name) ||
|
@@ -16,7 +16,7 @@ module RuboCop
|
|
16
16
|
# This cop is unsafe, because `sort...last` and `max` may not return the
|
17
17
|
# same element in all cases.
|
18
18
|
#
|
19
|
-
# In an enumerable where there are multiple elements where
|
19
|
+
# In an enumerable where there are multiple elements where ``a <=> b == 0``,
|
20
20
|
# or where the transformation done by the `sort_by` block has the
|
21
21
|
# same result, `sort.last` and `max` (or `sort_by.last` and `max_by`)
|
22
22
|
# will return different elements. `sort.last` will return the last
|
@@ -87,15 +87,15 @@ module RuboCop
|
|
87
87
|
# @!method redundant_sort?(node)
|
88
88
|
def_node_matcher :redundant_sort?, <<~MATCHER
|
89
89
|
{
|
90
|
-
(
|
91
|
-
(
|
90
|
+
(call $(call _ $:sort) ${:last :first})
|
91
|
+
(call $(call _ $:sort) ${:[] :at :slice} {(int 0) (int -1)})
|
92
92
|
|
93
|
-
(
|
93
|
+
(call $(call _ $:sort_by _) ${:last :first})
|
94
94
|
(send $(send _ $:sort_by _) ${:[] :at :slice} {(int 0) (int -1)})
|
95
95
|
|
96
|
-
(
|
97
|
-
(
|
98
|
-
({block numblock} $(
|
96
|
+
(call ({block numblock} $(call _ ${:sort_by :sort}) ...) ${:last :first})
|
97
|
+
(call
|
98
|
+
({block numblock} $(call _ ${:sort_by :sort}) ...)
|
99
99
|
${:[] :at :slice} {(int 0) (int -1)}
|
100
100
|
)
|
101
101
|
}
|
@@ -108,6 +108,7 @@ module RuboCop
|
|
108
108
|
|
109
109
|
register_offense(ancestor, sort_node, sorter, accessor)
|
110
110
|
end
|
111
|
+
alias on_csend on_send
|
111
112
|
|
112
113
|
private
|
113
114
|
|
@@ -180,7 +181,7 @@ module RuboCop
|
|
180
181
|
end
|
181
182
|
|
182
183
|
def arg_node(node)
|
183
|
-
node.
|
184
|
+
node.first_argument
|
184
185
|
end
|
185
186
|
|
186
187
|
def arg_value(node)
|
@@ -46,12 +46,12 @@ module RuboCop
|
|
46
46
|
|
47
47
|
# @!method redundant_sort_by_block(node)
|
48
48
|
def_node_matcher :redundant_sort_by_block, <<~PATTERN
|
49
|
-
(block $(
|
49
|
+
(block $(call _ :sort_by) (args (arg $_x)) (lvar _x))
|
50
50
|
PATTERN
|
51
51
|
|
52
52
|
# @!method redundant_sort_by_numblock(node)
|
53
53
|
def_node_matcher :redundant_sort_by_numblock, <<~PATTERN
|
54
|
-
(numblock $(
|
54
|
+
(numblock $(call _ :sort_by) 1 (lvar :_1))
|
55
55
|
PATTERN
|
56
56
|
|
57
57
|
def sort_by_range(send, node)
|
@@ -133,7 +133,7 @@ module RuboCop
|
|
133
133
|
end
|
134
134
|
|
135
135
|
def percent_array_literal?(node)
|
136
|
-
|
136
|
+
percent_w_literal?(node) || percent_w_upper_literal?(node)
|
137
137
|
end
|
138
138
|
|
139
139
|
def heredoc_with_disabled_interpolation?(node)
|
@@ -35,7 +35,7 @@ module RuboCop
|
|
35
35
|
|
36
36
|
# @!method sample_candidate?(node)
|
37
37
|
def_node_matcher :sample_candidate?, <<~PATTERN
|
38
|
-
(
|
38
|
+
(call $(call _ :shuffle $...) ${:#{RESTRICT_ON_SEND.join(' :')}} $...)
|
39
39
|
PATTERN
|
40
40
|
|
41
41
|
def on_send(node)
|
@@ -52,6 +52,7 @@ module RuboCop
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
|
+
alias on_csend on_send
|
55
56
|
|
56
57
|
private
|
57
58
|
|
@@ -55,8 +55,8 @@ module RuboCop
|
|
55
55
|
# @!method regexp_match?(node)
|
56
56
|
def_node_matcher :regexp_match?, <<~PATTERN
|
57
57
|
{
|
58
|
-
(block
|
59
|
-
(numblock
|
58
|
+
(block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
|
59
|
+
(numblock call $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
|
60
60
|
}
|
61
61
|
PATTERN
|
62
62
|
|
@@ -64,9 +64,9 @@ module RuboCop
|
|
64
64
|
# @!method creates_hash?(node)
|
65
65
|
def_node_matcher :creates_hash?, <<~PATTERN
|
66
66
|
{
|
67
|
-
(
|
68
|
-
(block (
|
69
|
-
(
|
67
|
+
(call (const _ :Hash) {:new :[]} ...)
|
68
|
+
(block (call (const _ :Hash) :new ...) ...)
|
69
|
+
(call _ { :to_h :to_hash } ...)
|
70
70
|
}
|
71
71
|
PATTERN
|
72
72
|
|
@@ -100,6 +100,7 @@ module RuboCop
|
|
100
100
|
register_offense(node, block_node, regexp, replacement)
|
101
101
|
end
|
102
102
|
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
103
|
+
alias on_csend on_send
|
103
104
|
|
104
105
|
private
|
105
106
|
|
@@ -146,7 +147,7 @@ module RuboCop
|
|
146
147
|
return node.child_nodes.first if node.match_with_lvasgn_type?
|
147
148
|
|
148
149
|
if node.receiver.lvar_type? &&
|
149
|
-
(block.numblock_type? || node.receiver.source == block.
|
150
|
+
(block.numblock_type? || node.receiver.source == block.first_argument.source)
|
150
151
|
node.first_argument
|
151
152
|
elsif node.first_argument.lvar_type?
|
152
153
|
node.receiver
|
@@ -80,6 +80,7 @@ module RuboCop
|
|
80
80
|
processed_source.tokens.group_by(&:line)
|
81
81
|
end
|
82
82
|
|
83
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
83
84
|
def semicolon_position(tokens)
|
84
85
|
if tokens.last.semicolon?
|
85
86
|
-1
|
@@ -90,10 +91,13 @@ module RuboCop
|
|
90
91
|
elsif exist_semicolon_after_left_curly_brace?(tokens) ||
|
91
92
|
exist_semicolon_after_left_string_interpolation_brace?(tokens)
|
92
93
|
2
|
94
|
+
elsif exist_semicolon_after_left_lambda_curly_brace?(tokens)
|
95
|
+
3
|
93
96
|
elsif exist_semicolon_before_right_string_interpolation_brace?(tokens)
|
94
97
|
-4
|
95
98
|
end
|
96
99
|
end
|
100
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
97
101
|
|
98
102
|
def exist_semicolon_before_right_curly_brace?(tokens)
|
99
103
|
tokens[-2]&.right_curly_brace? && tokens[-3]&.semicolon?
|
@@ -103,6 +107,10 @@ module RuboCop
|
|
103
107
|
tokens[1]&.left_curly_brace? && tokens[2]&.semicolon?
|
104
108
|
end
|
105
109
|
|
110
|
+
def exist_semicolon_after_left_lambda_curly_brace?(tokens)
|
111
|
+
tokens[2]&.type == :tLAMBEG && tokens[3]&.semicolon?
|
112
|
+
end
|
113
|
+
|
106
114
|
def exist_semicolon_before_right_string_interpolation_brace?(tokens)
|
107
115
|
tokens[-3]&.type == :tSTRING_DEND && tokens[-4]&.semicolon?
|
108
116
|
end
|
@@ -3,8 +3,11 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
# Sometimes using dig method ends up with just a single
|
7
|
-
# argument. In such cases, dig should be replaced with []
|
6
|
+
# Sometimes using `dig` method ends up with just a single
|
7
|
+
# argument. In such cases, dig should be replaced with `[]`.
|
8
|
+
#
|
9
|
+
# Since replacing `hash&.dig(:key)` with `hash[:key]` could potentially lead to error,
|
10
|
+
# calls to the `dig` method using safe navigation will be ignored.
|
8
11
|
#
|
9
12
|
# @safety
|
10
13
|
# This cop is unsafe because it cannot be guaranteed that the receiver
|
@@ -33,6 +36,7 @@ module RuboCop
|
|
33
36
|
|
34
37
|
MSG = 'Use `%<receiver>s[%<argument>s]` instead of `%<original>s`.'
|
35
38
|
RESTRICT_ON_SEND = %i[dig].freeze
|
39
|
+
IGNORED_ARGUMENT_TYPES = %i[block_pass forwarded_restarg forwarded_args hash].freeze
|
36
40
|
|
37
41
|
# @!method single_argument_dig?(node)
|
38
42
|
def_node_matcher :single_argument_dig?, <<~PATTERN
|
@@ -44,7 +48,7 @@ module RuboCop
|
|
44
48
|
|
45
49
|
expression = single_argument_dig?(node)
|
46
50
|
return unless expression
|
47
|
-
return if expression.
|
51
|
+
return if IGNORED_ARGUMENT_TYPES.include?(expression.type)
|
48
52
|
|
49
53
|
receiver = node.receiver.source
|
50
54
|
argument = expression.source
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for single-line `do`...`end` block.
|
7
|
+
#
|
8
|
+
# In practice a single line `do`...`end` is autocorrected when `EnforcedStyle: semantic`
|
9
|
+
# in `Style/BlockDelimiters`. The autocorrection maintains the `do` ... `end` syntax to
|
10
|
+
# preserve semantics and does not change it to `{`...`}` block.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# foo do |arg| bar(arg) end
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# foo do |arg|
|
19
|
+
# bar(arg)
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # bad
|
23
|
+
# ->(arg) do bar(arg) end
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# ->(arg) { bar(arg) }
|
27
|
+
#
|
28
|
+
class SingleLineDoEndBlock < Base
|
29
|
+
extend AutoCorrector
|
30
|
+
|
31
|
+
MSG = 'Prefer multiline `do`...`end` block.'
|
32
|
+
|
33
|
+
# rubocop:disable Metrics/AbcSize
|
34
|
+
def on_block(node)
|
35
|
+
return if !node.single_line? || node.braces?
|
36
|
+
|
37
|
+
add_offense(node) do |corrector|
|
38
|
+
corrector.insert_after(do_line(node), "\n")
|
39
|
+
|
40
|
+
node_body = node.body
|
41
|
+
|
42
|
+
if node_body.respond_to?(:heredoc?) && node_body.heredoc?
|
43
|
+
corrector.remove(node.loc.end)
|
44
|
+
corrector.insert_after(node_body.loc.heredoc_end, "\nend")
|
45
|
+
else
|
46
|
+
corrector.insert_before(node.loc.end, "\n")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# rubocop:enable Metrics/AbcSize
|
51
|
+
alias on_numblock on_block
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def do_line(node)
|
56
|
+
if node.numblock_type? || node.arguments.children.empty? || node.send_node.lambda_literal?
|
57
|
+
node.loc.begin
|
58
|
+
else
|
59
|
+
node.arguments
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def x(corrector, node); end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|