rubocop 1.84.2 → 1.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/default.yml +86 -14
- 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 +1 -1
- data/lib/rubocop/config_finder.rb +1 -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/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 +3 -2
- 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/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 +50 -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/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/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 +6 -5
- 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/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/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_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/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 +95 -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_min_max_by.rb +93 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +22 -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 +104 -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 +198 -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/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 +20 -3
|
@@ -7,8 +7,16 @@ module RuboCop
|
|
|
7
7
|
# the configuration.
|
|
8
8
|
#
|
|
9
9
|
# @example EnforcedStyle: no_empty_lines (default)
|
|
10
|
-
# #
|
|
10
|
+
# # bad
|
|
11
|
+
# class Foo
|
|
12
|
+
#
|
|
13
|
+
# def bar
|
|
14
|
+
# # ...
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# end
|
|
11
18
|
#
|
|
19
|
+
# # good
|
|
12
20
|
# class Foo
|
|
13
21
|
# def bar
|
|
14
22
|
# # ...
|
|
@@ -16,8 +24,14 @@ module RuboCop
|
|
|
16
24
|
# end
|
|
17
25
|
#
|
|
18
26
|
# @example EnforcedStyle: empty_lines
|
|
19
|
-
# #
|
|
27
|
+
# # bad
|
|
28
|
+
# class Foo
|
|
29
|
+
# def bar
|
|
30
|
+
# # ...
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
20
33
|
#
|
|
34
|
+
# # good
|
|
21
35
|
# class Foo
|
|
22
36
|
#
|
|
23
37
|
# def bar
|
|
@@ -7,8 +7,16 @@ module RuboCop
|
|
|
7
7
|
# the configuration.
|
|
8
8
|
#
|
|
9
9
|
# @example EnforcedStyle: no_empty_lines (default)
|
|
10
|
-
# #
|
|
10
|
+
# # bad
|
|
11
|
+
# module Foo
|
|
12
|
+
#
|
|
13
|
+
# def bar
|
|
14
|
+
# # ...
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# end
|
|
11
18
|
#
|
|
19
|
+
# # good
|
|
12
20
|
# module Foo
|
|
13
21
|
# def bar
|
|
14
22
|
# # ...
|
|
@@ -16,8 +24,14 @@ module RuboCop
|
|
|
16
24
|
# end
|
|
17
25
|
#
|
|
18
26
|
# @example EnforcedStyle: empty_lines
|
|
19
|
-
# #
|
|
27
|
+
# # bad
|
|
28
|
+
# module Foo
|
|
29
|
+
# def bar
|
|
30
|
+
# # ...
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
20
33
|
#
|
|
34
|
+
# # good
|
|
21
35
|
# module Foo
|
|
22
36
|
#
|
|
23
37
|
# def bar
|
|
@@ -129,8 +129,9 @@ module RuboCop
|
|
|
129
129
|
# we check if it's an if/unless/while/until.
|
|
130
130
|
return unless (rhs = first_part_of_call_chain(rhs))
|
|
131
131
|
|
|
132
|
-
# If `rhs` is a `begin` node
|
|
133
|
-
|
|
132
|
+
# If `rhs` is a `begin` node or a logical operator,
|
|
133
|
+
# unwrap to find the leading conditional.
|
|
134
|
+
rhs = rhs.child_nodes.first while rhs.type?(:begin, :or, :and)
|
|
134
135
|
|
|
135
136
|
return unless rhs.conditional?
|
|
136
137
|
return if rhs.if_type? && rhs.ternary?
|
|
@@ -135,7 +135,13 @@ module RuboCop
|
|
|
135
135
|
private
|
|
136
136
|
|
|
137
137
|
def autocorrect(corrector, node)
|
|
138
|
-
|
|
138
|
+
line_range = if !node.is_a?(AST::Node) || node.value.first_line <= node.key.first_line
|
|
139
|
+
node
|
|
140
|
+
else
|
|
141
|
+
processed_source.buffer.line_range(node.loc.line)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
AlignmentCorrector.correct(corrector, processed_source, line_range, @column_delta)
|
|
139
145
|
end
|
|
140
146
|
|
|
141
147
|
def brace_alignment_style
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Layout
|
|
6
|
-
#
|
|
6
|
+
# Checks that the keys, separators, and values of a multi-line hash
|
|
7
7
|
# literal are aligned according to configuration. The configuration
|
|
8
8
|
# options are:
|
|
9
9
|
#
|
|
@@ -69,14 +69,18 @@ module RuboCop
|
|
|
69
69
|
SAME_LINE_OFFENSE = 'Right hand side of multi-line assignment is not ' \
|
|
70
70
|
'on the same line as the assignment operator `=`.'
|
|
71
71
|
|
|
72
|
+
BLOCK_TYPES = %i[block numblock itblock].freeze
|
|
73
|
+
|
|
74
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
72
75
|
def check_assignment(node, rhs)
|
|
73
76
|
return if node.send_type? && node.loc.operator&.source != '='
|
|
74
77
|
return unless rhs
|
|
75
78
|
return unless supported_types.include?(rhs.type)
|
|
76
|
-
return if rhs.single_line?
|
|
79
|
+
return if rhs.single_line? && (!rhs.block_type? || same_line?(node, rhs.loc.begin))
|
|
77
80
|
|
|
78
81
|
check_by_enforced_style(node, rhs)
|
|
79
82
|
end
|
|
83
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
80
84
|
|
|
81
85
|
def check_by_enforced_style(node, rhs)
|
|
82
86
|
case style
|
|
@@ -109,7 +113,10 @@ module RuboCop
|
|
|
109
113
|
private
|
|
110
114
|
|
|
111
115
|
def supported_types
|
|
112
|
-
@supported_types ||= cop_config['SupportedTypes'].
|
|
116
|
+
@supported_types ||= cop_config['SupportedTypes'].flat_map do |type|
|
|
117
|
+
sym = type.to_sym
|
|
118
|
+
sym == :block ? BLOCK_TYPES : sym
|
|
119
|
+
end
|
|
113
120
|
end
|
|
114
121
|
end
|
|
115
122
|
end
|
|
@@ -128,12 +128,15 @@ module RuboCop
|
|
|
128
128
|
if hash_pair_indented?(node, pair_ancestor, given_style)
|
|
129
129
|
return check_hash_pair_indented_style(rhs, pair_ancestor)
|
|
130
130
|
end
|
|
131
|
-
|
|
132
|
-
return false if !pair_ancestor && not_for_this_cop?(node)
|
|
131
|
+
return false if skip_for_context?(node, pair_ancestor)
|
|
133
132
|
|
|
134
133
|
check_regular_indentation(node, lhs, rhs, given_style)
|
|
135
134
|
end
|
|
136
135
|
|
|
136
|
+
def skip_for_context?(node, pair_ancestor)
|
|
137
|
+
pair_ancestor ? inside_multiline_chain_arg?(node) : not_for_this_cop?(node)
|
|
138
|
+
end
|
|
139
|
+
|
|
137
140
|
def hash_pair_aligned?(pair_ancestor, given_style)
|
|
138
141
|
pair_ancestor && given_style == :aligned
|
|
139
142
|
end
|
|
@@ -152,7 +155,10 @@ module RuboCop
|
|
|
152
155
|
end
|
|
153
156
|
|
|
154
157
|
def check_hash_pair_indentation(node, lhs, rhs)
|
|
155
|
-
@base = find_hash_pair_alignment_base(node)
|
|
158
|
+
@base = find_hash_pair_alignment_base(node)
|
|
159
|
+
return false if !@base && inside_multiline_chain_arg?(node)
|
|
160
|
+
|
|
161
|
+
@base ||= lhs.source_range
|
|
156
162
|
return if aligned_with_first_line_dot?(node, rhs)
|
|
157
163
|
|
|
158
164
|
calculate_column_delta_offense(rhs, @base.column)
|
|
@@ -166,6 +172,25 @@ module RuboCop
|
|
|
166
172
|
first_call.loc.dot.join(first_call.loc.selector)
|
|
167
173
|
end
|
|
168
174
|
|
|
175
|
+
def inside_multiline_chain_arg?(node)
|
|
176
|
+
enclosing_call = find_enclosing_chain_call(node)
|
|
177
|
+
return false unless enclosing_call
|
|
178
|
+
|
|
179
|
+
!same_line?(enclosing_call.loc.selector, enclosing_call.receiver.source_range)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def find_enclosing_chain_call(node)
|
|
183
|
+
hash_ancestor = find_pair_ancestor(node).parent
|
|
184
|
+
enclosing_call = hash_ancestor.parent
|
|
185
|
+
return unless hash_arg_in_chain?(enclosing_call, hash_ancestor)
|
|
186
|
+
|
|
187
|
+
enclosing_call
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def hash_arg_in_chain?(call, hash_node)
|
|
191
|
+
call&.call_type? && call.receiver != hash_node && call.loc?(:dot)
|
|
192
|
+
end
|
|
193
|
+
|
|
169
194
|
def aligned_with_first_line_dot?(node, rhs)
|
|
170
195
|
return false unless rhs.source.start_with?('.', '&.')
|
|
171
196
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Layout
|
|
6
|
-
#
|
|
6
|
+
# Checks that the parameters on a multi-line method call or definition are aligned.
|
|
7
7
|
#
|
|
8
8
|
# To set the alignment of the first argument, use the
|
|
9
9
|
# `Layout/FirstParameterIndentation` cop.
|
|
@@ -114,7 +114,7 @@ module RuboCop
|
|
|
114
114
|
|
|
115
115
|
def other_cop_takes_precedence?(node)
|
|
116
116
|
single_line_block_chain_enabled? && any_descendant?(node, :any_block) do |block_node|
|
|
117
|
-
block_node.parent.send_type? && block_node.parent.loc.dot &&
|
|
117
|
+
block_node.parent.send_type? && block_node.parent.loc.dot && block_node.single_line?
|
|
118
118
|
end
|
|
119
119
|
end
|
|
120
120
|
|
|
@@ -29,7 +29,7 @@ module RuboCop
|
|
|
29
29
|
include RangeHelp
|
|
30
30
|
extend AutoCorrector
|
|
31
31
|
|
|
32
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
32
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
33
33
|
arguments = node.arguments
|
|
34
34
|
|
|
35
35
|
return unless node.arguments? && pipes?(arguments)
|
|
@@ -47,9 +47,11 @@ module RuboCop
|
|
|
47
47
|
check(node, [:operator].freeze) if node.keyword?
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
def on_block(node)
|
|
50
|
+
def on_block(node)
|
|
51
51
|
check(node, %i[begin end].freeze)
|
|
52
52
|
end
|
|
53
|
+
alias on_numblock on_block
|
|
54
|
+
alias on_itblock on_block
|
|
53
55
|
|
|
54
56
|
def on_break(node)
|
|
55
57
|
check(node, [:keyword].freeze)
|
|
@@ -26,6 +26,7 @@ module RuboCop
|
|
|
26
26
|
|
|
27
27
|
MSG_REQUIRE_SPACE = 'Use a space between `->` and `(` in lambda literals.'
|
|
28
28
|
MSG_REQUIRE_NO_SPACE = 'Do not use spaces between `->` and `(` in lambda literals.'
|
|
29
|
+
RESTRICT_ON_SEND = %i[lambda].freeze
|
|
29
30
|
|
|
30
31
|
def on_send(node)
|
|
31
32
|
return unless arrow_lambda_with_args?(node)
|
|
@@ -6,8 +6,11 @@ module RuboCop
|
|
|
6
6
|
# Checks for constant reassignments.
|
|
7
7
|
#
|
|
8
8
|
# Emulates Ruby's runtime warning "already initialized constant X"
|
|
9
|
-
# when a constant is reassigned in the same file and namespace
|
|
10
|
-
#
|
|
9
|
+
# when a constant is reassigned in the same file and namespace.
|
|
10
|
+
#
|
|
11
|
+
# The cop tracks constants defined via `NAME = value` syntax as well as
|
|
12
|
+
# class/module keyword definitions. It detects reassignment when a constant
|
|
13
|
+
# is first defined one way and then redefined using the `NAME = value` syntax.
|
|
11
14
|
#
|
|
12
15
|
# The cop cannot catch all offenses, like, for example, when a constant
|
|
13
16
|
# is reassigned in another file, or when using metaprogramming (`Module#const_set`).
|
|
@@ -36,6 +39,14 @@ module RuboCop
|
|
|
36
39
|
# X = :bar
|
|
37
40
|
# end
|
|
38
41
|
#
|
|
42
|
+
# # bad
|
|
43
|
+
# class FooError < StandardError; end
|
|
44
|
+
# FooError = Class.new(RuntimeError)
|
|
45
|
+
#
|
|
46
|
+
# # bad
|
|
47
|
+
# module M; end
|
|
48
|
+
# M = 1
|
|
49
|
+
#
|
|
39
50
|
# # good - keep only one assignment
|
|
40
51
|
# X = :bar
|
|
41
52
|
#
|
|
@@ -69,16 +80,30 @@ module RuboCop
|
|
|
69
80
|
|
|
70
81
|
# @!method remove_constant(node)
|
|
71
82
|
def_node_matcher :remove_constant, <<~PATTERN
|
|
72
|
-
(send
|
|
83
|
+
(send {nil? self} :remove_const
|
|
73
84
|
({sym str} $_))
|
|
74
85
|
PATTERN
|
|
75
86
|
|
|
87
|
+
def on_class(node)
|
|
88
|
+
return unless unconditional_definition?(node)
|
|
89
|
+
|
|
90
|
+
constant_definitions[definition_name(node)] ||= :class
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def on_module(node)
|
|
94
|
+
return unless unconditional_definition?(node)
|
|
95
|
+
|
|
96
|
+
constant_definitions[definition_name(node)] ||= :module
|
|
97
|
+
end
|
|
98
|
+
|
|
76
99
|
def on_casgn(node)
|
|
77
100
|
return unless fixed_constant_path?(node)
|
|
78
101
|
return unless simple_assignment?(node)
|
|
79
|
-
return if constant_names.add?(fully_qualified_constant_name(node))
|
|
80
102
|
|
|
81
|
-
|
|
103
|
+
name = fully_qualified_constant_name(node)
|
|
104
|
+
return constant_definitions[name] = :casgn unless constant_definitions.key?(name)
|
|
105
|
+
|
|
106
|
+
add_offense(node, message: format(MSG, constant: constant_display_name(node)))
|
|
82
107
|
end
|
|
83
108
|
|
|
84
109
|
def on_send(node)
|
|
@@ -90,7 +115,7 @@ module RuboCop
|
|
|
90
115
|
|
|
91
116
|
return if namespaces.none?
|
|
92
117
|
|
|
93
|
-
|
|
118
|
+
constant_definitions.delete(fully_qualified_name_for(namespaces, constant))
|
|
94
119
|
end
|
|
95
120
|
|
|
96
121
|
private
|
|
@@ -104,7 +129,7 @@ module RuboCop
|
|
|
104
129
|
return true if ancestor.type?(:module, :class)
|
|
105
130
|
|
|
106
131
|
ancestor.begin_type? || ancestor.literal? || ancestor.casgn_type? ||
|
|
107
|
-
freeze_method?(ancestor)
|
|
132
|
+
ancestor.type?(:masgn, :mlhs) || freeze_method?(ancestor)
|
|
108
133
|
end
|
|
109
134
|
end
|
|
110
135
|
|
|
@@ -128,6 +153,10 @@ module RuboCop
|
|
|
128
153
|
['', *namespaces, constant].join('::')
|
|
129
154
|
end
|
|
130
155
|
|
|
156
|
+
def constant_display_name(node)
|
|
157
|
+
[*constant_namespaces(node), node.name].join('::')
|
|
158
|
+
end
|
|
159
|
+
|
|
131
160
|
def constant_namespaces(node)
|
|
132
161
|
node.each_path.select(&:const_type?).map(&:short_name)
|
|
133
162
|
end
|
|
@@ -139,8 +168,29 @@ module RuboCop
|
|
|
139
168
|
.reverse
|
|
140
169
|
end
|
|
141
170
|
|
|
142
|
-
def
|
|
143
|
-
|
|
171
|
+
def unconditional_definition?(node)
|
|
172
|
+
node.each_ancestor.all? do |ancestor|
|
|
173
|
+
ancestor.type?(:begin, :module, :class)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def definition_name(node)
|
|
178
|
+
identifier = node.identifier
|
|
179
|
+
|
|
180
|
+
if identifier.namespace&.cbase_type?
|
|
181
|
+
fully_qualified_name_for([], identifier.short_name)
|
|
182
|
+
else
|
|
183
|
+
namespaces = ancestor_namespaces(node) + identifier_namespaces(identifier)
|
|
184
|
+
fully_qualified_name_for(namespaces, identifier.short_name)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def identifier_namespaces(identifier)
|
|
189
|
+
identifier.each_path.select(&:const_type?).map(&:short_name)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def constant_definitions
|
|
193
|
+
@constant_definitions ||= {}
|
|
144
194
|
end
|
|
145
195
|
end
|
|
146
196
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# Checks unexpected overrides of the `Data` built-in methods
|
|
7
|
+
# via `Data.define`.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# # bad
|
|
11
|
+
# Bad = Data.define(:members, :clone, :to_s)
|
|
12
|
+
# b = Bad.new(members: [], clone: true, to_s: 'bad')
|
|
13
|
+
# b.members #=> [] (overriding `Data#members`)
|
|
14
|
+
# b.clone #=> true (overriding `Object#clone`)
|
|
15
|
+
# b.to_s #=> "bad" (overriding `Data#to_s`)
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# Good = Data.define(:id, :name)
|
|
19
|
+
# g = Good.new(id: 1, name: "foo")
|
|
20
|
+
# g.members #=> [:id, :name]
|
|
21
|
+
# g.clone #=> #<data Good id=1, name="foo">
|
|
22
|
+
#
|
|
23
|
+
class DataDefineOverride < Base
|
|
24
|
+
MSG = '`%<member_name>s` member overrides `Data#%<method_name>s` and it may be unexpected.'
|
|
25
|
+
RESTRICT_ON_SEND = %i[define].freeze
|
|
26
|
+
|
|
27
|
+
# This is based on `Data.define.instance_methods.sort` in Ruby 4.0.0.
|
|
28
|
+
DATA_METHOD_NAMES = %i[
|
|
29
|
+
! != !~ <=> == === __id__ __send__ class clone deconstruct deconstruct_keys
|
|
30
|
+
define_singleton_method display dup enum_for eql? equal? extend freeze frozen? hash
|
|
31
|
+
inspect instance_eval instance_exec instance_of? instance_variable_defined?
|
|
32
|
+
instance_variable_get instance_variable_set instance_variables is_a? itself kind_of?
|
|
33
|
+
members method methods nil? object_id private_methods protected_methods
|
|
34
|
+
public_method public_methods public_send remove_instance_variable respond_to? send
|
|
35
|
+
singleton_class singleton_method singleton_methods tap then to_enum to_h to_s with
|
|
36
|
+
yield_self
|
|
37
|
+
].freeze
|
|
38
|
+
MEMBER_NAME_TYPES = %i[sym str].freeze
|
|
39
|
+
|
|
40
|
+
# @!method data_define(node)
|
|
41
|
+
def_node_matcher :data_define, <<~PATTERN
|
|
42
|
+
(send
|
|
43
|
+
(const {nil? cbase} :Data) :define ...)
|
|
44
|
+
PATTERN
|
|
45
|
+
|
|
46
|
+
def on_send(node)
|
|
47
|
+
return unless data_define(node)
|
|
48
|
+
|
|
49
|
+
node.arguments.each do |arg|
|
|
50
|
+
next unless MEMBER_NAME_TYPES.include?(arg.type)
|
|
51
|
+
|
|
52
|
+
member_name = arg.value
|
|
53
|
+
|
|
54
|
+
next unless DATA_METHOD_NAMES.include?(member_name.to_sym)
|
|
55
|
+
|
|
56
|
+
message = format(MSG, member_name: member_name.inspect, method_name: member_name.to_s)
|
|
57
|
+
add_offense(arg, message: message)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -197,6 +197,13 @@ module RuboCop
|
|
|
197
197
|
# @!method sym_name(node)
|
|
198
198
|
def_node_matcher :sym_name, '(sym $_name)'
|
|
199
199
|
|
|
200
|
+
# @!method class_or_module_new_block?(node)
|
|
201
|
+
def_node_matcher :class_or_module_new_block?, <<~PATTERN
|
|
202
|
+
(block
|
|
203
|
+
(send (const _ {:Class :Module}) :new ...)
|
|
204
|
+
...)
|
|
205
|
+
PATTERN
|
|
206
|
+
|
|
200
207
|
def on_send(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
201
208
|
name, original_name = alias_method?(node)
|
|
202
209
|
|
|
@@ -233,9 +240,12 @@ module RuboCop
|
|
|
233
240
|
|
|
234
241
|
def check_self_receiver(node, name)
|
|
235
242
|
enclosing = node.parent_module_name
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
243
|
+
if enclosing
|
|
244
|
+
found_method(node, "#{enclosing}.#{name}")
|
|
245
|
+
elsif (anon_block = anonymous_class_block(node))
|
|
246
|
+
scope = qualified_name(anon_block.parent_module_name, nil, 'Object')
|
|
247
|
+
found_method(node, "#{scope}.#{name}", scope_id: anon_block_scope_id(anon_block))
|
|
248
|
+
end
|
|
239
249
|
end
|
|
240
250
|
|
|
241
251
|
def inside_condition?(node)
|
|
@@ -274,16 +284,45 @@ module RuboCop
|
|
|
274
284
|
end
|
|
275
285
|
|
|
276
286
|
def found_instance_method(node, name)
|
|
277
|
-
|
|
287
|
+
if (scope = node.parent_module_name)
|
|
288
|
+
found_method(node, "#{humanize_scope(scope)}#{name}")
|
|
289
|
+
elsif (anon_block = anonymous_class_block(node))
|
|
290
|
+
base = qualified_name(anon_block.parent_module_name, nil, 'Object')
|
|
291
|
+
scope = node.each_ancestor(:sclass).any? ? "#<Class:#{base}>" : base
|
|
292
|
+
found_method(
|
|
293
|
+
node, "#{humanize_scope(scope)}#{name}", scope_id: anon_block_scope_id(anon_block)
|
|
294
|
+
)
|
|
295
|
+
else
|
|
296
|
+
found_sclass_method(node, name)
|
|
297
|
+
end
|
|
298
|
+
end
|
|
278
299
|
|
|
279
|
-
|
|
300
|
+
def humanize_scope(scope)
|
|
280
301
|
scope = scope.sub(
|
|
281
302
|
/(?:(?<name>.*)::)#<Class:\k<name>>|#<Class:(?<name>.*)>(?:::)?/,
|
|
282
303
|
'\k<name>.'
|
|
283
304
|
)
|
|
284
|
-
scope
|
|
305
|
+
scope.end_with?('.') ? scope : "#{scope}#"
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def anonymous_class_block(node)
|
|
309
|
+
first_block = node.each_ancestor(:block).first
|
|
310
|
+
return unless class_or_module_new_block?(first_block)
|
|
311
|
+
return if first_block.parent&.type?(:lvasgn, :block)
|
|
312
|
+
return if node.each_ancestor(:sclass).any? { |s| !s.children.first.self_type? }
|
|
313
|
+
|
|
314
|
+
first_block
|
|
315
|
+
end
|
|
285
316
|
|
|
286
|
-
|
|
317
|
+
def anon_block_scope_id(anon_block)
|
|
318
|
+
parent = anon_block.parent
|
|
319
|
+
return unless parent&.call_type?
|
|
320
|
+
|
|
321
|
+
if parent.receiver
|
|
322
|
+
"#{parent.receiver.source}.#{parent.method_name}"
|
|
323
|
+
else
|
|
324
|
+
source_location(anon_block)
|
|
325
|
+
end
|
|
287
326
|
end
|
|
288
327
|
|
|
289
328
|
def found_sclass_method(node, name)
|
|
@@ -296,8 +335,10 @@ module RuboCop
|
|
|
296
335
|
found_method(node, "#{singleton_receiver_node.method_name}.#{name}")
|
|
297
336
|
end
|
|
298
337
|
|
|
299
|
-
|
|
338
|
+
# rubocop:disable Metrics/AbcSize
|
|
339
|
+
def found_method(node, method_name, scope_id: nil)
|
|
300
340
|
key = method_key(node, method_name)
|
|
341
|
+
key = "#{key}@#{scope_id}" if scope_id
|
|
301
342
|
scope = node.each_ancestor(:rescue, :ensure).first&.type
|
|
302
343
|
|
|
303
344
|
if @definitions.key?(key)
|
|
@@ -314,6 +355,7 @@ module RuboCop
|
|
|
314
355
|
@definitions[key] = node
|
|
315
356
|
end
|
|
316
357
|
end
|
|
358
|
+
# rubocop:enable Metrics/AbcSize
|
|
317
359
|
|
|
318
360
|
def method_key(node, method_name)
|
|
319
361
|
if (ancestor_def = node.each_ancestor(:any_def).first)
|
|
@@ -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
|
|