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
|
@@ -69,9 +69,13 @@ module RuboCop
|
|
|
69
69
|
|
|
70
70
|
# @!method variable_argument?(node)
|
|
71
71
|
def_node_matcher :variable_argument?, <<~PATTERN
|
|
72
|
-
(send {str dstr} :%
|
|
72
|
+
(send {str dstr} :% !#autocorrectable?)
|
|
73
73
|
PATTERN
|
|
74
74
|
|
|
75
|
+
def on_new_investigation
|
|
76
|
+
@corrected_ranges = nil
|
|
77
|
+
end
|
|
78
|
+
|
|
75
79
|
def on_send(node)
|
|
76
80
|
formatter(node) do |selector|
|
|
77
81
|
detected_style = selector == :% ? :percent : selector
|
|
@@ -86,10 +90,12 @@ module RuboCop
|
|
|
86
90
|
|
|
87
91
|
private
|
|
88
92
|
|
|
93
|
+
# Only an argument that is provably not an array can be corrected,
|
|
94
|
+
# because `String#%` spreads an array argument and `format` does not.
|
|
89
95
|
def autocorrectable?(node)
|
|
90
|
-
return true if node.
|
|
96
|
+
return true if node.literal?
|
|
91
97
|
|
|
92
|
-
node.
|
|
98
|
+
node.call_type? && AUTOCORRECTABLE_METHODS.include?(node.method_name)
|
|
93
99
|
end
|
|
94
100
|
|
|
95
101
|
def message(detected_style)
|
|
@@ -101,7 +107,9 @@ module RuboCop
|
|
|
101
107
|
end
|
|
102
108
|
|
|
103
109
|
def autocorrect(corrector, node)
|
|
104
|
-
return if variable_argument?(node)
|
|
110
|
+
return if variable_argument?(node) || within_corrected_range?(node)
|
|
111
|
+
|
|
112
|
+
corrected_ranges << node.source_range
|
|
105
113
|
|
|
106
114
|
case node.method_name
|
|
107
115
|
when :%
|
|
@@ -116,6 +124,14 @@ module RuboCop
|
|
|
116
124
|
end
|
|
117
125
|
end
|
|
118
126
|
|
|
127
|
+
def within_corrected_range?(node)
|
|
128
|
+
corrected_ranges.any? { |range| range.contains?(node.source_range) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def corrected_ranges
|
|
132
|
+
@corrected_ranges ||= []
|
|
133
|
+
end
|
|
134
|
+
|
|
119
135
|
def autocorrect_from_percent(corrector, node)
|
|
120
136
|
percent_rhs = node.first_argument
|
|
121
137
|
args = case percent_rhs.type
|
|
@@ -146,14 +162,20 @@ module RuboCop
|
|
|
146
162
|
def format_single_parameter(arg)
|
|
147
163
|
# `format(fmt, *args)` is equivalent to `fmt % args`, so unwrap the splat
|
|
148
164
|
# and render the argument it splats.
|
|
149
|
-
return
|
|
165
|
+
return format_splatted_parameter(arg.children.first) if arg.splat_type?
|
|
150
166
|
|
|
151
167
|
source = arg.source
|
|
152
|
-
return "{ #{source} }" if arg.hash_type?
|
|
168
|
+
return "{ #{source} }" if arg.hash_type? && !arg.braces?
|
|
153
169
|
|
|
154
170
|
requires_parentheses?(arg) ? "(#{source})" : source
|
|
155
171
|
end
|
|
156
172
|
|
|
173
|
+
def format_splatted_parameter(arg)
|
|
174
|
+
return "#{arg.source}.to_a" if arg.hash_type?
|
|
175
|
+
|
|
176
|
+
format_single_parameter(arg)
|
|
177
|
+
end
|
|
178
|
+
|
|
157
179
|
# An argument that binds looser than `%` (a ternary, range, assignment, or
|
|
158
180
|
# operator call) must be parenthesized to keep its meaning.
|
|
159
181
|
def requires_parentheses?(arg)
|
|
@@ -142,7 +142,8 @@ module RuboCop
|
|
|
142
142
|
end
|
|
143
143
|
|
|
144
144
|
next_token = processed_source.tokens[token_number]
|
|
145
|
-
if next_token&.text&.valid_encoding? &&
|
|
145
|
+
if next_token&.text&.valid_encoding? &&
|
|
146
|
+
MagicComment.parse(next_token.text).encoding_specified?
|
|
146
147
|
token = next_token
|
|
147
148
|
end
|
|
148
149
|
|
|
@@ -186,40 +186,37 @@ module RuboCop
|
|
|
186
186
|
return if trivial?(node)
|
|
187
187
|
|
|
188
188
|
example = "#{conditional_keyword} #{condition.source}; #{scope_exiting_keyword}; end"
|
|
189
|
-
replacement =
|
|
190
|
-
#{conditional_keyword} #{condition.source}
|
|
191
|
-
#{scope_exiting_keyword}
|
|
192
|
-
end
|
|
193
|
-
RUBY
|
|
189
|
+
replacement = "#{conditional_keyword} #{condition.source}\n #{scope_exiting_keyword}"
|
|
194
190
|
end
|
|
195
191
|
|
|
196
192
|
add_offense(node.loc.keyword, message: format(MSG, example: example)) do |corrector|
|
|
197
193
|
next if node.else? && guard.nil?
|
|
198
194
|
|
|
199
|
-
autocorrect(corrector, node,
|
|
195
|
+
autocorrect(corrector, node, example, replacement, guard)
|
|
200
196
|
end
|
|
201
197
|
end
|
|
202
198
|
|
|
203
199
|
# rubocop:disable-next Metrics/AbcSize
|
|
204
|
-
def autocorrect(corrector, node,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if_branch = node.if_branch
|
|
208
|
-
else_branch = node.else_branch
|
|
209
|
-
|
|
200
|
+
def autocorrect(corrector, node, example, multiline_replacement, guard)
|
|
201
|
+
range = node.loc.keyword.join(node.condition.source_range)
|
|
202
|
+
corrector.replace(range, multiline_replacement || example)
|
|
210
203
|
corrector.replace(node.loc.begin, "\n") if node.then?
|
|
211
204
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
elsif (else_heredoc = find_heredoc_argument(else_branch))
|
|
215
|
-
autocorrect_heredoc_argument(corrector, node, else_heredoc, if_branch, guard)
|
|
216
|
-
else
|
|
217
|
-
corrector.remove(node.loc.end)
|
|
218
|
-
return unless node.else?
|
|
205
|
+
branches = [node.if_branch, node.else_branch]
|
|
206
|
+
guard_branch, leave_branch = guard == :else ? branches.reverse : branches
|
|
219
207
|
|
|
220
|
-
|
|
221
|
-
|
|
208
|
+
heredoc = find_heredoc_argument(guard_branch)
|
|
209
|
+
close_multiline_guard_clause(corrector, range, heredoc, guard) if multiline_replacement
|
|
210
|
+
|
|
211
|
+
if heredoc
|
|
212
|
+
return autocorrect_heredoc_argument(corrector, node, heredoc, leave_branch, guard)
|
|
222
213
|
end
|
|
214
|
+
|
|
215
|
+
corrector.remove(node.loc.end)
|
|
216
|
+
return unless node.else?
|
|
217
|
+
|
|
218
|
+
corrector.remove(node.loc.else)
|
|
219
|
+
corrector.remove(range_of_branch_to_remove(node, guard))
|
|
223
220
|
end
|
|
224
221
|
|
|
225
222
|
def heredoc?(argument)
|
|
@@ -241,6 +238,10 @@ module RuboCop
|
|
|
241
238
|
find_heredoc_argument(node.receiver)
|
|
242
239
|
end
|
|
243
240
|
|
|
241
|
+
def close_multiline_guard_clause(corrector, range, heredoc, guard)
|
|
242
|
+
corrector.insert_after(guard && heredoc ? heredoc.loc.heredoc_end : range, "\nend")
|
|
243
|
+
end
|
|
244
|
+
|
|
244
245
|
def autocorrect_heredoc_argument(corrector, node, heredoc_node, leave_branch, guard)
|
|
245
246
|
remove_whole_lines(corrector, node.loc.end)
|
|
246
247
|
return unless node.else?
|
|
@@ -282,7 +283,15 @@ module RuboCop
|
|
|
282
283
|
end
|
|
283
284
|
|
|
284
285
|
def accepted_form?(node, ending: false)
|
|
285
|
-
accepted_if?(node, ending) || node.condition.multiline? ||
|
|
286
|
+
accepted_if?(node, ending) || node.condition.multiline? ||
|
|
287
|
+
node.parent&.assignment? || endless_method_body?(node)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# The body of an endless method is a single expression, so there is no
|
|
291
|
+
# room for a guard clause followed by the rest of the code.
|
|
292
|
+
def endless_method_body?(node)
|
|
293
|
+
parent = node.parent
|
|
294
|
+
parent&.any_def_type? && parent.endless?
|
|
286
295
|
end
|
|
287
296
|
|
|
288
297
|
def trivial?(node)
|
|
@@ -44,6 +44,10 @@ module RuboCop
|
|
|
44
44
|
include ConfigurableEnforcedStyle
|
|
45
45
|
extend AutoCorrector
|
|
46
46
|
|
|
47
|
+
def self.autocorrect_incompatible_with
|
|
48
|
+
[Layout::MultilineArrayBraceLayout, Style::TrailingCommaInArrayLiteral]
|
|
49
|
+
end
|
|
50
|
+
|
|
47
51
|
def on_hash(node)
|
|
48
52
|
return if node.children.first&.kwsplat_type?
|
|
49
53
|
return unless (array = containing_array(node))
|
|
@@ -53,6 +53,12 @@ module RuboCop
|
|
|
53
53
|
# @!method hash_from_array?(node)
|
|
54
54
|
def_node_matcher :hash_from_array?, '(send (const {nil? cbase} :Hash) :[] ...)'
|
|
55
55
|
|
|
56
|
+
# A hash literal as the first argument needs the parentheses of the call,
|
|
57
|
+
# which `Style/MethodCallWithArgsParentheses` removes in the same pass.
|
|
58
|
+
def self.autocorrect_incompatible_with
|
|
59
|
+
[Style::MethodCallWithArgsParentheses]
|
|
60
|
+
end
|
|
61
|
+
|
|
56
62
|
def on_send(node)
|
|
57
63
|
return if part_of_ignored_node?(node) || !hash_from_array?(node)
|
|
58
64
|
|
|
@@ -95,12 +101,30 @@ module RuboCop
|
|
|
95
101
|
def register_offense_for_hash(node, hash_argument)
|
|
96
102
|
add_offense(node, message: MSG_LITERAL_HASH_ARG) do |corrector|
|
|
97
103
|
corrector.replace(node, "{#{hash_argument.source}}")
|
|
98
|
-
|
|
99
|
-
parent = node.parent
|
|
100
|
-
add_parentheses(parent, corrector) if parent&.send_type? && !parent.parenthesized?
|
|
104
|
+
add_parentheses_to_parent_call(node, corrector)
|
|
101
105
|
end
|
|
102
106
|
end
|
|
103
107
|
|
|
108
|
+
# Without parentheses, a hash literal at the start of the first argument would be
|
|
109
|
+
# parsed as a block. That includes an argument like `Hash[a, b].merge(c)`.
|
|
110
|
+
def add_parentheses_to_parent_call(node, corrector)
|
|
111
|
+
node = node.parent while starts_parent?(node)
|
|
112
|
+
parent = node.parent
|
|
113
|
+
return unless parent&.type?(:call, :super, :yield)
|
|
114
|
+
return if parent.parenthesized? || operator_syntax?(parent)
|
|
115
|
+
|
|
116
|
+
add_parentheses(parent, corrector)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def starts_parent?(node)
|
|
120
|
+
node.parent && node.parent.source_range.begin_pos == node.source_range.begin_pos
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# `foo[x]`, `foo.bar = x` and `foo == x` take a hash literal as it is.
|
|
124
|
+
def operator_syntax?(call)
|
|
125
|
+
call.setter_method? || (call.operator_method? && !call.loc?(:dot))
|
|
126
|
+
end
|
|
127
|
+
|
|
104
128
|
def register_offense_for_zip_method(node, zip_method)
|
|
105
129
|
add_offense(node, message: MSG_TO_H) do |corrector|
|
|
106
130
|
if zip_method.parenthesized?
|
|
@@ -135,11 +159,7 @@ module RuboCop
|
|
|
135
159
|
def correct_multi_argument(node)
|
|
136
160
|
add_offense(node, message: MSG_LITERAL_MULTI_ARG) do |corrector|
|
|
137
161
|
corrector.replace(node, args_to_hash(node.arguments))
|
|
138
|
-
|
|
139
|
-
parent = node.parent
|
|
140
|
-
if parent&.send_type? && !parent.method?(:to_h) && !parent.parenthesized?
|
|
141
|
-
add_parentheses(parent, corrector)
|
|
142
|
-
end
|
|
162
|
+
add_parentheses_to_parent_call(node, corrector)
|
|
143
163
|
end
|
|
144
164
|
end
|
|
145
165
|
|
|
@@ -34,6 +34,9 @@ module RuboCop
|
|
|
34
34
|
# # good - fetch with block is allowed
|
|
35
35
|
# hash.fetch(key) { default }
|
|
36
36
|
#
|
|
37
|
+
# # good - `ENV.fetch` is left to `Style/FetchEnvVar` when it is enabled
|
|
38
|
+
# ENV.fetch(key)
|
|
39
|
+
#
|
|
37
40
|
# @example EnforcedStyle: fetch
|
|
38
41
|
# # bad
|
|
39
42
|
# hash[key]
|
|
@@ -55,6 +58,9 @@ module RuboCop
|
|
|
55
58
|
|
|
56
59
|
RESTRICT_ON_SEND = %i[[] fetch].freeze
|
|
57
60
|
|
|
61
|
+
# @!method env_const?(node)
|
|
62
|
+
def_node_matcher :env_const?, '(const {nil? cbase} :ENV)'
|
|
63
|
+
|
|
58
64
|
def on_send(node)
|
|
59
65
|
return if (receiver = node.receiver) && allowed_receiver?(receiver)
|
|
60
66
|
return if part_of_ignored_node?(node)
|
|
@@ -76,28 +82,55 @@ module RuboCop
|
|
|
76
82
|
private
|
|
77
83
|
|
|
78
84
|
def offense_for_brackets?(node)
|
|
79
|
-
style
|
|
80
|
-
|
|
85
|
+
return false if style != :brackets || !node.receiver
|
|
86
|
+
return false if !node.method?(:fetch) || !node.arguments.one?
|
|
87
|
+
return false if node.block_literal? || node.csend_type?
|
|
88
|
+
|
|
89
|
+
!exempt_from_brackets?(node)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def exempt_from_brackets?(node)
|
|
93
|
+
return true if forwarded_or_splatted?(node.first_argument)
|
|
94
|
+
|
|
95
|
+
comment_before_dot?(node) || env_fetch_preferred?(node.receiver)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def forwarded_or_splatted?(argument)
|
|
99
|
+
if argument.hash_type? && !argument.braces?
|
|
100
|
+
argument.children.any? { |child| child.type?(:kwsplat, :forwarded_kwrestarg) }
|
|
101
|
+
else
|
|
102
|
+
argument.type?(:forwarded_args, :forwarded_restarg, :splat, :block_pass)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def comment_before_dot?(node)
|
|
107
|
+
processed_source.contains_comment?(node.receiver.source_range.end.join(node.loc.dot))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def env_fetch_preferred?(receiver)
|
|
111
|
+
env_const?(receiver) && processed_source.registry.enabled?(Style::FetchEnvVar, config)
|
|
81
112
|
end
|
|
82
113
|
|
|
83
114
|
def offense_for_fetch?(node)
|
|
84
|
-
style == :fetch && node.method?(:[]) && node.arguments.one?
|
|
115
|
+
style == :fetch && node.method?(:[]) && node.arguments.one? &&
|
|
116
|
+
!compound_assignment_target?(node)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def compound_assignment_target?(node)
|
|
120
|
+
node.parent&.type?(:op_asgn, :or_asgn, :and_asgn) && node.parent.children.first == node
|
|
85
121
|
end
|
|
86
122
|
|
|
87
123
|
def correct_fetch_to_brackets(corrector, node)
|
|
88
124
|
key = node.first_argument.source
|
|
89
125
|
|
|
90
|
-
corrector.replace(node.
|
|
126
|
+
corrector.replace(node.receiver.source_range.end.join(node.source_range.end), "[#{key}]")
|
|
91
127
|
end
|
|
92
128
|
|
|
93
129
|
def correct_brackets_to_fetch(corrector, node)
|
|
94
130
|
key = node.first_argument.source
|
|
131
|
+
replacement = node.loc.dot ? "fetch(#{key})" : ".fetch(#{key})"
|
|
95
132
|
|
|
96
|
-
|
|
97
|
-
corrector.replace(node.loc.dot.join(node.source_range.end), "&.fetch(#{key})")
|
|
98
|
-
else
|
|
99
|
-
corrector.replace(node.loc.selector.join(node.source_range.end), ".fetch(#{key})")
|
|
100
|
-
end
|
|
133
|
+
corrector.replace(node.loc.selector.join(node.source_range.end), replacement)
|
|
101
134
|
end
|
|
102
135
|
end
|
|
103
136
|
end
|
|
@@ -142,6 +142,13 @@ module RuboCop
|
|
|
142
142
|
MSG_HASH_ROCKETS = 'Use hash rockets syntax.'
|
|
143
143
|
NO_MIXED_KEYS_STYLES = %i[ruby19_no_mixed_keys no_mixed_keys].freeze
|
|
144
144
|
|
|
145
|
+
# Omitting the last hash value of a call without parentheses adds the parentheses,
|
|
146
|
+
# which `Style/MethodCallWithArgsParentheses` and `Style/SuperWithArgsParentheses`
|
|
147
|
+
# would add again in the same pass.
|
|
148
|
+
def self.autocorrect_incompatible_with
|
|
149
|
+
[Style::MethodCallWithArgsParentheses, Style::SuperWithArgsParentheses]
|
|
150
|
+
end
|
|
151
|
+
|
|
145
152
|
def on_hash(node)
|
|
146
153
|
pairs = node.pairs
|
|
147
154
|
|
|
@@ -241,6 +248,8 @@ module RuboCop
|
|
|
241
248
|
def check(pairs, delim, msg)
|
|
242
249
|
pairs.each do |pair|
|
|
243
250
|
if pair.delimiter == delim
|
|
251
|
+
next if pair.value_omission? && keyword_value?(pair)
|
|
252
|
+
|
|
244
253
|
location = pair.source_range.begin.join(pair.loc.operator)
|
|
245
254
|
add_offense(location, message: msg) do |corrector|
|
|
246
255
|
autocorrect(corrector, pair)
|
|
@@ -283,12 +292,16 @@ module RuboCop
|
|
|
283
292
|
end
|
|
284
293
|
|
|
285
294
|
def autocorrect_hash_rockets(corrector, pair_node)
|
|
286
|
-
op = pair_node.loc.operator
|
|
287
|
-
|
|
288
295
|
key_with_hash_rocket = ":#{pair_node.key.source}#{pair_node.inverse_delimiter(true)}"
|
|
289
296
|
key_with_hash_rocket += pair_node.key.source if pair_node.value_omission?
|
|
290
297
|
corrector.replace(pair_node.key, key_with_hash_rocket)
|
|
291
|
-
corrector.remove(
|
|
298
|
+
corrector.remove(operator_range(pair_node))
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def operator_range(pair_node)
|
|
302
|
+
op = pair_node.loc.operator
|
|
303
|
+
|
|
304
|
+
range_with_surrounding_space(op, newlines: !pair_node.value_omission?)
|
|
292
305
|
end
|
|
293
306
|
|
|
294
307
|
def autocorrect_no_mixed_keys(corrector, pair_node)
|
|
@@ -188,7 +188,7 @@ module RuboCop
|
|
|
188
188
|
end
|
|
189
189
|
end
|
|
190
190
|
|
|
191
|
-
# rubocop:disable-next Metrics/
|
|
191
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
192
192
|
def check_expressions(node, expressions, insert_position)
|
|
193
193
|
return if expressions.any?(&:nil?)
|
|
194
194
|
|
|
@@ -198,8 +198,7 @@ module RuboCop
|
|
|
198
198
|
add_offense(expression) do |corrector|
|
|
199
199
|
next if node.if_type? && (node.ternary? || node.then?)
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
corrector.remove(range)
|
|
201
|
+
corrector.remove(removal_range(expression))
|
|
203
202
|
next if inserted_expression
|
|
204
203
|
|
|
205
204
|
if node.parent&.assignment?
|
|
@@ -213,6 +212,26 @@ module RuboCop
|
|
|
213
212
|
end
|
|
214
213
|
end
|
|
215
214
|
|
|
215
|
+
def removal_range(expression)
|
|
216
|
+
range = expression.source_range
|
|
217
|
+
whole_lines = range_by_whole_lines(range, include_final_newline: true)
|
|
218
|
+
return whole_lines if whole_lines.source.strip == expression.source
|
|
219
|
+
|
|
220
|
+
range_with_separator(range)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def range_with_separator(range)
|
|
224
|
+
source = range.source_buffer.source
|
|
225
|
+
|
|
226
|
+
if (following = source[range.end_pos..][/\A[ \t]*;[ \t]*/])
|
|
227
|
+
range.adjust(end_pos: following.length)
|
|
228
|
+
elsif (preceding = source[0...range.begin_pos][/[ \t]*;[ \t]*\z/])
|
|
229
|
+
range.adjust(begin_pos: -preceding.length)
|
|
230
|
+
else
|
|
231
|
+
range_with_surrounding_space(range, side: :right, newlines: false)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
216
235
|
def correct_assignment(corrector, node, expression, insert_position)
|
|
217
236
|
indentation = indentation_of(node.parent)
|
|
218
237
|
|
|
@@ -75,7 +75,7 @@ module RuboCop
|
|
|
75
75
|
return if comments_between_else_and_if?(node, else_branch)
|
|
76
76
|
|
|
77
77
|
add_offense(else_branch.loc.keyword) do |corrector|
|
|
78
|
-
next if part_of_ignored_node?(node)
|
|
78
|
+
next if part_of_ignored_node?(node) || shares_lines_with_other_code?(else_branch)
|
|
79
79
|
|
|
80
80
|
autocorrect(corrector, else_branch)
|
|
81
81
|
ignore_node(node)
|
|
@@ -84,6 +84,26 @@ module RuboCop
|
|
|
84
84
|
|
|
85
85
|
private
|
|
86
86
|
|
|
87
|
+
# The `elsif` correction moves and removes whole lines, so it can't be applied when
|
|
88
|
+
# the nested `if` condition, its `if` branch or its `end` shares a line with other code.
|
|
89
|
+
# A modifier `if` on the `else` line would leave its body on the `elsif` line.
|
|
90
|
+
def shares_lines_with_other_code?(node)
|
|
91
|
+
return false if node.then?
|
|
92
|
+
return !begins_its_line?(node.source_range) if node.modifier_form?
|
|
93
|
+
|
|
94
|
+
moved_ranges(node).any? { |range| !begins_its_line?(range) || !ends_its_line?(range) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def moved_ranges(node)
|
|
98
|
+
ranges = [if_condition_range(node, node.condition), find_end_range(node)]
|
|
99
|
+
ranges << range_with_comments(node.if_branch) if node.if_branch
|
|
100
|
+
ranges
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def ends_its_line?(range)
|
|
104
|
+
processed_source.lines[range.last_line - 1][range.last_column..].match?(/\A\s*(#|\z)/)
|
|
105
|
+
end
|
|
106
|
+
|
|
87
107
|
def autocorrect(corrector, node)
|
|
88
108
|
if node.then?
|
|
89
109
|
# If the nested `if` is a then node, correct it first,
|
|
@@ -100,6 +100,7 @@ module RuboCop
|
|
|
100
100
|
pattern_matching_nodes(condition).any?
|
|
101
101
|
|
|
102
102
|
return unless (msg = message(node))
|
|
103
|
+
return if modifier_sharing_line_in_logical_operator?(node)
|
|
103
104
|
|
|
104
105
|
add_offense(node.loc.keyword, message: format(msg, keyword: node.keyword)) do |corrector|
|
|
105
106
|
next if part_of_ignored_node?(node)
|
|
@@ -159,7 +160,7 @@ module RuboCop
|
|
|
159
160
|
corrector.replace(node, replacement)
|
|
160
161
|
end
|
|
161
162
|
|
|
162
|
-
def replacement_for_modifier_form(corrector, node)
|
|
163
|
+
def replacement_for_modifier_form(corrector, node)
|
|
163
164
|
comment = comment_on_node_line(node)
|
|
164
165
|
if comment && too_long_due_to_comment_after_modifier?(node, comment)
|
|
165
166
|
remove_comment(corrector, node, comment)
|
|
@@ -167,12 +168,11 @@ module RuboCop
|
|
|
167
168
|
return to_modifier_form_with_move_comment(node, indent(node), comment)
|
|
168
169
|
end
|
|
169
170
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
remove_heredoc(corrector, heredoc)
|
|
171
|
+
heredoc_ranges = heredoc_ranges_in(node)
|
|
172
|
+
unless heredoc_ranges.empty?
|
|
173
|
+
remove_heredoc(corrector, heredoc_ranges)
|
|
174
174
|
|
|
175
|
-
return to_normal_form_with_heredoc(node, indent(node)
|
|
175
|
+
return to_normal_form_with_heredoc(node, indent(node))
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
to_normal_form(node, indent(node))
|
|
@@ -234,11 +234,18 @@ module RuboCop
|
|
|
234
234
|
end
|
|
235
235
|
|
|
236
236
|
def collection_from_ancestor(node)
|
|
237
|
-
return node if node&.type?(:array, :call)
|
|
237
|
+
return node if node&.type?(:array, :call, :and, :or)
|
|
238
238
|
|
|
239
239
|
node.parent if node&.type?(:pair)
|
|
240
240
|
end
|
|
241
241
|
|
|
242
|
+
def modifier_sharing_line_in_logical_operator?(node)
|
|
243
|
+
collection = find_containing_collection(node)
|
|
244
|
+
return false if !node.modifier_form? || !collection&.operator_keyword?
|
|
245
|
+
|
|
246
|
+
another_modifier_if_on_same_line?(node)
|
|
247
|
+
end
|
|
248
|
+
|
|
242
249
|
def another_modifier_if_on_same_line?(node)
|
|
243
250
|
collection = find_containing_collection(node)
|
|
244
251
|
return false unless collection
|
|
@@ -275,16 +282,20 @@ module RuboCop
|
|
|
275
282
|
RUBY
|
|
276
283
|
end
|
|
277
284
|
|
|
278
|
-
def
|
|
279
|
-
|
|
285
|
+
def remove_heredoc(corrector, heredoc_ranges)
|
|
286
|
+
heredoc_ranges.each do |range|
|
|
287
|
+
corrector.remove(range_by_whole_lines(range, include_final_newline: true))
|
|
288
|
+
end
|
|
289
|
+
end
|
|
280
290
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
#{
|
|
284
|
-
|
|
285
|
-
#{indentation} #{
|
|
286
|
-
|
|
287
|
-
|
|
291
|
+
def to_normal_form_with_heredoc(node, indentation)
|
|
292
|
+
[
|
|
293
|
+
"#{node.keyword} #{node.condition.source}",
|
|
294
|
+
*heredoc_ranges_in(node.condition).map(&:source),
|
|
295
|
+
"#{indentation} #{node.body.source}",
|
|
296
|
+
*heredoc_ranges_in(node.if_branch).map(&:source),
|
|
297
|
+
"#{indentation}end"
|
|
298
|
+
].join("\n")
|
|
288
299
|
end
|
|
289
300
|
|
|
290
301
|
def to_modifier_form_with_move_comment(node, indentation, comment)
|
|
@@ -294,17 +305,12 @@ module RuboCop
|
|
|
294
305
|
RUBY
|
|
295
306
|
end
|
|
296
307
|
|
|
297
|
-
def
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
[heredoc_body, heredoc_end]
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
def remove_heredoc(corrector, heredoc)
|
|
305
|
-
heredoc.each do |range|
|
|
306
|
-
corrector.remove(range_by_whole_lines(range, include_final_newline: true))
|
|
308
|
+
def heredoc_ranges_in(body)
|
|
309
|
+
ranges = body.each_node(:any_str).select(&:heredoc?).map do |heredoc|
|
|
310
|
+
range_by_whole_lines(heredoc.loc.heredoc_body.join(heredoc.loc.heredoc_end))
|
|
307
311
|
end
|
|
312
|
+
# A heredoc opened in another heredoc's interpolation is moved along with it.
|
|
313
|
+
ranges.reject { |range| ranges.any? { |other| other.contains?(range) } }
|
|
308
314
|
end
|
|
309
315
|
|
|
310
316
|
def comment_on_node_line(node)
|
|
@@ -28,6 +28,10 @@ module RuboCop
|
|
|
28
28
|
|
|
29
29
|
MSG = 'Avoid modifier `%<keyword>s` after another conditional.'
|
|
30
30
|
|
|
31
|
+
def self.autocorrect_incompatible_with
|
|
32
|
+
[Style::SoleNestedConditional]
|
|
33
|
+
end
|
|
34
|
+
|
|
31
35
|
# rubocop:disable-next Metrics/AbcSize
|
|
32
36
|
def on_if(node)
|
|
33
37
|
return unless node.modifier_form? && node.body.if_type?
|
|
@@ -21,6 +21,10 @@ module RuboCop
|
|
|
21
21
|
MSG_NEWLINE = 'Do not use `%<keyword>s %<expr>s;` - use a newline instead.'
|
|
22
22
|
MSG_TERNARY = 'Do not use `%<keyword>s %<expr>s;` - use a ternary operator instead.'
|
|
23
23
|
|
|
24
|
+
LOWER_PRECEDENCE_THAN_TERNARY_TYPES = %i[
|
|
25
|
+
and or if rescue any_match_pattern while until while_post until_post
|
|
26
|
+
].freeze
|
|
27
|
+
|
|
24
28
|
def on_normal_if_unless(node)
|
|
25
29
|
return if node.parent&.if_type?
|
|
26
30
|
return if part_of_ignored_node?(node)
|
|
@@ -77,12 +81,14 @@ module RuboCop
|
|
|
77
81
|
def replacement(node)
|
|
78
82
|
return correct_elsif(node) if node.else_branch&.if_type?
|
|
79
83
|
|
|
80
|
-
then_code = node.if_branch ?
|
|
81
|
-
else_code = node.else_branch ?
|
|
84
|
+
then_code = node.if_branch ? ternary_operand(node.if_branch) : 'nil'
|
|
85
|
+
else_code = node.else_branch ? ternary_operand(node.else_branch) : 'nil'
|
|
82
86
|
|
|
83
87
|
then_code, else_code = else_code, then_code if node.unless?
|
|
84
88
|
|
|
85
|
-
"#{ternary_condition(node)} ? #{then_code} : #{else_code}"
|
|
89
|
+
ternary = "#{ternary_condition(node)} ? #{then_code} : #{else_code}"
|
|
90
|
+
|
|
91
|
+
operand_of_parent?(node) ? "(#{ternary})" : ternary
|
|
86
92
|
end
|
|
87
93
|
|
|
88
94
|
def correct_elsif(node)
|
|
@@ -108,7 +114,34 @@ module RuboCop
|
|
|
108
114
|
# `(a = b) ? c : d`), changing what gets assigned.
|
|
109
115
|
def ternary_condition(node)
|
|
110
116
|
condition = node.condition
|
|
111
|
-
condition.assignment? ? "(#{condition.source})" : condition
|
|
117
|
+
condition.assignment? ? "(#{condition.source})" : ternary_operand(condition)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def ternary_operand(expr)
|
|
121
|
+
lower_precedence_than_ternary?(expr) ? "(#{expr.source})" : build_expression(expr)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Expressions that bind looser than `?:` (or cannot appear unparenthesized
|
|
125
|
+
# inside it) would otherwise absorb or break the ternary.
|
|
126
|
+
def lower_precedence_than_ternary?(node)
|
|
127
|
+
return true if node.type?(*LOWER_PRECEDENCE_THAN_TERNARY_TYPES)
|
|
128
|
+
return true if node.send_type? && (node.prefix_not? || prefix_bang_on_command?(node))
|
|
129
|
+
|
|
130
|
+
node.type?(:yield, :super, :defined?) && node.arguments.any? && !node.parenthesized?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def operand_of_parent?(node)
|
|
134
|
+
return false unless (parent = node.parent)
|
|
135
|
+
return true if parent.operator_keyword?
|
|
136
|
+
|
|
137
|
+
parent.call_type? && call_operand?(parent, node)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def call_operand?(call, node)
|
|
141
|
+
return false if call.prefix_not?
|
|
142
|
+
return true if call.receiver.equal?(node)
|
|
143
|
+
|
|
144
|
+
call.operator_method? && !call.method?(:[]) && !call.method?(:[]=)
|
|
112
145
|
end
|
|
113
146
|
|
|
114
147
|
def build_else_branch(second_condition)
|
|
@@ -131,6 +164,16 @@ module RuboCop
|
|
|
131
164
|
result
|
|
132
165
|
end
|
|
133
166
|
|
|
167
|
+
# `!foo a` leaves the arguments of `foo` unparenthesized at the end of the operand,
|
|
168
|
+
# where they would absorb the rest of the ternary.
|
|
169
|
+
def prefix_bang_on_command?(node)
|
|
170
|
+
return false unless node.prefix_bang?
|
|
171
|
+
|
|
172
|
+
receiver = node.receiver
|
|
173
|
+
|
|
174
|
+
require_argument_parentheses?(receiver) || lower_precedence_than_ternary?(receiver)
|
|
175
|
+
end
|
|
176
|
+
|
|
134
177
|
def require_argument_parentheses?(node)
|
|
135
178
|
return false if !node.call_type? || node.arithmetic_operation?
|
|
136
179
|
|