rubocop 1.79.2 → 1.88.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/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +259 -90
- data/config/obsoletion.yml +30 -1
- data/exe/rubocop +1 -8
- data/lib/rubocop/cache_config.rb +29 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +36 -4
- data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
- data/lib/rubocop/cli/command/lsp.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 +4 -8
- data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
- data/lib/rubocop/cli.rb +35 -9
- data/lib/rubocop/comment_config.rb +59 -17
- data/lib/rubocop/config.rb +14 -10
- data/lib/rubocop/config_finder.rb +1 -1
- data/lib/rubocop/config_loader.rb +37 -23
- data/lib/rubocop/config_loader_resolver.rb +20 -10
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
- data/lib/rubocop/config_store.rb +7 -2
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
- data/lib/rubocop/cop/base.rb +25 -4
- data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
- data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +26 -7
- data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
- data/lib/rubocop/cop/correctors.rb +28 -0
- data/lib/rubocop/cop/documentation.rb +2 -3
- data/lib/rubocop/cop/exclude_limit.rb +31 -5
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +5 -5
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +12 -7
- data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
- data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
- data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
- 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/begin_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +41 -4
- data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
- data/lib/rubocop/cop/layout/class_structure.rb +14 -7
- data/lib/rubocop/cop/layout/dot_position.rb +2 -2
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +26 -7
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +31 -13
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +2 -2
- 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 +10 -3
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +34 -1
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
- data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
- data/lib/rubocop/cop/layout/hash_alignment.rb +3 -6
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +33 -3
- data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +123 -7
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +26 -9
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +229 -39
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
- data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
- data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +4 -2
- data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +45 -3
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +4 -3
- data/lib/rubocop/cop/lint/constant_reassignment.rb +93 -11
- data/lib/rubocop/cop/lint/constant_resolution.rb +6 -6
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
- data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
- data/lib/rubocop/cop/lint/debugger.rb +0 -3
- data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
- data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
- data/lib/rubocop/cop/lint/duplicate_methods.rb +111 -12
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/else_layout.rb +19 -0
- data/lib/rubocop/cop/lint/empty_block.rb +4 -4
- 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_interpolation.rb +11 -0
- data/lib/rubocop/cop/lint/empty_when.rb +8 -1
- data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
- data/lib/rubocop/cop/lint/float_comparison.rb +2 -1
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +25 -5
- data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -1
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +9 -12
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +19 -10
- data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +20 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +19 -10
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
- data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
- data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
- data/lib/rubocop/cop/lint/rand_one.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +27 -10
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +6 -12
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +36 -12
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +12 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +10 -3
- data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
- data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
- data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
- data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
- data/lib/rubocop/cop/lint/require_relative_self_path.rb +7 -5
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +18 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
- data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
- data/lib/rubocop/cop/lint/script_permission.rb +5 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +39 -7
- data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
- data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
- data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
- data/lib/rubocop/cop/lint/syntax.rb +25 -1
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +28 -1
- data/lib/rubocop/cop/lint/to_json.rb +12 -16
- data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +5 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +4 -2
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +7 -5
- 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/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +53 -25
- 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/useless_or.rb +15 -2
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +8 -4
- data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
- data/lib/rubocop/cop/lint/useless_times.rb +22 -1
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +37 -11
- data/lib/rubocop/cop/lint/void.rb +39 -12
- data/lib/rubocop/cop/message_annotator.rb +1 -1
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
- data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
- data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
- data/lib/rubocop/cop/migration/department_name.rb +12 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +4 -6
- data/lib/rubocop/cop/mixin/code_length.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +5 -5
- 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/mixin/line_length_help.rb +21 -2
- data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
- data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
- data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
- data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
- data/lib/rubocop/cop/mixin.rb +86 -0
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +5 -3
- data/lib/rubocop/cop/naming/predicate_method.rb +32 -8
- data/lib/rubocop/cop/naming/predicate_prefix.rb +12 -12
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +17 -1
- data/lib/rubocop/cop/registry.rb +62 -38
- data/lib/rubocop/cop/security/eval.rb +15 -2
- data/lib/rubocop/cop/security/io_methods.rb +1 -1
- data/lib/rubocop/cop/security/json_load.rb +33 -11
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +15 -4
- data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
- data/lib/rubocop/cop/style/alias.rb +15 -3
- data/lib/rubocop/cop/style/and_or.rb +2 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
- data/lib/rubocop/cop/style/array_first_last.rb +12 -1
- data/lib/rubocop/cop/style/array_intersect.rb +50 -12
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +50 -0
- 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 +4 -3
- data/lib/rubocop/cop/style/begin_block.rb +3 -1
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +39 -32
- data/lib/rubocop/cop/style/case_equality.rb +29 -15
- data/lib/rubocop/cop/style/character_literal.rb +2 -2
- data/lib/rubocop/cop/style/class_and_module_children.rb +19 -2
- data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
- data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
- data/lib/rubocop/cop/style/collection_compact.rb +36 -16
- data/lib/rubocop/cop/style/colon_method_call.rb +16 -7
- data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
- data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +7 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +14 -19
- data/lib/rubocop/cop/style/constant_visibility.rb +20 -12
- data/lib/rubocop/cop/style/copyright.rb +22 -11
- data/lib/rubocop/cop/style/date_time.rb +4 -4
- data/lib/rubocop/cop/style/dig_chain.rb +5 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
- data/lib/rubocop/cop/style/documentation.rb +6 -6
- data/lib/rubocop/cop/style/documentation_method.rb +8 -8
- data/lib/rubocop/cop/style/double_negation.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 +119 -0
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +0 -6
- 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 +23 -5
- 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/file_open.rb +84 -0
- data/lib/rubocop/cop/style/file_write.rb +21 -16
- data/lib/rubocop/cop/style/float_division.rb +15 -1
- data/lib/rubocop/cop/style/for.rb +3 -0
- data/lib/rubocop/cop/style/format_string.rb +4 -3
- data/lib/rubocop/cop/style/format_string_token.rb +49 -5
- data/lib/rubocop/cop/style/global_vars.rb +5 -2
- data/lib/rubocop/cop/style/guard_clause.rb +27 -22
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +27 -9
- data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
- data/lib/rubocop/cop/style/hash_lookup_method.rb +106 -0
- data/lib/rubocop/cop/style/hash_slice.rb +16 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- 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 +16 -7
- data/lib/rubocop/cop/style/if_unless_modifier.rb +58 -18
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +4 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- 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/lambda_call.rb +8 -8
- data/lib/rubocop/cop/style/magic_comment_format.rb +3 -3
- data/lib/rubocop/cop/style/map_join.rb +123 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
- data/lib/rubocop/cop/style/method_def_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
- data/lib/rubocop/cop/style/module_member_existence_check.rb +110 -0
- data/lib/rubocop/cop/style/multiline_if_then.rb +4 -4
- data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
- data/lib/rubocop/cop/style/mutable_constant.rb +106 -12
- data/lib/rubocop/cop/style/negative_array_index.rb +220 -0
- data/lib/rubocop/cop/style/nil_comparison.rb +11 -10
- 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 +21 -12
- data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +14 -3
- 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/preferred_hash_methods.rb +12 -12
- 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 +200 -0
- data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
- data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
- data/lib/rubocop/cop/style/redundant_begin.rb +37 -3
- data/lib/rubocop/cop/style/redundant_condition.rb +6 -3
- data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
- data/lib/rubocop/cop/style/redundant_each.rb +3 -3
- data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +27 -5
- data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
- 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 +36 -30
- data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
- data/lib/rubocop/cop/style/redundant_return.rb +3 -1
- data/lib/rubocop/cop/style/redundant_self.rb +2 -2
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
- data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
- data/lib/rubocop/cop/style/regexp_literal.rb +31 -2
- data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
- data/lib/rubocop/cop/style/reverse_find.rb +51 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +25 -8
- 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/self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +41 -8
- 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/sole_nested_conditional.rb +12 -3
- data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
- data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
- data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
- data/lib/rubocop/cop/style/super_arguments.rb +2 -2
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
- data/lib/rubocop/cop/style/tally_method.rb +181 -0
- data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -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/trailing_underscore_variable.rb +11 -11
- data/lib/rubocop/cop/style/unless_else.rb +10 -9
- data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
- data/lib/rubocop/cop/style/while_until_do.rb +7 -0
- data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
- data/lib/rubocop/cop/style/word_array.rb +1 -0
- data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
- data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
- data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
- data/lib/rubocop/cop/team.rb +87 -36
- data/lib/rubocop/cop/util.rb +2 -3
- data/lib/rubocop/cop/utils/format_string.rb +10 -0
- data/lib/rubocop/cop/variable_force/branch.rb +30 -6
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +9 -7
- data/lib/rubocop/cops_documentation_generator.rb +4 -4
- data/lib/rubocop/directive_comment.rb +48 -4
- data/lib/rubocop/file_patterns.rb +9 -1
- data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +38 -14
- data/lib/rubocop/formatter/formatter_set.rb +2 -2
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
- data/lib/rubocop/formatter/tap_formatter.rb +5 -2
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/formatter.rb +22 -21
- data/lib/rubocop/lsp/diagnostic.rb +18 -33
- data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
- data/lib/rubocop/lsp/routes.rb +43 -7
- data/lib/rubocop/lsp/runtime.rb +13 -4
- data/lib/rubocop/lsp/stdin_runner.rb +8 -17
- data/lib/rubocop/magic_comment.rb +20 -0
- data/lib/rubocop/mcp/server.rb +200 -0
- data/lib/rubocop/options.rb +35 -4
- data/lib/rubocop/path_util.rb +14 -2
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/project_index_loader.rb +66 -0
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +10 -8
- data/lib/rubocop/result_cache.rb +61 -38
- data/lib/rubocop/rspec/cop_helper.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +39 -5
- data/lib/rubocop/rspec/support.rb +2 -1
- data/lib/rubocop/runner.rb +134 -57
- data/lib/rubocop/server/cache.rb +6 -29
- data/lib/rubocop/server/core.rb +8 -0
- data/lib/rubocop/target_finder.rb +17 -10
- data/lib/rubocop/target_ruby.rb +31 -14
- data/lib/rubocop/version.rb +21 -3
- data/lib/rubocop.rb +28 -96
- data/lib/ruby_lsp/rubocop/addon.rb +23 -8
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +38 -9
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for usage of `Module` methods returning arrays that can be replaced
|
|
7
|
+
# with equivalent predicates.
|
|
8
|
+
#
|
|
9
|
+
# Calling a method returning an array then checking if an element is inside
|
|
10
|
+
# it is much slower than using an equivalent predicate method. For example,
|
|
11
|
+
# `instance_methods.include?` will return an array of all public and protected
|
|
12
|
+
# instance methods in the module, then check if a given method is inside that
|
|
13
|
+
# array, while `method_defined?` will do direct method lookup, which is much
|
|
14
|
+
# faster and consumes less memory.
|
|
15
|
+
#
|
|
16
|
+
# NOTE: `constants.include?` is not handled by this cop because
|
|
17
|
+
# `Module#const_defined?` has different lookup behavior than
|
|
18
|
+
# `Module#constants` - `const_defined?` searches up to `Object`
|
|
19
|
+
# (top-level constants like `String`, `Integer`, etc.) while
|
|
20
|
+
# `constants` does not, which can cause behavior changes after autocorrection.
|
|
21
|
+
#
|
|
22
|
+
# @example
|
|
23
|
+
# # bad
|
|
24
|
+
# Array.instance_methods.include?(:size)
|
|
25
|
+
# Array.instance_methods.member?(:size)
|
|
26
|
+
# Array.instance_methods(true).include?(:size)
|
|
27
|
+
#
|
|
28
|
+
# Array.instance_methods(false).include?(:find)
|
|
29
|
+
#
|
|
30
|
+
# # good
|
|
31
|
+
# Array.method_defined?(:size)
|
|
32
|
+
#
|
|
33
|
+
# Array.method_defined?(:find, false)
|
|
34
|
+
#
|
|
35
|
+
# # bad
|
|
36
|
+
# Array.class_variables.include?(:foo)
|
|
37
|
+
# Array.private_instance_methods.include?(:foo)
|
|
38
|
+
# Array.protected_instance_methods.include?(:foo)
|
|
39
|
+
# Array.public_instance_methods.include?(:foo)
|
|
40
|
+
#
|
|
41
|
+
# # good
|
|
42
|
+
# Array.class_variable_defined?(:foo)
|
|
43
|
+
# Array.private_method_defined?(:foo)
|
|
44
|
+
# Array.protected_method_defined?(:foo)
|
|
45
|
+
# Array.public_method_defined?(:foo)
|
|
46
|
+
#
|
|
47
|
+
class ModuleMemberExistenceCheck < Base
|
|
48
|
+
extend AutoCorrector
|
|
49
|
+
|
|
50
|
+
MSG = 'Use `%<replacement>s` instead.'
|
|
51
|
+
|
|
52
|
+
# @!method module_member_inclusion?(node)
|
|
53
|
+
def_node_matcher :module_member_inclusion?, <<~PATTERN
|
|
54
|
+
(call
|
|
55
|
+
{(call _ %METHODS_WITH_INHERIT_PARAM _?) (call _ %METHODS_WITHOUT_INHERIT_PARAM)}
|
|
56
|
+
{:include? :member?}
|
|
57
|
+
_)
|
|
58
|
+
PATTERN
|
|
59
|
+
|
|
60
|
+
METHOD_REPLACEMENTS = {
|
|
61
|
+
class_variables: :class_variable_defined?,
|
|
62
|
+
instance_methods: :method_defined?,
|
|
63
|
+
private_instance_methods: :private_method_defined?,
|
|
64
|
+
protected_instance_methods: :protected_method_defined?,
|
|
65
|
+
public_instance_methods: :public_method_defined?
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
68
|
+
METHODS_WITHOUT_INHERIT_PARAM = Set[:class_variables].freeze
|
|
69
|
+
METHODS_WITH_INHERIT_PARAM =
|
|
70
|
+
(METHOD_REPLACEMENTS.keys.to_set - METHODS_WITHOUT_INHERIT_PARAM).freeze
|
|
71
|
+
|
|
72
|
+
RESTRICT_ON_SEND = METHOD_REPLACEMENTS.keys.freeze
|
|
73
|
+
|
|
74
|
+
def on_send(node)
|
|
75
|
+
return unless (parent = node.parent)
|
|
76
|
+
return unless module_member_inclusion?(parent)
|
|
77
|
+
return unless simple_method_argument?(node) && simple_method_argument?(parent)
|
|
78
|
+
|
|
79
|
+
offense_range = node.location.selector.join(parent.source_range.end)
|
|
80
|
+
replacement = replacement_for(node, parent)
|
|
81
|
+
|
|
82
|
+
add_offense(offense_range, message: format(MSG, replacement: replacement)) do |corrector|
|
|
83
|
+
corrector.replace(offense_range, replacement)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
alias on_csend on_send
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def replacement_for(node, parent)
|
|
91
|
+
replacement_method = METHOD_REPLACEMENTS.fetch(node.method_name)
|
|
92
|
+
without_inherit_param = METHODS_WITHOUT_INHERIT_PARAM.include?(node.method_name)
|
|
93
|
+
|
|
94
|
+
if without_inherit_param || node.first_argument.nil? || node.first_argument.true_type?
|
|
95
|
+
"#{replacement_method}(#{parent.first_argument.source})"
|
|
96
|
+
else
|
|
97
|
+
"#{replacement_method}(#{parent.first_argument.source}, #{node.first_argument.source})"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def simple_method_argument?(node)
|
|
102
|
+
return false if node.splat_argument? || node.block_argument?
|
|
103
|
+
return false if node.first_argument&.hash_type?
|
|
104
|
+
|
|
105
|
+
true
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for uses of the `then` keyword in multi-line if statements.
|
|
6
|
+
# Checks for uses of the `then` keyword in multi-line `if` statements.
|
|
7
|
+
# In multi-line `if` statements, `then` is redundant because the newline
|
|
8
|
+
# already separates the condition from the body.
|
|
7
9
|
#
|
|
8
10
|
# @example
|
|
9
11
|
# # bad
|
|
@@ -21,8 +23,6 @@ module RuboCop
|
|
|
21
23
|
include RangeHelp
|
|
22
24
|
extend AutoCorrector
|
|
23
25
|
|
|
24
|
-
NON_MODIFIER_THEN = /then\s*(#.*)?$/.freeze
|
|
25
|
-
|
|
26
26
|
MSG = 'Do not use `then` for multi-line `%<keyword>s`.'
|
|
27
27
|
|
|
28
28
|
def on_normal_if_unless(node)
|
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
|
36
36
|
private
|
|
37
37
|
|
|
38
38
|
def non_modifier_then?(node)
|
|
39
|
-
|
|
39
|
+
node.then? && node.loc.begin.line != node.if_branch&.loc&.line
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
end
|
|
@@ -75,6 +75,8 @@ module RuboCop
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def correction_exceeds_max_line_length?(node)
|
|
78
|
+
return false unless max_line_length
|
|
79
|
+
|
|
78
80
|
indentation_width(node) + definition_width(node) > max_line_length
|
|
79
81
|
end
|
|
80
82
|
|
|
@@ -85,10 +87,6 @@ module RuboCop
|
|
|
85
87
|
def definition_width(node)
|
|
86
88
|
node.source_range.begin.join(node.arguments.source_range.end).length
|
|
87
89
|
end
|
|
88
|
-
|
|
89
|
-
def max_line_length
|
|
90
|
-
config.for_cop('Layout/LineLength')['Max'] || 120
|
|
91
|
-
end
|
|
92
90
|
end
|
|
93
91
|
end
|
|
94
92
|
end
|
|
@@ -6,6 +6,14 @@ module RuboCop
|
|
|
6
6
|
# Checks whether some constant value isn't a
|
|
7
7
|
# mutable literal (e.g. array or hash).
|
|
8
8
|
#
|
|
9
|
+
# When the `Recursive` option is enabled, mutable literals nested inside
|
|
10
|
+
# arrays and hashes are also frozen, so an offense on the outermost
|
|
11
|
+
# unfrozen literal will autocorrect every nested mutable literal as well.
|
|
12
|
+
# When the outer literal already has `.freeze` appended, the cop descends
|
|
13
|
+
# into it and reports each outermost unfrozen literal underneath. The
|
|
14
|
+
# option is disabled by default to preserve existing behavior; opt in to
|
|
15
|
+
# get strict nested freezing.
|
|
16
|
+
#
|
|
9
17
|
# Strict mode can be used to freeze all constants, rather than
|
|
10
18
|
# just literals.
|
|
11
19
|
# Strict mode is considered an experimental feature. It has not been
|
|
@@ -49,6 +57,17 @@ module RuboCop
|
|
|
49
57
|
# CONST = Something.new
|
|
50
58
|
#
|
|
51
59
|
#
|
|
60
|
+
# @example Recursive: false (default)
|
|
61
|
+
# # good - only the outer container needs to be frozen
|
|
62
|
+
# CONST = [{ a: [], b: 'foo' }].freeze
|
|
63
|
+
#
|
|
64
|
+
# @example Recursive: true
|
|
65
|
+
# # bad - nested mutable literals must be frozen too
|
|
66
|
+
# CONST = [{ a: [], b: 'foo' }].freeze
|
|
67
|
+
#
|
|
68
|
+
# # good
|
|
69
|
+
# CONST = [{ a: [].freeze, b: 'foo'.freeze }.freeze].freeze
|
|
70
|
+
#
|
|
52
71
|
# @example EnforcedStyle: strict
|
|
53
72
|
# # bad
|
|
54
73
|
# CONST = Something.new
|
|
@@ -125,7 +144,7 @@ module RuboCop
|
|
|
125
144
|
MSG = 'Freeze mutable objects assigned to constants.'
|
|
126
145
|
|
|
127
146
|
def on_casgn(node)
|
|
128
|
-
if node.expression.nil? # This is only the case for `CONST += ...` or
|
|
147
|
+
if node.expression.nil? # This is only the case for `CONST += ...` or similar
|
|
129
148
|
parent = node.parent
|
|
130
149
|
return unless parent.or_asgn_type? # We only care about `CONST ||= ...`
|
|
131
150
|
|
|
@@ -138,10 +157,30 @@ module RuboCop
|
|
|
138
157
|
private
|
|
139
158
|
|
|
140
159
|
def on_assignment(value)
|
|
141
|
-
|
|
142
|
-
|
|
160
|
+
nodes = mutable_nodes(value) do |node|
|
|
161
|
+
if style == :strict
|
|
162
|
+
strict_check(node)
|
|
163
|
+
else
|
|
164
|
+
literal_check(node)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
nodes.each do |node|
|
|
169
|
+
add_offense(node) { |corrector| autocorrect(corrector, node) }
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def mutable_nodes(value, &block)
|
|
174
|
+
if recursive? && explicitly_frozen_literal?(value)
|
|
175
|
+
literal_children(value.receiver).flat_map { |c| mutable_nodes(c, &block) }
|
|
143
176
|
else
|
|
144
|
-
|
|
177
|
+
node_offending = yield(value)
|
|
178
|
+
|
|
179
|
+
if node_offending
|
|
180
|
+
[value]
|
|
181
|
+
else
|
|
182
|
+
[]
|
|
183
|
+
end
|
|
145
184
|
end
|
|
146
185
|
end
|
|
147
186
|
|
|
@@ -151,18 +190,20 @@ module RuboCop
|
|
|
151
190
|
return if frozen_string_literal?(value)
|
|
152
191
|
return if shareable_constant_value?(value)
|
|
153
192
|
|
|
154
|
-
|
|
193
|
+
true
|
|
155
194
|
end
|
|
156
195
|
|
|
157
|
-
def
|
|
158
|
-
|
|
159
|
-
return unless mutable_literal?(value) ||
|
|
160
|
-
(target_ruby_version <= 2.7 && range_enclosed_in_parentheses)
|
|
161
|
-
|
|
196
|
+
def literal_check(value)
|
|
197
|
+
return unless mutable_or_unfrozen_range?(value)
|
|
162
198
|
return if frozen_string_literal?(value)
|
|
163
199
|
return if shareable_constant_value?(value)
|
|
164
200
|
|
|
165
|
-
|
|
201
|
+
true
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def mutable_or_unfrozen_range?(value)
|
|
205
|
+
mutable_literal?(value) ||
|
|
206
|
+
(target_ruby_version <= 2.7 && range_enclosed_in_parentheses?(value))
|
|
166
207
|
end
|
|
167
208
|
|
|
168
209
|
def autocorrect(corrector, node)
|
|
@@ -171,13 +212,66 @@ module RuboCop
|
|
|
171
212
|
splat_value = splat_value(node)
|
|
172
213
|
if splat_value
|
|
173
214
|
correct_splat_expansion(corrector, expr, splat_value)
|
|
174
|
-
|
|
215
|
+
corrector.insert_after(expr, '.freeze')
|
|
216
|
+
return
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
if node.array_type? && !node.bracketed?
|
|
175
220
|
corrector.wrap(expr, '[', ']')
|
|
176
221
|
elsif requires_parentheses?(node)
|
|
177
222
|
corrector.wrap(expr, '(', ')')
|
|
178
223
|
end
|
|
179
224
|
|
|
180
225
|
corrector.insert_after(expr, '.freeze')
|
|
226
|
+
|
|
227
|
+
freeze_nested_literals(corrector, node) if recursive?
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Recursively freezes every nested mutable literal inside an array or
|
|
231
|
+
# hash literal. Already-frozen subtrees are not re-frozen, but their
|
|
232
|
+
# children are still inspected for unfrozen literals deeper down.
|
|
233
|
+
def freeze_nested_literals(corrector, node)
|
|
234
|
+
literal_children(node).each do |child|
|
|
235
|
+
if explicitly_frozen_literal?(child)
|
|
236
|
+
freeze_nested_literals(corrector, child.receiver)
|
|
237
|
+
elsif freezable_nested_literal?(child)
|
|
238
|
+
autocorrect(corrector, child)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def freezable_nested_literal?(node)
|
|
244
|
+
return false if frozen_string_literal?(node)
|
|
245
|
+
return false if shareable_constant_value?(node)
|
|
246
|
+
|
|
247
|
+
mutable_literal?(node)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Returns the child literals of an array or hash node that may
|
|
251
|
+
# themselves need freezing. For hashes, both keys and values are
|
|
252
|
+
# included. Percent-literal arrays (e.g. `%w(a b)`) are skipped because
|
|
253
|
+
# `.freeze` cannot be appended to their contents.
|
|
254
|
+
def literal_children(node)
|
|
255
|
+
case node.type
|
|
256
|
+
when :array
|
|
257
|
+
return [] if node.percent_literal?
|
|
258
|
+
|
|
259
|
+
node.children
|
|
260
|
+
when :hash
|
|
261
|
+
node.children.flat_map { |child| child.pair_type? ? child.children : [] }
|
|
262
|
+
else
|
|
263
|
+
[]
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def explicitly_frozen_literal?(node)
|
|
268
|
+
return false unless node.send_type? && node.method?(:freeze)
|
|
269
|
+
|
|
270
|
+
node.receiver && mutable_literal?(node.receiver)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def recursive?
|
|
274
|
+
cop_config.fetch('Recursive', false)
|
|
181
275
|
end
|
|
182
276
|
|
|
183
277
|
def mutable_literal?(value)
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Identifies usages of `arr[arr.length - n]`, `arr[arr.size - n]`, or
|
|
7
|
+
# `arr[arr.count - n]` and suggests to change them to use `arr[-n]` instead.
|
|
8
|
+
# Also handles range patterns like `arr[0..(arr.length - n)]`.
|
|
9
|
+
#
|
|
10
|
+
# The cop recognizes preserving methods (`sort`, `reverse`, `shuffle`, `rotate`)
|
|
11
|
+
# and their combinations, allowing safe replacement when the receiver matches.
|
|
12
|
+
# It works with variables, instance variables, class variables, and constants.
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# # bad
|
|
16
|
+
# arr[arr.count - 2]
|
|
17
|
+
# arr[0..(arr.length - 2)]
|
|
18
|
+
# arr[0...(arr.length - 4)]
|
|
19
|
+
# arr.sort[arr.reverse.length - 2]
|
|
20
|
+
# arr.sort.reverse[arr.sort.size - 2]
|
|
21
|
+
#
|
|
22
|
+
# # good
|
|
23
|
+
# arr[-2]
|
|
24
|
+
# arr[0..-2]
|
|
25
|
+
# arr[0...-4]
|
|
26
|
+
# arr.sort[-2]
|
|
27
|
+
# arr.sort.reverse[-2]
|
|
28
|
+
#
|
|
29
|
+
class NegativeArrayIndex < Base
|
|
30
|
+
extend AutoCorrector
|
|
31
|
+
include RangeHelp
|
|
32
|
+
|
|
33
|
+
MSG = 'Use `%<receiver>s[-%<index>s]` instead of `%<current>s`.'
|
|
34
|
+
MSG_RANGE = 'Use `%<receiver>s[%<start>s%<range_op>s-%<index>s]` instead of `%<current>s`.'
|
|
35
|
+
RESTRICT_ON_SEND = %i[[]].freeze
|
|
36
|
+
|
|
37
|
+
LENGTH_METHODS = %i[length size count].freeze
|
|
38
|
+
|
|
39
|
+
PRESERVING_METHODS = %i[sort reverse shuffle rotate].freeze
|
|
40
|
+
|
|
41
|
+
# @!method length_subtraction?(node)
|
|
42
|
+
def_node_matcher :length_subtraction?, <<~PATTERN
|
|
43
|
+
(send
|
|
44
|
+
(send $_ {:length :size :count}) :-
|
|
45
|
+
(int $_))
|
|
46
|
+
PATTERN
|
|
47
|
+
|
|
48
|
+
def on_send(node)
|
|
49
|
+
return if node.arguments.empty?
|
|
50
|
+
|
|
51
|
+
index_arg = node.first_argument
|
|
52
|
+
range_node = extract_range_from_begin(index_arg)
|
|
53
|
+
if range_with_length_subtraction?(range_node, node.receiver)
|
|
54
|
+
receiver = node.receiver.source
|
|
55
|
+
return handle_range_pattern(receiver, range_node, index_arg)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
handle_simple_index_pattern(node, index_arg)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
alias on_csend on_send
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def handle_simple_index_pattern(node, index_arg)
|
|
66
|
+
length_receiver, negative_index = length_subtraction?(index_arg)
|
|
67
|
+
|
|
68
|
+
return unless negative_index&.positive?
|
|
69
|
+
return unless receivers_match?(length_receiver, node.receiver)
|
|
70
|
+
|
|
71
|
+
add_offense_for_subtraction(node, index_arg, negative_index)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def extract_range_from_begin(node)
|
|
75
|
+
node.begin_type? ? node.children.first : node
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def extract_inner_end(node)
|
|
79
|
+
node.children.size == 1 ? node.children.first : node
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def add_offense_for_subtraction(node, index_arg, negative_index)
|
|
83
|
+
receiver = node.receiver.source
|
|
84
|
+
offense_range = index_arg.source_range
|
|
85
|
+
current = "#{receiver}[#{index_arg.source}]"
|
|
86
|
+
|
|
87
|
+
message = format(MSG, receiver: receiver, index: negative_index, current: current)
|
|
88
|
+
|
|
89
|
+
add_offense(offense_range, message: message) do |corrector|
|
|
90
|
+
corrector.replace(offense_range, "-#{negative_index}")
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def range_with_length_subtraction?(range_node, array_receiver)
|
|
95
|
+
return false unless range_node.range_type?
|
|
96
|
+
|
|
97
|
+
range_end = range_node.end
|
|
98
|
+
range_start = range_node.begin
|
|
99
|
+
return false unless range_end && range_start
|
|
100
|
+
|
|
101
|
+
return false unless preserving_method?(range_start)
|
|
102
|
+
|
|
103
|
+
inner_end = extract_inner_end(range_end)
|
|
104
|
+
length_receiver, negative_index = length_subtraction?(inner_end)
|
|
105
|
+
|
|
106
|
+
return false unless negative_index&.positive?
|
|
107
|
+
|
|
108
|
+
receivers_match_strict?(length_receiver, array_receiver)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def handle_range_pattern(receiver, range_node, index_arg)
|
|
112
|
+
range_end = range_node.end
|
|
113
|
+
inner_end = extract_inner_end(range_end)
|
|
114
|
+
_length_receiver, negative_index = length_subtraction?(inner_end)
|
|
115
|
+
|
|
116
|
+
message, replacement = build_range_offense_data(
|
|
117
|
+
receiver, range_node, range_end, inner_end, negative_index, index_arg
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
add_offense(range_end, message: message) do |corrector|
|
|
121
|
+
corrector.replace(index_arg, replacement)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# rubocop:disable Metrics/ParameterLists
|
|
126
|
+
def build_range_offense_data(receiver, range_node, range_end, inner_end, negative_index,
|
|
127
|
+
index_arg)
|
|
128
|
+
range_op = range_node.erange_type? ? '...' : '..'
|
|
129
|
+
range_start = range_node.begin.source
|
|
130
|
+
|
|
131
|
+
range_without_parens =
|
|
132
|
+
build_range_without_parens(range_start, range_op, range_end, inner_end)
|
|
133
|
+
current_source = build_current_source(receiver, range_without_parens, index_arg)
|
|
134
|
+
start, index = format_range_message_parts(range_start, negative_index, index_arg)
|
|
135
|
+
|
|
136
|
+
message = build_message_for_range(receiver, start, range_op, index, current_source)
|
|
137
|
+
replacement = build_replacement_string(range_start, range_op, negative_index, index_arg)
|
|
138
|
+
|
|
139
|
+
[message, replacement]
|
|
140
|
+
end
|
|
141
|
+
# rubocop:enable Metrics/ParameterLists
|
|
142
|
+
|
|
143
|
+
def format_range_message_parts(range_start, negative_index, index_arg)
|
|
144
|
+
has_parentheses = index_arg.begin_type?
|
|
145
|
+
start = has_parentheses ? "(#{range_start}" : range_start
|
|
146
|
+
index = has_parentheses ? "#{negative_index})" : negative_index
|
|
147
|
+
|
|
148
|
+
[start, index]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def build_message_for_range(receiver, start, range_op, index, current)
|
|
152
|
+
format(
|
|
153
|
+
MSG_RANGE,
|
|
154
|
+
receiver: receiver, start: start, range_op: range_op, index: index, current: current
|
|
155
|
+
)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def build_replacement_string(range_start, range_op, negative_index, index_arg)
|
|
159
|
+
has_parentheses = index_arg.begin_type?
|
|
160
|
+
|
|
161
|
+
if has_parentheses
|
|
162
|
+
"(#{range_start}#{range_op}-#{negative_index})"
|
|
163
|
+
else
|
|
164
|
+
"#{range_start}#{range_op}-#{negative_index}"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def build_current_source(receiver, range_without_parens, index_arg)
|
|
169
|
+
has_parentheses = index_arg.begin_type?
|
|
170
|
+
|
|
171
|
+
if has_parentheses
|
|
172
|
+
"#{receiver}[(#{range_without_parens})]"
|
|
173
|
+
else
|
|
174
|
+
"#{receiver}[#{range_without_parens}]"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def build_range_without_parens(range_start, range_op, range_end, inner_end)
|
|
179
|
+
end_expression = range_end.begin_type? ? range_end.source : inner_end.source
|
|
180
|
+
|
|
181
|
+
"#{range_start}#{range_op}#{end_expression}"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def receivers_match?(length_receiver, array_receiver)
|
|
185
|
+
return array_receiver.self_type? unless length_receiver
|
|
186
|
+
|
|
187
|
+
unless preserving_method?(array_receiver) && preserving_method?(length_receiver)
|
|
188
|
+
return false
|
|
189
|
+
end
|
|
190
|
+
return true if length_receiver.source == array_receiver.source
|
|
191
|
+
|
|
192
|
+
!extract_base_receiver(array_receiver).nil?
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def receivers_match_strict?(length_receiver, array_receiver)
|
|
196
|
+
preserving_method?(array_receiver) &&
|
|
197
|
+
length_receiver.source == array_receiver.source
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def extract_base_receiver(node)
|
|
201
|
+
receiver = node.receiver
|
|
202
|
+
|
|
203
|
+
return nil unless receiver
|
|
204
|
+
return receiver unless receiver.receiver
|
|
205
|
+
|
|
206
|
+
extract_base_receiver(receiver)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def preserving_method?(node)
|
|
210
|
+
return true if node.receiver.nil?
|
|
211
|
+
|
|
212
|
+
method_name = node.method_name
|
|
213
|
+
return false unless PRESERVING_METHODS.include?(method_name)
|
|
214
|
+
|
|
215
|
+
preserving_method?(node.receiver)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
@@ -4,9 +4,8 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for comparison of something with nil using `==` and
|
|
7
|
-
# `nil?`.
|
|
8
|
-
#
|
|
9
|
-
# Supported styles are: predicate, comparison.
|
|
7
|
+
# `nil?`. Enforcing a consistent style (either the `nil?`
|
|
8
|
+
# predicate or `==` comparison) improves readability.
|
|
10
9
|
#
|
|
11
10
|
# @example EnforcedStyle: predicate (default)
|
|
12
11
|
#
|
|
@@ -43,24 +42,26 @@ module RuboCop
|
|
|
43
42
|
# @!method nil_check?(node)
|
|
44
43
|
def_node_matcher :nil_check?, '(send _ :nil?)'
|
|
45
44
|
|
|
45
|
+
# rubocop:disable Metrics/AbcSize
|
|
46
46
|
def on_send(node)
|
|
47
47
|
return unless node.receiver
|
|
48
48
|
|
|
49
49
|
style_check?(node) do
|
|
50
50
|
add_offense(node.loc.selector) do |corrector|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
if prefer_comparison?
|
|
52
|
+
range = node.loc.dot.join(node.loc.selector.end)
|
|
53
|
+
corrector.replace(range, ' == nil')
|
|
54
|
+
else
|
|
55
|
+
range = node.receiver.source_range.end.join(node.source_range.end)
|
|
56
|
+
corrector.replace(range, '.nil?')
|
|
57
|
+
end
|
|
58
58
|
|
|
59
59
|
parent = node.parent
|
|
60
60
|
corrector.wrap(node, '(', ')') if parent.respond_to?(:method?) && parent.method?(:!)
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
|
+
# rubocop:enable Metrics/AbcSize
|
|
64
65
|
|
|
65
66
|
private
|
|
66
67
|
|
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
|
43
43
|
{ ({return next break} nil) (nil) }
|
|
44
44
|
PATTERN
|
|
45
45
|
|
|
46
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
46
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
47
47
|
return unless node.lambda_or_proc?
|
|
48
48
|
return unless nil_return?(node.body)
|
|
49
49
|
|
|
@@ -121,17 +121,11 @@ module RuboCop
|
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def autocorrect_comparison(corrector, node)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
expr.sub(/^(\S*)\s*!=\s*nil/, '!\1.nil?')
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
return if expr == new_code
|
|
133
|
-
|
|
134
|
-
corrector.replace(node, new_code)
|
|
124
|
+
if include_semantic_changes?
|
|
125
|
+
corrector.replace(node, node.receiver.source)
|
|
126
|
+
else
|
|
127
|
+
corrector.replace(node, "!#{node.receiver.source}.nil?")
|
|
128
|
+
end
|
|
135
129
|
end
|
|
136
130
|
|
|
137
131
|
def autocorrect_non_nil(corrector, node, inner_node)
|
|
@@ -4,7 +4,8 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for big numeric literals without `_` between groups
|
|
7
|
-
# of digits in them.
|
|
7
|
+
# of digits in them. Underscores make large numbers easier to
|
|
8
|
+
# read by visually separating groups of digits.
|
|
8
9
|
#
|
|
9
10
|
# Additional allowed patterns can be added by adding regexps to
|
|
10
11
|
# the `AllowedPatterns` configuration. All regexps are treated
|
|
@@ -116,7 +117,7 @@ module RuboCop
|
|
|
116
117
|
end
|
|
117
118
|
|
|
118
119
|
def allowed_numbers
|
|
119
|
-
cop_config.fetch('AllowedNumbers', []).map(&:to_s)
|
|
120
|
+
@allowed_numbers ||= cop_config.fetch('AllowedNumbers', []).map(&:to_s).freeze
|
|
120
121
|
end
|
|
121
122
|
|
|
122
123
|
def allowed_patterns
|