rubocop 1.91.0 → 1.92.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +3 -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_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_validator.rb +11 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
- 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 +22 -7
- data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +23 -2
- 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/block_alignment.rb +27 -0
- data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
- data/lib/rubocop/cop/layout/class_structure.rb +9 -1
- data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +14 -5
- data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +16 -10
- data/lib/rubocop/cop/layout/extra_spacing.rb +4 -0
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +5 -3
- 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 +2 -0
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
- 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 +52 -18
- 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 +7 -0
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +14 -0
- 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 +15 -2
- 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_before_block_braces.rb +4 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +31 -8
- 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/empty_conditional_body.rb +3 -1
- 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/number_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +12 -1
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
- 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/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 +17 -14
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +5 -1
- data/lib/rubocop/cop/mixin/code_length.rb +10 -2
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
- data/lib/rubocop/cop/mixin/endless_method_body_help.rb +69 -0
- data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -1
- 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 +22 -2
- 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/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 +7 -0
- 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 +4 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +4 -1
- 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 +29 -3
- data/lib/rubocop/cop/style/case_like_if.rb +6 -3
- data/lib/rubocop/cop/style/class_and_module_children.rb +54 -16
- data/lib/rubocop/cop/style/class_methods_definitions.rb +8 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +85 -11
- data/lib/rubocop/cop/style/concat_array_literals.rb +46 -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/endless_method.rb +67 -6
- 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 +22 -2
- data/lib/rubocop/cop/style/for.rb +40 -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 +9 -1
- data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
- data/lib/rubocop/cop/style/hash_lookup_method.rb +32 -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 +19 -20
- 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 +32 -3
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
- data/lib/rubocop/cop/style/lambda.rb +18 -0
- 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 +45 -10
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +25 -2
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
- data/lib/rubocop/cop/style/method_def_parentheses.rb +29 -3
- data/lib/rubocop/cop/style/missing_else.rb +7 -1
- 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 +28 -1
- 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 +21 -5
- data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
- data/lib/rubocop/cop/style/reduce_to_hash.rb +17 -1
- data/lib/rubocop/cop/style/redundant_begin.rb +20 -3
- 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 +15 -3
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +9 -1
- data/lib/rubocop/cop/style/redundant_freeze.rb +10 -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/regexp_literal.rb +75 -26
- data/lib/rubocop/cop/style/require_order.rb +9 -0
- data/lib/rubocop/cop/style/rescue_modifier.rb +5 -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/single_line_methods.rb +16 -4
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
- data/lib/rubocop/cop/style/string_concatenation.rb +9 -3
- data/lib/rubocop/cop/style/super_arguments.rb +3 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +23 -5
- 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/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 +12 -5
- data/lib/rubocop/remote_config.rb +4 -1
- data/lib/rubocop/result_cache.rb +20 -13
- data/lib/rubocop/runner.rb +109 -41
- data/lib/rubocop/server/cache.rb +2 -2
- data/lib/rubocop/util.rb +69 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +5 -0
- metadata +16 -5
|
@@ -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,27 @@ 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
|
+
operands_losing_to_f(node).any? do |operand|
|
|
150
|
+
next false unless to_f_method?(operand)
|
|
151
|
+
|
|
152
|
+
range = operand.receiver.source_range.end.join(operand.loc.selector.begin)
|
|
153
|
+
processed_source.contains_comment?(range)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def operands_losing_to_f(node)
|
|
158
|
+
case style
|
|
159
|
+
when :left_coerce, :single_coerce
|
|
160
|
+
[node.first_argument]
|
|
161
|
+
when :right_coerce
|
|
162
|
+
[node.receiver]
|
|
163
|
+
else
|
|
164
|
+
[node.receiver, node.first_argument]
|
|
165
|
+
end
|
|
146
166
|
end
|
|
147
167
|
|
|
148
168
|
def correct_from_slash_to_fdiv(corrector, receiver, argument)
|
|
@@ -52,12 +52,21 @@ 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
|
+
def self.autocorrect_incompatible_with
|
|
60
|
+
[Style::RedundantBegin]
|
|
61
|
+
end
|
|
55
62
|
|
|
56
63
|
def on_for(node)
|
|
57
64
|
if style == :each
|
|
58
65
|
add_offense(node, message: PREFER_EACH) do |corrector|
|
|
59
|
-
ForToEachCorrector.new(node).call(corrector)
|
|
60
66
|
opposite_style_detected
|
|
67
|
+
next unless convertible_to_each?(node)
|
|
68
|
+
|
|
69
|
+
ForToEachCorrector.new(node).call(corrector)
|
|
61
70
|
end
|
|
62
71
|
else
|
|
63
72
|
correct_style_detected
|
|
@@ -69,7 +78,7 @@ module RuboCop
|
|
|
69
78
|
|
|
70
79
|
if style == :for
|
|
71
80
|
return unless node.receiver
|
|
72
|
-
return if rescue_or_ensure_body?(node)
|
|
81
|
+
return if rescue_or_ensure_body?(node) || do_end_block_in_collection?(node)
|
|
73
82
|
|
|
74
83
|
add_offense(node, message: PREFER_FOR) do |corrector|
|
|
75
84
|
EachToForCorrector.new(node).call(corrector)
|
|
@@ -85,6 +94,31 @@ module RuboCop
|
|
|
85
94
|
|
|
86
95
|
private
|
|
87
96
|
|
|
97
|
+
# The loop variable becomes a block parameter, which can only be a local
|
|
98
|
+
# variable, and the body can't refer to an implicit block parameter
|
|
99
|
+
# (`_1` or `it`) once the block has an explicit one.
|
|
100
|
+
def convertible_to_each?(node)
|
|
101
|
+
return false unless node.variable.each_node.all? { |n| n.type?(:lvasgn, :mlhs, :splat) }
|
|
102
|
+
return true unless node.body
|
|
103
|
+
|
|
104
|
+
node.body.each_node(:send, :lvar).none? { |ref| implicit_parameter?(ref, node) }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Inside a numbered or `it` block, `_1` and `it` are local variables
|
|
108
|
+
# rather than method calls.
|
|
109
|
+
def implicit_parameter?(ref, for_node)
|
|
110
|
+
return false unless implicit_parameter_reference?(ref)
|
|
111
|
+
|
|
112
|
+
ref.each_ancestor.take_while { |ancestor| !ancestor.equal?(for_node) }
|
|
113
|
+
.none?(&:any_block_type?)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def implicit_parameter_reference?(ref)
|
|
117
|
+
return IMPLICIT_PARAMETERS.include?(ref.name) if ref.lvar_type?
|
|
118
|
+
|
|
119
|
+
IMPLICIT_PARAMETERS.include?(ref.method_name) && !ref.receiver && !ref.arguments?
|
|
120
|
+
end
|
|
121
|
+
|
|
88
122
|
def suspect_enumerable?(node)
|
|
89
123
|
node.multiline? && node.method?(:each) && !node.send_node.arguments?
|
|
90
124
|
end
|
|
@@ -92,6 +126,10 @@ module RuboCop
|
|
|
92
126
|
def rescue_or_ensure_body?(node)
|
|
93
127
|
node.body&.type?(:rescue, :ensure)
|
|
94
128
|
end
|
|
129
|
+
|
|
130
|
+
def do_end_block_in_collection?(node)
|
|
131
|
+
node.receiver.each_node(:any_block).any?(&:keywords?)
|
|
132
|
+
end
|
|
95
133
|
end
|
|
96
134
|
end
|
|
97
135
|
end
|
|
@@ -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
|
|
|
@@ -282,7 +282,15 @@ module RuboCop
|
|
|
282
282
|
end
|
|
283
283
|
|
|
284
284
|
def accepted_form?(node, ending: false)
|
|
285
|
-
accepted_if?(node, ending) || node.condition.multiline? ||
|
|
285
|
+
accepted_if?(node, ending) || node.condition.multiline? ||
|
|
286
|
+
node.parent&.assignment? || endless_method_body?(node)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# The body of an endless method is a single expression, so there is no
|
|
290
|
+
# room for a guard clause followed by the rest of the code.
|
|
291
|
+
def endless_method_body?(node)
|
|
292
|
+
parent = node.parent
|
|
293
|
+
parent&.any_def_type? && parent.endless?
|
|
286
294
|
end
|
|
287
295
|
|
|
288
296
|
def trivial?(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,45 @@ 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
|
+
comment_before_dot?(node) || env_fetch_preferred?(node.receiver)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def comment_before_dot?(node)
|
|
97
|
+
processed_source.contains_comment?(node.receiver.source_range.end.join(node.loc.dot))
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def env_fetch_preferred?(receiver)
|
|
101
|
+
env_const?(receiver) && processed_source.registry.enabled?(Style::FetchEnvVar, config)
|
|
81
102
|
end
|
|
82
103
|
|
|
83
104
|
def offense_for_fetch?(node)
|
|
84
|
-
style == :fetch && node.method?(:[]) && node.arguments.one?
|
|
105
|
+
style == :fetch && node.method?(:[]) && node.arguments.one? &&
|
|
106
|
+
!compound_assignment_target?(node)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def compound_assignment_target?(node)
|
|
110
|
+
node.parent&.type?(:op_asgn, :or_asgn, :and_asgn) && node.parent.children.first == node
|
|
85
111
|
end
|
|
86
112
|
|
|
87
113
|
def correct_fetch_to_brackets(corrector, node)
|
|
88
114
|
key = node.first_argument.source
|
|
89
115
|
|
|
90
|
-
corrector.replace(node.
|
|
116
|
+
corrector.replace(node.receiver.source_range.end.join(node.source_range.end), "[#{key}]")
|
|
91
117
|
end
|
|
92
118
|
|
|
93
119
|
def correct_brackets_to_fetch(corrector, node)
|
|
94
120
|
key = node.first_argument.source
|
|
121
|
+
replacement = node.loc.dot ? "fetch(#{key})" : ".fetch(#{key})"
|
|
95
122
|
|
|
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
|
|
123
|
+
corrector.replace(node.loc.selector.join(node.source_range.end), replacement)
|
|
101
124
|
end
|
|
102
125
|
end
|
|
103
126
|
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,
|
|
@@ -159,7 +159,7 @@ module RuboCop
|
|
|
159
159
|
corrector.replace(node, replacement)
|
|
160
160
|
end
|
|
161
161
|
|
|
162
|
-
def replacement_for_modifier_form(corrector, node)
|
|
162
|
+
def replacement_for_modifier_form(corrector, node)
|
|
163
163
|
comment = comment_on_node_line(node)
|
|
164
164
|
if comment && too_long_due_to_comment_after_modifier?(node, comment)
|
|
165
165
|
remove_comment(corrector, node, comment)
|
|
@@ -167,12 +167,11 @@ module RuboCop
|
|
|
167
167
|
return to_modifier_form_with_move_comment(node, indent(node), comment)
|
|
168
168
|
end
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
remove_heredoc(corrector, heredoc)
|
|
170
|
+
heredoc_ranges = heredoc_ranges_in(node.if_branch)
|
|
171
|
+
unless heredoc_ranges.empty?
|
|
172
|
+
remove_heredoc(corrector, heredoc_ranges)
|
|
174
173
|
|
|
175
|
-
return to_normal_form_with_heredoc(node, indent(node),
|
|
174
|
+
return to_normal_form_with_heredoc(node, indent(node), heredoc_ranges)
|
|
176
175
|
end
|
|
177
176
|
|
|
178
177
|
to_normal_form(node, indent(node))
|
|
@@ -275,14 +274,19 @@ module RuboCop
|
|
|
275
274
|
RUBY
|
|
276
275
|
end
|
|
277
276
|
|
|
278
|
-
def
|
|
279
|
-
|
|
277
|
+
def remove_heredoc(corrector, heredoc_ranges)
|
|
278
|
+
heredoc_ranges.each do |range|
|
|
279
|
+
corrector.remove(range_by_whole_lines(range, include_final_newline: true))
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def to_normal_form_with_heredoc(node, indentation, heredoc_ranges)
|
|
284
|
+
heredoc_lines = heredoc_ranges.map(&:source)
|
|
280
285
|
|
|
281
286
|
<<~RUBY.chomp
|
|
282
287
|
#{node.keyword} #{node.condition.source}
|
|
283
288
|
#{indentation} #{node.body.source}
|
|
284
|
-
#{
|
|
285
|
-
#{indentation} #{heredoc_end.source.chomp}
|
|
289
|
+
#{heredoc_lines.join("\n")}
|
|
286
290
|
#{indentation}end
|
|
287
291
|
RUBY
|
|
288
292
|
end
|
|
@@ -294,17 +298,12 @@ module RuboCop
|
|
|
294
298
|
RUBY
|
|
295
299
|
end
|
|
296
300
|
|
|
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))
|
|
301
|
+
def heredoc_ranges_in(body)
|
|
302
|
+
ranges = body.each_node(:any_str).select(&:heredoc?).map do |heredoc|
|
|
303
|
+
range_by_whole_lines(heredoc.loc.heredoc_body.join(heredoc.loc.heredoc_end))
|
|
307
304
|
end
|
|
305
|
+
# A heredoc opened in another heredoc's interpolation is moved along with it.
|
|
306
|
+
ranges.reject { |range| ranges.any? { |other| other.contains?(range) } }
|
|
308
307
|
end
|
|
309
308
|
|
|
310
309
|
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?
|