rubocop 1.70.0 → 1.71.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +17 -0
- data/lib/rubocop/cli/command/show_cops.rb +24 -2
- data/lib/rubocop/comment_config.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +1 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/location_expression.rb +2 -1
- data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +3 -2
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +63 -0
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +131 -0
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +229 -0
- data/lib/rubocop/cop/internal_affairs/node_type_multiple_predicates.rb +126 -0
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +90 -0
- data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
- data/lib/rubocop/cop/internal_affairs.rb +3 -0
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +9 -9
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +7 -5
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +1 -1
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +1 -0
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +6 -5
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -1
- data/lib/rubocop/cop/layout/space_after_colon.rb +2 -2
- data/lib/rubocop/cop/layout/space_after_comma.rb +1 -1
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -0
- data/lib/rubocop/cop/layout/space_before_comma.rb +1 -1
- data/lib/rubocop/cop/layout/space_before_semicolon.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +119 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -3
- data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +1 -1
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +3 -3
- data/lib/rubocop/cop/lint/constant_reassignment.rb +2 -6
- data/lib/rubocop/cop/lint/debugger.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_set_element.rb +1 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +5 -2
- data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +13 -3
- data/lib/rubocop/cop/lint/missing_super.rb +2 -2
- data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -1
- data/lib/rubocop/cop/lint/nested_method_definition.rb +3 -3
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +13 -18
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -5
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_string_coercion.rb +2 -2
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -1
- data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -3
- data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/unexpected_block_arity.rb +1 -1
- data/lib/rubocop/cop/lint/unreachable_code.rb +1 -1
- data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +4 -4
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
- data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +2 -1
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -2
- data/lib/rubocop/cop/lint/void.rb +1 -1
- data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
- data/lib/rubocop/cop/metrics/collection_literal_length.rb +7 -0
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
- data/lib/rubocop/cop/metrics/module_length.rb +1 -1
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -5
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +4 -4
- data/lib/rubocop/cop/mixin/hash_subset.rb +188 -0
- data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +22 -8
- data/lib/rubocop/cop/mixin/statement_modifier.rb +7 -2
- data/lib/rubocop/cop/mixin/string_help.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +3 -3
- data/lib/rubocop/cop/naming/block_forwarding.rb +18 -14
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +3 -3
- data/lib/rubocop/cop/security/compound_hash.rb +1 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -4
- data/lib/rubocop/cop/style/arguments_forwarding.rb +38 -19
- data/lib/rubocop/cop/style/array_first_last.rb +18 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +7 -20
- data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
- data/lib/rubocop/cop/style/collection_methods.rb +1 -1
- data/lib/rubocop/cop/style/combinable_defined.rb +1 -1
- data/lib/rubocop/cop/style/combinable_loops.rb +2 -2
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +6 -4
- data/lib/rubocop/cop/style/documentation.rb +1 -1
- data/lib/rubocop/cop/style/double_negation.rb +3 -3
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/exact_regexp_match.rb +1 -1
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
- data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
- data/lib/rubocop/cop/style/hash_each_methods.rb +3 -6
- data/lib/rubocop/cop/style/hash_except.rb +20 -131
- data/lib/rubocop/cop/style/hash_slice.rb +80 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
- data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +6 -6
- data/lib/rubocop/cop/style/it_assignment.rb +1 -1
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
- data/lib/rubocop/cop/style/map_into_array.rb +1 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
- data/lib/rubocop/cop/style/map_to_set.rb +3 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +10 -13
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -1
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +2 -4
- data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +2 -2
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
- data/lib/rubocop/cop/style/open_struct_use.rb +5 -5
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -5
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/proc.rb +1 -2
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +2 -2
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -10
- data/lib/rubocop/cop/style/redundant_each.rb +1 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +2 -2
- data/lib/rubocop/cop/style/redundant_freeze.rb +2 -2
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +27 -10
- data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -6
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +12 -27
- data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
- data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -2
- data/lib/rubocop/cop/style/return_nil.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +1 -1
- data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -2
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/string_literals.rb +1 -1
- data/lib/rubocop/cop/style/string_methods.rb +1 -1
- data/lib/rubocop/cop/style/super_arguments.rb +4 -4
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -1
- data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
- data/lib/rubocop/cop/util.rb +2 -2
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable_table.rb +3 -3
- data/lib/rubocop/cops_documentation_generator.rb +13 -13
- data/lib/rubocop/directive_comment.rb +9 -8
- data/lib/rubocop/options.rb +2 -1
- data/lib/rubocop/result_cache.rb +13 -13
- data/lib/rubocop/rspec/expect_offense.rb +6 -2
- data/lib/rubocop/rspec/support.rb +1 -2
- data/lib/rubocop/target_finder.rb +1 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -0
- metadata +15 -11
- data/lib/rubocop/rspec/host_environment_simulation_helper.rb +0 -28
@@ -108,7 +108,7 @@ module RuboCop
|
|
108
108
|
private
|
109
109
|
|
110
110
|
def loop_method?(node)
|
111
|
-
return false unless node.
|
111
|
+
return false unless node.any_block_type?
|
112
112
|
|
113
113
|
send_node = node.send_node
|
114
114
|
loopable = send_node.enumerable_method? || send_node.enumerator_method? ||
|
@@ -159,12 +159,12 @@ module RuboCop
|
|
159
159
|
|
160
160
|
# @!method dynamic_method_definition?(node)
|
161
161
|
def_node_matcher :dynamic_method_definition?, <<~PATTERN
|
162
|
-
{(send nil? :define_method ...) (
|
162
|
+
{(send nil? :define_method ...) (any_block (send nil? :define_method ...) ...)}
|
163
163
|
PATTERN
|
164
164
|
|
165
165
|
# @!method class_or_instance_eval?(node)
|
166
166
|
def_node_matcher :class_or_instance_eval?, <<~PATTERN
|
167
|
-
(
|
167
|
+
(any_block (send _ {:class_eval :instance_eval}) ...)
|
168
168
|
PATTERN
|
169
169
|
|
170
170
|
def check_node(node)
|
@@ -268,7 +268,7 @@ module RuboCop
|
|
268
268
|
end
|
269
269
|
|
270
270
|
def start_of_new_scope?(child)
|
271
|
-
child.
|
271
|
+
child.type?(:module, :class, :sclass) || eval_call?(child)
|
272
272
|
end
|
273
273
|
|
274
274
|
def eval_call?(child)
|
@@ -282,7 +282,7 @@ module RuboCop
|
|
282
282
|
matcher_name = :"#{m}_block?"
|
283
283
|
unless respond_to?(matcher_name)
|
284
284
|
self.class.def_node_matcher matcher_name, <<~PATTERN
|
285
|
-
(
|
285
|
+
(any_block (send {nil? const} {:#{m}} ...) ...)
|
286
286
|
PATTERN
|
287
287
|
end
|
288
288
|
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
17
17
|
# rescue, ensure, etc.
|
18
18
|
#
|
19
19
|
# This cop's autocorrection avoids cases like `a ||= 1` because removing assignment from
|
20
|
-
# operator assignment can cause NameError if this assignment has been used to declare
|
20
|
+
# operator assignment can cause `NameError` if this assignment has been used to declare
|
21
21
|
# a local variable. For example, replacing `a ||= 1` with `a || 1` may cause
|
22
22
|
# "undefined local variable or method `a' for main:Object (NameError)".
|
23
23
|
#
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def use_rest_or_optional_args?(node)
|
62
|
-
node.arguments.any? { |arg| arg.
|
62
|
+
node.arguments.any? { |arg| arg.type?(:restarg, :optarg, :kwoptarg) }
|
63
63
|
end
|
64
64
|
|
65
65
|
def delegating?(node, def_node)
|
@@ -35,7 +35,7 @@ module RuboCop
|
|
35
35
|
RESTRICT_ON_SEND = %i[+ - * / **].freeze
|
36
36
|
|
37
37
|
# @!method useless_operation?(node)
|
38
|
-
def_node_matcher :useless_operation?, '(
|
38
|
+
def_node_matcher :useless_operation?, '(call (call nil? $_) $_ (int $_))'
|
39
39
|
|
40
40
|
# @!method useless_abbreviated_assignment?(node)
|
41
41
|
def_node_matcher :useless_abbreviated_assignment?, '(op-asgn (lvasgn $_) $_ (int $_))'
|
@@ -50,6 +50,7 @@ module RuboCop
|
|
50
50
|
corrector.replace(node, variable)
|
51
51
|
end
|
52
52
|
end
|
53
|
+
alias on_csend on_send
|
53
54
|
|
54
55
|
def on_op_asgn(node)
|
55
56
|
return unless useless_abbreviated_assignment?(node)
|
@@ -72,7 +72,7 @@ module RuboCop
|
|
72
72
|
def_node_matcher :method_definition, <<~PATTERN
|
73
73
|
{
|
74
74
|
(def %1 ...)
|
75
|
-
(
|
75
|
+
(any_block (send _ :define_method (sym %1)) ...)
|
76
76
|
}
|
77
77
|
PATTERN
|
78
78
|
|
@@ -108,7 +108,7 @@ module RuboCop
|
|
108
108
|
|
109
109
|
def find_method_definition(node, method_name)
|
110
110
|
node.each_ancestor.lazy.map do |ancestor|
|
111
|
-
ancestor.each_child_node(:def, :
|
111
|
+
ancestor.each_child_node(:def, :any_block).find do |child|
|
112
112
|
method_definition(child, method_name)
|
113
113
|
end
|
114
114
|
end.find(&:itself)
|
@@ -179,7 +179,7 @@ module RuboCop
|
|
179
179
|
end
|
180
180
|
|
181
181
|
def check_nonmutating(node)
|
182
|
-
return
|
182
|
+
return unless node.type?(:send, :any_block)
|
183
183
|
|
184
184
|
method_name = node.method_name
|
185
185
|
return unless NONMUTATING_METHODS.include?(method_name)
|
@@ -52,12 +52,19 @@ module RuboCop
|
|
52
52
|
'Prefer reading the data from an external source.'
|
53
53
|
RESTRICT_ON_SEND = [:[]].freeze
|
54
54
|
|
55
|
+
# @!method set_const?(node)
|
56
|
+
def_node_matcher :set_const?, <<~PATTERN
|
57
|
+
(const {cbase nil?} :Set)
|
58
|
+
PATTERN
|
59
|
+
|
55
60
|
def on_array(node)
|
56
61
|
add_offense(node) if node.children.length >= collection_threshold
|
57
62
|
end
|
58
63
|
alias on_hash on_array
|
59
64
|
|
60
65
|
def on_index(node)
|
66
|
+
return unless set_const?(node.receiver)
|
67
|
+
|
61
68
|
add_offense(node) if node.arguments.length >= collection_threshold
|
62
69
|
end
|
63
70
|
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
36
36
|
include MethodComplexity
|
37
37
|
include Utils::IteratingBlock
|
38
38
|
|
39
|
-
MSG = 'Cyclomatic complexity for
|
39
|
+
MSG = 'Cyclomatic complexity for `%<method>s` is too high. [%<complexity>d/%<max>d]'
|
40
40
|
COUNTED_NODES = %i[if while until for csend block block_pass
|
41
41
|
rescue when in_pattern and or or_asgn and_asgn].freeze
|
42
42
|
|
@@ -50,7 +50,7 @@ module RuboCop
|
|
50
50
|
|
51
51
|
# @!method module_definition?(node)
|
52
52
|
def_node_matcher :module_definition?, <<~PATTERN
|
53
|
-
(casgn nil? _ (
|
53
|
+
(casgn nil? _ (any_block (send (const {nil? cbase} :Module) :new) ...))
|
54
54
|
PATTERN
|
55
55
|
|
56
56
|
def message(length, max_length)
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
27
27
|
# end # ===
|
28
28
|
# end # 7 complexity points
|
29
29
|
class PerceivedComplexity < CyclomaticComplexity
|
30
|
-
MSG = 'Perceived complexity for
|
30
|
+
MSG = 'Perceived complexity for `%<method>s` is too high. [%<complexity>d/%<max>d]'
|
31
31
|
|
32
32
|
COUNTED_NODES = (CyclomaticComplexity::COUNTED_NODES - [:when] + [:case]).freeze
|
33
33
|
|
@@ -50,7 +50,7 @@ module RuboCop
|
|
50
50
|
return extract_breakable_node_from_elements(node, args, max)
|
51
51
|
elsif node.def_type?
|
52
52
|
return extract_breakable_node_from_elements(node, node.arguments, max)
|
53
|
-
elsif node.
|
53
|
+
elsif node.type?(:array, :hash)
|
54
54
|
return extract_breakable_node_from_elements(node, node.children, max)
|
55
55
|
end
|
56
56
|
nil
|
@@ -101,7 +101,7 @@ module RuboCop
|
|
101
101
|
# If a `send` or `csend` node contains a heredoc argument, splitting cannot happen
|
102
102
|
# after the heredoc or else it will cause a syntax error.
|
103
103
|
def shift_elements_for_heredoc_arg(node, elements, index)
|
104
|
-
return index unless node.
|
104
|
+
return index unless node.type?(:call, :array)
|
105
105
|
|
106
106
|
heredoc_index = elements.index { |arg| arg.respond_to?(:heredoc?) && arg.heredoc? }
|
107
107
|
return index unless heredoc_index
|
@@ -154,7 +154,7 @@ module RuboCop
|
|
154
154
|
# Ignore ancestors on different lines.
|
155
155
|
break if ancestor.first_line != node.first_line
|
156
156
|
|
157
|
-
if ancestor.
|
157
|
+
if ancestor.type?(:hash, :array)
|
158
158
|
elements = ancestor.children
|
159
159
|
elsif ancestor.call_type?
|
160
160
|
elements = process_args(ancestor.arguments)
|
@@ -171,7 +171,7 @@ module RuboCop
|
|
171
171
|
# @api private
|
172
172
|
def contained_by_multiline_collection_that_could_be_broken_up?(node)
|
173
173
|
node.each_ancestor.find do |ancestor|
|
174
|
-
if
|
174
|
+
if ancestor.type?(:hash, :array) &&
|
175
175
|
breakable_collection?(ancestor, ancestor.children)
|
176
176
|
return children_could_be_broken_up?(ancestor.children)
|
177
177
|
end
|
@@ -227,7 +227,7 @@ module RuboCop
|
|
227
227
|
|
228
228
|
def chained_to_heredoc?(node)
|
229
229
|
while (node = node.receiver)
|
230
|
-
return true if
|
230
|
+
return true if node.type?(:str, :dstr, :xstr) && node.heredoc?
|
231
231
|
end
|
232
232
|
|
233
233
|
false
|
@@ -76,7 +76,7 @@ module RuboCop
|
|
76
76
|
elsif node.if? && node.parent && parentheses?(node.parent)
|
77
77
|
node.parent.loc.end.line
|
78
78
|
end
|
79
|
-
elsif node.
|
79
|
+
elsif node.any_block_type?
|
80
80
|
node.loc.end.line
|
81
81
|
elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node) &&
|
82
82
|
next_sibling.source_range
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
module FrozenStringLiteral
|
7
7
|
module_function
|
8
8
|
|
9
|
-
|
9
|
+
FROZEN_STRING_LITERAL_REGEXP = /#\s*frozen[-_]?string[-_]?literal:/i.freeze
|
10
10
|
FROZEN_STRING_LITERAL_ENABLED = '# frozen_string_literal: true'
|
11
11
|
FROZEN_STRING_LITERAL_TYPES_RUBY27 = %i[str dstr].freeze
|
12
12
|
|
@@ -86,7 +86,7 @@ module RuboCop
|
|
86
86
|
return true if !node.key.sym_type? || require_hash_value_for_around_hash_literal?(node)
|
87
87
|
|
88
88
|
hash_value = node.value
|
89
|
-
return true unless hash_value.
|
89
|
+
return true unless hash_value.type?(:send, :lvar)
|
90
90
|
|
91
91
|
hash_key_source != hash_value.source || hash_key_source.end_with?('!', '?')
|
92
92
|
end
|
@@ -109,7 +109,7 @@ module RuboCop
|
|
109
109
|
return if dispatch_node.parent && parentheses?(dispatch_node.parent)
|
110
110
|
return if last_expression?(dispatch_node) && !method_dispatch_as_argument?(dispatch_node)
|
111
111
|
|
112
|
-
def_node = node.each_ancestor(:
|
112
|
+
def_node = node.each_ancestor(:call, :super, :yield).first
|
113
113
|
|
114
114
|
DefNode.new(def_node) unless def_node && def_node.arguments.empty?
|
115
115
|
end
|
@@ -117,7 +117,7 @@ module RuboCop
|
|
117
117
|
|
118
118
|
def find_ancestor_method_dispatch_node(node)
|
119
119
|
return unless (ancestor = node.parent.parent)
|
120
|
-
return unless ancestor.
|
120
|
+
return unless ancestor.type?(:call, :super, :yield)
|
121
121
|
return if brackets?(ancestor)
|
122
122
|
|
123
123
|
ancestor
|
@@ -150,7 +150,7 @@ module RuboCop
|
|
150
150
|
parent = method_dispatch_node.parent
|
151
151
|
return false unless parent
|
152
152
|
|
153
|
-
parent.
|
153
|
+
parent.type?(:call, :super, :yield)
|
154
154
|
end
|
155
155
|
|
156
156
|
def breakdown_value_types_of_hash(hash_node)
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Common functionality for Style/HashExcept and Style/HashSlice cops.
|
6
|
+
# It registers an offense on methods with blocks that are equivalent
|
7
|
+
# to Hash#except or Hash#slice.
|
8
|
+
# rubocop:disable Metrics/ModuleLength
|
9
|
+
module HashSubset
|
10
|
+
include RangeHelp
|
11
|
+
extend NodePattern::Macros
|
12
|
+
|
13
|
+
RESTRICT_ON_SEND = %i[reject select filter].freeze
|
14
|
+
|
15
|
+
SUBSET_METHODS = %i[== != eql? include?].freeze
|
16
|
+
ACTIVE_SUPPORT_SUBSET_METHODS = (SUBSET_METHODS + %i[in? exclude?]).freeze
|
17
|
+
|
18
|
+
MSG = 'Use `%<prefer>s` instead.'
|
19
|
+
|
20
|
+
# @!method block_with_first_arg_check?(node)
|
21
|
+
def_node_matcher :block_with_first_arg_check?, <<~PATTERN
|
22
|
+
(block
|
23
|
+
(call _ _)
|
24
|
+
(args
|
25
|
+
$(arg _key)
|
26
|
+
(arg _))
|
27
|
+
{
|
28
|
+
$(send
|
29
|
+
{(lvar _key) $_ _ | _ $_ (lvar _key)})
|
30
|
+
(send
|
31
|
+
$(send
|
32
|
+
{(lvar _key) $_ _ | _ $_ (lvar _key)}) :!)
|
33
|
+
})
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def on_send(node)
|
37
|
+
offense_range, key_source = extract_offense(node)
|
38
|
+
|
39
|
+
return unless offense_range
|
40
|
+
return unless semantically_subset_method?(node)
|
41
|
+
|
42
|
+
preferred_method = "#{preferred_method_name}(#{key_source})"
|
43
|
+
add_offense(offense_range, message: format(MSG, prefer: preferred_method)) do |corrector|
|
44
|
+
corrector.replace(offense_range, preferred_method)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
alias on_csend on_send
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def semantically_subset_method?(node)
|
52
|
+
raise NotImplementedError
|
53
|
+
end
|
54
|
+
|
55
|
+
def preferred_method_name
|
56
|
+
raise NotImplementedError
|
57
|
+
end
|
58
|
+
|
59
|
+
def extract_offense(node)
|
60
|
+
block = node.parent
|
61
|
+
return unless extracts_hash_subset?(block)
|
62
|
+
|
63
|
+
except_key = except_key(block)
|
64
|
+
return if except_key.nil? || !safe_to_register_offense?(block, except_key)
|
65
|
+
|
66
|
+
[offense_range(node), except_key_source(except_key)]
|
67
|
+
end
|
68
|
+
|
69
|
+
def extracts_hash_subset?(block)
|
70
|
+
block_with_first_arg_check?(block) do |key_arg, send_node, method|
|
71
|
+
# Only consider methods that have one argument
|
72
|
+
return false unless send_node.arguments.one?
|
73
|
+
|
74
|
+
return false unless supported_subset_method?(method)
|
75
|
+
return false if range_include?(send_node)
|
76
|
+
|
77
|
+
case method
|
78
|
+
when :include?, :exclude?
|
79
|
+
send_node.first_argument.source == key_arg.source
|
80
|
+
when :in?
|
81
|
+
send_node.receiver.source == key_arg.source
|
82
|
+
else
|
83
|
+
true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def range_include?(send_node)
|
89
|
+
# When checking `include?`, `exclude?` and `in?` for offenses, if the receiver
|
90
|
+
# or first argument is a range, an offense should not be registered.
|
91
|
+
# ie. `(1..5).include?(k)` or `k.in?('a'..'z')`
|
92
|
+
|
93
|
+
return true if send_node.first_argument.range_type?
|
94
|
+
|
95
|
+
receiver = send_node.receiver
|
96
|
+
receiver = receiver.child_nodes.first while receiver.begin_type?
|
97
|
+
receiver.range_type?
|
98
|
+
end
|
99
|
+
|
100
|
+
def supported_subset_method?(method)
|
101
|
+
if active_support_extensions_enabled?
|
102
|
+
ACTIVE_SUPPORT_SUBSET_METHODS.include?(method)
|
103
|
+
else
|
104
|
+
SUBSET_METHODS.include?(method)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def semantically_except_method?(node)
|
109
|
+
block = node.parent
|
110
|
+
body, negated = extract_body_if_negated(block.body)
|
111
|
+
|
112
|
+
if node.method?('reject')
|
113
|
+
body.method?('==') || body.method?('eql?') || included?(body, negated)
|
114
|
+
else
|
115
|
+
body.method?('!=') || not_included?(body, negated)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def semantically_slice_method?(node)
|
120
|
+
!semantically_except_method?(node)
|
121
|
+
end
|
122
|
+
|
123
|
+
def included?(body, negated)
|
124
|
+
if negated
|
125
|
+
body.method?('exclude?')
|
126
|
+
else
|
127
|
+
body.method?('include?') || body.method?('in?')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def not_included?(body, negated)
|
132
|
+
included?(body, !negated)
|
133
|
+
end
|
134
|
+
|
135
|
+
def safe_to_register_offense?(block, except_key)
|
136
|
+
body = block.body
|
137
|
+
|
138
|
+
if body.method?('==') || body.method?('!=')
|
139
|
+
except_key.type?(:sym, :str)
|
140
|
+
else
|
141
|
+
true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def extract_body_if_negated(body)
|
146
|
+
if body.method?('!')
|
147
|
+
[body.receiver, true]
|
148
|
+
else
|
149
|
+
[body, false]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def except_key_source(key)
|
154
|
+
if key.array_type?
|
155
|
+
key = if key.percent_literal?
|
156
|
+
key.each_value.map { |v| decorate_source(v) }
|
157
|
+
else
|
158
|
+
key.each_value.map(&:source)
|
159
|
+
end
|
160
|
+
return key.join(', ')
|
161
|
+
end
|
162
|
+
|
163
|
+
key.literal? ? key.source : "*#{key.source}"
|
164
|
+
end
|
165
|
+
|
166
|
+
def decorate_source(value)
|
167
|
+
return ":\"#{value.source}\"" if value.dsym_type?
|
168
|
+
return "\"#{value.source}\"" if value.dstr_type?
|
169
|
+
return ":#{value.source}" if value.sym_type?
|
170
|
+
|
171
|
+
"'#{value.source}'"
|
172
|
+
end
|
173
|
+
|
174
|
+
def except_key(node)
|
175
|
+
key_arg = node.argument_list.first.source
|
176
|
+
body, = extract_body_if_negated(node.body)
|
177
|
+
lhs, _method_name, rhs = *body
|
178
|
+
|
179
|
+
lhs.source == key_arg ? rhs : lhs
|
180
|
+
end
|
181
|
+
|
182
|
+
def offense_range(node)
|
183
|
+
range_between(node.loc.selector.begin_pos, node.parent.loc.end.end_pos)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
# rubocop:enable Metrics/ModuleLength
|
187
|
+
end
|
188
|
+
end
|
@@ -4,6 +4,7 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
# Common functionality for checking whether an AST node/token is aligned
|
6
6
|
# with something on a preceding or following line
|
7
|
+
# rubocop:disable Metrics/ModuleLength
|
7
8
|
module PrecedingFollowingAlignment
|
8
9
|
# Tokens that end with an `=`, as well as `<<`, that can be aligned together:
|
9
10
|
# `=`, `==`, `===`, `!=`, `<=`, `>=`, `<<` and operator assignment (`+=`, etc).
|
@@ -156,10 +157,14 @@ module RuboCop
|
|
156
157
|
@assignment_tokens ||= begin
|
157
158
|
tokens = processed_source.tokens.select(&:equal_sign?)
|
158
159
|
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
|
160
|
+
# We don't want to operate on equals signs which are part of an `optarg` in a
|
161
|
+
# method definition, or the separator of an endless method definition.
|
162
|
+
# For example (the equals sign to ignore is highlighted with ^):
|
163
|
+
# def method(optarg = default_val); end
|
164
|
+
# ^
|
165
|
+
# def method = foo
|
166
|
+
# ^
|
167
|
+
tokens = remove_equals_in_def(tokens, processed_source)
|
163
168
|
|
164
169
|
# Only attempt to align the first = on each line
|
165
170
|
Set.new(tokens.uniq(&:line))
|
@@ -195,11 +200,20 @@ module RuboCop
|
|
195
200
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
196
201
|
# rubocop:enable Metrics/PerceivedComplexity, Metrics/MethodLength
|
197
202
|
|
198
|
-
def
|
199
|
-
|
200
|
-
|
201
|
-
|
203
|
+
def remove_equals_in_def(asgn_tokens, processed_source)
|
204
|
+
nodes = processed_source.ast.each_node(:optarg, :def)
|
205
|
+
eqls_to_ignore = nodes.with_object([]) do |node, arr|
|
206
|
+
loc = if node.def_type?
|
207
|
+
node.loc.assignment if node.endless?
|
208
|
+
else
|
209
|
+
node.loc.operator
|
210
|
+
end
|
211
|
+
arr << loc.begin_pos if loc
|
212
|
+
end
|
213
|
+
|
214
|
+
asgn_tokens.reject { |t| eqls_to_ignore.include?(t.begin_pos) }
|
202
215
|
end
|
203
216
|
end
|
217
|
+
# rubocop:enable Metrics/ModuleLength
|
204
218
|
end
|
205
219
|
end
|
@@ -55,14 +55,19 @@ module RuboCop
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def if_body_source(if_body)
|
58
|
-
if if_body.call_type? &&
|
59
|
-
if_body.last_argument&.hash_type? && if_body.last_argument.pairs.last&.value_omission?
|
58
|
+
if if_body.call_type? && !if_body.method?(:[]=) && omitted_value_in_last_hash_arg?(if_body)
|
60
59
|
"#{method_source(if_body)}(#{if_body.arguments.map(&:source).join(', ')})"
|
61
60
|
else
|
62
61
|
if_body.source
|
63
62
|
end
|
64
63
|
end
|
65
64
|
|
65
|
+
def omitted_value_in_last_hash_arg?(if_body)
|
66
|
+
return false unless (last_argument = if_body.last_argument)
|
67
|
+
|
68
|
+
last_argument.hash_type? && last_argument.pairs.last&.value_omission?
|
69
|
+
end
|
70
|
+
|
66
71
|
def method_source(if_body)
|
67
72
|
end_range = if_body.implicit_call? ? if_body.loc.dot.end : if_body.loc.selector
|
68
73
|
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
# A :begin node inside a :dstr, :dsym, or :regexp node is an interpolation.
|
34
34
|
node.ancestors
|
35
35
|
.drop_while { |a| !a.begin_type? }
|
36
|
-
.any? { |a| a.
|
36
|
+
.any? { |a| a.type?(:dstr, :dsym, :regexp) }
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
|
-
# Common methods shared by Style/TrailingCommaInArguments
|
6
|
-
# Style/
|
5
|
+
# Common methods shared by Style/TrailingCommaInArguments,
|
6
|
+
# Style/TrailingCommaInArrayLiteral and Style/TrailingCommaInHashLiteral
|
7
7
|
module TrailingComma
|
8
8
|
include ConfigurableEnforcedStyle
|
9
9
|
include RangeHelp
|
@@ -181,7 +181,7 @@ module RuboCop
|
|
181
181
|
# ...
|
182
182
|
# SOURCE
|
183
183
|
# })
|
184
|
-
return heredoc?(node.children.last) if node.
|
184
|
+
return heredoc?(node.children.last) if node.type?(:pair, :hash)
|
185
185
|
|
186
186
|
false
|
187
187
|
end
|
@@ -13,6 +13,20 @@ module RuboCop
|
|
13
13
|
# The default variable name is `block`. If the name is already in use, it will not be
|
14
14
|
# autocorrected.
|
15
15
|
#
|
16
|
+
# [NOTE]
|
17
|
+
# --
|
18
|
+
# Because of a bug in Ruby 3.3.0, when a block is referenced inside of another block,
|
19
|
+
# no offense will be registered until Ruby 3.4:
|
20
|
+
|
21
|
+
# [source,ruby]
|
22
|
+
# ----
|
23
|
+
# def foo(&block)
|
24
|
+
# # Using an anonymous block would be a syntax error on Ruby 3.3.0
|
25
|
+
# block_method { bar(&block) }
|
26
|
+
# end
|
27
|
+
# ----
|
28
|
+
# --
|
29
|
+
#
|
16
30
|
# @example EnforcedStyle: anonymous (default)
|
17
31
|
#
|
18
32
|
# # bad
|
@@ -90,21 +104,11 @@ module RuboCop
|
|
90
104
|
last_argument.source == block_pass_node.source
|
91
105
|
end
|
92
106
|
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
# def foo(&)
|
97
|
-
# block_method do
|
98
|
-
# bar(&)
|
99
|
-
# end
|
100
|
-
# end
|
101
|
-
#
|
102
|
-
# $ ruby -vc foo.rb
|
103
|
-
# ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22]
|
104
|
-
# foo.rb: foo.rb:4: anonymous block parameter is also used within block (SyntaxError)
|
105
|
-
#
|
107
|
+
# Ruby 3.3.0 had a bug where accessing an anonymous block argument inside of a block
|
108
|
+
# was a syntax error in unambiguous cases: https://bugs.ruby-lang.org/issues/20090
|
109
|
+
# We disallow this also for earlier Ruby versions so that code is forwards compatible.
|
106
110
|
def invalidates_syntax?(block_pass_node)
|
107
|
-
block_pass_node.each_ancestor(:
|
111
|
+
target_ruby_version <= 3.3 && block_pass_node.each_ancestor(:any_block).any?
|
108
112
|
end
|
109
113
|
|
110
114
|
def use_kwarg_in_method_definition?(node)
|
@@ -113,7 +113,7 @@ module RuboCop
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
-
# rubocop:disable Metrics/
|
116
|
+
# rubocop:disable Metrics/MethodLength
|
117
117
|
def correct_node(corrector, node, offending_name, preferred_name)
|
118
118
|
return unless node
|
119
119
|
|
@@ -129,13 +129,13 @@ module RuboCop
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
-
if child_node.
|
132
|
+
if child_node.type?(:masgn, :lvasgn)
|
133
133
|
correct_reassignment(corrector, child_node, offending_name, preferred_name)
|
134
134
|
break
|
135
135
|
end
|
136
136
|
end
|
137
137
|
end
|
138
|
-
# rubocop:enable Metrics/
|
138
|
+
# rubocop:enable Metrics/MethodLength
|
139
139
|
|
140
140
|
# If the exception variable is reassigned, that assignment needs to be corrected.
|
141
141
|
# Further `lvar` nodes will not be corrected though since they now refer to a
|