rubocop 1.84.2 → 1.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/default.yml +106 -16
- data/config/obsoletion.yml +5 -0
- data/lib/rubocop/cache_config.rb +1 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +28 -2
- data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
- 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 +9 -7
- data/lib/rubocop/comment_config.rb +12 -15
- data/lib/rubocop/config.rb +14 -10
- data/lib/rubocop/config_finder.rb +1 -1
- data/lib/rubocop/config_loader.rb +17 -2
- data/lib/rubocop/config_loader_resolver.rb +13 -4
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
- data/lib/rubocop/config_store.rb +2 -2
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +2 -1
- data/lib/rubocop/cop/base.rb +8 -2
- data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
- 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/require_mfa.rb +5 -5
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
- data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +23 -7
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
- data/lib/rubocop/cop/layout/end_alignment.rb +8 -5
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +12 -0
- data/lib/rubocop/cop/layout/line_length.rb +5 -3
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +53 -3
- 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/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
- data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
- 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/data_define_override.rb +63 -0
- data/lib/rubocop/cop/lint/deprecated_constants.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
- data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
- data/lib/rubocop/cop/lint/empty_when.rb +8 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
- 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/non_deterministic_require_order.rb +4 -2
- data/lib/rubocop/cop/lint/number_conversion.rb +6 -6
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +2 -11
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +3 -3
- data/lib/rubocop/cop/lint/require_relative_self_path.rb +3 -1
- 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/shadowed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/syntax.rb +25 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -1
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +2 -2
- 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 +4 -9
- 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_ruby2_keywords.rb +1 -1
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +35 -9
- data/lib/rubocop/cop/lint/void.rb +32 -12
- 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/method_length.rb +1 -1
- 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 +1 -1
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
- data/lib/rubocop/cop/mixin/configurable_max.rb +6 -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/project_index_help.rb +48 -0
- 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/predicate_method.rb +2 -2
- data/lib/rubocop/cop/naming/predicate_prefix.rb +1 -1
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +8 -0
- 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/style/access_modifier_declarations.rb +14 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
- data/lib/rubocop/cop/style/alias.rb +14 -2
- data/lib/rubocop/cop/style/and_or.rb +1 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
- data/lib/rubocop/cop/style/array_join.rb +4 -2
- data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
- data/lib/rubocop/cop/style/attr.rb +5 -2
- data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
- data/lib/rubocop/cop/style/begin_block.rb +3 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +25 -33
- data/lib/rubocop/cop/style/case_equality.rb +4 -0
- data/lib/rubocop/cop/style/character_literal.rb +2 -2
- data/lib/rubocop/cop/style/class_and_module_children.rb +18 -2
- data/lib/rubocop/cop/style/collection_compact.rb +36 -16
- data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +0 -4
- data/lib/rubocop/cop/style/copyright.rb +22 -11
- data/lib/rubocop/cop/style/date_time.rb +2 -2
- 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/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/each_with_object.rb +2 -0
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_class_definition.rb +43 -20
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/encoding.rb +7 -1
- data/lib/rubocop/cop/style/end_block.rb +3 -1
- data/lib/rubocop/cop/style/endless_method.rb +8 -3
- data/lib/rubocop/cop/style/file_open.rb +84 -0
- data/lib/rubocop/cop/style/file_write.rb +18 -16
- 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 +29 -2
- data/lib/rubocop/cop/style/global_vars.rb +5 -2
- data/lib/rubocop/cop/style/guard_clause.rb +9 -6
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
- data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
- data/lib/rubocop/cop/style/hash_lookup_method.rb +19 -7
- 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 +14 -3
- data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
- data/lib/rubocop/cop/style/inline_comment.rb +4 -1
- data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
- data/lib/rubocop/cop/style/magic_comment_format.rb +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 +5 -3
- data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
- data/lib/rubocop/cop/style/module_member_existence_check.rb +7 -14
- data/lib/rubocop/cop/style/multiline_if_then.rb +3 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
- data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
- data/lib/rubocop/cop/style/not.rb +2 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
- data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
- data/lib/rubocop/cop/style/parallel_assignment.rb +4 -0
- data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
- data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
- data/lib/rubocop/cop/style/proc.rb +3 -2
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
- data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
- data/lib/rubocop/cop/style/redundant_begin.rb +3 -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_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
- data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +25 -22
- data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
- 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_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/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/self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +2 -0
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
- data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
- 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_block_args.rb +1 -1
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
- data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
- data/lib/rubocop/cop/style/while_until_modifier.rb +16 -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/team.rb +86 -35
- data/lib/rubocop/cop/variable_force/branch.rb +2 -2
- data/lib/rubocop/directive_comment.rb +2 -1
- data/lib/rubocop/file_patterns.rb +9 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -2
- data/lib/rubocop/formatter/formatter_set.rb +1 -1
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/formatter.rb +22 -21
- data/lib/rubocop/lsp/diagnostic.rb +1 -0
- data/lib/rubocop/lsp/routes.rb +10 -3
- data/lib/rubocop/lsp/runtime.rb +1 -2
- 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/result_cache.rb +22 -10
- data/lib/rubocop/rspec/cop_helper.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +32 -2
- data/lib/rubocop/runner.rb +124 -53
- data/lib/rubocop/server/cache.rb +5 -7
- data/lib/rubocop/server/core.rb +2 -0
- data/lib/rubocop/target_finder.rb +14 -7
- data/lib/rubocop/target_ruby.rb +18 -12
- data/lib/rubocop/version.rb +21 -3
- data/lib/rubocop.rb +22 -96
- metadata +27 -5
|
@@ -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
|
|
@@ -5,8 +5,8 @@ module RuboCop
|
|
|
5
5
|
module Style
|
|
6
6
|
# Enforces using `//` or `%r` around regular expressions.
|
|
7
7
|
#
|
|
8
|
-
# NOTE: The following `%r` cases using a regexp starts with a blank or `=`
|
|
9
|
-
# as a method argument allowed to prevent syntax errors.
|
|
8
|
+
# NOTE: The following `%r` cases using a regexp that starts with a blank or `=`
|
|
9
|
+
# as a method argument are allowed to prevent syntax errors.
|
|
10
10
|
#
|
|
11
11
|
# [source,ruby]
|
|
12
12
|
# ----
|
|
@@ -98,7 +98,16 @@ module RuboCop
|
|
|
98
98
|
MSG_USE_SLASHES = 'Use `//` around regular expression.'
|
|
99
99
|
MSG_USE_PERCENT_R = 'Use `%r` around regular expression.'
|
|
100
100
|
|
|
101
|
+
PAIR_DELIMITER_PATTERNS = {
|
|
102
|
+
['(', ')'] => /\\.|[()]/,
|
|
103
|
+
['[', ']'] => /\\.|[\[\]]/,
|
|
104
|
+
['{', '}'] => /\\.|[{}]/,
|
|
105
|
+
['<', '>'] => /\\.|[<>]/
|
|
106
|
+
}.freeze
|
|
107
|
+
|
|
101
108
|
def on_regexp(node)
|
|
109
|
+
return if slash_literal?(node) && percent_r_delimiters_conflict?(node)
|
|
110
|
+
|
|
102
111
|
message = if slash_literal?(node)
|
|
103
112
|
MSG_USE_PERCENT_R unless allowed_slash_literal?(node)
|
|
104
113
|
else
|
|
@@ -115,6 +124,26 @@ module RuboCop
|
|
|
115
124
|
|
|
116
125
|
private
|
|
117
126
|
|
|
127
|
+
def percent_r_delimiters_conflict?(node)
|
|
128
|
+
opening, closing = preferred_delimiters
|
|
129
|
+
return false unless (pattern = PAIR_DELIMITER_PATTERNS[[opening, closing]])
|
|
130
|
+
|
|
131
|
+
!balanced_delimiters?(node_body(node), opening, closing, pattern)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def balanced_delimiters?(text, opening, closing, pattern)
|
|
135
|
+
depth = 0
|
|
136
|
+
text.scan(pattern) do |match|
|
|
137
|
+
if match == opening
|
|
138
|
+
depth += 1
|
|
139
|
+
elsif match == closing
|
|
140
|
+
depth -= 1
|
|
141
|
+
return false if depth.negative?
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
depth.zero?
|
|
145
|
+
end
|
|
146
|
+
|
|
118
147
|
def allowed_slash_literal?(node)
|
|
119
148
|
(style == :slashes && !contains_disallowed_slash?(node)) || allowed_mixed_slash?(node)
|
|
120
149
|
end
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for uses of `rescue` in its modifier form is added for following
|
|
6
|
+
# Checks for uses of `rescue` in its modifier form. It is added for the following
|
|
7
7
|
# reasons:
|
|
8
8
|
#
|
|
9
9
|
# * The syntax of modifier form `rescue` can be misleading because it
|
|
10
10
|
# might lead us to believe that `rescue` handles the given exception
|
|
11
|
-
# but it actually
|
|
11
|
+
# but it actually rescues all exceptions to return the given rescue
|
|
12
12
|
# block. In this case, value returned by handle_error or
|
|
13
13
|
# SomeException.
|
|
14
14
|
#
|
|
15
15
|
# * Modifier form `rescue` would rescue all the exceptions. It would
|
|
16
|
-
# silently skip all
|
|
16
|
+
# silently skip all exceptions or errors and handle the error.
|
|
17
17
|
# Example: If `NoMethodError` is raised, modifier form rescue would
|
|
18
18
|
# handle the exception.
|
|
19
19
|
#
|
|
@@ -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
|
|
@@ -0,0 +1,197 @@
|
|
|
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 range
|
|
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.between?(1, 10) }
|
|
24
|
+
# array.select { |x| (1..10).cover?(x) }
|
|
25
|
+
# array.select { |x| (1..10).include?(x) }
|
|
26
|
+
#
|
|
27
|
+
# # bad (reject)
|
|
28
|
+
# array.reject { |x| x.between?(1, 10) }
|
|
29
|
+
#
|
|
30
|
+
# # bad (find or detect)
|
|
31
|
+
# array.find { |x| x.between?(1, 10) }
|
|
32
|
+
# array.detect { |x| (1..10).cover?(x) }
|
|
33
|
+
#
|
|
34
|
+
# # bad (negative form)
|
|
35
|
+
# array.reject { |x| !x.between?(1, 10) }
|
|
36
|
+
# array.find { |x| !(1..10).cover?(x) }
|
|
37
|
+
#
|
|
38
|
+
# # good
|
|
39
|
+
# array.grep(1..10)
|
|
40
|
+
# array.grep_v(1..10)
|
|
41
|
+
# array.grep(1..10).first
|
|
42
|
+
# array.grep_v(1..10).first
|
|
43
|
+
class SelectByRange < Base
|
|
44
|
+
extend AutoCorrector
|
|
45
|
+
include RangeHelp
|
|
46
|
+
|
|
47
|
+
MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a range check.'
|
|
48
|
+
RESTRICT_ON_SEND = %i[select filter find_all reject find detect].freeze
|
|
49
|
+
SELECT_METHODS = %i[select filter find_all].freeze
|
|
50
|
+
FIND_METHODS = %i[find detect].freeze
|
|
51
|
+
|
|
52
|
+
# @!method range_check?(node)
|
|
53
|
+
# Matches: x.between?(min, max) or (min..max).cover?(x) or (min..max).include?(x)
|
|
54
|
+
def_node_matcher :range_check?, <<~PATTERN
|
|
55
|
+
{
|
|
56
|
+
(block call (args (arg $_)) ${(send (lvar _) :between? _ _)})
|
|
57
|
+
(block call (args (arg $_)) ${(send {range (begin range)} {:cover? :include?} (lvar _))})
|
|
58
|
+
(block call (args (arg $_)) ${(send (send (lvar _) :between? _ _) :!)})
|
|
59
|
+
(block call (args (arg $_)) ${(send (send {range (begin range)} {:cover? :include?} (lvar _)) :!)})
|
|
60
|
+
(block call (args (arg $_)) ${(send (begin (send (lvar _) :between? _ _)) :!)})
|
|
61
|
+
(block call (args (arg $_)) ${(send (begin (send {range (begin range)} {:cover? :include?} (lvar _))) :!)})
|
|
62
|
+
(numblock call $1 ${(send (lvar _) :between? _ _)})
|
|
63
|
+
(numblock call $1 ${(send {range (begin range)} {:cover? :include?} (lvar _))})
|
|
64
|
+
(numblock call $1 ${(send (send (lvar _) :between? _ _) :!)})
|
|
65
|
+
(numblock call $1 ${(send (send {range (begin range)} {:cover? :include?} (lvar _)) :!)})
|
|
66
|
+
(numblock call $1 ${(send (begin (send (lvar _) :between? _ _)) :!)})
|
|
67
|
+
(numblock call $1 ${(send (begin (send {range (begin range)} {:cover? :include?} (lvar _))) :!)})
|
|
68
|
+
(itblock call $_ ${(send (lvar _) :between? _ _)})
|
|
69
|
+
(itblock call $_ ${(send {range (begin range)} {:cover? :include?} (lvar _))})
|
|
70
|
+
(itblock call $_ ${(send (send (lvar _) :between? _ _) :!)})
|
|
71
|
+
(itblock call $_ ${(send (send {range (begin range)} {:cover? :include?} (lvar _)) :!)})
|
|
72
|
+
(itblock call $_ ${(send (begin (send (lvar _) :between? _ _)) :!)})
|
|
73
|
+
(itblock call $_ ${(send (begin (send {range (begin range)} {:cover? :include?} (lvar _))) :!)})
|
|
74
|
+
}
|
|
75
|
+
PATTERN
|
|
76
|
+
|
|
77
|
+
# Returns true if a node appears to return a hash
|
|
78
|
+
# @!method creates_hash?(node)
|
|
79
|
+
def_node_matcher :creates_hash?, <<~PATTERN
|
|
80
|
+
{
|
|
81
|
+
(call (const _ :Hash) {:new :[]} ...)
|
|
82
|
+
(block (call (const _ :Hash) :new ...) ...)
|
|
83
|
+
(call _ { :to_h :to_hash } ...)
|
|
84
|
+
}
|
|
85
|
+
PATTERN
|
|
86
|
+
|
|
87
|
+
# @!method env_const?(node)
|
|
88
|
+
def_node_matcher :env_const?, <<~PATTERN
|
|
89
|
+
(const {nil? cbase} :ENV)
|
|
90
|
+
PATTERN
|
|
91
|
+
|
|
92
|
+
# @!method between_call?(node, name)
|
|
93
|
+
def_node_matcher :between_call?, <<~PATTERN
|
|
94
|
+
(send (lvar %1) :between? _ _)
|
|
95
|
+
PATTERN
|
|
96
|
+
|
|
97
|
+
# @!method range_cover_call?(node, name)
|
|
98
|
+
def_node_matcher :range_cover_call?, <<~PATTERN
|
|
99
|
+
(send {range (begin range)} {:cover? :include?} (lvar %1))
|
|
100
|
+
PATTERN
|
|
101
|
+
|
|
102
|
+
def on_send(node)
|
|
103
|
+
return unless (block_node = node.block_node)
|
|
104
|
+
return if block_node.body&.begin_type?
|
|
105
|
+
return if receiver_allowed?(block_node.receiver)
|
|
106
|
+
return unless (range_check_send_node = extract_send_node(block_node))
|
|
107
|
+
|
|
108
|
+
replacement = replacement(range_check_send_node, node)
|
|
109
|
+
range_literal = find_range(range_check_send_node)
|
|
110
|
+
|
|
111
|
+
register_offense(node, block_node, range_literal, replacement)
|
|
112
|
+
end
|
|
113
|
+
alias on_csend on_send
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
def receiver_allowed?(node)
|
|
118
|
+
return false unless node
|
|
119
|
+
|
|
120
|
+
node.hash_type? || creates_hash?(node) || env_const?(node)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def replacement(range_check_send_node, node)
|
|
124
|
+
negated = negated?(range_check_send_node)
|
|
125
|
+
method_name = node.method_name
|
|
126
|
+
|
|
127
|
+
if SELECT_METHODS.include?(method_name)
|
|
128
|
+
negated ? 'grep_v' : 'grep'
|
|
129
|
+
elsif FIND_METHODS.include?(method_name)
|
|
130
|
+
negated ? 'grep_v(...).first' : 'grep(...).first'
|
|
131
|
+
else # reject
|
|
132
|
+
negated ? 'grep' : 'grep_v'
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def register_offense(node, block_node, range_literal, replacement)
|
|
137
|
+
message = format(MSG, replacement: replacement, original_method: node.method_name)
|
|
138
|
+
|
|
139
|
+
add_offense(block_node, message: message) do |corrector|
|
|
140
|
+
if range_literal
|
|
141
|
+
range = range_between(node.loc.selector.begin_pos, block_node.loc.end.end_pos)
|
|
142
|
+
grep_method = replacement.include?('grep_v') ? 'grep_v' : 'grep'
|
|
143
|
+
suffix = replacement.include?('.first') ? '.first' : ''
|
|
144
|
+
corrector.replace(range, "#{grep_method}(#{range_literal})#{suffix}")
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def extract_send_node(block_node)
|
|
150
|
+
return unless (block_arg_name, range_check_send_node = range_check?(block_node))
|
|
151
|
+
|
|
152
|
+
block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
|
|
153
|
+
block_arg_name = :it if block_node.type?(:itblock)
|
|
154
|
+
|
|
155
|
+
inner_node = unwrap_negation(range_check_send_node)
|
|
156
|
+
|
|
157
|
+
range_check_send_node if calls_lvar_in_range_check?(inner_node, block_arg_name)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def calls_lvar_in_range_check?(node, block_arg_name)
|
|
161
|
+
between_call?(node, block_arg_name) || range_cover_call?(node, block_arg_name)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def negated?(range_check_send_node)
|
|
165
|
+
range_check_send_node.send_type? && range_check_send_node.method?(:!)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def unwrap_negation(node)
|
|
169
|
+
if node.send_type? && node.method?(:!)
|
|
170
|
+
receiver = node.receiver
|
|
171
|
+
receiver = receiver.children.first if receiver.begin_type?
|
|
172
|
+
receiver
|
|
173
|
+
else
|
|
174
|
+
node
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def find_range(node)
|
|
179
|
+
inner = unwrap_negation(node)
|
|
180
|
+
|
|
181
|
+
if inner.method?(:between?)
|
|
182
|
+
# x.between?(min, max) -> min..max
|
|
183
|
+
min = inner.first_argument.source
|
|
184
|
+
max = inner.arguments[1].source
|
|
185
|
+
"#{min}..#{max}"
|
|
186
|
+
else
|
|
187
|
+
# (min..max).cover?(x) or (min..max).include?(x)
|
|
188
|
+
receiver = inner.receiver
|
|
189
|
+
# Unwrap begin node from parentheses
|
|
190
|
+
receiver = receiver.children.first if receiver.begin_type?
|
|
191
|
+
receiver.source
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|