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
|
@@ -17,6 +17,7 @@ module RuboCop
|
|
|
17
17
|
class MultilineIfModifier < Base
|
|
18
18
|
include StatementModifier
|
|
19
19
|
include Alignment
|
|
20
|
+
include RangeHelp
|
|
20
21
|
extend AutoCorrector
|
|
21
22
|
|
|
22
23
|
MSG = 'Favor a normal %<keyword>s-statement over a modifier ' \
|
|
@@ -27,31 +28,70 @@ module RuboCop
|
|
|
27
28
|
return unless node.modifier_form? && node.body.multiline?
|
|
28
29
|
|
|
29
30
|
add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
|
|
30
|
-
corrector
|
|
31
|
+
autocorrect(corrector, node)
|
|
31
32
|
end
|
|
32
33
|
ignore_node(node)
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
private
|
|
36
37
|
|
|
37
|
-
def
|
|
38
|
+
def autocorrect(corrector, node)
|
|
39
|
+
condition_heredoc, body_heredoc = [node.condition, node.body].map do |part|
|
|
40
|
+
next unless (range = heredoc_after(part, node))
|
|
41
|
+
|
|
42
|
+
corrector.remove(range)
|
|
43
|
+
range.source.chomp
|
|
44
|
+
end
|
|
45
|
+
corrector.replace(node, to_normal_if(node, condition_heredoc, body_heredoc))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Heredocs opened on the modifier line have their bodies after it, so
|
|
49
|
+
# they are moved along with the part of the `if` they belong to.
|
|
50
|
+
def heredoc_after(part, node)
|
|
51
|
+
ranges = part.each_node(:any_str).select(&:heredoc?).map do |heredoc|
|
|
52
|
+
heredoc.loc.heredoc_body.join(heredoc.loc.heredoc_end)
|
|
53
|
+
end
|
|
54
|
+
ranges.select! { |range| range.line > node.last_line }
|
|
55
|
+
return if ranges.empty?
|
|
56
|
+
|
|
57
|
+
range_by_whole_lines(ranges.reduce(:join), include_final_newline: true)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_normal_if(node, condition_heredoc, body_heredoc)
|
|
38
61
|
indented_body = indented_body(node.body, node)
|
|
39
62
|
condition = "#{node.keyword} #{node.condition.source}"
|
|
40
63
|
indented_end = "#{offset(node)}end"
|
|
41
64
|
|
|
42
|
-
[condition, indented_body, indented_end]
|
|
65
|
+
lines = [condition, condition_heredoc, indented_body, body_heredoc, indented_end]
|
|
66
|
+
lines.compact.join("\n")
|
|
43
67
|
end
|
|
44
68
|
|
|
45
69
|
def indented_body(body, node)
|
|
46
70
|
body_source = "#{offset(node)}#{body.source}"
|
|
47
|
-
|
|
48
|
-
|
|
71
|
+
verbatim_lines = verbatim_lines_in(body)
|
|
72
|
+
body_source.each_line.with_index(body.first_line).map do |line, line_number|
|
|
73
|
+
if line == "\n" || verbatim_lines.include?(line_number)
|
|
49
74
|
line
|
|
50
75
|
else
|
|
51
76
|
line.sub(/^\s{#{offset(node).length}}/, indentation(node))
|
|
52
77
|
end
|
|
53
78
|
end.join
|
|
54
79
|
end
|
|
80
|
+
|
|
81
|
+
def verbatim_lines_in(body)
|
|
82
|
+
body.each_node(:any_str).filter_map { |str| verbatim_line_range(str) }.flat_map(&:to_a)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# The indentation of a line that starts inside a string is part of the
|
|
86
|
+
# string. Only squiggly heredocs strip it from their bodies, and a `<<`
|
|
87
|
+
# terminator has to stay at the start of its line.
|
|
88
|
+
def verbatim_line_range(str)
|
|
89
|
+
if str.heredoc?
|
|
90
|
+
str.loc.heredoc_body.line..str.loc.heredoc_end.line unless str.source.start_with?('<<~')
|
|
91
|
+
elsif str.loc?(:begin) && str.loc?(:end)
|
|
92
|
+
(str.first_line + 1)..str.last_line
|
|
93
|
+
end
|
|
94
|
+
end
|
|
55
95
|
end
|
|
56
96
|
end
|
|
57
97
|
end
|
|
@@ -50,10 +50,17 @@ module RuboCop
|
|
|
50
50
|
# Requires `then` for write `in` and its body on the same line.
|
|
51
51
|
def require_then?(in_pattern_node)
|
|
52
52
|
return true unless in_pattern_node.pattern.single_line?
|
|
53
|
+
return true if ends_with_endless_range?(in_pattern_node.pattern)
|
|
53
54
|
return false unless in_pattern_node.body
|
|
54
55
|
|
|
55
56
|
same_line?(in_pattern_node, in_pattern_node.body)
|
|
56
57
|
end
|
|
58
|
+
|
|
59
|
+
def ends_with_endless_range?(node)
|
|
60
|
+
node.each_node(:range).any? do |range|
|
|
61
|
+
range.end.nil? && range.source_range.end_pos == node.source_range.end_pos
|
|
62
|
+
end
|
|
63
|
+
end
|
|
57
64
|
end
|
|
58
65
|
end
|
|
59
66
|
end
|
|
@@ -27,6 +27,7 @@ module RuboCop
|
|
|
27
27
|
def on_def(node)
|
|
28
28
|
return unless node.arguments?
|
|
29
29
|
return if opening_line(node) == closing_line(node)
|
|
30
|
+
return if node.arguments.any?(&:multiline?)
|
|
30
31
|
return if correction_exceeds_max_line_length?(node)
|
|
31
32
|
return unless (begin_of_arguments = node.arguments.loc.begin)
|
|
32
33
|
|
|
@@ -78,11 +79,7 @@ module RuboCop
|
|
|
78
79
|
def correction_exceeds_max_line_length?(node)
|
|
79
80
|
return false unless max_line_length
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def indentation_width(node)
|
|
85
|
-
processed_source.line_indentation(node.source_range.line)
|
|
82
|
+
node.source_range.column + definition_width(node) > max_line_length
|
|
86
83
|
end
|
|
87
84
|
|
|
88
85
|
def definition_width(node)
|
|
@@ -7,7 +7,8 @@ module RuboCop
|
|
|
7
7
|
#
|
|
8
8
|
# NOTE: `return if ... else ... end` is syntax error. If `return` is used before
|
|
9
9
|
# multiline ternary operator expression, it will be autocorrected to single-line
|
|
10
|
-
# ternary operator. The same is true for `break`, `next`,
|
|
10
|
+
# ternary operator. The same is true for `break`, `next`, `yield`, `super`,
|
|
11
|
+
# `defined?` without parentheses, and method call.
|
|
11
12
|
#
|
|
12
13
|
# @example
|
|
13
14
|
# # bad
|
|
@@ -39,7 +40,7 @@ module RuboCop
|
|
|
39
40
|
|
|
40
41
|
MSG_IF = 'Avoid multi-line ternary operators, use `if` or `unless` instead.'
|
|
41
42
|
MSG_SINGLE_LINE = 'Avoid multi-line ternary operators, use single-line instead.'
|
|
42
|
-
SINGLE_LINE_TYPES = %i[return break next send csend].freeze
|
|
43
|
+
SINGLE_LINE_TYPES = %i[return break next send csend super yield].freeze
|
|
43
44
|
|
|
44
45
|
def on_if(node)
|
|
45
46
|
return unless offense?(node)
|
|
@@ -90,7 +91,10 @@ module RuboCop
|
|
|
90
91
|
end
|
|
91
92
|
|
|
92
93
|
def enforce_single_line_ternary_operator?(node)
|
|
93
|
-
|
|
94
|
+
return false unless (parent = node.parent)
|
|
95
|
+
return !parent.parenthesized? if parent.defined_type?
|
|
96
|
+
|
|
97
|
+
SINGLE_LINE_TYPES.include?(parent.type) && !use_assignment_method?(parent)
|
|
94
98
|
end
|
|
95
99
|
|
|
96
100
|
def use_assignment_method?(node)
|
|
@@ -50,10 +50,17 @@ module RuboCop
|
|
|
50
50
|
unless when_node.conditions.first.first_line == when_node.conditions.last.last_line
|
|
51
51
|
return true
|
|
52
52
|
end
|
|
53
|
+
return true if ends_with_endless_range?(when_node.conditions.last)
|
|
53
54
|
return false unless when_node.body
|
|
54
55
|
|
|
55
56
|
same_line?(when_node, when_node.body)
|
|
56
57
|
end
|
|
58
|
+
|
|
59
|
+
def ends_with_endless_range?(node)
|
|
60
|
+
node.each_node(:range).any? do |range|
|
|
61
|
+
range.end.nil? && range.source_range.end_pos == node.source_range.end_pos
|
|
62
|
+
end
|
|
63
|
+
end
|
|
57
64
|
end
|
|
58
65
|
end
|
|
59
66
|
end
|
|
@@ -146,6 +146,13 @@ module RuboCop
|
|
|
146
146
|
|
|
147
147
|
MSG = 'Freeze mutable objects assigned to constants.'
|
|
148
148
|
|
|
149
|
+
# With `Recursive: true`, `.freeze` is appended to a nested literal. When the literal
|
|
150
|
+
# is the last element and spans multiple lines, the trailing comma lands between the literal
|
|
151
|
+
# and the `.freeze` appended in the same pass.
|
|
152
|
+
def self.autocorrect_incompatible_with
|
|
153
|
+
[Style::HashAsLastArrayItem, Style::TrailingCommaInArrayLiteral, Style::TrailingCommaInHashLiteral]
|
|
154
|
+
end
|
|
155
|
+
|
|
149
156
|
def on_casgn(node)
|
|
150
157
|
if node.expression.nil? # This is only the case for `CONST += ...` or similar
|
|
151
158
|
parent = node.parent
|
|
@@ -188,10 +195,12 @@ module RuboCop
|
|
|
188
195
|
end
|
|
189
196
|
|
|
190
197
|
def strict_check(value)
|
|
198
|
+
return if value.assignment?
|
|
191
199
|
return if immutable_literal?(value)
|
|
192
200
|
return if operation_produces_immutable_object?(value)
|
|
193
201
|
return if frozen_string_literal?(value)
|
|
194
202
|
return if shareable_constant_value?(value)
|
|
203
|
+
return if parenthesized_immutable_value?(value)
|
|
195
204
|
|
|
196
205
|
true
|
|
197
206
|
end
|
|
@@ -219,15 +228,32 @@ module RuboCop
|
|
|
219
228
|
return
|
|
220
229
|
end
|
|
221
230
|
|
|
231
|
+
return expand_omitted_hash_value(corrector, node.parent) if omitted_hash_value?(node)
|
|
232
|
+
|
|
233
|
+
wrap_for_freeze(corrector, node, expr)
|
|
234
|
+
corrector.insert_after(expr, '.freeze')
|
|
235
|
+
|
|
236
|
+
freeze_nested_literals(corrector, node) if recursive?
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def omitted_hash_value?(node)
|
|
240
|
+
node.parent&.pair_type? && node.parent.value_omission?
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def expand_omitted_hash_value(corrector, pair)
|
|
244
|
+
key = pair.key.value
|
|
245
|
+
|
|
246
|
+
corrector.replace(pair, "#{key}: #{key}.freeze")
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def wrap_for_freeze(corrector, node, expr)
|
|
222
250
|
if node.array_type? && !node.bracketed?
|
|
223
251
|
corrector.wrap(expr, '[', ']')
|
|
252
|
+
elsif node.hash_type? && !node.braces?
|
|
253
|
+
corrector.wrap(expr, '{ ', ' }')
|
|
224
254
|
elsif requires_parentheses?(node)
|
|
225
255
|
corrector.wrap(expr, '(', ')')
|
|
226
256
|
end
|
|
227
|
-
|
|
228
|
-
corrector.insert_after(expr, '.freeze')
|
|
229
|
-
|
|
230
|
-
freeze_nested_literals(corrector, node) if recursive?
|
|
231
257
|
end
|
|
232
258
|
|
|
233
259
|
# Recursively freezes every nested mutable literal inside an array or
|
|
@@ -253,13 +279,14 @@ module RuboCop
|
|
|
253
279
|
# Returns the child literals of an array or hash node that may
|
|
254
280
|
# themselves need freezing. For hashes, both keys and values are
|
|
255
281
|
# included. Percent-literal arrays (e.g. `%w(a b)`) are skipped because
|
|
256
|
-
# `.freeze` cannot be appended to their contents.
|
|
282
|
+
# `.freeze` cannot be appended to their contents. Splat elements are
|
|
283
|
+
# skipped because the splatted object is not stored in the array.
|
|
257
284
|
def literal_children(node)
|
|
258
285
|
case node.type
|
|
259
286
|
when :array
|
|
260
287
|
return [] if node.percent_literal?
|
|
261
288
|
|
|
262
|
-
node.children
|
|
289
|
+
node.children.reject(&:splat_type?)
|
|
263
290
|
when :hash
|
|
264
291
|
node.children.flat_map { |child| child.pair_type? ? child.children : [] }
|
|
265
292
|
else
|
|
@@ -293,12 +320,30 @@ module RuboCop
|
|
|
293
320
|
recent_shareable_value? node
|
|
294
321
|
end
|
|
295
322
|
|
|
323
|
+
def parenthesized_immutable_value?(value)
|
|
324
|
+
return false if !value.begin_type? || !value.children.one?
|
|
325
|
+
|
|
326
|
+
inner = value.children.first
|
|
327
|
+
|
|
328
|
+
!inner.assignment? && !strict_check(inner)
|
|
329
|
+
end
|
|
330
|
+
|
|
296
331
|
def frozen_regexp_or_range_literals?(node)
|
|
297
332
|
target_ruby_version >= 3.0 && node.type?(:regexp, :range)
|
|
298
333
|
end
|
|
299
334
|
|
|
300
335
|
def requires_parentheses?(node)
|
|
301
|
-
node.range_type? ||
|
|
336
|
+
if node.range_type? || node.operator_keyword? || unparenthesized_method_call?(node)
|
|
337
|
+
return true
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
node.if_type? && node.ternary?
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def unparenthesized_method_call?(node)
|
|
344
|
+
return false unless node.call_type?
|
|
345
|
+
|
|
346
|
+
node.loc.dot.nil? || (node.arguments.any? && !node.parenthesized?)
|
|
302
347
|
end
|
|
303
348
|
|
|
304
349
|
def correct_splat_expansion(corrector, expr, splat_value)
|
|
@@ -118,7 +118,8 @@ module RuboCop
|
|
|
118
118
|
if node.ternary?
|
|
119
119
|
node.if_branch
|
|
120
120
|
else
|
|
121
|
-
|
|
121
|
+
if_start = node.loc.begin || node.condition.source_range
|
|
122
|
+
range_between(if_start.end_pos, node.loc.else.begin_pos)
|
|
122
123
|
end
|
|
123
124
|
end
|
|
124
125
|
|
|
@@ -11,6 +11,13 @@ module RuboCop
|
|
|
11
11
|
# and their combinations, allowing safe replacement when the receiver matches.
|
|
12
12
|
# It works with variables, instance variables, class variables, and constants.
|
|
13
13
|
#
|
|
14
|
+
# @safety
|
|
15
|
+
# This cop is unsafe because the receiver may not be an Array. For a Hash,
|
|
16
|
+
# `hash[hash.size - 1]` looks up the key `hash.size - 1`, which `hash[-1]`
|
|
17
|
+
# does not. Even for an Array, `arr[arr.length - n]` wraps around to an
|
|
18
|
+
# element from the end when `n` exceeds the length, while `arr[-n]`
|
|
19
|
+
# returns `nil`.
|
|
20
|
+
#
|
|
14
21
|
# @example
|
|
15
22
|
# # bad
|
|
16
23
|
# arr[arr.count - 2]
|
|
@@ -188,8 +195,9 @@ module RuboCop
|
|
|
188
195
|
return false
|
|
189
196
|
end
|
|
190
197
|
return true if length_receiver.source == array_receiver.source
|
|
198
|
+
return false unless (base_receiver = extract_base_receiver(array_receiver))
|
|
191
199
|
|
|
192
|
-
|
|
200
|
+
base_receiver.source == (extract_base_receiver(length_receiver) || length_receiver).source
|
|
193
201
|
end
|
|
194
202
|
|
|
195
203
|
def receivers_match_strict?(length_receiver, array_receiver)
|
|
@@ -19,6 +19,19 @@ module RuboCop
|
|
|
19
19
|
|
|
20
20
|
MSG = 'Avoid using nested modifiers.'
|
|
21
21
|
|
|
22
|
+
# Operands that would otherwise bind differently next to `&&`/`||`, or
|
|
23
|
+
# not parse at all.
|
|
24
|
+
# @!method low_precedence?(node)
|
|
25
|
+
def_node_matcher :low_precedence?, <<~PATTERN
|
|
26
|
+
{
|
|
27
|
+
[{and or} semantic_operator?]
|
|
28
|
+
[send prefix_not?]
|
|
29
|
+
[if ternary?]
|
|
30
|
+
assignment?
|
|
31
|
+
[{csend yield super} arguments? !parenthesized? !operator_method?]
|
|
32
|
+
}
|
|
33
|
+
PATTERN
|
|
34
|
+
|
|
22
35
|
def on_while(node)
|
|
23
36
|
check(node)
|
|
24
37
|
end
|
|
@@ -63,8 +76,10 @@ module RuboCop
|
|
|
63
76
|
end
|
|
64
77
|
|
|
65
78
|
def left_hand_operand(node, operator)
|
|
66
|
-
expr = node.condition
|
|
67
|
-
|
|
79
|
+
expr = operand_source(node.condition)
|
|
80
|
+
if (node.condition.or_type? && operator == '&&') || low_precedence?(node.condition)
|
|
81
|
+
expr = "(#{expr})"
|
|
82
|
+
end
|
|
68
83
|
expr
|
|
69
84
|
end
|
|
70
85
|
|
|
@@ -72,17 +87,20 @@ module RuboCop
|
|
|
72
87
|
condition = node.condition
|
|
73
88
|
negated = left_hand_keyword != node.keyword
|
|
74
89
|
|
|
75
|
-
expr =
|
|
76
|
-
!condition.operator_method?
|
|
77
|
-
add_parentheses_to_method_arguments(condition)
|
|
78
|
-
else
|
|
79
|
-
condition.source
|
|
80
|
-
end
|
|
90
|
+
expr = operand_source(condition)
|
|
81
91
|
expr = "(#{expr})" if requires_parens?(condition, negated)
|
|
82
92
|
expr = "!#{expr}" if negated
|
|
83
93
|
expr
|
|
84
94
|
end
|
|
85
95
|
|
|
96
|
+
def operand_source(condition)
|
|
97
|
+
if condition.send_type? && !condition.arguments.empty? && !condition.operator_method?
|
|
98
|
+
add_parentheses_to_method_arguments(condition)
|
|
99
|
+
else
|
|
100
|
+
condition.source
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
86
104
|
def add_parentheses_to_method_arguments(send_node)
|
|
87
105
|
expr = +''
|
|
88
106
|
expr << "#{send_node.receiver.source}." if send_node.receiver
|
|
@@ -97,6 +115,7 @@ module RuboCop
|
|
|
97
115
|
# whole expression (e.g. `!(a && b)`, not `!a && b`).
|
|
98
116
|
(negated && node.operator_keyword?) ||
|
|
99
117
|
node.or_type? ||
|
|
118
|
+
low_precedence?(node) ||
|
|
100
119
|
!(RuboCop::AST::Node::COMPARISON_OPERATORS & node.children).empty?
|
|
101
120
|
end
|
|
102
121
|
end
|
|
@@ -26,7 +26,7 @@ module RuboCop
|
|
|
26
26
|
|
|
27
27
|
node.each_descendant(:if).select(&:ternary?).each do |nested_ternary|
|
|
28
28
|
add_offense(nested_ternary) do |corrector|
|
|
29
|
-
next if part_of_ignored_node?(node)
|
|
29
|
+
next if part_of_ignored_node?(node) || contains_heredoc?(node)
|
|
30
30
|
|
|
31
31
|
autocorrect(corrector, node)
|
|
32
32
|
ignore_node(node)
|
|
@@ -36,21 +36,39 @@ module RuboCop
|
|
|
36
36
|
|
|
37
37
|
private
|
|
38
38
|
|
|
39
|
+
def contains_heredoc?(node)
|
|
40
|
+
node.each_descendant(:any_str).any?(&:heredoc?)
|
|
41
|
+
end
|
|
42
|
+
|
|
39
43
|
def autocorrect(corrector, if_node)
|
|
40
44
|
replace_loc_and_whitespace(corrector, if_node.loc.question, "\n")
|
|
41
45
|
replace_loc_and_whitespace(corrector, if_node.loc.colon, "\nelse\n")
|
|
42
|
-
corrector
|
|
43
|
-
corrector.wrap(if_node, 'if ', "\nend")
|
|
44
|
-
end
|
|
46
|
+
remove_parentheses(corrector, if_node.if_branch)
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
source.delete_prefix('(').delete_suffix(')')
|
|
48
|
+
if modifier_position?(if_node)
|
|
49
|
+
corrector.wrap(if_node, '(if ', "\nend)")
|
|
49
50
|
else
|
|
50
|
-
|
|
51
|
+
corrector.wrap(if_node, 'if ', "\nend")
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
54
|
|
|
55
|
+
def remove_parentheses(corrector, node)
|
|
56
|
+
return unless node.begin_type? && node.parenthesized_call?
|
|
57
|
+
|
|
58
|
+
corrector.remove(node.loc.begin)
|
|
59
|
+
corrector.remove(node.loc.end)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# An `if` right after `return`, as the operand of `not` or `defined?`, or as
|
|
63
|
+
# the first argument of a call without parentheses would be parsed as a modifier.
|
|
64
|
+
def modifier_position?(node)
|
|
65
|
+
return false unless (parent = node.parent)
|
|
66
|
+
return true if parent.type?(:return, :break, :next)
|
|
67
|
+
return false if !parent.type?(:call, :super, :yield, :defined?) || parent.parenthesized?
|
|
68
|
+
|
|
69
|
+
parent.prefix_not? || parent.first_argument.equal?(node)
|
|
70
|
+
end
|
|
71
|
+
|
|
54
72
|
def replace_loc_and_whitespace(corrector, range, replacement)
|
|
55
73
|
corrector.replace(
|
|
56
74
|
range_with_surrounding_space(range: range, whitespace: true),
|
|
@@ -181,11 +181,11 @@ module RuboCop
|
|
|
181
181
|
def autocorrect_modifier(corrector, node)
|
|
182
182
|
body = node.if_branch || node.else_branch
|
|
183
183
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
"#{' ' * node.source_range.column}#{body.source}"
|
|
184
|
+
next_code = "next #{node.inverse_keyword} #{node.condition.source}\n" \
|
|
185
|
+
"#{' ' * node.source_range.column}"
|
|
187
186
|
|
|
188
|
-
corrector.
|
|
187
|
+
corrector.insert_before(node, next_code)
|
|
188
|
+
corrector.remove(range_between(body.source_range.end_pos, node.source_range.end_pos))
|
|
189
189
|
end
|
|
190
190
|
|
|
191
191
|
def autocorrect_block(corrector, node)
|
|
@@ -231,7 +231,7 @@ module RuboCop
|
|
|
231
231
|
|
|
232
232
|
# end_range starts with the final newline of the if body
|
|
233
233
|
lines = (node.source_range.line + 1)...node.loc.end.line
|
|
234
|
-
lines = lines.to_a -
|
|
234
|
+
lines = lines.to_a - string_content_lines(node)
|
|
235
235
|
# Skip blank lines
|
|
236
236
|
lines.reject { |lineno| /\A\s*\z/.match?(buffer.source_line(lineno)) }
|
|
237
237
|
end
|
|
@@ -250,11 +250,25 @@ module RuboCop
|
|
|
250
250
|
lines.map { |lineno| buffer.source_line(lineno) =~ /\S/ }.min
|
|
251
251
|
end
|
|
252
252
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
253
|
+
# Lines that start inside a string literal or a heredoc body, whose
|
|
254
|
+
# leading whitespace is part of the string value.
|
|
255
|
+
def string_content_lines(node)
|
|
256
|
+
buffer = node.source_range.source_buffer
|
|
257
|
+
ranges = node.each_node(:any_str).filter_map { |str| string_content_range(str) }
|
|
258
|
+
|
|
259
|
+
ranges.flat_map do |range|
|
|
260
|
+
(range.line..range.last_line).select do |lineno|
|
|
261
|
+
(range.begin_pos...range.end_pos).cover?(buffer.line_range(lineno).begin_pos)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def string_content_range(node)
|
|
267
|
+
if node.heredoc?
|
|
268
|
+
node.loc.heredoc_body
|
|
269
|
+
elsif node.loc?(:begin) && node.loc?(:end)
|
|
270
|
+
node.loc.begin.end.join(node.loc.end.begin)
|
|
271
|
+
end
|
|
258
272
|
end
|
|
259
273
|
|
|
260
274
|
def reindent_line(corrector, lineno, delta, buffer)
|
|
@@ -44,6 +44,7 @@ module RuboCop
|
|
|
44
44
|
|
|
45
45
|
def on_send(node)
|
|
46
46
|
return unless node.receiver
|
|
47
|
+
return if prefer_comparison? && comment_before_dot?(node)
|
|
47
48
|
|
|
48
49
|
style_check?(node) do
|
|
49
50
|
add_offense(node.loc.selector) do |corrector|
|
|
@@ -58,12 +59,19 @@ module RuboCop
|
|
|
58
59
|
|
|
59
60
|
private
|
|
60
61
|
|
|
62
|
+
def comment_before_dot?(node)
|
|
63
|
+
return false unless node.loc.dot
|
|
64
|
+
|
|
65
|
+
processed_source.contains_comment?(node.receiver.source_range.end.join(node.loc.dot))
|
|
66
|
+
end
|
|
67
|
+
|
|
61
68
|
def autocorrect_to_comparison(corrector, node)
|
|
62
|
-
range = node.
|
|
69
|
+
range = node.receiver.source_range.end.join(node.loc.selector.end)
|
|
63
70
|
corrector.replace(range, ' == nil')
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
|
|
72
|
+
return if !operator_expression?(node.parent) && !receiver_of_parent?(node)
|
|
73
|
+
|
|
74
|
+
corrector.wrap(node, '(', ')')
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
def autocorrect_to_predicate(corrector, node)
|
|
@@ -88,6 +96,10 @@ module RuboCop
|
|
|
88
96
|
node.assignment?
|
|
89
97
|
end
|
|
90
98
|
|
|
99
|
+
def receiver_of_parent?(node)
|
|
100
|
+
node.parent&.call_type? && node.parent.receiver.equal?(node)
|
|
101
|
+
end
|
|
102
|
+
|
|
91
103
|
def operator_send?(node)
|
|
92
104
|
node.send_type? && node.operator_method? && !node.method?(:[]) && !node.method?(:[]=)
|
|
93
105
|
end
|
|
@@ -130,7 +130,7 @@ module RuboCop
|
|
|
130
130
|
|
|
131
131
|
def operator_expression?(node)
|
|
132
132
|
node.operator_keyword? ||
|
|
133
|
-
(node.send_type? && node.
|
|
133
|
+
(node.send_type? && node.operator_method?) ||
|
|
134
134
|
(node.if_type? && node.ternary?) ||
|
|
135
135
|
node.type?(:range, :iflipflop, :eflipflop) ||
|
|
136
136
|
node.assignment?
|
|
@@ -54,11 +54,10 @@ module RuboCop
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def requires_parens?(child)
|
|
57
|
-
child.operator_keyword? ||
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
child.assignment?
|
|
57
|
+
return true if child.operator_keyword? || operator_method_requiring_parens?(child)
|
|
58
|
+
return true if child.if_type? && child.ternary?
|
|
59
|
+
|
|
60
|
+
child.type?(:range, :iflipflop, :eflipflop) || child.assignment?
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
def correct_opposite_method(corrector, range, child)
|
|
@@ -74,6 +73,12 @@ module RuboCop
|
|
|
74
73
|
def correct_without_parens(corrector, range)
|
|
75
74
|
corrector.replace(range, '!')
|
|
76
75
|
end
|
|
76
|
+
|
|
77
|
+
def operator_method_requiring_parens?(child)
|
|
78
|
+
return false unless child.send_type?
|
|
79
|
+
|
|
80
|
+
child.binary_operation? || (child.unary_operation? && child.method?(:~))
|
|
81
|
+
end
|
|
77
82
|
end
|
|
78
83
|
end
|
|
79
84
|
end
|
|
@@ -90,6 +90,7 @@ module RuboCop
|
|
|
90
90
|
def on_send(node)
|
|
91
91
|
numeric, replacement = check(node)
|
|
92
92
|
return unless numeric
|
|
93
|
+
return if handled_by_bitwise_predicate?(node)
|
|
93
94
|
|
|
94
95
|
return if allowed_method_name?(node.method_name) ||
|
|
95
96
|
node.each_ancestor(:send, :any_block).any? do |ancestor|
|
|
@@ -98,7 +99,10 @@ module RuboCop
|
|
|
98
99
|
|
|
99
100
|
message = format(MSG, prefer: replacement, current: node.source)
|
|
100
101
|
add_offense(node, message: message) do |corrector|
|
|
102
|
+
next if inside_corrected_predicate?(node)
|
|
103
|
+
|
|
101
104
|
corrector.replace(node, replacement)
|
|
105
|
+
ignore_node(node)
|
|
102
106
|
end
|
|
103
107
|
end
|
|
104
108
|
|
|
@@ -108,6 +112,16 @@ module RuboCop
|
|
|
108
112
|
allowed_method?(name) || matches_allowed_pattern?(name)
|
|
109
113
|
end
|
|
110
114
|
|
|
115
|
+
def handled_by_bitwise_predicate?(node)
|
|
116
|
+
bitwise_predicate_candidate?(node) &&
|
|
117
|
+
config.for_cop('Style/BitwisePredicate').fetch('Enabled') &&
|
|
118
|
+
BitwisePredicate.support_target_ruby_version?(target_ruby_version)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def inside_corrected_predicate?(node)
|
|
122
|
+
node.each_ancestor(:send).any? { |ancestor| ignored_node?(ancestor) }
|
|
123
|
+
end
|
|
124
|
+
|
|
111
125
|
def check(node)
|
|
112
126
|
numeric, operator =
|
|
113
127
|
if style == :predicate
|
|
@@ -124,7 +138,7 @@ module RuboCop
|
|
|
124
138
|
def replacement(node, numeric, operation)
|
|
125
139
|
if style == :predicate
|
|
126
140
|
[parenthesized_source(numeric), REPLACEMENTS.invert[operation.to_s]].join('.')
|
|
127
|
-
elsif
|
|
141
|
+
elsif comparison_requires_parentheses?(node)
|
|
128
142
|
"(#{numeric.source} #{REPLACEMENTS[operation.to_s]} 0)"
|
|
129
143
|
else
|
|
130
144
|
[numeric.source, REPLACEMENTS[operation.to_s], 0].join(' ')
|
|
@@ -140,7 +154,7 @@ module RuboCop
|
|
|
140
154
|
end
|
|
141
155
|
|
|
142
156
|
def require_parentheses?(node)
|
|
143
|
-
node.send_type? && node.
|
|
157
|
+
node.send_type? && node.operator_method? && !node.parenthesized?
|
|
144
158
|
end
|
|
145
159
|
|
|
146
160
|
def replacement_supported?(operator)
|
|
@@ -159,10 +173,11 @@ module RuboCop
|
|
|
159
173
|
end
|
|
160
174
|
end
|
|
161
175
|
|
|
162
|
-
def
|
|
163
|
-
return false unless (parent = node.parent)
|
|
176
|
+
def comparison_requires_parentheses?(node)
|
|
177
|
+
return false unless (parent = node.parent)&.call_type?
|
|
178
|
+
return true if parent.receiver.equal?(node)
|
|
164
179
|
|
|
165
|
-
parent.
|
|
180
|
+
parent.operator_method? && !parent.method?(:[]) && !parent.method?(:[]=)
|
|
166
181
|
end
|
|
167
182
|
|
|
168
183
|
# @!method predicate(node)
|
|
@@ -179,6 +194,14 @@ module RuboCop
|
|
|
179
194
|
def_node_matcher :inverted_comparison, <<~PATTERN
|
|
180
195
|
(send (int 0) ${:== :> :<} [$(...) !gvar_type?])
|
|
181
196
|
PATTERN
|
|
197
|
+
|
|
198
|
+
# @!method bitwise_predicate_candidate?(node)
|
|
199
|
+
def_node_matcher :bitwise_predicate_candidate?, <<~PATTERN
|
|
200
|
+
{
|
|
201
|
+
(send {(begin (send _ :& _)) (send _ :& _)} {:positive? :zero?})
|
|
202
|
+
(send {(begin (send _ :& _)) (send _ :& _)} {:> :==} (int 0))
|
|
203
|
+
}
|
|
204
|
+
PATTERN
|
|
182
205
|
end
|
|
183
206
|
end
|
|
184
207
|
end
|