rubocop 1.91.0 → 1.92.1
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_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/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_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 +19 -17
- data/lib/rubocop/cop/layout/extra_spacing.rb +4 -0
- 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 +153 -21
- 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 +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/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/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 +3 -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/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/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 +24 -5
- 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 +25 -4
- 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 +6 -2
- 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_like_if.rb +6 -3
- data/lib/rubocop/cop/style/class_and_module_children.rb +56 -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 +71 -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 +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 +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 +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 +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/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 +21 -5
- data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +1 -1
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
- data/lib/rubocop/cop/style/raise_args.rb +19 -9
- data/lib/rubocop/cop/style/reduce_to_hash.rb +17 -1
- data/lib/rubocop/cop/style/redundant_begin.rb +22 -4
- 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 +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_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 +21 -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/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 +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 +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
|
@@ -66,16 +66,18 @@ module RuboCop
|
|
|
66
66
|
|
|
67
67
|
# @!method bit_operation?(node)
|
|
68
68
|
def_node_matcher :bit_operation?, <<~PATTERN
|
|
69
|
-
|
|
70
|
-
(send _ :& _))
|
|
69
|
+
{
|
|
70
|
+
(begin (send _ :& _))
|
|
71
|
+
(send _ :& _)
|
|
72
|
+
}
|
|
71
73
|
PATTERN
|
|
72
74
|
|
|
73
75
|
# rubocop:disable-next Metrics/AbcSize
|
|
74
76
|
def on_send(node)
|
|
75
|
-
return unless node.receiver
|
|
77
|
+
return unless bit_operation?(node.receiver)
|
|
76
78
|
return unless (preferred_method = preferred_method(node))
|
|
77
79
|
|
|
78
|
-
bit_operation = node.receiver.children.first
|
|
80
|
+
bit_operation = node.receiver.begin_type? ? node.receiver.children.first : node.receiver
|
|
79
81
|
lhs, _operator, rhs = *bit_operation
|
|
80
82
|
|
|
81
83
|
preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source
|
|
@@ -177,7 +177,7 @@ module RuboCop
|
|
|
177
177
|
BRACES_REQUIRED_MESSAGE = "Brace delimiters `{...}` required for '%<method_name>s' method."
|
|
178
178
|
|
|
179
179
|
def self.autocorrect_incompatible_with
|
|
180
|
-
[Style::RedundantBegin]
|
|
180
|
+
[Layout::SpaceBeforeBlockBraces, Style::MethodCallWithArgsParentheses, Style::RedundantBegin]
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
def on_send(node)
|
|
@@ -196,6 +196,8 @@ module RuboCop
|
|
|
196
196
|
end
|
|
197
197
|
end
|
|
198
198
|
alias on_csend on_send
|
|
199
|
+
alias on_super on_send
|
|
200
|
+
alias on_yield on_send
|
|
199
201
|
|
|
200
202
|
def on_block(node)
|
|
201
203
|
return if part_of_ignored_node?(node)
|
|
@@ -292,7 +294,7 @@ module RuboCop
|
|
|
292
294
|
corrector.replace(b, '{')
|
|
293
295
|
corrector.replace(e, '}')
|
|
294
296
|
|
|
295
|
-
corrector
|
|
297
|
+
wrap_body_with_begin(corrector, node.body) if begin_required?(node)
|
|
296
298
|
end
|
|
297
299
|
|
|
298
300
|
def whitespace_before?(range)
|
|
@@ -304,6 +306,8 @@ module RuboCop
|
|
|
304
306
|
end
|
|
305
307
|
|
|
306
308
|
def move_comment_before_block(corrector, comment, block_node, closing_brace)
|
|
309
|
+
return if directive_comment?(comment)
|
|
310
|
+
|
|
307
311
|
range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
|
|
308
312
|
|
|
309
313
|
# It is possible that there is code between the block and the comment
|
|
@@ -317,6 +321,10 @@ module RuboCop
|
|
|
317
321
|
corrector.insert_before(block_node, "#{comment.text}\n")
|
|
318
322
|
end
|
|
319
323
|
|
|
324
|
+
def directive_comment?(comment)
|
|
325
|
+
comment.text.match?(DirectiveComment::DIRECTIVE_COMMENT_REGEXP)
|
|
326
|
+
end
|
|
327
|
+
|
|
320
328
|
def source_range_before_comment(range, comment)
|
|
321
329
|
range = range.end.join(comment.source_range.begin)
|
|
322
330
|
|
|
@@ -372,6 +380,7 @@ module RuboCop
|
|
|
372
380
|
# rubocop:disable-next Metrics/CyclomaticComplexity -- inlined special_method checks to avoid double evaluation
|
|
373
381
|
def proper_block_style?(node)
|
|
374
382
|
return true if require_do_end?(node)
|
|
383
|
+
return true if collection_of_for_loop?(node)
|
|
375
384
|
|
|
376
385
|
method_name = node.method_name
|
|
377
386
|
return true if allowed_method?(method_name) || matches_allowed_pattern?(method_name)
|
|
@@ -398,6 +407,10 @@ module RuboCop
|
|
|
398
407
|
!modifier_rescue?(body)
|
|
399
408
|
end
|
|
400
409
|
|
|
410
|
+
def collection_of_for_loop?(node)
|
|
411
|
+
node.parent&.for_type? && node.parent.collection == node
|
|
412
|
+
end
|
|
413
|
+
|
|
401
414
|
def modifier_rescue?(rescue_node)
|
|
402
415
|
return false if rescue_node.body.nil? || rescue_node.else_branch
|
|
403
416
|
return false unless rescue_node.resbody_branches.one?
|
|
@@ -438,11 +451,20 @@ module RuboCop
|
|
|
438
451
|
end
|
|
439
452
|
|
|
440
453
|
def correction_would_break_code?(node)
|
|
441
|
-
return
|
|
454
|
+
return loop_condition?(node) if node.braces?
|
|
442
455
|
|
|
443
456
|
node.send_node.arguments? && !node.send_node.parenthesized?
|
|
444
457
|
end
|
|
445
458
|
|
|
459
|
+
# A `do` in the condition of `while` or `until` is taken as the start of
|
|
460
|
+
# the loop body. Inside the body of another block it is unambiguous again.
|
|
461
|
+
def loop_condition?(node)
|
|
462
|
+
ancestors = node.each_ancestor.take_while { |ancestor| !ancestor.any_block_type? }
|
|
463
|
+
[node, *ancestors].each_cons(2).any? do |child, parent|
|
|
464
|
+
parent.type?(:while, :until) && parent.condition.equal?(child)
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
446
468
|
def functional_method?(method_name)
|
|
447
469
|
functional_methods.include?(method_name)
|
|
448
470
|
end
|
|
@@ -490,6 +512,16 @@ module RuboCop
|
|
|
490
512
|
node.type?(:array, :range)
|
|
491
513
|
end
|
|
492
514
|
|
|
515
|
+
def wrap_body_with_begin(corrector, body)
|
|
516
|
+
heredoc_ends = body.each_node(:any_str).select(&:heredoc?).map do |str|
|
|
517
|
+
str.loc.heredoc_end
|
|
518
|
+
end
|
|
519
|
+
body_end = [body.source_range, *heredoc_ends].max_by(&:end_pos)
|
|
520
|
+
|
|
521
|
+
corrector.insert_before(body, "begin\n")
|
|
522
|
+
corrector.insert_after(body_end, "\nend")
|
|
523
|
+
end
|
|
524
|
+
|
|
493
525
|
def begin_required?(block_node)
|
|
494
526
|
# If the block contains `rescue` or `ensure`, it needs to be wrapped in
|
|
495
527
|
# `begin`...`end` when changing `do-end` to `{}`.
|
|
@@ -54,6 +54,10 @@ module RuboCop
|
|
|
54
54
|
|
|
55
55
|
MSG = 'Convert `if-elsif` to `case-when`.'
|
|
56
56
|
|
|
57
|
+
def self.autocorrect_incompatible_with
|
|
58
|
+
[Style::IfInsideElse]
|
|
59
|
+
end
|
|
60
|
+
|
|
57
61
|
def on_if(node)
|
|
58
62
|
return unless should_check?(node)
|
|
59
63
|
|
|
@@ -224,10 +228,9 @@ module RuboCop
|
|
|
224
228
|
end
|
|
225
229
|
|
|
226
230
|
def branch_conditions(node)
|
|
227
|
-
conditions = []
|
|
228
|
-
while node&.if_type? &&
|
|
231
|
+
conditions = [node.condition]
|
|
232
|
+
while (node = node.else_branch)&.if_type? && node.elsif?
|
|
229
233
|
conditions << node.condition
|
|
230
|
-
node = node.else_branch
|
|
231
234
|
end
|
|
232
235
|
conditions
|
|
233
236
|
end
|
|
@@ -65,6 +65,12 @@ module RuboCop
|
|
|
65
65
|
NESTED_MSG = 'Use nested module/class definitions instead of compact style.'
|
|
66
66
|
COMPACT_MSG = 'Use compact module/class definition instead of nested style.'
|
|
67
67
|
|
|
68
|
+
# `Style/StructInheritance` turns a class definition into a constant assignment in
|
|
69
|
+
# the same pass, which the compacted `class` keyword would then precede.
|
|
70
|
+
def self.autocorrect_incompatible_with
|
|
71
|
+
[Style::StructInheritance]
|
|
72
|
+
end
|
|
73
|
+
|
|
68
74
|
def on_class(node)
|
|
69
75
|
return if node.parent_class && style_for_classes != :nested
|
|
70
76
|
|
|
@@ -117,7 +123,10 @@ module RuboCop
|
|
|
117
123
|
end
|
|
118
124
|
|
|
119
125
|
def heuristic_namespace_keyword(node)
|
|
120
|
-
|
|
126
|
+
left_sibling = node.left_sibling
|
|
127
|
+
return 'module' unless left_sibling.is_a?(AST::Node)
|
|
128
|
+
|
|
129
|
+
class_definition = left_sibling.each_node(:class).find do |class_node|
|
|
121
130
|
class_node.identifier == node.identifier.namespace
|
|
122
131
|
end
|
|
123
132
|
|
|
@@ -143,7 +152,9 @@ module RuboCop
|
|
|
143
152
|
# making autocorrection ping-pong between the two forms.
|
|
144
153
|
return if style_for_kind(node.body.type) == :nested
|
|
145
154
|
return unless compactible_namespace?(node)
|
|
155
|
+
return if node.each_ancestor(:class, :module).any? { |ancestor| ignored_node?(ancestor) }
|
|
146
156
|
|
|
157
|
+
ignore_node(node)
|
|
147
158
|
compact_node(corrector, node)
|
|
148
159
|
remove_end(corrector, node.body)
|
|
149
160
|
unindent(corrector, node)
|
|
@@ -194,33 +205,62 @@ module RuboCop
|
|
|
194
205
|
"#{node.body.children.first.const_name}"
|
|
195
206
|
end
|
|
196
207
|
|
|
197
|
-
# rubocop:disable-next Metrics/AbcSize
|
|
198
208
|
def remove_end(corrector, body)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
adjustment = processed_source.raw_source[remove_begin_pos] == ';' ? 0 : 1
|
|
205
|
-
range = range_between(remove_begin_pos, body.loc.end.end_pos + adjustment)
|
|
209
|
+
range = if same_line?(body.loc.name, body.loc.end)
|
|
210
|
+
single_line_end_range(body)
|
|
211
|
+
else
|
|
212
|
+
multiline_end_range(body)
|
|
213
|
+
end
|
|
206
214
|
|
|
207
215
|
corrector.remove(range)
|
|
208
216
|
end
|
|
209
217
|
|
|
210
|
-
def
|
|
211
|
-
|
|
218
|
+
def single_line_end_range(body)
|
|
219
|
+
raw = processed_source.raw_source
|
|
220
|
+
pos = body.loc.end.begin_pos
|
|
221
|
+
pos -= 1 while raw[pos - 1] == ' '
|
|
222
|
+
pos -= 1 if raw[pos - 1] == ';'
|
|
223
|
+
range_between(pos, body.loc.end.end_pos)
|
|
224
|
+
end
|
|
212
225
|
|
|
213
|
-
|
|
214
|
-
|
|
226
|
+
def multiline_end_range(body)
|
|
227
|
+
remove_begin_pos = body.loc.end.begin_pos - leading_spaces(body).size
|
|
228
|
+
adjustment = processed_source.raw_source[remove_begin_pos] == ';' ? 0 : 1
|
|
229
|
+
range_between(remove_begin_pos, body.loc.end.end_pos + adjustment)
|
|
230
|
+
end
|
|
215
231
|
|
|
216
|
-
|
|
217
|
-
|
|
232
|
+
def unindent(corrector, node)
|
|
233
|
+
column_delta = unindent_column_delta(node)
|
|
234
|
+
return unless column_delta&.negative?
|
|
235
|
+
|
|
236
|
+
range = unindentable_range(node.body)
|
|
237
|
+
return unless range
|
|
218
238
|
|
|
219
239
|
AlignmentCorrector.correct(
|
|
220
|
-
corrector, processed_source,
|
|
240
|
+
corrector, processed_source, range, column_delta, tab_indentation: true
|
|
221
241
|
)
|
|
222
242
|
end
|
|
223
243
|
|
|
244
|
+
def unindent_column_delta(node)
|
|
245
|
+
body = node.body.children.last
|
|
246
|
+
return unless body
|
|
247
|
+
|
|
248
|
+
node_indentation = spaces_size(leading_spaces(node))
|
|
249
|
+
body_indentation = spaces_size(leading_spaces(body))
|
|
250
|
+
|
|
251
|
+
node_indentation + configured_indentation_width - body_indentation
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def unindentable_range(body)
|
|
255
|
+
first_line = body.loc.name.line + 1
|
|
256
|
+
last_line = body.loc.end.line - 1
|
|
257
|
+
return if first_line > last_line
|
|
258
|
+
|
|
259
|
+
buffer = processed_source.buffer
|
|
260
|
+
range_between(buffer.line_range(first_line).begin_pos,
|
|
261
|
+
buffer.line_range(last_line).end_pos)
|
|
262
|
+
end
|
|
263
|
+
|
|
224
264
|
def leading_spaces(node)
|
|
225
265
|
node.source_range.source_line[/\A\s*/]
|
|
226
266
|
end
|
|
@@ -72,9 +72,13 @@ module RuboCop
|
|
|
72
72
|
return unless def_self_style?
|
|
73
73
|
return unless node.identifier.self_type?
|
|
74
74
|
return unless all_methods_public?(node)
|
|
75
|
+
return unless def_nodes(node).all? { |def_node| no_code_after?(def_node.source_range) }
|
|
75
76
|
|
|
76
77
|
add_offense(node, message: MSG_SCLASS) do |corrector|
|
|
78
|
+
next if inside_corrected_sclass?(node)
|
|
79
|
+
|
|
77
80
|
autocorrect_sclass(node, corrector)
|
|
81
|
+
ignore_node(node)
|
|
78
82
|
end
|
|
79
83
|
end
|
|
80
84
|
|
|
@@ -112,6 +116,17 @@ module RuboCop
|
|
|
112
116
|
end
|
|
113
117
|
end
|
|
114
118
|
|
|
119
|
+
def no_code_after?(range)
|
|
120
|
+
trailing = processed_source.lines[range.last_line - 1][range.last_column..].to_s.strip
|
|
121
|
+
trailing = trailing.sub(/\A;+\s*/, '')
|
|
122
|
+
|
|
123
|
+
trailing.empty? || trailing.start_with?('#')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def inside_corrected_sclass?(node)
|
|
127
|
+
node.each_ancestor(:sclass).any? { |ancestor| ignored_node?(ancestor) }
|
|
128
|
+
end
|
|
129
|
+
|
|
115
130
|
def autocorrect_sclass(node, corrector)
|
|
116
131
|
rewritten_defs = []
|
|
117
132
|
|
|
@@ -15,31 +15,24 @@ module RuboCop
|
|
|
15
15
|
# would yield false positives. For example, `String` implements `length`
|
|
16
16
|
# and `size`, but it doesn't include `Enumerable`.
|
|
17
17
|
#
|
|
18
|
+
# When `count` is used without a block, it measures the collection's
|
|
19
|
+
# size, so the offense is corrected to `empty?` or `!empty?`. The
|
|
20
|
+
# predicate methods `any?`, `none?` and `one?` test element truthiness
|
|
21
|
+
# instead and would return different results for collections with falsey
|
|
22
|
+
# elements (e.g. `[false].count.zero?` is `false`, but `[false].none?`
|
|
23
|
+
# is `true`). For the same reason, `count == 1` and `count > 1` without
|
|
24
|
+
# a block are not flagged.
|
|
25
|
+
#
|
|
18
26
|
# @safety
|
|
19
27
|
# The cop is unsafe because receiver might not include `Enumerable`, or
|
|
20
28
|
# it has nonstandard implementation of `count` or any replacement
|
|
21
29
|
# methods.
|
|
22
30
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# `
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# ----
|
|
29
|
-
# [nil, false].count.positive?
|
|
30
|
-
# [nil].count == 1
|
|
31
|
-
# # => true
|
|
32
|
-
#
|
|
33
|
-
# [nil, false].any?
|
|
34
|
-
# [nil].one?
|
|
35
|
-
# # => false
|
|
36
|
-
#
|
|
37
|
-
# [nil].count == 0
|
|
38
|
-
# # => false
|
|
39
|
-
#
|
|
40
|
-
# [nil].none?
|
|
41
|
-
# # => true
|
|
42
|
-
# ----
|
|
31
|
+
# Blockless `count` is corrected to `empty?`, which `Enumerable` doesn't
|
|
32
|
+
# define. `Range`, `Enumerator` (including lazy ones), `Struct`, `StringIO`
|
|
33
|
+
# and any class that just includes `Enumerable` respond to `count` but not
|
|
34
|
+
# to `empty?`, so `(1...1).count.zero?` is `true` while `(1...1).empty?`
|
|
35
|
+
# raises `NoMethodError`.
|
|
43
36
|
#
|
|
44
37
|
# Autocorrection is unsafe when replacement methods don't iterate over
|
|
45
38
|
# every element in collection and the given block runs side effects:
|
|
@@ -60,12 +53,14 @@ module RuboCop
|
|
|
60
53
|
# x.count > 0
|
|
61
54
|
# x.count != 0
|
|
62
55
|
#
|
|
56
|
+
# # good
|
|
57
|
+
# !x.empty?
|
|
58
|
+
#
|
|
59
|
+
# # bad
|
|
63
60
|
# x.count(&:foo?).positive?
|
|
64
61
|
# x.count { |item| item.foo? }.positive?
|
|
65
62
|
#
|
|
66
63
|
# # good
|
|
67
|
-
# x.any?
|
|
68
|
-
#
|
|
69
64
|
# x.any?(&:foo?)
|
|
70
65
|
# x.any? { |item| item.foo? }
|
|
71
66
|
#
|
|
@@ -74,24 +69,33 @@ module RuboCop
|
|
|
74
69
|
# x.count == 0
|
|
75
70
|
#
|
|
76
71
|
# # good
|
|
77
|
-
# x.
|
|
72
|
+
# x.empty?
|
|
78
73
|
#
|
|
79
74
|
# # bad
|
|
80
|
-
# x.count
|
|
81
|
-
# x.
|
|
75
|
+
# x.count(&:foo?).zero?
|
|
76
|
+
# x.count(&:foo?) == 0
|
|
77
|
+
#
|
|
78
|
+
# # good
|
|
79
|
+
# x.none?(&:foo?)
|
|
80
|
+
#
|
|
81
|
+
# # bad
|
|
82
|
+
# x.count(&:foo?) == 1
|
|
83
|
+
#
|
|
84
|
+
# # good
|
|
85
|
+
# x.one?(&:foo?)
|
|
82
86
|
#
|
|
83
87
|
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
|
84
88
|
#
|
|
85
89
|
# # good
|
|
86
|
-
# x.count > 1
|
|
90
|
+
# x.count(&:foo?) > 1
|
|
87
91
|
#
|
|
88
92
|
# @example AllCops:ActiveSupportExtensionsEnabled: true
|
|
89
93
|
#
|
|
90
94
|
# # bad
|
|
91
|
-
# x.count > 1
|
|
95
|
+
# x.count(&:foo?) > 1
|
|
92
96
|
#
|
|
93
97
|
# # good
|
|
94
|
-
# x.many?
|
|
98
|
+
# x.many?(&:foo?)
|
|
95
99
|
#
|
|
96
100
|
class CollectionQuerying < Base
|
|
97
101
|
include RangeHelp
|
|
@@ -102,13 +106,21 @@ module RuboCop
|
|
|
102
106
|
RESTRICT_ON_SEND = %i[positive? > != zero? ==].freeze
|
|
103
107
|
|
|
104
108
|
REPLACEMENTS = {
|
|
105
|
-
[:positive?, nil] =>
|
|
106
|
-
[:>, 0] =>
|
|
107
|
-
[:!=, 0] =>
|
|
108
|
-
[:zero?, nil] =>
|
|
109
|
-
[:==, 0] =>
|
|
110
|
-
[:==, 1] =>
|
|
111
|
-
[:>, 1] =>
|
|
109
|
+
[:positive?, nil] => 'any?',
|
|
110
|
+
[:>, 0] => 'any?',
|
|
111
|
+
[:!=, 0] => 'any?',
|
|
112
|
+
[:zero?, nil] => 'none?',
|
|
113
|
+
[:==, 0] => 'none?',
|
|
114
|
+
[:==, 1] => 'one?',
|
|
115
|
+
[:>, 1] => 'many?'
|
|
116
|
+
}.freeze
|
|
117
|
+
|
|
118
|
+
NO_BLOCK_REPLACEMENTS = {
|
|
119
|
+
[:positive?, nil] => '!empty?',
|
|
120
|
+
[:>, 0] => '!empty?',
|
|
121
|
+
[:!=, 0] => '!empty?',
|
|
122
|
+
[:zero?, nil] => 'empty?',
|
|
123
|
+
[:==, 0] => 'empty?'
|
|
112
124
|
}.freeze
|
|
113
125
|
|
|
114
126
|
# @!method count_predicate(node)
|
|
@@ -132,28 +144,49 @@ module RuboCop
|
|
|
132
144
|
def on_send(node)
|
|
133
145
|
return unless (count_node = count_predicate(node))
|
|
134
146
|
|
|
135
|
-
|
|
147
|
+
replacement = replacement_method(node, count_node)
|
|
136
148
|
|
|
137
|
-
return unless replacement_supported?(
|
|
149
|
+
return unless replacement_supported?(node, replacement)
|
|
138
150
|
|
|
139
151
|
offense_range = count_node.loc.selector.join(node.source_range.end)
|
|
140
|
-
add_offense(offense_range,
|
|
141
|
-
|
|
142
|
-
corrector.replace(count_node.loc.selector, replacement_method)
|
|
143
|
-
corrector.remove(removal_range(node))
|
|
152
|
+
add_offense(offense_range, message: format(MSG, prefer: replacement)) do |corrector|
|
|
153
|
+
autocorrect(corrector, node, count_node, replacement)
|
|
144
154
|
end
|
|
145
155
|
end
|
|
146
156
|
|
|
147
157
|
private
|
|
148
158
|
|
|
149
|
-
def replacement_method(node)
|
|
150
|
-
|
|
159
|
+
def replacement_method(node, count_node)
|
|
160
|
+
key = [node.method_name, node.first_argument&.value]
|
|
161
|
+
return REPLACEMENTS.fetch(key) if block_given_to_count?(count_node)
|
|
162
|
+
|
|
163
|
+
NO_BLOCK_REPLACEMENTS[key]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def block_given_to_count?(count_node)
|
|
167
|
+
count_node.parent&.any_block_type? || count_node.last_argument&.block_pass_type?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def replacement_supported?(node, replacement)
|
|
171
|
+
return false unless replacement
|
|
172
|
+
return false if replacement == 'many?' && !active_support_extensions_enabled?
|
|
173
|
+
|
|
174
|
+
!(replacement.start_with?('!') && receiver_of_call?(node))
|
|
151
175
|
end
|
|
152
176
|
|
|
153
|
-
def
|
|
154
|
-
|
|
177
|
+
def receiver_of_call?(node)
|
|
178
|
+
node.parent&.call_type? && node.parent.receiver == node && node.parent.loc.dot
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def autocorrect(corrector, node, count_node, replacement)
|
|
182
|
+
if replacement.start_with?('!')
|
|
183
|
+
corrector.insert_before(count_node, '!')
|
|
184
|
+
corrector.replace(count_node.loc.selector, 'empty?')
|
|
185
|
+
else
|
|
186
|
+
corrector.replace(count_node.loc.selector, replacement)
|
|
187
|
+
end
|
|
155
188
|
|
|
156
|
-
|
|
189
|
+
corrector.remove(removal_range(node))
|
|
157
190
|
end
|
|
158
191
|
|
|
159
192
|
def removal_range(node)
|
|
@@ -60,19 +60,22 @@ module RuboCop
|
|
|
60
60
|
# end
|
|
61
61
|
#
|
|
62
62
|
class CombinableLoops < Base
|
|
63
|
+
include RangeHelp
|
|
63
64
|
extend AutoCorrector
|
|
64
65
|
|
|
65
66
|
MSG = 'Combine this loop with the previous loop.'
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
def self.autocorrect_incompatible_with
|
|
69
|
+
[Layout::BlockEndNewline, Layout::RedundantLineBreak]
|
|
70
|
+
end
|
|
71
|
+
|
|
68
72
|
def on_block(node)
|
|
69
73
|
return unless node.parent&.begin_type?
|
|
70
74
|
return unless collection_looping_method?(node)
|
|
71
|
-
return unless
|
|
72
|
-
return unless node.body && node.left_sibling.body
|
|
75
|
+
return unless combinable_looping_blocks?(node, node.left_sibling)
|
|
73
76
|
|
|
74
77
|
add_offense(node) do |corrector|
|
|
75
|
-
next unless node
|
|
78
|
+
next unless combinable_blocks?(node, node.left_sibling)
|
|
76
79
|
|
|
77
80
|
combine_with_left_sibling(corrector, node)
|
|
78
81
|
end
|
|
@@ -87,9 +90,7 @@ module RuboCop
|
|
|
87
90
|
return unless node.body && node.left_sibling.body
|
|
88
91
|
|
|
89
92
|
add_offense(node) do |corrector|
|
|
90
|
-
|
|
91
|
-
# body referencing an undefined variable, so only autocorrect when they match.
|
|
92
|
-
next unless node.variable == node.left_sibling.variable
|
|
93
|
+
next unless combinable_fors?(node, node.left_sibling)
|
|
93
94
|
|
|
94
95
|
combine_with_left_sibling(corrector, node)
|
|
95
96
|
end
|
|
@@ -110,25 +111,102 @@ module RuboCop
|
|
|
110
111
|
sibling.send_node.arguments == node.send_node.arguments
|
|
111
112
|
end
|
|
112
113
|
|
|
114
|
+
def combinable_looping_blocks?(node, sibling)
|
|
115
|
+
same_collection_looping_block?(node, sibling) && node.body && sibling.body
|
|
116
|
+
end
|
|
117
|
+
|
|
113
118
|
def same_collection_looping_for?(node, sibling)
|
|
114
119
|
sibling&.for_type? && node.collection == sibling.collection
|
|
115
120
|
end
|
|
116
121
|
|
|
122
|
+
def combinable_blocks?(node, sibling)
|
|
123
|
+
return false unless node.arguments == sibling.arguments
|
|
124
|
+
# Numbered and `it` parameters are implicit, so `arguments` is empty for both.
|
|
125
|
+
return false unless node.argument_list == sibling.argument_list
|
|
126
|
+
return false if directive_after_opening?(node)
|
|
127
|
+
|
|
128
|
+
!node.body.type?(:rescue, :ensure) && !sibling.body.type?(:rescue, :ensure)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Combining loops with different iteration variables would leave the second
|
|
132
|
+
# body referencing an undefined variable, so only autocorrect when they match.
|
|
133
|
+
def combinable_fors?(node, sibling)
|
|
134
|
+
node.variable == sibling.variable && !directive_after_opening?(node)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# The opening is removed, so a directive after it would end up on a line of
|
|
138
|
+
# its own, where it covers the rest of the file instead of a single line.
|
|
139
|
+
def directive_after_opening?(node)
|
|
140
|
+
opening_line = loop_opening(node).line
|
|
141
|
+
return false if opening_line == node.body.first_line
|
|
142
|
+
|
|
143
|
+
comment = processed_source.comment_at_line(opening_line)
|
|
144
|
+
comment && DirectiveComment.new(comment).start_with_marker?
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# The block arguments or `{`/`do` of a block, or the `do` or collection of a `for` loop.
|
|
148
|
+
def loop_opening(node)
|
|
149
|
+
if node.for_type?
|
|
150
|
+
node.loc.begin || node.collection.source_range
|
|
151
|
+
else
|
|
152
|
+
(node.block_type? && node.arguments.source_range) || node.loc.begin
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
117
156
|
def combine_with_left_sibling(corrector, node)
|
|
118
|
-
corrector.remove(node.left_sibling
|
|
119
|
-
corrector.remove(
|
|
157
|
+
corrector.remove(closing_with_preceding_space(node.left_sibling, node))
|
|
158
|
+
corrector.remove(opening_with_following_space(node))
|
|
120
159
|
|
|
121
160
|
correct_end_of_block(corrector, node)
|
|
122
161
|
end
|
|
123
162
|
|
|
163
|
+
# Comments and heredoc bodies before the closing delimiter are kept. When
|
|
164
|
+
# the next loop starts on the delimiter's line, the line break before the
|
|
165
|
+
# delimiter is kept too, so that a comment doesn't swallow the next loop.
|
|
166
|
+
def closing_with_preceding_space(loop, next_loop)
|
|
167
|
+
separate_lines = !same_line?(loop.loc.end, next_loop)
|
|
168
|
+
|
|
169
|
+
range_with_surrounding_space(
|
|
170
|
+
loop.loc.end, side: :left, newlines: separate_lines, whitespace: separate_lines
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Comments between the opening and the body are kept.
|
|
175
|
+
def opening_with_following_space(node)
|
|
176
|
+
opening = node.source_range.begin.join(loop_opening(node))
|
|
177
|
+
|
|
178
|
+
range_with_surrounding_space(opening, side: :right, whitespace: true)
|
|
179
|
+
end
|
|
180
|
+
|
|
124
181
|
def correct_end_of_block(corrector, node)
|
|
125
182
|
return unless node.left_sibling.respond_to?(:braces?)
|
|
126
|
-
return if node
|
|
183
|
+
return if combined_with_right_sibling?(node)
|
|
127
184
|
|
|
128
|
-
end_of_block = node.
|
|
185
|
+
end_of_block = first_combined_block(node).braces? ? '}' : ' end'
|
|
129
186
|
corrector.remove(node.loc.end)
|
|
130
187
|
corrector.insert_before(node.source_range.end, end_of_block)
|
|
131
188
|
end
|
|
189
|
+
|
|
190
|
+
def combined_with_right_sibling?(node)
|
|
191
|
+
sibling = node.right_sibling
|
|
192
|
+
return false unless sibling&.any_block_type?
|
|
193
|
+
# A disabled loop isn't corrected, so it doesn't get merged into this one.
|
|
194
|
+
return false unless enabled_lines?(sibling.source_range)
|
|
195
|
+
|
|
196
|
+
same_collection_looping_block?(sibling, node) && sibling.body &&
|
|
197
|
+
combinable_blocks?(sibling, node)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def first_combined_block(node)
|
|
201
|
+
first = node.left_sibling
|
|
202
|
+
|
|
203
|
+
while (previous = first.left_sibling) && enabled_lines?(first.source_range) &&
|
|
204
|
+
combinable_looping_blocks?(first, previous) && combinable_blocks?(first, previous)
|
|
205
|
+
first = previous
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
first
|
|
209
|
+
end
|
|
132
210
|
end
|
|
133
211
|
end
|
|
134
212
|
end
|