rubocop 1.84.2 → 1.86.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/config/default.yml +91 -15
- data/config/obsoletion.yml +5 -0
- data/lib/rubocop/cache_config.rb +1 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +1 -1
- data/lib/rubocop/cli/command/mcp.rb +19 -0
- data/lib/rubocop/cli/command/show_cops.rb +2 -2
- data/lib/rubocop/cli/command/show_docs_url.rb +1 -1
- data/lib/rubocop/cli.rb +6 -3
- data/lib/rubocop/config.rb +14 -10
- data/lib/rubocop/config_finder.rb +1 -1
- data/lib/rubocop/config_loader_resolver.rb +2 -1
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
- data/lib/rubocop/config_store.rb +1 -1
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
- data/lib/rubocop/cop/documentation.rb +2 -3
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +9 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
- data/lib/rubocop/cop/layout/end_alignment.rb +6 -3
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +5 -3
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +28 -3
- data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +59 -9
- data/lib/rubocop/cop/lint/constant_resolution.rb +1 -1
- data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
- data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
- data/lib/rubocop/cop/lint/empty_when.rb +8 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +0 -9
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
- data/lib/rubocop/cop/lint/syntax.rb +25 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +22 -7
- data/lib/rubocop/cop/lint/void.rb +32 -12
- data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
- data/lib/rubocop/cop/migration/department_name.rb +12 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
- data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/registry.rb +20 -13
- data/lib/rubocop/cop/security/eval.rb +15 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
- data/lib/rubocop/cop/style/alias.rb +4 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
- data/lib/rubocop/cop/style/array_join.rb +4 -2
- data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
- data/lib/rubocop/cop/style/attr.rb +5 -2
- data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
- data/lib/rubocop/cop/style/begin_block.rb +3 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +25 -33
- data/lib/rubocop/cop/style/case_equality.rb +4 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +10 -2
- data/lib/rubocop/cop/style/collection_compact.rb +36 -16
- data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +0 -4
- data/lib/rubocop/cop/style/copyright.rb +1 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/each_with_object.rb +2 -0
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_class_definition.rb +43 -20
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/encoding.rb +7 -1
- data/lib/rubocop/cop/style/end_block.rb +3 -1
- data/lib/rubocop/cop/style/endless_method.rb +8 -3
- data/lib/rubocop/cop/style/file_open.rb +84 -0
- data/lib/rubocop/cop/style/for.rb +3 -0
- data/lib/rubocop/cop/style/format_string_token.rb +29 -2
- data/lib/rubocop/cop/style/global_vars.rb +5 -2
- data/lib/rubocop/cop/style/guard_clause.rb +9 -6
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
- data/lib/rubocop/cop/style/hash_lookup_method.rb +7 -0
- data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
- data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -5
- data/lib/rubocop/cop/style/if_unless_modifier.rb +14 -3
- data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
- data/lib/rubocop/cop/style/inline_comment.rb +4 -1
- data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
- data/lib/rubocop/cop/style/magic_comment_format.rb +2 -2
- data/lib/rubocop/cop/style/map_join.rb +123 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
- data/lib/rubocop/cop/style/module_member_existence_check.rb +1 -11
- data/lib/rubocop/cop/style/multiline_if_then.rb +3 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
- data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
- data/lib/rubocop/cop/style/not.rb +2 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
- data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
- data/lib/rubocop/cop/style/parallel_assignment.rb +4 -0
- data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
- data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
- data/lib/rubocop/cop/style/proc.rb +3 -2
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/reduce_to_hash.rb +184 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
- data/lib/rubocop/cop/style/redundant_each.rb +3 -3
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
- data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +25 -22
- data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
- data/lib/rubocop/cop/style/redundant_return.rb +3 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
- data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
- data/lib/rubocop/cop/style/select_by_range.rb +197 -0
- data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
- data/lib/rubocop/cop/style/semicolon.rb +2 -0
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +4 -3
- data/lib/rubocop/cop/style/tally_method.rb +181 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
- data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
- data/lib/rubocop/cop/variable_force/branch.rb +2 -2
- data/lib/rubocop/directive_comment.rb +2 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
- data/lib/rubocop/formatter/formatter_set.rb +1 -1
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/formatter.rb +22 -21
- data/lib/rubocop/lsp/diagnostic.rb +1 -0
- data/lib/rubocop/lsp/routes.rb +10 -3
- data/lib/rubocop/mcp/server.rb +200 -0
- data/lib/rubocop/options.rb +10 -1
- data/lib/rubocop/path_util.rb +14 -2
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/result_cache.rb +22 -10
- data/lib/rubocop/rspec/cop_helper.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +11 -2
- data/lib/rubocop/runner.rb +8 -3
- data/lib/rubocop/server/cache.rb +5 -7
- data/lib/rubocop/server/core.rb +2 -0
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +18 -12
- data/lib/rubocop/version.rb +2 -2
- data/lib/rubocop.rb +14 -0
- metadata +22 -5
|
@@ -63,7 +63,7 @@ module RuboCop
|
|
|
63
63
|
class EmptyBlock < Base
|
|
64
64
|
MSG = 'Empty block detected.'
|
|
65
65
|
|
|
66
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
66
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
67
67
|
return if node.body
|
|
68
68
|
return if allow_empty_lambdas? && node.lambda_or_proc?
|
|
69
69
|
return if cop_config['AllowComments'] && allow_comment?(node)
|
|
@@ -70,7 +70,7 @@ module RuboCop
|
|
|
70
70
|
|
|
71
71
|
def on_if(node)
|
|
72
72
|
return if node.body || same_line?(node.loc.begin, node.loc.end)
|
|
73
|
-
return if
|
|
73
|
+
return if allow_comments?(node)
|
|
74
74
|
|
|
75
75
|
range = offense_range(node)
|
|
76
76
|
|
|
@@ -83,6 +83,11 @@ module RuboCop
|
|
|
83
83
|
|
|
84
84
|
private
|
|
85
85
|
|
|
86
|
+
def allow_comments?(node)
|
|
87
|
+
cop_config['AllowComments'] && contains_comments?(node) &&
|
|
88
|
+
!comments_contain_disables?(node, name)
|
|
89
|
+
end
|
|
90
|
+
|
|
86
91
|
def offense_range(node)
|
|
87
92
|
if node.loc.else
|
|
88
93
|
node.source_range.begin.join(node.loc.else.begin)
|
|
@@ -53,11 +53,18 @@ module RuboCop
|
|
|
53
53
|
def on_case_match(node)
|
|
54
54
|
node.in_pattern_branches.each do |branch|
|
|
55
55
|
next if branch.body
|
|
56
|
-
next if
|
|
56
|
+
next if allow_comments?(branch)
|
|
57
57
|
|
|
58
58
|
add_offense(branch)
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def allow_comments?(node)
|
|
65
|
+
cop_config['AllowComments'] && contains_comments?(node) &&
|
|
66
|
+
!comments_contain_disables?(node, name)
|
|
67
|
+
end
|
|
61
68
|
end
|
|
62
69
|
end
|
|
63
70
|
end
|
|
@@ -50,11 +50,18 @@ module RuboCop
|
|
|
50
50
|
def on_case(node)
|
|
51
51
|
node.when_branches.each do |when_node|
|
|
52
52
|
next if when_node.body
|
|
53
|
-
next if
|
|
53
|
+
next if allow_comments?(when_node)
|
|
54
54
|
|
|
55
55
|
add_offense(when_node)
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def allow_comments?(node)
|
|
62
|
+
cop_config['AllowComments'] && contains_comments?(node) &&
|
|
63
|
+
!comments_contain_disables?(node, name)
|
|
64
|
+
end
|
|
58
65
|
end
|
|
59
66
|
end
|
|
60
67
|
end
|
|
@@ -54,9 +54,14 @@ module RuboCop
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def valid_syntax?(node)
|
|
57
|
-
double_quoted_string = node.source.
|
|
57
|
+
double_quoted_string = if node.source.include?('"')
|
|
58
|
+
node.source.sub(/\A'/, '%{').sub(/'\z/, '}')
|
|
59
|
+
else
|
|
60
|
+
node.source.gsub(/\A'|'\z/, '"')
|
|
61
|
+
end
|
|
58
62
|
|
|
59
|
-
parse(double_quoted_string)
|
|
63
|
+
processed_source = parse(double_quoted_string)
|
|
64
|
+
processed_source.valid_syntax? && processed_source.ast.dstr_type?
|
|
60
65
|
end
|
|
61
66
|
end
|
|
62
67
|
end
|
|
@@ -31,6 +31,7 @@ module RuboCop
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
alias on_numblock on_block
|
|
34
|
+
alias on_itblock on_block
|
|
34
35
|
|
|
35
36
|
private
|
|
36
37
|
|
|
@@ -39,6 +40,7 @@ module RuboCop
|
|
|
39
40
|
{
|
|
40
41
|
(block (call _recv {:reduce :inject} !sym) _blockargs $(begin ...))
|
|
41
42
|
(numblock (call _recv {:reduce :inject} !sym) _argscount $(begin ...))
|
|
43
|
+
(itblock (call _recv {:reduce :inject} !sym) _argscount $(begin ...))
|
|
42
44
|
}
|
|
43
45
|
PATTERN
|
|
44
46
|
|
|
@@ -65,7 +65,9 @@ module RuboCop
|
|
|
65
65
|
|
|
66
66
|
maximum_target_ruby_version 2.7
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
# NOTE: itblock is not handled because this cop is limited to Ruby <= 2.7
|
|
69
|
+
# via `maximum_target_ruby_version`, so itblock nodes (Ruby 3.4+) are never encountered.
|
|
70
|
+
def on_block(node) # rubocop:disable InternalAffairs/ItblockHandler
|
|
69
71
|
return unless node.body
|
|
70
72
|
return unless unsorted_dir_loop?(node.send_node)
|
|
71
73
|
|
|
@@ -162,7 +162,7 @@ module RuboCop
|
|
|
162
162
|
end
|
|
163
163
|
|
|
164
164
|
def allow_receiver?(receiver)
|
|
165
|
-
if receiver.numeric_type? || (receiver.
|
|
165
|
+
if receiver.numeric_type? || (receiver.call_type? &&
|
|
166
166
|
(conversion_method?(receiver.method_name) ||
|
|
167
167
|
allowed_method_name?(receiver.method_name)))
|
|
168
168
|
true
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# The Lint/RedundantCopEnableDirective and Lint/RedundantCopDisableDirective
|
|
4
|
-
# cops need to be disabled so as to be able to provide a (bad) example of an
|
|
5
|
-
# unneeded enable.
|
|
6
|
-
|
|
7
|
-
# rubocop:disable Lint/RedundantCopEnableDirective
|
|
8
|
-
# rubocop:disable Lint/RedundantCopDisableDirective
|
|
9
3
|
module RuboCop
|
|
10
4
|
module Cop
|
|
11
5
|
module Lint
|
|
@@ -130,6 +124,3 @@ module RuboCop
|
|
|
130
124
|
end
|
|
131
125
|
end
|
|
132
126
|
end
|
|
133
|
-
|
|
134
|
-
# rubocop:enable Lint/RedundantCopDisableDirective
|
|
135
|
-
# rubocop:enable Lint/RedundantCopEnableDirective
|
|
@@ -62,6 +62,22 @@ module RuboCop
|
|
|
62
62
|
# do_something if attrs.respond_to?(:[])
|
|
63
63
|
#
|
|
64
64
|
# # bad
|
|
65
|
+
# foo&.bar ? foo&.bar.baz : qux
|
|
66
|
+
#
|
|
67
|
+
# # good
|
|
68
|
+
# foo&.bar ? foo.bar.baz : qux
|
|
69
|
+
#
|
|
70
|
+
# # bad
|
|
71
|
+
# if foo&.bar
|
|
72
|
+
# foo&.bar.baz
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
# # good
|
|
76
|
+
# if foo&.bar
|
|
77
|
+
# foo.bar.baz
|
|
78
|
+
# end
|
|
79
|
+
#
|
|
80
|
+
# # bad
|
|
65
81
|
# while node&.is_a?(BeginNode)
|
|
66
82
|
# node = node.parent
|
|
67
83
|
# end
|
|
@@ -71,8 +87,9 @@ module RuboCop
|
|
|
71
87
|
# node = node.parent
|
|
72
88
|
# end
|
|
73
89
|
#
|
|
74
|
-
# # good - without `&.` this
|
|
90
|
+
# # good - without `&.` this changes the return value for `nil`
|
|
75
91
|
# foo&.respond_to?(:to_a)
|
|
92
|
+
# foo&.respond_to?(:class)
|
|
76
93
|
#
|
|
77
94
|
# # bad - for `nil`s conversion methods return default values for the type
|
|
78
95
|
# foo&.to_h || {}
|
|
@@ -151,15 +168,15 @@ module RuboCop
|
|
|
151
168
|
MSG_NON_NIL = 'Redundant safe navigation on non-nil receiver (detected by analyzing ' \
|
|
152
169
|
'previous code/method invocations).'
|
|
153
170
|
|
|
154
|
-
|
|
171
|
+
NIL_METHODS = nil.methods.to_set.freeze
|
|
155
172
|
|
|
156
173
|
SNAKE_CASE = /\A[[:digit:][:upper:]_]+\z/.freeze
|
|
157
174
|
|
|
158
175
|
GUARANTEED_INSTANCE_METHODS = %i[to_s to_i to_f to_a to_h].freeze
|
|
159
176
|
|
|
160
|
-
# @!method
|
|
161
|
-
def_node_matcher :
|
|
162
|
-
(csend _ :respond_to? (sym %
|
|
177
|
+
# @!method respond_to_nil_method?(node)
|
|
178
|
+
def_node_matcher :respond_to_nil_method?, <<~PATTERN
|
|
179
|
+
(csend _ :respond_to? (sym %NIL_METHODS))
|
|
163
180
|
PATTERN
|
|
164
181
|
|
|
165
182
|
# @!method conversion_with_default?(node)
|
|
@@ -189,7 +206,7 @@ module RuboCop
|
|
|
189
206
|
|
|
190
207
|
unless assume_receiver_instance_exists?(node.receiver)
|
|
191
208
|
return if !guaranteed_instance?(node.receiver) && !check?(node)
|
|
192
|
-
return if
|
|
209
|
+
return if respond_to_nil_method?(node)
|
|
193
210
|
end
|
|
194
211
|
|
|
195
212
|
add_offense(range) { |corrector| corrector.replace(range, '.') }
|
|
@@ -37,20 +37,25 @@ module RuboCop
|
|
|
37
37
|
}
|
|
38
38
|
PATTERN
|
|
39
39
|
|
|
40
|
+
# rubocop:disable Metrics/AbcSize
|
|
40
41
|
def on_send(node)
|
|
41
42
|
return unless require_safe_navigation?(node)
|
|
42
43
|
|
|
43
44
|
bad_method?(node) do |safe_nav, method|
|
|
44
45
|
return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
|
|
46
|
+
return if ternary_safe_navigation?(node, safe_nav)
|
|
45
47
|
|
|
46
48
|
begin_range = node.loc.dot || safe_nav.source_range.end
|
|
47
49
|
location = begin_range.join(node.source_range.end)
|
|
48
50
|
|
|
49
51
|
add_offense(location) do |corrector|
|
|
52
|
+
next if ternary_else_branch?(node, safe_nav)
|
|
53
|
+
|
|
50
54
|
autocorrect(corrector, offense_range: location, send_node: node)
|
|
51
55
|
end
|
|
52
56
|
end
|
|
53
57
|
end
|
|
58
|
+
# rubocop:enable Metrics/AbcSize
|
|
54
59
|
|
|
55
60
|
private
|
|
56
61
|
|
|
@@ -61,6 +66,18 @@ module RuboCop
|
|
|
61
66
|
parent.rhs != node || parent.lhs.receiver != parent.rhs.receiver
|
|
62
67
|
end
|
|
63
68
|
|
|
69
|
+
def ternary_safe_navigation?(node, safe_nav)
|
|
70
|
+
return false unless (parent = node.parent)
|
|
71
|
+
|
|
72
|
+
parent.if_type? && node.equal?(parent.if_branch) && parent.condition == safe_nav
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def ternary_else_branch?(node, safe_nav)
|
|
76
|
+
return false unless (parent = node.parent)
|
|
77
|
+
|
|
78
|
+
parent.if_type? && node.equal?(parent.else_branch) && parent.condition == safe_nav
|
|
79
|
+
end
|
|
80
|
+
|
|
64
81
|
# @param [Parser::Source::Range] offense_range
|
|
65
82
|
# @param [RuboCop::AST::SendNode] send_node
|
|
66
83
|
# @return [String]
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Lint
|
|
6
|
-
#
|
|
6
|
+
# Checks that if safe navigation is used in an `&&` or `||` condition,
|
|
7
7
|
# consistent and appropriate safe navigation, without excess or deficiency,
|
|
8
8
|
# is used for all method calls on the same object.
|
|
9
9
|
#
|
|
10
|
+
# @safety
|
|
11
|
+
# Autocorrection is unsafe because if the receiver is not a local variable
|
|
12
|
+
# but a method call, it may not be idempotent. For example, replacing
|
|
13
|
+
# `foo&.bar` with `foo.bar` could raise `NoMethodError` if `foo` returns
|
|
14
|
+
# `nil` on a subsequent call.
|
|
15
|
+
#
|
|
10
16
|
# @example
|
|
11
17
|
# # bad
|
|
12
18
|
# foo&.bar && foo&.baz
|
|
@@ -26,7 +26,31 @@ module RuboCop
|
|
|
26
26
|
"#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
|
|
27
27
|
'configure using `TargetRubyVersion` parameter, under `AllCops`)'
|
|
28
28
|
end
|
|
29
|
-
|
|
29
|
+
location = diagnostic_location(diagnostic.location)
|
|
30
|
+
add_offense(location, message: message, severity: diagnostic.level)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Expand zero-length diagnostic ranges so that editors and formatters
|
|
34
|
+
# can display them. This typically occurs when the parser reports
|
|
35
|
+
# `unexpected token $end` at EOF.
|
|
36
|
+
def diagnostic_location(location)
|
|
37
|
+
return location if location.size.positive?
|
|
38
|
+
|
|
39
|
+
source_buffer = location.source_buffer
|
|
40
|
+
if location.end_pos < source_buffer.source.size
|
|
41
|
+
location.resize(1)
|
|
42
|
+
elsif location.begin_pos.positive?
|
|
43
|
+
location.adjust(begin_pos: -1)
|
|
44
|
+
else
|
|
45
|
+
location
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Override to skip multiline_ranges check which requires AST.
|
|
50
|
+
# Syntax errors mean the AST is nil, so we go directly to
|
|
51
|
+
# the EOL comment insertion path.
|
|
52
|
+
def disable_offense(offense_range)
|
|
53
|
+
disable_offense_with_eol_or_surround_comment(offense_range)
|
|
30
54
|
end
|
|
31
55
|
|
|
32
56
|
def add_offense_from_error(error)
|
|
@@ -32,6 +32,7 @@ module RuboCop
|
|
|
32
32
|
include RangeHelp
|
|
33
33
|
|
|
34
34
|
MSG = 'Avoid leaving a trailing comma in attribute declarations.'
|
|
35
|
+
RESTRICT_ON_SEND = %i[attr_reader attr_writer attr_accessor attr].freeze
|
|
35
36
|
|
|
36
37
|
def on_send(node)
|
|
37
38
|
return unless node.attribute_accessor? && node.last_argument.def_type?
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# Checks for unreachable `in` pattern branches in `case...in` statements.
|
|
7
|
+
#
|
|
8
|
+
# An `in` branch is unreachable when a previous branch uses an unguarded
|
|
9
|
+
# catch-all pattern that matches any value unconditionally. Any `in` branches
|
|
10
|
+
# (and `else`) that follow such a catch-all are dead code.
|
|
11
|
+
#
|
|
12
|
+
# A catch-all pattern is one of:
|
|
13
|
+
#
|
|
14
|
+
# * A bare variable capture (`in x`)
|
|
15
|
+
# * An underscore (`in _`)
|
|
16
|
+
# * A pattern alias where the left side is a catch-all (`in _ => y`)
|
|
17
|
+
# * An alternation pattern where at least one alternative is a catch-all
|
|
18
|
+
# (`in _ | Integer`)
|
|
19
|
+
#
|
|
20
|
+
# NOTE: A catch-all pattern with a guard clause (e.g., `in _ if condition`)
|
|
21
|
+
# does NOT make subsequent branches unreachable because the guard might
|
|
22
|
+
# not be satisfied.
|
|
23
|
+
#
|
|
24
|
+
# @example
|
|
25
|
+
#
|
|
26
|
+
# # bad
|
|
27
|
+
# case value
|
|
28
|
+
# in Integer
|
|
29
|
+
# handle_integer
|
|
30
|
+
# in x
|
|
31
|
+
# handle_other
|
|
32
|
+
# in String
|
|
33
|
+
# handle_string
|
|
34
|
+
# else
|
|
35
|
+
# handle_else
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# # good
|
|
39
|
+
# case value
|
|
40
|
+
# in Integer
|
|
41
|
+
# handle_integer
|
|
42
|
+
# in String
|
|
43
|
+
# handle_string
|
|
44
|
+
# in x
|
|
45
|
+
# handle_other
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# # bad - else is unreachable after catch-all
|
|
49
|
+
# case value
|
|
50
|
+
# in Integer
|
|
51
|
+
# handle_integer
|
|
52
|
+
# in _
|
|
53
|
+
# handle_other
|
|
54
|
+
# else
|
|
55
|
+
# handle_else
|
|
56
|
+
# end
|
|
57
|
+
#
|
|
58
|
+
# # good - guard clause means catch-all might not match
|
|
59
|
+
# case value
|
|
60
|
+
# in x if x.positive?
|
|
61
|
+
# handle_positive
|
|
62
|
+
# in Integer
|
|
63
|
+
# handle_integer
|
|
64
|
+
# else
|
|
65
|
+
# handle_other
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
class UnreachablePatternBranch < Base
|
|
69
|
+
extend TargetRubyVersion
|
|
70
|
+
|
|
71
|
+
MSG = 'Unreachable `in` pattern branch detected.'
|
|
72
|
+
MSG_ELSE = 'Unreachable `else` branch detected.'
|
|
73
|
+
|
|
74
|
+
minimum_target_ruby_version 2.7
|
|
75
|
+
|
|
76
|
+
def on_case_match(case_node)
|
|
77
|
+
catch_all_found = false
|
|
78
|
+
|
|
79
|
+
case_node.in_pattern_branches.each do |in_pattern_node|
|
|
80
|
+
if catch_all_found
|
|
81
|
+
add_offense(in_pattern_node)
|
|
82
|
+
next
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
pattern = in_pattern_node.pattern
|
|
86
|
+
guard = in_pattern_node.children[1]
|
|
87
|
+
|
|
88
|
+
catch_all_found = true if catch_all_pattern?(pattern) && guard.nil?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
return unless catch_all_found && case_node.else?
|
|
92
|
+
|
|
93
|
+
add_offense(case_node.loc.else, message: MSG_ELSE)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def catch_all_pattern?(pattern)
|
|
99
|
+
case pattern.type
|
|
100
|
+
when :match_var
|
|
101
|
+
true
|
|
102
|
+
when :match_as, :begin
|
|
103
|
+
catch_all_pattern?(pattern.children[0])
|
|
104
|
+
when :match_alt
|
|
105
|
+
pattern.children.any? { |child| catch_all_pattern?(child) }
|
|
106
|
+
else
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -95,10 +95,20 @@ module RuboCop
|
|
|
95
95
|
return unless variable.method_argument?
|
|
96
96
|
return if variable.keyword_argument? && cop_config['AllowUnusedKeywordArguments']
|
|
97
97
|
return if ignored_method?(variable.scope.node.body)
|
|
98
|
+
return if block_argument_with_yield?(variable)
|
|
98
99
|
|
|
99
100
|
super
|
|
100
101
|
end
|
|
101
102
|
|
|
103
|
+
def block_argument_with_yield?(variable)
|
|
104
|
+
return false unless variable.declaration_node.blockarg_type?
|
|
105
|
+
|
|
106
|
+
method_body = variable.scope.node.body
|
|
107
|
+
return false unless method_body
|
|
108
|
+
|
|
109
|
+
method_body.yield_type? || method_body.each_descendant(:yield).any?
|
|
110
|
+
end
|
|
111
|
+
|
|
102
112
|
def ignored_method?(body)
|
|
103
113
|
(cop_config['IgnoreEmptyMethods'] && body.nil?) ||
|
|
104
114
|
(cop_config['IgnoreNotImplementedMethods'] && not_implemented?(body))
|
|
@@ -213,7 +213,7 @@ module RuboCop
|
|
|
213
213
|
end
|
|
214
214
|
|
|
215
215
|
def remove_local_variable_assignment_part(corrector, node)
|
|
216
|
-
corrector.
|
|
216
|
+
corrector.remove(node.loc.name.begin.join(node.expression.source_range.begin))
|
|
217
217
|
end
|
|
218
218
|
|
|
219
219
|
def variable_in_loop_condition?(assignment_node, variable)
|
|
@@ -48,12 +48,12 @@ module RuboCop
|
|
|
48
48
|
|
|
49
49
|
def after_private_modifier?(left_siblings)
|
|
50
50
|
access_modifier_candidates = left_siblings.compact.select do |left_sibling|
|
|
51
|
-
left_sibling.respond_to?(:
|
|
51
|
+
left_sibling.respond_to?(:bare_access_modifier?) && left_sibling.bare_access_modifier?
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
access_modifier_candidates.
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
return false if access_modifier_candidates.empty?
|
|
55
|
+
|
|
56
|
+
access_modifier_candidates.last.command?(:private)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def private_constantize?(right_siblings, const_value)
|
|
@@ -82,28 +82,35 @@ module RuboCop
|
|
|
82
82
|
!NIL_METHODS.include?(method_name) && !@additional_nil_methods.include?(method_name)
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
85
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
86
86
|
def sole_condition_of_parent_if?(node)
|
|
87
|
+
child = node
|
|
87
88
|
parent = node.parent
|
|
88
89
|
|
|
89
90
|
while parent
|
|
90
91
|
if parent.if_type?
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
end
|
|
92
|
+
condition = parent.condition
|
|
93
|
+
return true if !child.equal?(condition) && non_nil_condition?(condition, node)
|
|
94
|
+
|
|
95
|
+
parent = find_top_if(parent) if parent.elsif?
|
|
96
96
|
elsif else_branch?(parent)
|
|
97
97
|
# Find the top `if` for `else`.
|
|
98
98
|
parent = parent.parent
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
child = parent
|
|
101
102
|
parent = parent&.parent
|
|
102
103
|
end
|
|
103
104
|
|
|
104
105
|
false
|
|
105
106
|
end
|
|
106
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
107
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
108
|
+
|
|
109
|
+
def non_nil_condition?(condition, node)
|
|
110
|
+
return true if condition == node
|
|
111
|
+
|
|
112
|
+
condition.csend_type? && csend_root_receiver(condition) == node
|
|
113
|
+
end
|
|
107
114
|
|
|
108
115
|
def else_branch?(node)
|
|
109
116
|
node.parent&.if_type? && node.parent.else_branch == node
|
|
@@ -114,6 +121,14 @@ module RuboCop
|
|
|
114
121
|
|
|
115
122
|
node
|
|
116
123
|
end
|
|
124
|
+
|
|
125
|
+
def csend_root_receiver(node)
|
|
126
|
+
return unless (receiver = node.receiver)
|
|
127
|
+
|
|
128
|
+
receiver = receiver.receiver while receiver.call_type? && receiver.receiver
|
|
129
|
+
|
|
130
|
+
receiver
|
|
131
|
+
end
|
|
117
132
|
end
|
|
118
133
|
end
|
|
119
134
|
end
|
|
@@ -87,8 +87,9 @@ module RuboCop
|
|
|
87
87
|
def on_block(node)
|
|
88
88
|
return unless node.body && !node.body.begin_type?
|
|
89
89
|
return unless in_void_context?(node.body)
|
|
90
|
+
return if node.method?(:each)
|
|
90
91
|
|
|
91
|
-
check_void_op(node.body)
|
|
92
|
+
check_void_op(node.body)
|
|
92
93
|
check_expression(node.body)
|
|
93
94
|
end
|
|
94
95
|
alias on_numblock on_block
|
|
@@ -107,22 +108,23 @@ module RuboCop
|
|
|
107
108
|
|
|
108
109
|
def check_begin(node)
|
|
109
110
|
expressions = *node
|
|
110
|
-
|
|
111
|
+
inside_each_block = node.each_ancestor(:any_block).first&.method?(:each)
|
|
112
|
+
expressions.pop if !in_void_context?(node) || inside_each_block
|
|
111
113
|
expressions.each do |expr|
|
|
112
|
-
check_void_op(expr)
|
|
113
|
-
block_node = node.each_ancestor(:any_block).first
|
|
114
|
-
|
|
115
|
-
block_node&.method?(:each)
|
|
116
|
-
end
|
|
117
|
-
|
|
114
|
+
check_void_op(expr) { inside_each_block }
|
|
118
115
|
check_expression(expr)
|
|
119
116
|
end
|
|
120
117
|
end
|
|
121
118
|
|
|
122
119
|
def check_expression(expr)
|
|
123
|
-
|
|
124
|
-
return
|
|
120
|
+
return check_if_expression(expr) if expr.if_type?
|
|
121
|
+
return check_case_expression(expr) if expr.case_type?
|
|
122
|
+
return check_case_match_expression(expr) if expr.case_match_type?
|
|
123
|
+
|
|
124
|
+
check_void_expression_nodes(expr)
|
|
125
|
+
end
|
|
125
126
|
|
|
127
|
+
def check_void_expression_nodes(expr)
|
|
126
128
|
check_literal(expr)
|
|
127
129
|
check_var(expr)
|
|
128
130
|
check_self(expr)
|
|
@@ -132,6 +134,22 @@ module RuboCop
|
|
|
132
134
|
check_nonmutating(expr)
|
|
133
135
|
end
|
|
134
136
|
|
|
137
|
+
def check_if_expression(if_node)
|
|
138
|
+
check_void_expression_nodes(if_node.body) if if_node.body
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def check_case_expression(case_node)
|
|
142
|
+
case_node.each_when { |when_node| check_expression(when_node.body) if when_node.body }
|
|
143
|
+
check_expression(case_node.else_branch) if case_node.else_branch
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def check_case_match_expression(case_node)
|
|
147
|
+
case_node.each_in_pattern do |in_pattern_node|
|
|
148
|
+
check_expression(in_pattern_node.body) if in_pattern_node.body
|
|
149
|
+
end
|
|
150
|
+
check_expression(case_node.else_branch) if case_node.else_branch
|
|
151
|
+
end
|
|
152
|
+
|
|
135
153
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
136
154
|
def check_void_op(node, &block)
|
|
137
155
|
node = node.children.first while node&.begin_type?
|
|
@@ -168,7 +186,9 @@ module RuboCop
|
|
|
168
186
|
end
|
|
169
187
|
|
|
170
188
|
def check_literal(node)
|
|
171
|
-
|
|
189
|
+
if !entirely_literal?(node) || node.xstr_type? || node.range_type? || node.nil_type?
|
|
190
|
+
return
|
|
191
|
+
end
|
|
172
192
|
|
|
173
193
|
add_offense(node, message: format(LIT_MSG, lit: node.source)) do |corrector|
|
|
174
194
|
autocorrect_void_expression(corrector, node)
|
|
@@ -238,7 +258,7 @@ module RuboCop
|
|
|
238
258
|
end
|
|
239
259
|
|
|
240
260
|
def autocorrect_void_expression(corrector, node)
|
|
241
|
-
return if node.parent.
|
|
261
|
+
return if node.parent.type?(:if, :case, :when, :case_match, :in_pattern)
|
|
242
262
|
return if (def_node = node.each_ancestor(:any_def).first) && def_node.assignment_method?
|
|
243
263
|
|
|
244
264
|
corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
|