rubocop 1.91.0 → 1.92.2
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/README.md +1 -1
- data/config/default.yml +4 -1
- data/lib/rubocop/cached_data.rb +47 -7
- data/lib/rubocop/changed_files.rb +35 -13
- data/lib/rubocop/cli/command/cop_names.rb +57 -0
- data/lib/rubocop/cli/command/execute_runner.rb +2 -2
- data/lib/rubocop/cli/command/explain.rb +34 -0
- data/lib/rubocop/cli/command/show_cops.rb +6 -0
- data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
- data/lib/rubocop/cli.rb +3 -2
- data/lib/rubocop/comment_config.rb +17 -8
- data/lib/rubocop/config.rb +0 -4
- data/lib/rubocop/config_loader.rb +6 -3
- data/lib/rubocop/config_loader_resolver.rb +37 -12
- data/lib/rubocop/config_obsoletion/removed_cop.rb +1 -1
- data/lib/rubocop/config_obsoletion.rb +9 -0
- data/lib/rubocop/config_validator.rb +11 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +42 -21
- data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
- data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
- data/lib/rubocop/cop/cop_documentation.rb +81 -0
- data/lib/rubocop/cop/cop_explanation.rb +140 -0
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +37 -7
- data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +25 -4
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
- data/lib/rubocop/cop/documentation.rb +1 -1
- data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
- data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
- data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
- data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
- data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
- data/lib/rubocop/cop/layout/class_structure.rb +29 -7
- data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/dot_position.rb +25 -15
- data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
- data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
- 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 +9 -3
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +21 -25
- data/lib/rubocop/cop/layout/extra_spacing.rb +21 -6
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -1
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +16 -8
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
- data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +16 -12
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +7 -3
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
- data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
- data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
- data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
- data/lib/rubocop/cop/layout/line_length.rb +176 -26
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +9 -0
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +30 -5
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
- data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +16 -3
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
- data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +6 -11
- data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +31 -4
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +16 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +32 -9
- data/lib/rubocop/cop/layout/space_inside_range_literal.rb +29 -11
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
- data/lib/rubocop/cop/lint/interpolation_check.rb +20 -17
- data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
- data/lib/rubocop/cop/lint/loop.rb +24 -2
- data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +71 -11
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +3 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +3 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +22 -1
- data/lib/rubocop/cop/lint/percent_string_array.rb +75 -9
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +39 -9
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +11 -4
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
- data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
- data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
- data/lib/rubocop/cop/lint/void.rb +8 -1
- data/lib/rubocop/cop/metrics/class_length.rb +4 -2
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
- data/lib/rubocop/cop/metrics/module_length.rb +4 -2
- data/lib/rubocop/cop/mixin/alignment.rb +4 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -19
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +8 -2
- data/lib/rubocop/cop/mixin/code_length.rb +10 -2
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
- data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
- data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +41 -6
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +26 -2
- data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
- data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +17 -2
- data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
- data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
- data/lib/rubocop/cop/mixin/statement_modifier.rb +39 -5
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
- data/lib/rubocop/cop/mixin.rb +2 -0
- data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
- data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
- data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +59 -3
- data/lib/rubocop/cop/registry.rb +1 -1
- data/lib/rubocop/cop/security/json_load.rb +16 -4
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +37 -2
- data/lib/rubocop/cop/style/alias.rb +18 -5
- data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
- data/lib/rubocop/cop/style/and_or.rb +37 -10
- data/lib/rubocop/cop/style/arguments_forwarding.rb +32 -3
- data/lib/rubocop/cop/style/attr.rb +3 -4
- data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
- data/lib/rubocop/cop/style/block_delimiters.rb +35 -3
- data/lib/rubocop/cop/style/case_equality.rb +14 -3
- data/lib/rubocop/cop/style/case_like_if.rb +6 -3
- data/lib/rubocop/cop/style/class_and_module_children.rb +57 -16
- data/lib/rubocop/cop/style/class_methods_definitions.rb +15 -0
- data/lib/rubocop/cop/style/collection_querying.rb +79 -46
- data/lib/rubocop/cop/style/combinable_loops.rb +89 -11
- data/lib/rubocop/cop/style/concat_array_literals.rb +58 -7
- data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +65 -9
- data/lib/rubocop/cop/style/double_negation.rb +11 -0
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +3 -1
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
- data/lib/rubocop/cop/style/endless_method.rb +104 -11
- data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
- data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
- data/lib/rubocop/cop/style/float_division.rb +37 -2
- data/lib/rubocop/cop/style/for.rb +53 -2
- data/lib/rubocop/cop/style/format_string.rb +28 -6
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
- data/lib/rubocop/cop/style/guard_clause.rb +31 -22
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
- data/lib/rubocop/cop/style/hash_lookup_method.rb +42 -9
- data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
- data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -26
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
- data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
- data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
- data/lib/rubocop/cop/style/it_block_parameter.rb +94 -4
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
- data/lib/rubocop/cop/style/lambda.rb +34 -1
- data/lib/rubocop/cop/style/lambda_call.rb +7 -2
- data/lib/rubocop/cop/style/map_into_array.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +80 -13
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +26 -2
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
- data/lib/rubocop/cop/style/method_def_parentheses.rb +38 -4
- data/lib/rubocop/cop/style/missing_else.rb +7 -1
- data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
- data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
- data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
- data/lib/rubocop/cop/style/mutable_constant.rb +52 -7
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
- data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +27 -8
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
- data/lib/rubocop/cop/style/next.rb +24 -10
- data/lib/rubocop/cop/style/nil_comparison.rb +16 -4
- data/lib/rubocop/cop/style/non_nil_check.rb +1 -1
- data/lib/rubocop/cop/style/not.rb +10 -5
- data/lib/rubocop/cop/style/numeric_predicate.rb +28 -5
- data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +19 -1
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
- data/lib/rubocop/cop/style/quoted_symbols.rb +5 -7
- data/lib/rubocop/cop/style/raise_args.rb +80 -11
- data/lib/rubocop/cop/style/reduce_to_hash.rb +47 -33
- data/lib/rubocop/cop/style/redundant_begin.rb +45 -5
- data/lib/rubocop/cop/style/redundant_condition.rb +30 -13
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -0
- data/lib/rubocop/cop/style/redundant_exception.rb +27 -11
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +9 -1
- data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +12 -1
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +20 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
- data/lib/rubocop/cop/style/redundant_self.rb +9 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
- data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
- data/lib/rubocop/cop/style/require_order.rb +25 -8
- data/lib/rubocop/cop/style/rescue_modifier.rb +11 -9
- data/lib/rubocop/cop/style/return_nil.rb +17 -2
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
- data/lib/rubocop/cop/style/sample.rb +9 -8
- data/lib/rubocop/cop/style/self_assignment.rb +12 -15
- data/lib/rubocop/cop/style/semicolon.rb +16 -5
- data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +42 -4
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
- data/lib/rubocop/cop/style/string_concatenation.rb +21 -4
- data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
- data/lib/rubocop/cop/style/super_arguments.rb +3 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +24 -6
- data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
- data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
- data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
- data/lib/rubocop/cop/team.rb +10 -5
- data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +14 -1
- data/lib/rubocop/formatter/base_formatter.rb +22 -0
- data/lib/rubocop/formatter/formatter_set.rb +4 -0
- data/lib/rubocop/formatter/json_formatter.rb +71 -1
- data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
- data/lib/rubocop/formatter/sarif_formatter.rb +12 -7
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
- data/lib/rubocop/lsp/routes.rb +23 -23
- data/lib/rubocop/lsp/runtime.rb +47 -11
- data/lib/rubocop/lsp/stdin_runner.rb +11 -5
- data/lib/rubocop/lsp.rb +5 -2
- data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
- data/lib/rubocop/mcp/explain_tool.rb +68 -0
- data/lib/rubocop/mcp/scope.rb +65 -0
- data/lib/rubocop/mcp/server.rb +184 -76
- data/lib/rubocop/mcp/source_files.rb +35 -0
- data/lib/rubocop/offense_limit.rb +30 -0
- data/lib/rubocop/options.rb +45 -10
- data/lib/rubocop/plugin/configuration_integrator.rb +36 -8
- data/lib/rubocop/remote_config.rb +4 -1
- data/lib/rubocop/result_cache.rb +20 -13
- data/lib/rubocop/runner.rb +110 -42
- data/lib/rubocop/server/cache.rb +6 -6
- data/lib/rubocop/target_ruby.rb +21 -9
- data/lib/rubocop/util.rb +69 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +5 -0
- metadata +16 -5
|
@@ -45,6 +45,8 @@ module RuboCop
|
|
|
45
45
|
MSG_TRAILING_CONDITIONAL = 'Do not use trailing conditionals in string interpolation.'
|
|
46
46
|
MSG_TERNARY = 'Do not return empty strings in string interpolation.'
|
|
47
47
|
|
|
48
|
+
INTERPOLATED_TYPES = %i[dstr dsym regexp xstr].freeze
|
|
49
|
+
|
|
48
50
|
def on_interpolation(node)
|
|
49
51
|
node.each_child_node(:if) do |child_node|
|
|
50
52
|
if style == :trailing_conditional
|
|
@@ -63,28 +65,54 @@ module RuboCop
|
|
|
63
65
|
return if child_node.modifier_form?
|
|
64
66
|
|
|
65
67
|
if empty_if_outcome?(child_node)
|
|
66
|
-
ternary_style_autocorrect(child_node, child_node.else_branch
|
|
68
|
+
ternary_style_autocorrect(child_node, child_node.else_branch, 'unless')
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
return unless empty_else_outcome?(child_node)
|
|
70
72
|
|
|
71
|
-
ternary_style_autocorrect(child_node, child_node.if_branch
|
|
73
|
+
ternary_style_autocorrect(child_node, child_node.if_branch, 'if')
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
def ternary_correction(node, child_node)
|
|
75
77
|
return unless child_node.modifier_form?
|
|
76
78
|
|
|
77
|
-
ternary_component = if child_node.unless?
|
|
78
|
-
"'' : #{child_node.if_branch.source}"
|
|
79
|
-
else
|
|
80
|
-
"#{child_node.if_branch.source} : ''"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
79
|
add_offense(node, message: MSG_TRAILING_CONDITIONAL) do |corrector|
|
|
84
|
-
|
|
80
|
+
next if nested_trailing_conditional?(child_node)
|
|
81
|
+
|
|
82
|
+
autocorrect_to_ternary(corrector, child_node)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def nested_trailing_conditional?(if_node)
|
|
87
|
+
if_node.each_descendant(:if).any? do |node|
|
|
88
|
+
node.modifier_form? && node.parent.parent&.type?(*INTERPOLATED_TYPES)
|
|
85
89
|
end
|
|
86
90
|
end
|
|
87
91
|
|
|
92
|
+
def autocorrect_to_ternary(corrector, if_node)
|
|
93
|
+
condition = if_node.condition.source
|
|
94
|
+
branch = ternary_branch_source(if_node.if_branch)
|
|
95
|
+
outcomes = if_node.unless? ? "'' : #{branch}" : "#{branch} : ''"
|
|
96
|
+
|
|
97
|
+
corrector.replace(if_node, "#{condition} ? #{outcomes}")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def ternary_branch_source(branch)
|
|
101
|
+
return branch.source unless command_call?(branch)
|
|
102
|
+
|
|
103
|
+
arguments = branch.first_argument.source_range.join(branch.last_argument.source_range)
|
|
104
|
+
method = branch.source_range.begin.join(arguments.begin).source.rstrip
|
|
105
|
+
|
|
106
|
+
"#{method}(#{arguments.source})"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def command_call?(node)
|
|
110
|
+
return false unless node.type?(:call, :super, :yield) && node.arguments?
|
|
111
|
+
return false if node.call_type? && (node.operator_method? || node.setter_method?)
|
|
112
|
+
|
|
113
|
+
!node.parenthesized?
|
|
114
|
+
end
|
|
115
|
+
|
|
88
116
|
def empty_if_outcome?(node)
|
|
89
117
|
empty_branch_outcome?(node.if_branch)
|
|
90
118
|
end
|
|
@@ -99,9 +127,23 @@ module RuboCop
|
|
|
99
127
|
branch.nil_type? || (branch.str_type? && branch.value.empty?)
|
|
100
128
|
end
|
|
101
129
|
|
|
102
|
-
def ternary_style_autocorrect(node, outcome,
|
|
130
|
+
def ternary_style_autocorrect(node, outcome, keyword)
|
|
103
131
|
add_offense(node, message: MSG_TERNARY) do |corrector|
|
|
104
|
-
|
|
132
|
+
next if empty_string_conditional_in_condition?(node)
|
|
133
|
+
|
|
134
|
+
head = node.source_range.begin.join(outcome.source_range.begin)
|
|
135
|
+
tail = outcome.source_range.end.join(node.source_range.end)
|
|
136
|
+
|
|
137
|
+
corrector.remove(head)
|
|
138
|
+
corrector.replace(tail, " #{keyword} #{node.condition.source}")
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def empty_string_conditional_in_condition?(if_node)
|
|
143
|
+
if_node.condition.each_descendant(:if).any? do |node|
|
|
144
|
+
next false if node.modifier_form? || !node.parent.parent&.type?(*INTERPOLATED_TYPES)
|
|
145
|
+
|
|
146
|
+
empty_if_outcome?(node) || empty_else_outcome?(node)
|
|
105
147
|
end
|
|
106
148
|
end
|
|
107
149
|
end
|
|
@@ -131,7 +131,9 @@ module RuboCop
|
|
|
131
131
|
#
|
|
132
132
|
class EndlessMethod < Base
|
|
133
133
|
include ConfigurableEnforcedStyle
|
|
134
|
+
include LineLengthHelp
|
|
134
135
|
include EndlessMethodRewriter
|
|
136
|
+
include EndlessMethodBodyHelp
|
|
135
137
|
extend TargetRubyVersion
|
|
136
138
|
extend AutoCorrector
|
|
137
139
|
|
|
@@ -143,6 +145,25 @@ module RuboCop
|
|
|
143
145
|
MSG_REQUIRE_SINGLE = 'Use endless method definitions for single line methods.'
|
|
144
146
|
MSG_REQUIRE_ALWAYS = 'Use endless method definitions.'
|
|
145
147
|
|
|
148
|
+
# `Style/MethodDefParentheses` adds the same parentheses a parameter list needs in
|
|
149
|
+
# an endless definition, so letting both correct in the same pass would double them.
|
|
150
|
+
# `Style/GuardClause` turns a single `if` body into several statements,
|
|
151
|
+
# which an endless definition cannot hold.
|
|
152
|
+
# `Style/RaiseArgs` would drop the parentheses of a `raise` that becomes
|
|
153
|
+
# the body in the same pass, which a method passed as an argument needs.
|
|
154
|
+
# `Style/IfUnlessModifier` and `Style/WhileUntilModifier` would turn the body into
|
|
155
|
+
# a modifier form, which then applies to the whole endless definition.
|
|
156
|
+
def self.autocorrect_incompatible_with
|
|
157
|
+
[
|
|
158
|
+
Style::GuardClause,
|
|
159
|
+
Style::IfUnlessModifier,
|
|
160
|
+
Style::MethodCallWithArgsParentheses,
|
|
161
|
+
Style::MethodDefParentheses,
|
|
162
|
+
Style::RaiseArgs,
|
|
163
|
+
Style::WhileUntilModifier
|
|
164
|
+
]
|
|
165
|
+
end
|
|
166
|
+
|
|
146
167
|
def on_def(node)
|
|
147
168
|
return if node.assignment_method? || use_heredoc?(node)
|
|
148
169
|
|
|
@@ -177,9 +198,7 @@ module RuboCop
|
|
|
177
198
|
add_offense(node, message: MSG_MULTI_LINE) do |corrector|
|
|
178
199
|
correct_to_multiline(corrector, node)
|
|
179
200
|
end
|
|
180
|
-
elsif
|
|
181
|
-
return if too_long_when_made_endless?(node)
|
|
182
|
-
|
|
201
|
+
elsif convertible_to_endless?(node) && single_line_when_made_endless?(node)
|
|
183
202
|
add_offense(node, message: MSG_REQUIRE_SINGLE) do |corrector|
|
|
184
203
|
correct_to_endless(corrector, node)
|
|
185
204
|
end
|
|
@@ -187,14 +206,18 @@ module RuboCop
|
|
|
187
206
|
end
|
|
188
207
|
|
|
189
208
|
def handle_require_always_style(node)
|
|
190
|
-
return
|
|
191
|
-
return if too_long_when_made_endless?(node)
|
|
209
|
+
return unless convertible_to_endless?(node)
|
|
192
210
|
|
|
193
211
|
add_offense(node, message: MSG_REQUIRE_ALWAYS) do |corrector|
|
|
194
212
|
correct_to_endless(corrector, node)
|
|
195
213
|
end
|
|
196
214
|
end
|
|
197
215
|
|
|
216
|
+
def convertible_to_endless?(node)
|
|
217
|
+
!node.endless? && can_be_made_endless?(node) &&
|
|
218
|
+
!too_long_when_made_endless?(node) && !ambiguous_when_made_endless?(node)
|
|
219
|
+
end
|
|
220
|
+
|
|
198
221
|
def handle_disallow_style(node)
|
|
199
222
|
return unless node.endless?
|
|
200
223
|
|
|
@@ -213,12 +236,26 @@ module RuboCop
|
|
|
213
236
|
return false unless (body = node.body)
|
|
214
237
|
return true if body.any_str_type? && body.heredoc?
|
|
215
238
|
|
|
216
|
-
body.each_descendant(:
|
|
239
|
+
body.each_descendant(:any_str).any?(&:heredoc?)
|
|
217
240
|
end
|
|
218
241
|
|
|
219
242
|
def correct_to_endless(corrector, node)
|
|
220
|
-
|
|
221
|
-
|
|
243
|
+
if unparenthesized_arguments?(node)
|
|
244
|
+
corrector.replace(name_to_arguments_range(node), '(')
|
|
245
|
+
corrector.replace(signature_to_body_range(node), ') = ')
|
|
246
|
+
else
|
|
247
|
+
corrector.replace(signature_to_body_range(node), ' = ')
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
corrector.remove(body_to_end_range(node))
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def unparenthesized_arguments?(node)
|
|
254
|
+
node.arguments.any? && !node.arguments.loc.begin
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def name_to_arguments_range(node)
|
|
258
|
+
node.loc.name.end.join(node.arguments.source_range.begin)
|
|
222
259
|
end
|
|
223
260
|
|
|
224
261
|
def signature_to_body_range(node)
|
|
@@ -227,6 +264,10 @@ module RuboCop
|
|
|
227
264
|
signature_end.join(node.body.source_range.begin)
|
|
228
265
|
end
|
|
229
266
|
|
|
267
|
+
def body_to_end_range(node)
|
|
268
|
+
node.body.source_range.end.join(node.loc.end.end)
|
|
269
|
+
end
|
|
270
|
+
|
|
230
271
|
def endless_replacement(node)
|
|
231
272
|
<<~RUBY.strip
|
|
232
273
|
def #{receiver(node)}#{node.method_name}#{arguments(node)} = #{node.body.source}
|
|
@@ -238,21 +279,73 @@ module RuboCop
|
|
|
238
279
|
end
|
|
239
280
|
|
|
240
281
|
def arguments(node, missing = '')
|
|
241
|
-
node.arguments.any?
|
|
282
|
+
return missing unless node.arguments.any?
|
|
283
|
+
return parenthesized_arguments(node) if unparenthesized_arguments?(node)
|
|
284
|
+
|
|
285
|
+
node.arguments.source
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def parenthesized_arguments(node)
|
|
289
|
+
padding = space_inside_parens? ? ' ' : ''
|
|
290
|
+
|
|
291
|
+
"(#{padding}#{node.arguments.source}#{padding})"
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def space_inside_parens?
|
|
295
|
+
return false unless config.cop_enabled?('Layout/SpaceInsideParens')
|
|
296
|
+
|
|
297
|
+
config.for_cop('Layout/SpaceInsideParens')['EnforcedStyle'] != 'no_space'
|
|
242
298
|
end
|
|
243
299
|
|
|
244
300
|
def can_be_made_endless?(node)
|
|
245
|
-
|
|
301
|
+
return false unless node.body
|
|
302
|
+
return false if comment_in_discarded_range?(node)
|
|
303
|
+
|
|
304
|
+
endless_method_body?(node.body)
|
|
246
305
|
end
|
|
247
306
|
|
|
248
307
|
def single_line_when_made_endless?(node)
|
|
249
308
|
!endless_replacement(node).include?("\n")
|
|
250
309
|
end
|
|
251
310
|
|
|
311
|
+
def ambiguous_when_made_endless?(node)
|
|
312
|
+
(operation = ambiguous_operation(node)) && ambiguous_enclosing_operation?(operation)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# @!method ambiguous_operation(node)
|
|
316
|
+
def_node_matcher :ambiguous_operation, <<~PATTERN
|
|
317
|
+
^${
|
|
318
|
+
(if _ <any_def _>)
|
|
319
|
+
({and or} any_def _)
|
|
320
|
+
({while until} _ any_def)
|
|
321
|
+
}
|
|
322
|
+
PATTERN
|
|
323
|
+
|
|
324
|
+
def ambiguous_enclosing_operation?(operation)
|
|
325
|
+
operation.operator_keyword? || operation.modifier_form?
|
|
326
|
+
end
|
|
327
|
+
|
|
252
328
|
def too_long_when_made_endless?(node)
|
|
253
329
|
return false unless config.cop_enabled?('Layout/LineLength')
|
|
254
330
|
|
|
255
|
-
|
|
331
|
+
line_length(line_when_made_endless(node)) > max_line_length
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def comment_in_discarded_range?(node)
|
|
335
|
+
discarded_ranges = [signature_to_body_range(node), body_to_end_range(node)]
|
|
336
|
+
|
|
337
|
+
processed_source.each_comment_in_lines(node.first_line..node.last_line).any? do |comment|
|
|
338
|
+
discarded_ranges.any? { |range| range.contains?(comment.source_range) }
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def line_when_made_endless(node)
|
|
343
|
+
keyword_element = node.loc.keyword
|
|
344
|
+
code_before = keyword_element.source_line[0...keyword_element.column]
|
|
345
|
+
end_element = node.loc.end
|
|
346
|
+
code_after = end_element.source_line[end_element.last_column..]
|
|
347
|
+
|
|
348
|
+
"#{code_before}#{endless_replacement(node)}#{code_after}"
|
|
256
349
|
end
|
|
257
350
|
end
|
|
258
351
|
end
|
|
@@ -5,11 +5,15 @@ module RuboCop
|
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for exact regexp match inside `Regexp` literals.
|
|
7
7
|
#
|
|
8
|
+
# `=~` returns an index and `match` returns `MatchData`, so they are only
|
|
9
|
+
# autocorrected when their value is used as a condition. The same goes for
|
|
10
|
+
# safe navigation calls, which return `nil` for a `nil` receiver.
|
|
11
|
+
#
|
|
8
12
|
# @example
|
|
9
13
|
#
|
|
10
14
|
# # bad
|
|
11
15
|
# string =~ /\Astring\z/
|
|
12
|
-
#
|
|
16
|
+
# /\Astring\z/ === string
|
|
13
17
|
# string.match(/\Astring\z/)
|
|
14
18
|
# string.match?(/\Astring\z/)
|
|
15
19
|
#
|
|
@@ -27,14 +31,15 @@ module RuboCop
|
|
|
27
31
|
|
|
28
32
|
MSG = 'Use `%<prefer>s`.'
|
|
29
33
|
RESTRICT_ON_SEND = %i[=~ === !~ match match?].freeze
|
|
34
|
+
BOOLEAN_METHODS = %i[=== !~ match?].to_set.freeze
|
|
30
35
|
|
|
36
|
+
# `String#===` is plain equality, so `===` only matches with the regexp as its receiver.
|
|
31
37
|
# @!method exact_regexp_match(node)
|
|
32
38
|
def_node_matcher :exact_regexp_match, <<~PATTERN
|
|
33
|
-
|
|
34
|
-
_ {:=~
|
|
35
|
-
(regexp
|
|
36
|
-
|
|
37
|
-
(regopt)))
|
|
39
|
+
{
|
|
40
|
+
(call _ {:=~ :!~ :match :match?} (regexp (str $_) (regopt)))
|
|
41
|
+
(send (regexp (str $_) (regopt)) :=== _)
|
|
42
|
+
}
|
|
38
43
|
PATTERN
|
|
39
44
|
|
|
40
45
|
def on_send(node)
|
|
@@ -44,16 +49,48 @@ module RuboCop
|
|
|
44
49
|
return unless exact_match_pattern?(parsed_regexp)
|
|
45
50
|
|
|
46
51
|
string = escape_single_quotes(parsed_regexp[1].text)
|
|
47
|
-
|
|
52
|
+
subject = node.method?(:===) ? node.first_argument : receiver
|
|
53
|
+
prefer = "#{subject.source} #{new_method(node)} '#{string}'"
|
|
48
54
|
|
|
49
55
|
add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
|
|
50
|
-
corrector
|
|
56
|
+
autocorrect(corrector, node, prefer)
|
|
51
57
|
end
|
|
52
58
|
end
|
|
53
59
|
alias on_csend on_send
|
|
54
60
|
|
|
55
61
|
private
|
|
56
62
|
|
|
63
|
+
def autocorrect(corrector, node, prefer)
|
|
64
|
+
return unless boolean_result?(node) || truthiness_only?(node)
|
|
65
|
+
|
|
66
|
+
corrector.replace(node, parenthesize?(node) ? "(#{prefer})" : prefer)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# `=~` returns an index, `match` returns `MatchData` and `&.` can return `nil`,
|
|
70
|
+
# so replacing them with `==` is only safe where the value is used as a condition.
|
|
71
|
+
def boolean_result?(node)
|
|
72
|
+
node.send_type? && BOOLEAN_METHODS.include?(node.method_name)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def truthiness_only?(node)
|
|
76
|
+
return true unless node.value_used?
|
|
77
|
+
|
|
78
|
+
parent = node.parent
|
|
79
|
+
if parent.type?(:begin, :and, :or)
|
|
80
|
+
truthiness_only?(parent)
|
|
81
|
+
elsif parent.type?(:if, :while, :until)
|
|
82
|
+
parent.condition.equal?(node)
|
|
83
|
+
else
|
|
84
|
+
parent.send_type? && parent.method?(:!)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def parenthesize?(node)
|
|
89
|
+
return false unless (parent = node.parent)&.call_type?
|
|
90
|
+
|
|
91
|
+
parent.receiver.equal?(node) || (parent.operator_method? && !parent.method?(:[]))
|
|
92
|
+
end
|
|
93
|
+
|
|
57
94
|
# Escape characters that are special inside a single-quoted string so the
|
|
58
95
|
# generated literal (e.g. for `/\Afoo'bar\z/`) stays valid Ruby.
|
|
59
96
|
def escape_single_quotes(text)
|
|
@@ -50,8 +50,11 @@ module RuboCop
|
|
|
50
50
|
(block $_ (args $...) (yield $...))
|
|
51
51
|
PATTERN
|
|
52
52
|
|
|
53
|
+
# `Style/LambdaCall` rewrites the `call` that the block argument is appended to, and
|
|
54
|
+
# `Style/TrailingCommaInArguments` changes the trailing comma where it is appended,
|
|
55
|
+
# in the same pass.
|
|
53
56
|
def self.autocorrect_incompatible_with
|
|
54
|
-
[Lint::UnusedMethodArgument]
|
|
57
|
+
[Lint::UnusedMethodArgument, Style::LambdaCall, Style::TrailingCommaInArguments]
|
|
55
58
|
end
|
|
56
59
|
|
|
57
60
|
def initialize(config = nil, options = nil)
|
|
@@ -70,10 +73,13 @@ module RuboCop
|
|
|
70
73
|
# this is not a valid ruby pattern, but can happen in haml or erb,
|
|
71
74
|
# so this can cause crashes in haml_lint
|
|
72
75
|
return unless def_node
|
|
76
|
+
return if argument_forwarding?(def_node)
|
|
73
77
|
|
|
74
78
|
block_name = extract_block_name(def_node)
|
|
75
79
|
|
|
76
80
|
add_offense(block_node) do |corrector|
|
|
81
|
+
next unless can_add_block_argument?(def_node, send_node, block_name)
|
|
82
|
+
|
|
77
83
|
corrector.remove(block_body_range(block_node, send_node))
|
|
78
84
|
|
|
79
85
|
add_block_argument(send_node, corrector, block_name)
|
|
@@ -84,6 +90,11 @@ module RuboCop
|
|
|
84
90
|
|
|
85
91
|
private
|
|
86
92
|
|
|
93
|
+
# A block parameter can't be added next to `...`.
|
|
94
|
+
def argument_forwarding?(def_node)
|
|
95
|
+
def_node.arguments.any?(&:forward_arg_type?)
|
|
96
|
+
end
|
|
97
|
+
|
|
87
98
|
def extract_block_name(def_node)
|
|
88
99
|
if def_node.block_argument?
|
|
89
100
|
def_node.last_argument.name
|
|
@@ -92,6 +103,27 @@ module RuboCop
|
|
|
92
103
|
end
|
|
93
104
|
end
|
|
94
105
|
|
|
106
|
+
def can_add_block_argument?(def_node, send_node, block_name)
|
|
107
|
+
return false if block_name_taken?(def_node, block_name)
|
|
108
|
+
|
|
109
|
+
!send_node.zsuper_type? || zsuper_arguments_forwardable?(def_node)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def block_name_taken?(def_node, block_name)
|
|
113
|
+
return false if def_node.block_argument?
|
|
114
|
+
|
|
115
|
+
def_node.each_descendant(:argument, :lvasgn).any? { |node| node.name.to_s == block_name }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# A destructuring argument can only be passed on as the original value,
|
|
119
|
+
# and anonymous arguments can only be forwarded since Ruby 3.2.
|
|
120
|
+
def zsuper_arguments_forwardable?(def_node)
|
|
121
|
+
def_node.arguments.none? do |arg|
|
|
122
|
+
arg.mlhs_type? ||
|
|
123
|
+
(arg.type?(:restarg, :kwrestarg) && !arg.name && target_ruby_version < 3.2)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
95
127
|
def yielding_arguments?(block_args, yield_args)
|
|
96
128
|
yield_args = yield_args.dup.fill(
|
|
97
129
|
nil,
|
|
@@ -110,6 +142,8 @@ module RuboCop
|
|
|
110
142
|
insert_argument(node, corrector, block_name)
|
|
111
143
|
elsif empty_arguments?(node)
|
|
112
144
|
corrector.replace(node.arguments, "(&#{block_name})")
|
|
145
|
+
elsif index_call?(node)
|
|
146
|
+
corrector.replace(node.loc.selector, "[&#{block_name}]")
|
|
113
147
|
elsif call_like?(node)
|
|
114
148
|
correct_call_node(node, corrector, block_name)
|
|
115
149
|
else
|
|
@@ -122,6 +156,10 @@ module RuboCop
|
|
|
122
156
|
node.arguments.is_a?(RuboCop::AST::Node) && node.arguments.loc.begin
|
|
123
157
|
end
|
|
124
158
|
|
|
159
|
+
def index_call?(node)
|
|
160
|
+
node.send_type? && node.method?(:[]) && !node.loc?(:dot)
|
|
161
|
+
end
|
|
162
|
+
|
|
125
163
|
def call_like?(node)
|
|
126
164
|
node.type?(:call, :zsuper, :super)
|
|
127
165
|
end
|
|
@@ -141,19 +179,21 @@ module RuboCop
|
|
|
141
179
|
else
|
|
142
180
|
"&#{block_name}"
|
|
143
181
|
end
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
range = range_between(args_begin.begin_pos, args_end.end_pos)
|
|
150
|
-
corrector.remove(range)
|
|
182
|
+
if node.parenthesized?
|
|
183
|
+
corrector.replace(node.loc.begin.join(node.loc.end), "(#{new_arguments})")
|
|
184
|
+
else
|
|
185
|
+
corrector.insert_after(node, "(#{new_arguments})")
|
|
186
|
+
end
|
|
151
187
|
end
|
|
152
188
|
|
|
153
189
|
def build_new_arguments_for_zsuper(node)
|
|
154
190
|
def_node = node.each_ancestor(:any_def).first
|
|
155
|
-
def_node.arguments.
|
|
156
|
-
|
|
191
|
+
def_node.arguments.filter_map do |arg|
|
|
192
|
+
case arg.type
|
|
193
|
+
when :arg, :optarg then arg.name.to_s
|
|
194
|
+
when :kwarg, :kwoptarg then "#{arg.name}: #{arg.name}"
|
|
195
|
+
when :restarg, :kwrestarg then arg.source
|
|
196
|
+
end
|
|
157
197
|
end
|
|
158
198
|
end
|
|
159
199
|
|
|
@@ -97,6 +97,7 @@ module RuboCop
|
|
|
97
97
|
|
|
98
98
|
def on_send(node)
|
|
99
99
|
return unless offense_condition?(node)
|
|
100
|
+
return if comment_before_to_f?(node)
|
|
100
101
|
|
|
101
102
|
add_offense(node) do |corrector|
|
|
102
103
|
case style
|
|
@@ -141,8 +142,42 @@ module RuboCop
|
|
|
141
142
|
end
|
|
142
143
|
|
|
143
144
|
def remove_to_f_method(corrector, send_node)
|
|
144
|
-
corrector.remove(send_node.loc.
|
|
145
|
-
|
|
145
|
+
corrector.remove(send_node.receiver.source_range.end.join(send_node.loc.selector.end))
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def comment_before_to_f?(node)
|
|
149
|
+
return true if comment_around_fdiv_operator?(node)
|
|
150
|
+
|
|
151
|
+
operands_losing_to_f(node).any? do |operand|
|
|
152
|
+
next false unless to_f_method?(operand)
|
|
153
|
+
|
|
154
|
+
range = operand.receiver.source_range.end.join(operand.loc.selector.begin)
|
|
155
|
+
comment_in_range?(range)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def comment_around_fdiv_operator?(node)
|
|
160
|
+
return false unless style == :fdiv
|
|
161
|
+
|
|
162
|
+
range = node.receiver.source_range.end.join(node.first_argument.source_range.begin)
|
|
163
|
+
comment_in_range?(range)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def comment_in_range?(range)
|
|
167
|
+
processed_source.each_comment_in_lines(range.line..range.last_line).any? do |comment|
|
|
168
|
+
range.contains?(comment.source_range)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def operands_losing_to_f(node)
|
|
173
|
+
case style
|
|
174
|
+
when :left_coerce, :single_coerce
|
|
175
|
+
[node.first_argument]
|
|
176
|
+
when :right_coerce
|
|
177
|
+
[node.receiver]
|
|
178
|
+
else
|
|
179
|
+
[node.receiver, node.first_argument]
|
|
180
|
+
end
|
|
146
181
|
end
|
|
147
182
|
|
|
148
183
|
def correct_from_slash_to_fdiv(corrector, receiver, argument)
|
|
@@ -52,12 +52,23 @@ module RuboCop
|
|
|
52
52
|
EACH_LENGTH = 'each'.length
|
|
53
53
|
PREFER_EACH = 'Prefer `each` over `for`.'
|
|
54
54
|
PREFER_FOR = 'Prefer `for` over `each`.'
|
|
55
|
+
IMPLICIT_PARAMETERS = %i[_1 _2 _3 _4 _5 _6 _7 _8 _9 it].to_set.freeze
|
|
56
|
+
|
|
57
|
+
# `Style/RedundantBegin` removes the `begin` of a block body in the same pass,
|
|
58
|
+
# leaving a `rescue` or `ensure` clause directly in the `for` body.
|
|
59
|
+
# `Style/ItBlockParameter` replaces block parameter references in the body
|
|
60
|
+
# with `it` in the same pass, while they become `for` loop variables.
|
|
61
|
+
def self.autocorrect_incompatible_with
|
|
62
|
+
[Style::RedundantBegin, Style::ItBlockParameter]
|
|
63
|
+
end
|
|
55
64
|
|
|
56
65
|
def on_for(node)
|
|
57
66
|
if style == :each
|
|
58
67
|
add_offense(node, message: PREFER_EACH) do |corrector|
|
|
59
|
-
ForToEachCorrector.new(node).call(corrector)
|
|
60
68
|
opposite_style_detected
|
|
69
|
+
next unless convertible_to_each?(node)
|
|
70
|
+
|
|
71
|
+
ForToEachCorrector.new(node).call(corrector)
|
|
61
72
|
end
|
|
62
73
|
else
|
|
63
74
|
correct_style_detected
|
|
@@ -69,7 +80,8 @@ module RuboCop
|
|
|
69
80
|
|
|
70
81
|
if style == :for
|
|
71
82
|
return unless node.receiver
|
|
72
|
-
return
|
|
83
|
+
return unless convertible_to_for?(node)
|
|
84
|
+
return if rescue_or_ensure_body?(node) || do_end_block_in_collection?(node)
|
|
73
85
|
|
|
74
86
|
add_offense(node, message: PREFER_FOR) do |corrector|
|
|
75
87
|
EachToForCorrector.new(node).call(corrector)
|
|
@@ -85,13 +97,52 @@ module RuboCop
|
|
|
85
97
|
|
|
86
98
|
private
|
|
87
99
|
|
|
100
|
+
# The loop variable becomes a block parameter, which can only be a local
|
|
101
|
+
# variable, and the body can't refer to an implicit block parameter
|
|
102
|
+
# (`_1` or `it`) once the block has an explicit one.
|
|
103
|
+
def convertible_to_each?(node)
|
|
104
|
+
return false unless node.variable.each_node.all? { |n| n.type?(:lvasgn, :mlhs, :splat) }
|
|
105
|
+
return true unless node.body
|
|
106
|
+
|
|
107
|
+
node.body.each_node(:send, :lvar).none? { |ref| implicit_parameter?(ref, node) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Inside a numbered or `it` block, `_1` and `it` are local variables
|
|
111
|
+
# rather than method calls.
|
|
112
|
+
def implicit_parameter?(ref, for_node)
|
|
113
|
+
return false unless implicit_parameter_reference?(ref)
|
|
114
|
+
|
|
115
|
+
ref.each_ancestor.take_while { |ancestor| !ancestor.equal?(for_node) }
|
|
116
|
+
.none?(&:any_block_type?)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def implicit_parameter_reference?(ref)
|
|
120
|
+
return IMPLICIT_PARAMETERS.include?(ref.name) if ref.lvar_type?
|
|
121
|
+
|
|
122
|
+
IMPLICIT_PARAMETERS.include?(ref.method_name) && !ref.receiver && !ref.arguments?
|
|
123
|
+
end
|
|
124
|
+
|
|
88
125
|
def suspect_enumerable?(node)
|
|
89
126
|
node.multiline? && node.method?(:each) && !node.send_node.arguments?
|
|
90
127
|
end
|
|
91
128
|
|
|
129
|
+
def convertible_to_for?(node)
|
|
130
|
+
return false if node.type?(:numblock, :itblock)
|
|
131
|
+
return true unless node.arguments?
|
|
132
|
+
|
|
133
|
+
arguments = node.arguments
|
|
134
|
+
return false if arguments.children.one? && arguments.children.first.restarg_type?
|
|
135
|
+
|
|
136
|
+
arguments.each_descendant.all? { |arg| arg.type?(:arg, :restarg, :mlhs) }
|
|
137
|
+
end
|
|
138
|
+
|
|
92
139
|
def rescue_or_ensure_body?(node)
|
|
93
140
|
node.body&.type?(:rescue, :ensure)
|
|
94
141
|
end
|
|
142
|
+
|
|
143
|
+
def do_end_block_in_collection?(node)
|
|
144
|
+
node.receiver.each_node(:any_block).any?(&:keywords?)
|
|
145
|
+
end
|
|
95
146
|
end
|
|
96
147
|
end
|
|
97
148
|
end
|