rubocop 1.80.2 → 1.86.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +170 -19
- data/config/obsoletion.yml +9 -0
- data/lib/rubocop/cache_config.rb +29 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +3 -3
- 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 +1 -1
- data/lib/rubocop/cli.rb +28 -6
- data/lib/rubocop/comment_config.rb +62 -17
- data/lib/rubocop/config.rb +14 -10
- data/lib/rubocop/config_finder.rb +1 -1
- data/lib/rubocop/config_loader.rb +20 -21
- data/lib/rubocop/config_loader_resolver.rb +9 -7
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
- data/lib/rubocop/config_store.rb +6 -1
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -4
- 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 +22 -6
- data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
- data/lib/rubocop/cop/documentation.rb +2 -3
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
- 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/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/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/case_indentation.rb +3 -1
- data/lib/rubocop/cop/layout/class_structure.rb +13 -6
- data/lib/rubocop/cop/layout/dot_position.rb +2 -2
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +12 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +31 -13
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.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 +8 -1
- 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 +111 -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_indentation.rb +204 -39
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +6 -4
- 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/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_in_lambda_literal.rb +9 -8
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
- 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/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 -2
- 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/else_layout.rb +19 -0
- 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_interpolation.rb +11 -0
- data/lib/rubocop/cop/lint/empty_when.rb +8 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +16 -6
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -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_disable_directive.rb +23 -9
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +0 -9
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -2
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
- 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/self_assignment.rb +10 -2
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
- data/lib/rubocop/cop/lint/syntax.rb +25 -1
- data/lib/rubocop/cop/lint/to_json.rb +12 -16
- 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_code.rb +5 -3
- data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +45 -17
- 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/utils/nil_receiver_checker.rb +24 -9
- data/lib/rubocop/cop/lint/void.rb +39 -12
- data/lib/rubocop/cop/message_annotator.rb +1 -1
- data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
- 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/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/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/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +4 -2
- data/lib/rubocop/cop/naming/predicate_method.rb +27 -4
- data/lib/rubocop/cop/naming/predicate_prefix.rb +11 -11
- data/lib/rubocop/cop/offense.rb +9 -1
- data/lib/rubocop/cop/registry.rb +20 -13
- data/lib/rubocop/cop/security/eval.rb +15 -2
- 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 +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_intersect.rb +2 -2
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -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/block_delimiters.rb +27 -34
- data/lib/rubocop/cop/style/case_equality.rb +15 -13
- data/lib/rubocop/cop/style/class_and_module_children.rb +11 -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 +8 -18
- data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
- data/lib/rubocop/cop/style/copyright.rb +1 -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/file_open.rb +84 -0
- 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_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_lookup_method.rb +101 -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 +1 -5
- data/lib/rubocop/cop/style/if_unless_modifier.rb +57 -17
- 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/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 +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 +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/module_member_existence_check.rb +107 -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 +1 -1
- 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 +6 -2
- 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 +184 -0
- data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
- data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
- 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 +26 -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 +26 -22
- 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_escape.rb +8 -0
- 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_sort.rb +7 -7
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
- data/lib/rubocop/cop/style/reverse_find.rb +51 -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 +25 -7
- 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 +8 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
- data/lib/rubocop/cop/style/super_arguments.rb +2 -2
- 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_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/yoda_expression.rb +1 -1
- data/lib/rubocop/cop/team.rb +4 -4
- 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/cops_documentation_generator.rb +4 -4
- data/lib/rubocop/directive_comment.rb +48 -4
- data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
- 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 +12 -5
- data/lib/rubocop/lsp/runtime.rb +13 -3
- 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 +10 -1
- data/lib/rubocop/path_util.rb +14 -2
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +10 -8
- data/lib/rubocop/result_cache.rb +60 -37
- data/lib/rubocop/rspec/cop_helper.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +18 -5
- data/lib/rubocop/rspec/support.rb +2 -1
- data/lib/rubocop/runner.rb +12 -3
- data/lib/rubocop/server/cache.rb +6 -29
- data/lib/rubocop/server/core.rb +2 -0
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +31 -14
- data/lib/rubocop/version.rb +2 -2
- data/lib/rubocop.rb +20 -0
- data/lib/ruby_lsp/rubocop/addon.rb +23 -8
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +33 -9
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Identifies places where `max_by { ... }`, `min_by { ... }`, or
|
|
7
|
+
# `minmax_by { ... }` can be replaced by `max`, `min`, or `minmax`.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# # bad
|
|
11
|
+
# array.max_by { |x| x }
|
|
12
|
+
# array.min_by { |x| x }
|
|
13
|
+
# array.minmax_by { |x| x }
|
|
14
|
+
#
|
|
15
|
+
# # good
|
|
16
|
+
# array.max
|
|
17
|
+
# array.min
|
|
18
|
+
# array.minmax
|
|
19
|
+
class RedundantMinMaxBy < Base
|
|
20
|
+
include RangeHelp
|
|
21
|
+
extend AutoCorrector
|
|
22
|
+
|
|
23
|
+
MSG_BLOCK = 'Use `%<replacement>s` instead of `%<original>s { |%<var>s| %<var>s }`.'
|
|
24
|
+
MSG_NUMBLOCK = 'Use `%<replacement>s` instead of `%<original>s { _1 }`.'
|
|
25
|
+
MSG_ITBLOCK = 'Use `%<replacement>s` instead of `%<original>s { it }`.'
|
|
26
|
+
|
|
27
|
+
REPLACEMENTS = { max_by: 'max', min_by: 'min', minmax_by: 'minmax' }.freeze
|
|
28
|
+
|
|
29
|
+
def on_block(node)
|
|
30
|
+
redundant_minmax_by_block(node) do |send, var_name|
|
|
31
|
+
register_offense(send, node, message_block(send, var_name))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def on_numblock(node)
|
|
36
|
+
redundant_minmax_by_numblock(node) do |send|
|
|
37
|
+
register_offense(send, node, message_numblock(send))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def on_itblock(node)
|
|
42
|
+
redundant_minmax_by_itblock(node) do |send|
|
|
43
|
+
register_offense(send, node, message_itblock(send))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# @!method redundant_minmax_by_block(node)
|
|
50
|
+
def_node_matcher :redundant_minmax_by_block, <<~PATTERN
|
|
51
|
+
(block $(call _ {:max_by :min_by :minmax_by}) (args (arg $_x)) (lvar _x))
|
|
52
|
+
PATTERN
|
|
53
|
+
|
|
54
|
+
# @!method redundant_minmax_by_numblock(node)
|
|
55
|
+
def_node_matcher :redundant_minmax_by_numblock, <<~PATTERN
|
|
56
|
+
(numblock $(call _ {:max_by :min_by :minmax_by}) 1 (lvar :_1))
|
|
57
|
+
PATTERN
|
|
58
|
+
|
|
59
|
+
# @!method redundant_minmax_by_itblock(node)
|
|
60
|
+
def_node_matcher :redundant_minmax_by_itblock, <<~PATTERN
|
|
61
|
+
(itblock $(call _ {:max_by :min_by :minmax_by}) _ (lvar :it))
|
|
62
|
+
PATTERN
|
|
63
|
+
|
|
64
|
+
def register_offense(send, node, message)
|
|
65
|
+
range = offense_range(send, node)
|
|
66
|
+
|
|
67
|
+
add_offense(range, message: message) do |corrector|
|
|
68
|
+
corrector.replace(range, REPLACEMENTS[send.method_name])
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def offense_range(send, node)
|
|
73
|
+
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def message_block(send, var_name)
|
|
77
|
+
method = send.method_name
|
|
78
|
+
format(MSG_BLOCK, replacement: REPLACEMENTS[method], original: method, var: var_name)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def message_numblock(send)
|
|
82
|
+
method = send.method_name
|
|
83
|
+
format(MSG_NUMBLOCK, replacement: REPLACEMENTS[method], original: method)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def message_itblock(send)
|
|
87
|
+
method = send.method_name
|
|
88
|
+
format(MSG_ITBLOCK, replacement: REPLACEMENTS[method], original: method)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -114,7 +114,7 @@ module RuboCop
|
|
|
114
114
|
def first_arg_begins_with_hash_literal?(node)
|
|
115
115
|
# Don't flag `method ({key: value})` or `method ({key: value}.method)`
|
|
116
116
|
hash_literal = method_chain_begins_with_hash_literal(node.children.first)
|
|
117
|
-
if (root_method = node.each_ancestor(:
|
|
117
|
+
if (root_method = node.each_ancestor(:call).to_a.last)
|
|
118
118
|
parenthesized = root_method.parenthesized_call?
|
|
119
119
|
end
|
|
120
120
|
hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
|
|
@@ -139,6 +139,8 @@ module RuboCop
|
|
|
139
139
|
node = begin_node.children.first
|
|
140
140
|
|
|
141
141
|
if (message = find_offense_message(begin_node, node))
|
|
142
|
+
return offense(begin_node, message) if message == 'block body'
|
|
143
|
+
|
|
142
144
|
if node.range_type? && !argument_of_parenthesized_method_call?(begin_node, node)
|
|
143
145
|
begin_node = begin_node.parent
|
|
144
146
|
end
|
|
@@ -155,6 +157,8 @@ module RuboCop
|
|
|
155
157
|
return 'a literal' if node.literal? && disallowed_literal?(begin_node, node)
|
|
156
158
|
return 'a variable' if node.variable?
|
|
157
159
|
return 'a constant' if node.const_type?
|
|
160
|
+
return 'block body' if begin_node.parent&.any_block_type? || body_range?(begin_node, node)
|
|
161
|
+
|
|
158
162
|
if node.assignment? && (begin_node.parent.nil? || begin_node.parent.begin_type?)
|
|
159
163
|
return 'an assignment'
|
|
160
164
|
end
|
|
@@ -202,6 +206,7 @@ module RuboCop
|
|
|
202
206
|
return false unless node.rescue_type?
|
|
203
207
|
return false unless (parent = begin_node.parent)
|
|
204
208
|
return false if parent.if_type? && parent.ternary?
|
|
209
|
+
return false if parent.conditional? && parent.condition == begin_node
|
|
205
210
|
|
|
206
211
|
!parent.type?(:call, :array, :pair)
|
|
207
212
|
end
|
|
@@ -265,6 +270,18 @@ module RuboCop
|
|
|
265
270
|
end
|
|
266
271
|
end
|
|
267
272
|
|
|
273
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
274
|
+
def body_range?(begin_node, node)
|
|
275
|
+
return false if begin_node.chained?
|
|
276
|
+
return false unless node.range_type?
|
|
277
|
+
return false unless (parent = begin_node.parent)
|
|
278
|
+
return false unless parent.begin_type?
|
|
279
|
+
|
|
280
|
+
(node.begin.nil? && begin_node == parent.children.first) ||
|
|
281
|
+
(node.end.nil? && begin_node == parent.children.last)
|
|
282
|
+
end
|
|
283
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
284
|
+
|
|
268
285
|
def disallowed_one_line_pattern_matching?(begin_node, node)
|
|
269
286
|
if (parent = begin_node.parent)
|
|
270
287
|
return false if parent.any_def_type? && parent.endless?
|
|
@@ -307,10 +324,10 @@ module RuboCop
|
|
|
307
324
|
end
|
|
308
325
|
|
|
309
326
|
def singular_parenthesized_parent?(begin_node)
|
|
310
|
-
return true unless begin_node.parent
|
|
311
|
-
return false if
|
|
327
|
+
return true unless (parent = begin_node.parent)
|
|
328
|
+
return false if parent.type?(:splat, :kwsplat)
|
|
312
329
|
|
|
313
|
-
|
|
330
|
+
parent.children.one?
|
|
314
331
|
end
|
|
315
332
|
|
|
316
333
|
def only_begin_arg?(args)
|
|
@@ -318,28 +335,15 @@ module RuboCop
|
|
|
318
335
|
end
|
|
319
336
|
|
|
320
337
|
def first_argument?(node)
|
|
321
|
-
if
|
|
322
|
-
first_super_argument?(node) ||
|
|
323
|
-
first_yield_argument?(node)
|
|
324
|
-
return true
|
|
325
|
-
end
|
|
338
|
+
return true if first_call_argument?(node)
|
|
326
339
|
|
|
327
340
|
node.each_ancestor.any? { |ancestor| first_argument?(ancestor) }
|
|
328
341
|
end
|
|
329
342
|
|
|
330
|
-
# @!method
|
|
331
|
-
def_node_matcher :
|
|
332
|
-
^(
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
# @!method first_super_argument?(node)
|
|
336
|
-
def_node_matcher :first_super_argument?, <<~PATTERN
|
|
337
|
-
^(super equal?(%0) ...)
|
|
338
|
-
PATTERN
|
|
339
|
-
|
|
340
|
-
# @!method first_yield_argument?(node)
|
|
341
|
-
def_node_matcher :first_yield_argument?, <<~PATTERN
|
|
342
|
-
^(yield equal?(%0) ...)
|
|
343
|
+
# @!method first_call_argument?(node)
|
|
344
|
+
def_node_matcher :first_call_argument?, <<~PATTERN
|
|
345
|
+
{^(call _ _ equal?(%0) ...)
|
|
346
|
+
^({super yield} equal?(%0) ...)}
|
|
343
347
|
PATTERN
|
|
344
348
|
|
|
345
349
|
def call_chain_starts_with_int?(begin_node, send_node)
|
|
@@ -80,8 +80,7 @@ module RuboCop
|
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
def string_literal?(node)
|
|
83
|
-
node.loc
|
|
84
|
-
node.loc.begin && node.loc.end
|
|
83
|
+
node.loc?(:begin) && node.loc?(:end)
|
|
85
84
|
end
|
|
86
85
|
|
|
87
86
|
def start_with_percent_q_variant?(string)
|
|
@@ -91,7 +90,10 @@ module RuboCop
|
|
|
91
90
|
def acceptable_q?(node)
|
|
92
91
|
src = node.source
|
|
93
92
|
|
|
94
|
-
|
|
93
|
+
# If the string contains interpolation-like syntax and would be
|
|
94
|
+
# converted to a double-quoted string (because it contains single
|
|
95
|
+
# quotes), the replacement would activate interpolation.
|
|
96
|
+
return true if STRING_INTERPOLATION_REGEXP.match?(src) && src.include?(SINGLE_QUOTE)
|
|
95
97
|
|
|
96
98
|
src.scan(/\\./).any?(ESCAPED_NON_BACKSLASH)
|
|
97
99
|
end
|
|
@@ -66,6 +66,7 @@ module RuboCop
|
|
|
66
66
|
DETERMINISTIC_REGEX.match?(regexp_node.source)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
# rubocop:disable Metrics/MethodLength
|
|
69
70
|
def preferred_argument(regexp_node)
|
|
70
71
|
new_argument = replacement(regexp_node)
|
|
71
72
|
|
|
@@ -73,6 +74,13 @@ module RuboCop
|
|
|
73
74
|
new_argument.gsub!("'", "\\\\'")
|
|
74
75
|
new_argument.gsub!('\"', '"')
|
|
75
76
|
quote = "'"
|
|
77
|
+
elsif new_argument.include?("\\'")
|
|
78
|
+
# Add a backslash before single quotes preceded by an even number of backslashes.
|
|
79
|
+
# An even number (including zero) of backslashes before a quote means the quote itself
|
|
80
|
+
# is not escaped.
|
|
81
|
+
# Otherwise an odd number means the quote is already escaped so this doesn't touch it.
|
|
82
|
+
new_argument.gsub!(/(?<!\\)((?:\\\\)*)'/) { "#{::Regexp.last_match(1)}\\'" }
|
|
83
|
+
quote = "'"
|
|
76
84
|
elsif new_argument.include?('\'')
|
|
77
85
|
new_argument.gsub!("'", "\\\\'")
|
|
78
86
|
quote = "'"
|
|
@@ -84,6 +92,7 @@ module RuboCop
|
|
|
84
92
|
|
|
85
93
|
"#{quote}#{new_argument}#{quote}"
|
|
86
94
|
end
|
|
95
|
+
# rubocop:enable Metrics/MethodLength
|
|
87
96
|
|
|
88
97
|
def replacement(regexp_node)
|
|
89
98
|
regexp_content = regexp_node.content
|
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
|
41
41
|
ALLOWED_ALWAYS_ESCAPES = " \n[]^\\#".chars.freeze
|
|
42
42
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES = '-'.chars.freeze
|
|
43
43
|
ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES = '.*+?{}()|$'.chars.freeze
|
|
44
|
+
INTERPOLATION_SIGILS = %w[@ $].freeze
|
|
44
45
|
|
|
45
46
|
def on_regexp(node)
|
|
46
47
|
each_escape(node) do |char, index, within_character_class|
|
|
@@ -64,6 +65,7 @@ module RuboCop
|
|
|
64
65
|
# different versions of Ruby so that e.g. /\i/ != /i/
|
|
65
66
|
return true if /[[:alnum:]]/.match?(char)
|
|
66
67
|
return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
|
|
68
|
+
return true if requires_escape_to_avoid_interpolation?(node.source[index], char)
|
|
67
69
|
|
|
68
70
|
if within_character_class
|
|
69
71
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char) &&
|
|
@@ -95,6 +97,12 @@ module RuboCop
|
|
|
95
97
|
delimiters.include?(char)
|
|
96
98
|
end
|
|
97
99
|
|
|
100
|
+
def requires_escape_to_avoid_interpolation?(char_before_escape, escaped_char)
|
|
101
|
+
# Preserve escapes after '#' that would otherwise trigger interpolation:
|
|
102
|
+
# '#@ivar', '#@@cvar', and '#$gvar'.
|
|
103
|
+
char_before_escape == '#' && INTERPOLATION_SIGILS.include?(escaped_char)
|
|
104
|
+
end
|
|
105
|
+
|
|
98
106
|
def each_escape(node)
|
|
99
107
|
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
|
|
100
108
|
yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for redundant `return` expressions.
|
|
6
|
+
# Checks for redundant `return` expressions. Ruby methods
|
|
7
|
+
# implicitly return the value of the last evaluated expression,
|
|
8
|
+
# so an explicit `return` at the end of a method body is unnecessary.
|
|
7
9
|
#
|
|
8
10
|
# @example
|
|
9
11
|
# # These bad cases should be extended to handle methods whose body is
|
|
@@ -26,17 +26,17 @@ module RuboCop
|
|
|
26
26
|
#
|
|
27
27
|
# [source,ruby]
|
|
28
28
|
# ----
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
29
|
+
# class MyString < String; end
|
|
30
|
+
# strings = [MyString.new('test'), 'test']
|
|
31
|
+
# strings.sort.last.class #=> String
|
|
32
|
+
# strings.max.class #=> MyString
|
|
33
33
|
# ----
|
|
34
34
|
#
|
|
35
35
|
# [source,ruby]
|
|
36
36
|
# ----
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
37
|
+
# words = %w(dog horse mouse)
|
|
38
|
+
# words.sort_by { |word| word.length }.last #=> 'mouse'
|
|
39
|
+
# words.max_by { |word| word.length } #=> 'horse'
|
|
40
40
|
# ----
|
|
41
41
|
#
|
|
42
42
|
# @example
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for redundant `keyword_init` option for `Struct.new`.
|
|
7
|
+
#
|
|
8
|
+
# Since Ruby 3.2, `keyword_init` in `Struct.new` defaults to `nil` behavior.
|
|
9
|
+
# Therefore, this cop detects and autocorrects redundant `keyword_init: nil`
|
|
10
|
+
# and `keyword_init: true` in `Struct.new`.
|
|
11
|
+
#
|
|
12
|
+
# This cop is disabled by default because `keyword_init: true` is not purely
|
|
13
|
+
# redundant. It changes behavior in the following ways:
|
|
14
|
+
#
|
|
15
|
+
# - `Struct#keyword_init?` returns `true` instead of `nil`.
|
|
16
|
+
# - A `Struct` with `keyword_init: true` accepts a `Hash` argument and
|
|
17
|
+
# expands it as keyword arguments, whereas without it the `Hash` is
|
|
18
|
+
# treated as a positional argument.
|
|
19
|
+
# - `keyword_init: true` raises an `ArgumentError` for positional arguments,
|
|
20
|
+
# enforcing keyword-only initialization.
|
|
21
|
+
#
|
|
22
|
+
# @safety
|
|
23
|
+
# This autocorrect is unsafe because when the value of `keyword_init` changes
|
|
24
|
+
# from `true` to `nil`, the return value of `Struct#keyword_init?` changes.
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
#
|
|
28
|
+
# # bad
|
|
29
|
+
# Struct.new(:foo, keyword_init: nil)
|
|
30
|
+
# Struct.new(:foo, keyword_init: true)
|
|
31
|
+
#
|
|
32
|
+
# # good
|
|
33
|
+
# Struct.new(:foo)
|
|
34
|
+
#
|
|
35
|
+
class RedundantStructKeywordInit < Base
|
|
36
|
+
extend AutoCorrector
|
|
37
|
+
extend TargetRubyVersion
|
|
38
|
+
|
|
39
|
+
MSG = 'Remove the redundant `keyword_init: %<value>s`.'
|
|
40
|
+
RESTRICT_ON_SEND = %i[new].freeze
|
|
41
|
+
|
|
42
|
+
minimum_target_ruby_version 3.2
|
|
43
|
+
|
|
44
|
+
# @!method struct_new?(node)
|
|
45
|
+
def_node_matcher :struct_new?, <<~PATTERN
|
|
46
|
+
(call (const {nil? cbase} :Struct) :new ...)
|
|
47
|
+
PATTERN
|
|
48
|
+
|
|
49
|
+
# @!method keyword_init?(node)
|
|
50
|
+
def_node_matcher :keyword_init?, <<~PATTERN
|
|
51
|
+
{#redundant_keyword_init? #keyword_init_false?}
|
|
52
|
+
PATTERN
|
|
53
|
+
|
|
54
|
+
# @!method redundant_keyword_init?(node)
|
|
55
|
+
def_node_matcher :redundant_keyword_init?, <<~PATTERN
|
|
56
|
+
(pair (sym :keyword_init) {(true) (nil)})
|
|
57
|
+
PATTERN
|
|
58
|
+
|
|
59
|
+
# @!method keyword_init_false?(node)
|
|
60
|
+
def_node_matcher :keyword_init_false?, <<~PATTERN
|
|
61
|
+
(pair (sym :keyword_init) (false))
|
|
62
|
+
PATTERN
|
|
63
|
+
|
|
64
|
+
def on_send(node)
|
|
65
|
+
return if !struct_new?(node) || node.arguments.none? || !node.last_argument.hash_type?
|
|
66
|
+
|
|
67
|
+
keyword_init_nodes = select_keyword_init_nodes(node)
|
|
68
|
+
return if keyword_init_nodes.any? { |node| keyword_init_false?(node) }
|
|
69
|
+
|
|
70
|
+
redundant_keyword_init_nodes = select_redundant_keyword_init_nodes(keyword_init_nodes)
|
|
71
|
+
|
|
72
|
+
redundant_keyword_init_nodes.each do |redundant_keyword_init|
|
|
73
|
+
register_offense(redundant_keyword_init)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
alias on_csend on_send
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def select_keyword_init_nodes(node)
|
|
81
|
+
node.last_argument.pairs.select do |pair|
|
|
82
|
+
keyword_init?(pair)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def select_redundant_keyword_init_nodes(keyword_init_nodes)
|
|
87
|
+
keyword_init_nodes.select do |keyword_init_node|
|
|
88
|
+
redundant_keyword_init?(keyword_init_node)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def register_offense(keyword_init)
|
|
93
|
+
message = format(MSG, value: keyword_init.value.source)
|
|
94
|
+
|
|
95
|
+
add_offense(keyword_init, message: message) do |corrector|
|
|
96
|
+
range = range(keyword_init)
|
|
97
|
+
|
|
98
|
+
corrector.remove(range)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def range(redundant_keyword_init)
|
|
103
|
+
if redundant_keyword_init.parent.left_siblings.last.is_a?(AST::Node)
|
|
104
|
+
beginning_of_range = redundant_keyword_init.parent.left_siblings.last.source_range.end
|
|
105
|
+
|
|
106
|
+
beginning_of_range.join(redundant_keyword_init.source_range.end)
|
|
107
|
+
else
|
|
108
|
+
redundant_keyword_init
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Identifies places where `array.reverse.find` can be replaced by `array.rfind`.
|
|
7
|
+
#
|
|
8
|
+
# @safety
|
|
9
|
+
# This cop is unsafe because it cannot be guaranteed that the receiver
|
|
10
|
+
# is an `Array` or responds to the replacement method.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad
|
|
14
|
+
# array.reverse.find { |item| item.even? }
|
|
15
|
+
# array.reverse.detect { |item| item.even? }
|
|
16
|
+
# array.reverse_each.find { |item| item.even? }
|
|
17
|
+
# array.reverse_each.detect { |item| item.even? }
|
|
18
|
+
#
|
|
19
|
+
# # good
|
|
20
|
+
# array.rfind { |item| item.even? }
|
|
21
|
+
#
|
|
22
|
+
class ReverseFind < Base
|
|
23
|
+
extend AutoCorrector
|
|
24
|
+
extend TargetRubyVersion
|
|
25
|
+
|
|
26
|
+
MSG = 'Use `rfind` instead.'
|
|
27
|
+
RESTRICT_ON_SEND = %i[find detect].freeze
|
|
28
|
+
|
|
29
|
+
minimum_target_ruby_version 4.0
|
|
30
|
+
|
|
31
|
+
# @!method reverse_find?(node)
|
|
32
|
+
def_node_matcher :reverse_find?, <<~PATTERN
|
|
33
|
+
(call
|
|
34
|
+
(call
|
|
35
|
+
_ {:reverse :reverse_each}) {:find :detect} (block_pass sym)?)
|
|
36
|
+
PATTERN
|
|
37
|
+
|
|
38
|
+
def on_send(node)
|
|
39
|
+
return unless reverse_find?(node)
|
|
40
|
+
|
|
41
|
+
range = node.children.first.loc.selector.join(node.loc.selector)
|
|
42
|
+
|
|
43
|
+
add_offense(range) do |corrector|
|
|
44
|
+
corrector.replace(range, 'rfind')
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
alias on_csend on_send
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -367,13 +367,13 @@ module RuboCop
|
|
|
367
367
|
def unsafe_method_used?(node, method_chain, method)
|
|
368
368
|
return true if unsafe_method?(node, method)
|
|
369
369
|
|
|
370
|
-
method.each_ancestor(:send)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
break false if ancestor == method_chain
|
|
370
|
+
method.each_ancestor(:send) do |ancestor|
|
|
371
|
+
return true unless config.cop_enabled?('Lint/SafeNavigationChain')
|
|
372
|
+
return true if unsafe_method?(node, ancestor)
|
|
373
|
+
return true if nil_methods.include?(ancestor.method_name)
|
|
374
|
+
return false if ancestor == method_chain
|
|
376
375
|
end
|
|
376
|
+
false
|
|
377
377
|
end
|
|
378
378
|
|
|
379
379
|
def unsafe_method?(node, send_node)
|
|
@@ -409,7 +409,7 @@ module RuboCop
|
|
|
409
409
|
start_method,
|
|
410
410
|
method_chain)
|
|
411
411
|
start_method.each_ancestor do |ancestor|
|
|
412
|
-
break unless
|
|
412
|
+
break unless ancestor.type?(:call, :any_block)
|
|
413
413
|
next if !ancestor.send_type? || ancestor.operator_method?
|
|
414
414
|
|
|
415
415
|
corrector.insert_before(ancestor.loc.dot, '&')
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Looks for places where a subset of an Enumerable (array,
|
|
7
|
+
# range, set, etc.; see note below) is calculated based on a class type
|
|
8
|
+
# check, and suggests `grep` or `grep_v` instead.
|
|
9
|
+
#
|
|
10
|
+
# NOTE: Hashes do not behave as you may expect with `grep`, which
|
|
11
|
+
# means that `hash.grep` is not equivalent to `hash.select`. Although
|
|
12
|
+
# RuboCop is limited by static analysis, this cop attempts to avoid
|
|
13
|
+
# registering an offense when the receiver is a hash (hash literal,
|
|
14
|
+
# `Hash.new`, `Hash#[]`, or `to_h`/`to_hash`).
|
|
15
|
+
#
|
|
16
|
+
# @safety
|
|
17
|
+
# Autocorrection is marked as unsafe because the cop cannot guarantee
|
|
18
|
+
# that the receiver is actually an array by static analysis, so the
|
|
19
|
+
# correction may not be actually equivalent.
|
|
20
|
+
#
|
|
21
|
+
# @example
|
|
22
|
+
# # bad (select or find_all)
|
|
23
|
+
# array.select { |x| x.is_a?(Foo) }
|
|
24
|
+
# array.select { |x| x.kind_of?(Foo) }
|
|
25
|
+
#
|
|
26
|
+
# # bad (reject)
|
|
27
|
+
# array.reject { |x| x.is_a?(Foo) }
|
|
28
|
+
#
|
|
29
|
+
# # bad (negative form)
|
|
30
|
+
# array.reject { |x| !x.is_a?(Foo) }
|
|
31
|
+
#
|
|
32
|
+
# # good
|
|
33
|
+
# array.grep(Foo)
|
|
34
|
+
# array.grep_v(Foo)
|
|
35
|
+
class SelectByKind < Base
|
|
36
|
+
extend AutoCorrector
|
|
37
|
+
include RangeHelp
|
|
38
|
+
|
|
39
|
+
MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a kind check.'
|
|
40
|
+
RESTRICT_ON_SEND = %i[select filter find_all reject].freeze
|
|
41
|
+
SELECT_METHODS = %i[select filter find_all].freeze
|
|
42
|
+
CLASS_CHECK_METHODS = %i[is_a? kind_of?].to_set.freeze
|
|
43
|
+
|
|
44
|
+
# @!method class_check?(node)
|
|
45
|
+
def_node_matcher :class_check?, <<~PATTERN
|
|
46
|
+
{
|
|
47
|
+
(block call (args (arg $_)) ${(send (lvar _) %CLASS_CHECK_METHODS _)})
|
|
48
|
+
(block call (args (arg $_)) ${(send (send (lvar _) %CLASS_CHECK_METHODS _) :!)})
|
|
49
|
+
(numblock call $1 ${(send (lvar _) %CLASS_CHECK_METHODS _)})
|
|
50
|
+
(numblock call $1 ${(send (send (lvar _) %CLASS_CHECK_METHODS _) :!)})
|
|
51
|
+
(itblock call $_ ${(send (lvar _) %CLASS_CHECK_METHODS _)})
|
|
52
|
+
(itblock call $_ ${(send (send (lvar _) %CLASS_CHECK_METHODS _) :!)})
|
|
53
|
+
}
|
|
54
|
+
PATTERN
|
|
55
|
+
|
|
56
|
+
# Returns true if a node appears to return a hash
|
|
57
|
+
# @!method creates_hash?(node)
|
|
58
|
+
def_node_matcher :creates_hash?, <<~PATTERN
|
|
59
|
+
{
|
|
60
|
+
(call (const _ :Hash) {:new :[]} ...)
|
|
61
|
+
(block (call (const _ :Hash) :new ...) ...)
|
|
62
|
+
(call _ { :to_h :to_hash } ...)
|
|
63
|
+
}
|
|
64
|
+
PATTERN
|
|
65
|
+
|
|
66
|
+
# @!method env_const?(node)
|
|
67
|
+
def_node_matcher :env_const?, <<~PATTERN
|
|
68
|
+
(const {nil? cbase} :ENV)
|
|
69
|
+
PATTERN
|
|
70
|
+
|
|
71
|
+
# @!method calls_lvar?(node, name)
|
|
72
|
+
def_node_matcher :calls_lvar?, <<~PATTERN
|
|
73
|
+
(send (lvar %1) %CLASS_CHECK_METHODS _)
|
|
74
|
+
PATTERN
|
|
75
|
+
|
|
76
|
+
# @!method negated_calls_lvar?(node, name)
|
|
77
|
+
def_node_matcher :negated_calls_lvar?, <<~PATTERN
|
|
78
|
+
(send (send (lvar %1) %CLASS_CHECK_METHODS _) :!)
|
|
79
|
+
PATTERN
|
|
80
|
+
|
|
81
|
+
def on_send(node)
|
|
82
|
+
return unless (block_node = node.block_node)
|
|
83
|
+
return if block_node.body&.begin_type?
|
|
84
|
+
return if receiver_allowed?(block_node.receiver)
|
|
85
|
+
return unless (class_check_send_node = extract_send_node(block_node))
|
|
86
|
+
|
|
87
|
+
replacement = replacement(class_check_send_node, node)
|
|
88
|
+
class_constant = find_class_constant(class_check_send_node)
|
|
89
|
+
|
|
90
|
+
register_offense(node, block_node, class_constant, replacement)
|
|
91
|
+
end
|
|
92
|
+
alias on_csend on_send
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def receiver_allowed?(node)
|
|
97
|
+
return false unless node
|
|
98
|
+
|
|
99
|
+
node.hash_type? || creates_hash?(node) || env_const?(node)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def replacement(class_check_send_node, node)
|
|
103
|
+
negated = negated?(class_check_send_node)
|
|
104
|
+
|
|
105
|
+
method_name = node.method_name
|
|
106
|
+
|
|
107
|
+
if SELECT_METHODS.include?(method_name)
|
|
108
|
+
negated ? 'grep_v' : 'grep'
|
|
109
|
+
else # reject
|
|
110
|
+
negated ? 'grep' : 'grep_v'
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def register_offense(node, block_node, class_constant, replacement)
|
|
115
|
+
message = format(MSG, replacement: replacement, original_method: node.method_name)
|
|
116
|
+
|
|
117
|
+
add_offense(block_node, message: message) do |corrector|
|
|
118
|
+
if class_constant
|
|
119
|
+
range = range_between(node.loc.selector.begin_pos, block_node.loc.end.end_pos)
|
|
120
|
+
corrector.replace(range, "#{replacement}(#{class_constant.source})")
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def extract_send_node(block_node)
|
|
126
|
+
return unless (block_arg_name, class_check_send_node = class_check?(block_node))
|
|
127
|
+
|
|
128
|
+
block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
|
|
129
|
+
block_arg_name = :it if block_node.type?(:itblock)
|
|
130
|
+
|
|
131
|
+
inner_node = unwrap_negation(class_check_send_node)
|
|
132
|
+
|
|
133
|
+
if calls_lvar?(inner_node, block_arg_name) ||
|
|
134
|
+
negated_calls_lvar?(class_check_send_node, block_arg_name)
|
|
135
|
+
class_check_send_node
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def negated?(class_check_send_node)
|
|
140
|
+
class_check_send_node.send_type? && class_check_send_node.method?(:!)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def unwrap_negation(node)
|
|
144
|
+
if node.send_type? && node.method?(:!)
|
|
145
|
+
node.receiver
|
|
146
|
+
else
|
|
147
|
+
node
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def find_class_constant(node)
|
|
152
|
+
inner_node = unwrap_negation(node)
|
|
153
|
+
inner_node.first_argument if inner_node.send_type?
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|