rubocop 1.90.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 +241 -11
- data/config/obsoletion.yml +9 -0
- data/lib/rubocop/cached_data.rb +47 -7
- data/lib/rubocop/changed_files.rb +134 -0
- data/lib/rubocop/cli/command/cop_names.rb +57 -0
- data/lib/rubocop/cli/command/execute_runner.rb +23 -3
- 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 +11 -4
- data/lib/rubocop/comment_config/directive_range.rb +3 -1
- data/lib/rubocop/comment_config/disable_next.rb +49 -3
- data/lib/rubocop/comment_config/push_pop.rb +55 -0
- data/lib/rubocop/comment_config.rb +32 -53
- data/lib/rubocop/config.rb +10 -0
- data/lib/rubocop/config_loader.rb +18 -7
- data/lib/rubocop/config_loader_resolver.rb +79 -12
- data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
- data/lib/rubocop/config_obsoletion/removed_cop.rb +9 -1
- data/lib/rubocop/config_obsoletion.rb +1 -1
- data/lib/rubocop/config_validator.rb +29 -3
- data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +57 -24
- 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/correctors.rb +2 -2
- 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/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/generator.rb +1 -1
- 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/on_send_without_on_csend.rb +1 -1
- 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 +2 -0
- 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/comment_indentation.rb +8 -5
- data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -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_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 +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_array_element_indentation.rb +17 -4
- 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/first_parameter_indentation.rb +11 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +15 -10
- 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 +4 -4
- 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 +71 -19
- 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 +10 -4
- data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +5 -1
- 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/argument_mismatch.rb +14 -4
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +88 -14
- data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +23 -2
- 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 +245 -0
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +19 -4
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +42 -17
- 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/lint.rb +1 -0
- 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/migration/department_name.rb +1 -1
- data/lib/rubocop/cop/mixin/alignment.rb +5 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
- 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 +26 -2
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +31 -3
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
- 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 +29 -3
- 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 +4 -2
- data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
- data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
- data/lib/rubocop/cop/naming/block_parameter_name.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 +10 -2
- data/lib/rubocop/cop/security/json_load.rb +16 -4
- data/lib/rubocop/cop/severity.rb +12 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +10 -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 +4 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +6 -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 +29 -3
- data/lib/rubocop/cop/style/case_like_if.rb +9 -6
- 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/constant_visibility.rb +6 -2
- data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/directive_scope.rb +154 -21
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +118 -18
- data/lib/rubocop/cop/style/documentation_method.rb +1 -9
- data/lib/rubocop/cop/style/double_negation.rb +11 -0
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +15 -3
- data/lib/rubocop/cop/style/endless_method.rb +91 -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/file_null.rb +2 -2
- data/lib/rubocop/cop/style/float_division.rb +28 -14
- data/lib/rubocop/cop/style/for.rb +44 -1
- data/lib/rubocop/cop/style/format_string.rb +28 -6
- data/lib/rubocop/cop/style/format_string_token.rb +1 -1
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
- data/lib/rubocop/cop/style/guard_clause.rb +13 -1
- data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
- data/lib/rubocop/cop/style/hash_lookup_method.rb +35 -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/magic_comment_format.rb +1 -1
- 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 +36 -12
- 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 +48 -8
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
- data/lib/rubocop/cop/style/not.rb +10 -3
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
- data/lib/rubocop/cop/style/numeric_predicate.rb +21 -5
- data/lib/rubocop/cop/style/one_line_conditional.rb +14 -3
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
- 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 +34 -17
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +16 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +15 -3
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +10 -2
- data/lib/rubocop/cop/style/redundant_freeze.rb +10 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +23 -2
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +25 -5
- 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_block_params.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +16 -4
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
- 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 +14 -0
- 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_comma_in_block_args.rb +1 -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/style.rb +0 -1
- data/lib/rubocop/cop/team.rb +10 -5
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +14 -1
- data/lib/rubocop/cops_documentation_generator.rb +23 -2
- data/lib/rubocop/directive_comment.rb +37 -8
- data/lib/rubocop/formatter/base_formatter.rb +22 -0
- data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
- data/lib/rubocop/formatter/formatter_set.rb +6 -1
- data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
- data/lib/rubocop/formatter/html_formatter.rb +2 -2
- data/lib/rubocop/formatter/json_formatter.rb +71 -1
- data/lib/rubocop/formatter/junit_formatter.rb +1 -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 +186 -0
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
- data/lib/rubocop/formatter.rb +1 -0
- 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/magic_comment.rb +27 -0
- 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 +97 -4
- data/lib/rubocop/path_util.rb +1 -1
- data/lib/rubocop/plugin/configuration_integrator.rb +12 -5
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +8 -5
- data/lib/rubocop/result_cache.rb +36 -18
- data/lib/rubocop/runner.rb +160 -49
- data/lib/rubocop/server/cache.rb +2 -2
- data/lib/rubocop/unified_diff.rb +257 -0
- data/lib/rubocop/util.rb +69 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +9 -1
- metadata +21 -6
- data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
|
@@ -153,7 +153,7 @@ module RuboCop
|
|
|
153
153
|
else
|
|
154
154
|
column = node.source_range.column
|
|
155
155
|
return if multiline_block?(left_brace, right_brace) &&
|
|
156
|
-
aligned_braces?(inner, right_brace, column)
|
|
156
|
+
aligned_braces?(inner, right_brace, column, left_brace)
|
|
157
157
|
|
|
158
158
|
space_inside_right_brace(inner, right_brace, column)
|
|
159
159
|
end
|
|
@@ -163,14 +163,21 @@ module RuboCop
|
|
|
163
163
|
left_brace.first_line != right_brace.first_line
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
-
def aligned_braces?(inner, right_brace, column)
|
|
167
|
-
column == right_brace.column ||
|
|
166
|
+
def aligned_braces?(inner, right_brace, column, left_brace)
|
|
167
|
+
column == right_brace.column ||
|
|
168
|
+
column == inner_last_space_count(inner) ||
|
|
169
|
+
aligned_with_opening_brace_line?(left_brace, right_brace)
|
|
168
170
|
end
|
|
169
171
|
|
|
170
172
|
def inner_last_space_count(inner)
|
|
171
173
|
inner.split("\n").last.count(' ')
|
|
172
174
|
end
|
|
173
175
|
|
|
176
|
+
def aligned_with_opening_brace_line?(left_brace, right_brace)
|
|
177
|
+
begins_its_line?(right_brace) &&
|
|
178
|
+
right_brace.column == left_brace.source_line[/\A\s*/].length
|
|
179
|
+
end
|
|
180
|
+
|
|
174
181
|
def no_space_inside_left_brace(left_brace, args_delimiter)
|
|
175
182
|
if pipe?(args_delimiter)
|
|
176
183
|
if left_brace.end_pos == args_delimiter.begin_pos &&
|
|
@@ -197,9 +204,7 @@ module RuboCop
|
|
|
197
204
|
'Space between { and | detected.')
|
|
198
205
|
end
|
|
199
206
|
else
|
|
200
|
-
|
|
201
|
-
space(brace_with_space.begin_pos + 1, brace_with_space.end_pos,
|
|
202
|
-
'Space inside { detected.')
|
|
207
|
+
space_after_left_brace(left_brace)
|
|
203
208
|
end
|
|
204
209
|
end
|
|
205
210
|
|
|
@@ -207,6 +212,20 @@ module RuboCop
|
|
|
207
212
|
args_delimiter&.is?('|')
|
|
208
213
|
end
|
|
209
214
|
|
|
215
|
+
def space_after_left_brace(left_brace)
|
|
216
|
+
brace_with_space = range_with_surrounding_space(left_brace, side: :right)
|
|
217
|
+
return if brace_with_space.source.match?(/\R/)
|
|
218
|
+
return if comment_at?(brace_with_space.end_pos)
|
|
219
|
+
|
|
220
|
+
space(
|
|
221
|
+
brace_with_space.begin_pos + 1, brace_with_space.end_pos, 'Space inside { detected.'
|
|
222
|
+
)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def comment_at?(pos)
|
|
226
|
+
processed_source.comments.any? { |comment| comment.source_range.begin_pos == pos }
|
|
227
|
+
end
|
|
228
|
+
|
|
210
229
|
def space_inside_right_brace(inner, right_brace, column)
|
|
211
230
|
brace_with_space = range_with_surrounding_space(right_brace, side: :left)
|
|
212
231
|
begin_pos = brace_with_space.begin_pos
|
|
@@ -216,7 +235,7 @@ module RuboCop
|
|
|
216
235
|
begin_pos = end_pos - (right_brace.column - column)
|
|
217
236
|
end
|
|
218
237
|
|
|
219
|
-
if
|
|
238
|
+
if closing_bracket_on_own_line?(inner)
|
|
220
239
|
end_pos -= 1
|
|
221
240
|
begin_pos = end_pos - (inner_last_space_count(inner) - column)
|
|
222
241
|
end
|
|
@@ -224,6 +243,10 @@ module RuboCop
|
|
|
224
243
|
space(begin_pos, end_pos, 'Space inside } detected.')
|
|
225
244
|
end
|
|
226
245
|
|
|
246
|
+
def closing_bracket_on_own_line?(inner)
|
|
247
|
+
inner[/[^\n]*\z/].match?(/\A\s*\]\z/)
|
|
248
|
+
end
|
|
249
|
+
|
|
227
250
|
def no_space(begin_pos, end_pos, msg)
|
|
228
251
|
if style == :space
|
|
229
252
|
offense(begin_pos, end_pos, msg)
|
|
@@ -241,7 +264,7 @@ module RuboCop
|
|
|
241
264
|
end
|
|
242
265
|
|
|
243
266
|
def offense(begin_pos, end_pos, msg, style_param = 'EnforcedStyle')
|
|
244
|
-
return if begin_pos
|
|
267
|
+
return if begin_pos >= end_pos
|
|
245
268
|
|
|
246
269
|
range = range_between(begin_pos, end_pos)
|
|
247
270
|
add_offense(range, message: msg) do |corrector|
|
|
@@ -56,7 +56,8 @@ module RuboCop
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def on_send(node)
|
|
59
|
-
|
|
59
|
+
# `foo.*` is a regular method call, not a binary operator.
|
|
60
|
+
return if node.parenthesized? || node.loc.dot
|
|
60
61
|
|
|
61
62
|
return unless (parent = node.parent)
|
|
62
63
|
return unless operator?(parent)
|
|
@@ -21,7 +21,9 @@ module RuboCop
|
|
|
21
21
|
#
|
|
22
22
|
# Constructor calls (`Foo.new`) and keyword-argument validation are out of
|
|
23
23
|
# scope: only positional arity of explicitly defined singleton methods is
|
|
24
|
-
# checked.
|
|
24
|
+
# checked. `new` is skipped outright, because `Class#new` is not in the
|
|
25
|
+
# index: any `new` the index does find sits below it in the method
|
|
26
|
+
# resolution order, where Ruby would never reach it.
|
|
25
27
|
#
|
|
26
28
|
# @example
|
|
27
29
|
# # Given the project defines:
|
|
@@ -45,8 +47,7 @@ module RuboCop
|
|
|
45
47
|
MSG = 'Wrong number of arguments to `%<method>s` (given %<given>d, expected %<expected>s).'
|
|
46
48
|
|
|
47
49
|
def on_send(node)
|
|
48
|
-
return unless
|
|
49
|
-
return unless node.receiver&.const_type?
|
|
50
|
+
return unless checkable_call?(node)
|
|
50
51
|
|
|
51
52
|
shape = resolved_signature_shape(node)
|
|
52
53
|
return unless shape
|
|
@@ -61,6 +62,10 @@ module RuboCop
|
|
|
61
62
|
|
|
62
63
|
private
|
|
63
64
|
|
|
65
|
+
def checkable_call?(node)
|
|
66
|
+
project_index && node.receiver&.const_type? && !node.method?(:new)
|
|
67
|
+
end
|
|
68
|
+
|
|
64
69
|
def message(node, given, min, max)
|
|
65
70
|
format(MSG, method: node.method_name, given: given, expected: expected_range(min, max))
|
|
66
71
|
end
|
|
@@ -77,9 +82,14 @@ module RuboCop
|
|
|
77
82
|
nil
|
|
78
83
|
end
|
|
79
84
|
|
|
85
|
+
# A call with an explicit receiver cannot reach a private method, so its
|
|
86
|
+
# signature says nothing about the call. This matters because `Object`
|
|
87
|
+
# closes the singleton ancestry of every constant, and a bare `def` at
|
|
88
|
+
# the top level of any file — what every block-based DSL produces — is a
|
|
89
|
+
# private instance method of `Object`.
|
|
80
90
|
def singleton_signature_shape(declaration, method_name)
|
|
81
91
|
member = indexed_singleton_member(declaration, "#{method_name}()")
|
|
82
|
-
return nil unless member
|
|
92
|
+
return nil unless member&.public?
|
|
83
93
|
|
|
84
94
|
indexed_member_shape(member)
|
|
85
95
|
end
|
|
@@ -106,6 +106,8 @@ module RuboCop
|
|
|
106
106
|
def traverse_node(node, &block)
|
|
107
107
|
# if the node is a block, any assignments are irrelevant
|
|
108
108
|
return if node.any_block_type?
|
|
109
|
+
# an assignment inside `defined?` is never executed
|
|
110
|
+
return if node.defined_type?
|
|
109
111
|
|
|
110
112
|
result = yield node if ASGN_TYPES.include?(node.type)
|
|
111
113
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# rubocop:disable Lint/RedundantCopDisableDirective
|
|
4
|
-
# rubocop:disable Style/DoubleCopDisableDirective
|
|
5
3
|
module RuboCop
|
|
6
4
|
module Cop
|
|
7
5
|
module Lint
|
|
@@ -23,12 +21,11 @@ module RuboCop
|
|
|
23
21
|
# # good
|
|
24
22
|
# # rubocop:disable all
|
|
25
23
|
#
|
|
26
|
-
# # bad
|
|
24
|
+
# # bad - only the first directive takes effect
|
|
27
25
|
# # rubocop:disable Layout/LineLength # rubocop:disable Style/Encoding
|
|
28
26
|
#
|
|
29
27
|
# # good
|
|
30
|
-
# # rubocop:disable Layout/LineLength
|
|
31
|
-
# # rubocop:disable Style/Encoding
|
|
28
|
+
# # rubocop:disable Layout/LineLength, Style/Encoding
|
|
32
29
|
#
|
|
33
30
|
# # bad
|
|
34
31
|
# # rubocop:wrongmode Layout/LineLength
|
|
@@ -55,13 +52,19 @@ module RuboCop
|
|
|
55
52
|
# # rubocop:disable Layout/LineLength
|
|
56
53
|
#
|
|
57
54
|
class CopDirectiveSyntax < Base
|
|
55
|
+
extend AutoCorrector
|
|
56
|
+
|
|
58
57
|
COMMON_MSG = 'Malformed directive comment detected.'
|
|
59
58
|
|
|
60
59
|
MISSING_MODE_NAME_MSG = 'The mode name is missing.'
|
|
61
|
-
INVALID_MODE_NAME_MSG = 'The mode name must be one of `enable`, `disable`, `disable-next`, `todo`, `todo-next`, `push`, or `pop`.' # rubocop:disable Layout/LineLength
|
|
60
|
+
INVALID_MODE_NAME_MSG = 'The mode name must be one of `enable`, `disable`, `disable-next`, `enable-next`, `todo`, `todo-next`, `next`, `push`, or `pop`.' # rubocop:disable Layout/LineLength -- the message lists every mode and does not wrap
|
|
62
61
|
MISSING_COP_NAME_MSG = 'The cop name is missing.'
|
|
62
|
+
MULTIPLE_DIRECTIVES_MSG = 'Only the first directive on a line takes effect. ' \
|
|
63
|
+
'List the cop names in a single directive instead.'
|
|
63
64
|
MALFORMED_COP_NAMES_MSG = 'Cop names must be separated by commas. ' \
|
|
64
65
|
'Comment in the directive must start with `--`.'
|
|
66
|
+
INVALID_SIGNED_ARGS_MSG = '`push` and `next` arguments must be `+`- or `-`-prefixed ' \
|
|
67
|
+
'cop names, and `pop` takes no arguments.'
|
|
65
68
|
NEXT_DIRECTIVE_AT_EOL_MSG = 'A `-next` directive must be on its own line, above the ' \
|
|
66
69
|
'statement it applies to.'
|
|
67
70
|
INVALID_KEYWORD_MSG = 'The directive keyword must be `rubocop`, not `%<keyword>s`.'
|
|
@@ -89,7 +92,11 @@ module RuboCop
|
|
|
89
92
|
private
|
|
90
93
|
|
|
91
94
|
def check_directive(comment, directive_comment)
|
|
92
|
-
if directive_comment
|
|
95
|
+
if (directives = multiple_directives(comment, directive_comment))
|
|
96
|
+
add_offense(comment, message: "#{COMMON_MSG} #{MULTIPLE_DIRECTIVES_MSG}") do |corrector|
|
|
97
|
+
merge_directives(corrector, comment, directives)
|
|
98
|
+
end
|
|
99
|
+
elsif directive_comment.malformed?
|
|
93
100
|
add_offense(comment, message: offense_message(directive_comment))
|
|
94
101
|
elsif misplaced_next_directive?(directive_comment)
|
|
95
102
|
add_offense(comment, message: NEXT_DIRECTIVE_AT_EOL_MSG)
|
|
@@ -98,13 +105,72 @@ module RuboCop
|
|
|
98
105
|
end
|
|
99
106
|
end
|
|
100
107
|
|
|
101
|
-
#
|
|
102
|
-
#
|
|
108
|
+
# Everything after the first directive in a comment is trailing text as
|
|
109
|
+
# far as the parser is concerned, so a second directive written on the
|
|
110
|
+
# same line disables nothing at all. A valid `--` reason may mention a
|
|
111
|
+
# directive as plain text, which should not be treated as another one.
|
|
112
|
+
def multiple_directives(comment, directive_comment)
|
|
113
|
+
matches = comment.text.to_enum(:scan, DirectiveComment::DIRECTIVE_COMMENT_REGEXP)
|
|
114
|
+
.map { Regexp.last_match }
|
|
115
|
+
matches = filter_reason_matches(matches, directive_comment)
|
|
116
|
+
|
|
117
|
+
matches if matches.size > 1
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def filter_reason_matches(matches, directive_comment)
|
|
121
|
+
return matches unless directive_comment.reason
|
|
122
|
+
|
|
123
|
+
[matches.first, *matches.drop(1).reject { |match| match.post_match.match?(/\S/) }]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def merge_directives(corrector, comment, directives)
|
|
127
|
+
return unless directives_adjacent?(comment, directives)
|
|
128
|
+
return unless (mode = merged_mode(directives))
|
|
129
|
+
return unless (cops = merged_cops(directives))
|
|
130
|
+
|
|
131
|
+
corrector.replace(comment, "# rubocop:#{mode} #{cops.join(', ')}")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# `all` covers every cop already, so folding it into a list of names
|
|
135
|
+
# would be a rewrite rather than a merge.
|
|
136
|
+
def merged_cops(directives)
|
|
137
|
+
cops = directives.flat_map { |match| match[2].to_s.split(',').map(&:strip) }
|
|
138
|
+
cops = cops.reject(&:empty?).uniq
|
|
139
|
+
cops unless cops.empty? || cops.include?('all')
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Anything written between the directives is a `--` reason or prose,
|
|
143
|
+
# and merging around it would either move it or drop it.
|
|
144
|
+
def directives_adjacent?(comment, directives)
|
|
145
|
+
text = comment.text
|
|
146
|
+
bounds = directives.map { |match| match.end(0) }
|
|
147
|
+
.zip(directives.map { |match| match.begin(0) }[1..] + [text.length])
|
|
148
|
+
bounds.all? { |from, to| text[from...to].strip.empty? }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# `todo` is an alias of `disable`, so those two combine. Anything else
|
|
152
|
+
# (an `enable` beside a `disable`, or directives of different scopes)
|
|
153
|
+
# means different things per directive and can't be folded into one.
|
|
154
|
+
def merged_mode(directives)
|
|
155
|
+
modes = directives.map { |match| match[1] }.uniq
|
|
156
|
+
return modes.first if modes.size == 1
|
|
157
|
+
return modes.first if (modes - %w[disable todo]).empty?
|
|
158
|
+
|
|
159
|
+
nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# An EOL next-statement directive is not honored - it must fail
|
|
163
|
+
# loudly instead of silently doing nothing.
|
|
103
164
|
def misplaced_next_directive?(directive_comment)
|
|
104
|
-
directive_comment
|
|
165
|
+
next_statement_directive?(directive_comment) &&
|
|
105
166
|
!processed_source.comment_config.comment_only_line?(directive_comment.line_number)
|
|
106
167
|
end
|
|
107
168
|
|
|
169
|
+
def next_statement_directive?(directive_comment)
|
|
170
|
+
directive_comment.disable_next? || directive_comment.enable_next? ||
|
|
171
|
+
directive_comment.next?
|
|
172
|
+
end
|
|
173
|
+
|
|
108
174
|
# rubocop:disable-next Metrics/MethodLength
|
|
109
175
|
def offense_message(directive_comment)
|
|
110
176
|
comment = directive_comment.comment
|
|
@@ -116,6 +182,8 @@ module RuboCop
|
|
|
116
182
|
INVALID_MODE_NAME_MSG
|
|
117
183
|
elsif directive_comment.missing_cop_name?
|
|
118
184
|
MISSING_COP_NAME_MSG
|
|
185
|
+
elsif directive_comment.invalid_signed_args?
|
|
186
|
+
INVALID_SIGNED_ARGS_MSG
|
|
119
187
|
else
|
|
120
188
|
MALFORMED_COP_NAMES_MSG
|
|
121
189
|
end
|
|
@@ -142,10 +210,10 @@ module RuboCop
|
|
|
142
210
|
end
|
|
143
211
|
|
|
144
212
|
def unknown_cop_name(directive_comment)
|
|
145
|
-
return if directive_comment.
|
|
213
|
+
return if directive_comment.pop? || directive_comment.all_cops?
|
|
146
214
|
|
|
147
215
|
registry = directive_comment.cop_registry
|
|
148
|
-
directive_comment.
|
|
216
|
+
directive_names(directive_comment).find do |name|
|
|
149
217
|
next false if registry.department?(name)
|
|
150
218
|
|
|
151
219
|
qualified = registry.qualified_cop_name(name, nil, warn: false)
|
|
@@ -153,6 +221,14 @@ module RuboCop
|
|
|
153
221
|
end
|
|
154
222
|
end
|
|
155
223
|
|
|
224
|
+
def directive_names(directive_comment)
|
|
225
|
+
if directive_comment.push? || directive_comment.next?
|
|
226
|
+
directive_comment.signed_args.values.flatten
|
|
227
|
+
else
|
|
228
|
+
directive_comment.raw_cop_names
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
156
232
|
def unknown_cop_message(directive_comment, name)
|
|
157
233
|
similar = NameSimilarity.find_similar_name(name, directive_comment.cop_registry.names)
|
|
158
234
|
suggestion = similar ? " (did you mean `#{similar}`?)" : ''
|
|
@@ -162,5 +238,3 @@ module RuboCop
|
|
|
162
238
|
end
|
|
163
239
|
end
|
|
164
240
|
end
|
|
165
|
-
# rubocop:enable Lint/RedundantCopDisableDirective
|
|
166
|
-
# rubocop:enable Style/DoubleCopDisableDirective
|
|
@@ -435,7 +435,7 @@ module RuboCop
|
|
|
435
435
|
|
|
436
436
|
def anon_block_scope_id(anon_block)
|
|
437
437
|
parent = anon_block.parent
|
|
438
|
-
return unless parent&.type?(:any_block, :begin, :call, :casgn, :any_def)
|
|
438
|
+
return unless parent&.type?(:any_block, :begin, :call, :casgn, :ivasgn, :any_def)
|
|
439
439
|
|
|
440
440
|
if (receiver = scope_receiver(parent, anon_block))
|
|
441
441
|
"#{receiver.source}.#{parent.method_name}"
|
|
@@ -63,7 +63,7 @@ module RuboCop
|
|
|
63
63
|
class EmptyBlock < Base
|
|
64
64
|
MSG = 'Empty block detected.'
|
|
65
65
|
|
|
66
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
66
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- an empty block has no parameters to number
|
|
67
67
|
return if node.body
|
|
68
68
|
return if allow_empty_lambdas? && node.lambda_or_proc?
|
|
69
69
|
return if cop_config['AllowComments'] && allow_comment?(node)
|
|
@@ -97,12 +97,14 @@ module RuboCop
|
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
def can_simplify_conditional?(node)
|
|
100
|
-
node.else_branch && node.loc.else.source == 'else'
|
|
100
|
+
!node.elsif? && node.else_branch && node.loc.else.source == 'else'
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def remove_empty_branch(corrector, node)
|
|
104
104
|
range = if empty_if_branch?(node) && else_branch?(node)
|
|
105
105
|
branch_range(node)
|
|
106
|
+
elsif node.condition.last_line == node.loc.else.line
|
|
107
|
+
node.source_range.with(end_pos: node.loc.else.begin_pos)
|
|
106
108
|
else
|
|
107
109
|
deletion_range(branch_range(node))
|
|
108
110
|
end
|
|
@@ -48,7 +48,7 @@ module RuboCop
|
|
|
48
48
|
add_offense(node.lhs) do |corrector|
|
|
49
49
|
# Don't autocorrect `'foo' && return` because having `return` as
|
|
50
50
|
# the leftmost node can lead to a void value expression syntax error.
|
|
51
|
-
next if node.rhs
|
|
51
|
+
next if void_value_expression?(node.rhs)
|
|
52
52
|
|
|
53
53
|
corrector.replace(node, node.rhs.source)
|
|
54
54
|
end
|
|
@@ -60,7 +60,7 @@ module RuboCop
|
|
|
60
60
|
add_offense(node.lhs) do |corrector|
|
|
61
61
|
# Don't autocorrect `'foo' && return` because having `return` as
|
|
62
62
|
# the leftmost node can lead to a void value expression syntax error.
|
|
63
|
-
next if node.rhs
|
|
63
|
+
next if void_value_expression?(node.rhs)
|
|
64
64
|
|
|
65
65
|
corrector.replace(node, node.rhs.source)
|
|
66
66
|
end
|
|
@@ -98,6 +98,8 @@ module RuboCop
|
|
|
98
98
|
end
|
|
99
99
|
elsif node.condition.falsey_literal?
|
|
100
100
|
add_offense(node.condition) do |corrector|
|
|
101
|
+
next if orphans_loop_control_keyword?(node)
|
|
102
|
+
|
|
101
103
|
corrector.replace(node, node.body.child_nodes.map(&:source).join("\n"))
|
|
102
104
|
end
|
|
103
105
|
end
|
|
@@ -127,6 +129,8 @@ module RuboCop
|
|
|
127
129
|
end
|
|
128
130
|
elsif node.condition.truthy_literal?
|
|
129
131
|
add_offense(node.condition) do |corrector|
|
|
132
|
+
next if orphans_loop_control_keyword?(node)
|
|
133
|
+
|
|
130
134
|
corrector.replace(node, node.body.child_nodes.map(&:source).join("\n"))
|
|
131
135
|
end
|
|
132
136
|
end
|
|
@@ -175,6 +179,21 @@ module RuboCop
|
|
|
175
179
|
|
|
176
180
|
private
|
|
177
181
|
|
|
182
|
+
# Unwrapping the body of a loop that runs once would leave a `break`,
|
|
183
|
+
# `next` or `redo` that belongs to it outside of any loop.
|
|
184
|
+
def orphans_loop_control_keyword?(node)
|
|
185
|
+
node.body.each_node(:next, :break, :redo).any? do |control|
|
|
186
|
+
inner = control.each_ancestor.take_while { |ancestor| !ancestor.equal?(node) }
|
|
187
|
+
inner.none? { |ancestor| ancestor.any_block_type? || ancestor.loop_keyword? }
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def void_value_expression?(node)
|
|
192
|
+
node = node.children.last while node&.begin_type?
|
|
193
|
+
|
|
194
|
+
node&.type?(:return, :break, :next)
|
|
195
|
+
end
|
|
196
|
+
|
|
178
197
|
def check_for_literal(node)
|
|
179
198
|
cond = condition(node)
|
|
180
199
|
if cond.literal?
|
|
@@ -267,6 +286,8 @@ module RuboCop
|
|
|
267
286
|
"#{node.else_branch.source.sub('elsif', 'if')}\nend"
|
|
268
287
|
elsif node.else? || node.ternary?
|
|
269
288
|
node.else_branch.source
|
|
289
|
+
elsif node.value_used?
|
|
290
|
+
'nil'
|
|
270
291
|
else
|
|
271
292
|
'' # Equivalent to removing the node
|
|
272
293
|
end
|
|
@@ -24,30 +24,16 @@ module RuboCop
|
|
|
24
24
|
MSG = 'Literal interpolation detected.'
|
|
25
25
|
COMPOSITE = %i[array hash pair irange erange].freeze
|
|
26
26
|
|
|
27
|
-
# rubocop:disable-next Metrics/AbcSize
|
|
28
27
|
def on_interpolation(begin_node)
|
|
29
28
|
final_node = begin_node.children.last
|
|
30
29
|
return unless offending?(final_node)
|
|
31
|
-
|
|
32
|
-
# %W and %I split the content into words before expansion
|
|
33
|
-
# treating each interpolation as a word component, so
|
|
34
|
-
# interpolation should not be removed if the expanded value
|
|
35
|
-
# contains a space character.
|
|
36
|
-
expanded_value = autocorrected_value(final_node)
|
|
37
|
-
expanded_value = handle_special_regexp_chars(begin_node, expanded_value)
|
|
38
|
-
|
|
39
|
-
return if in_array_percent_literal?(begin_node) && /\s|\A\z/.match?(expanded_value)
|
|
30
|
+
return unless (expanded_value = correctable_value(begin_node, final_node))
|
|
40
31
|
|
|
41
32
|
add_offense(final_node) do |corrector|
|
|
42
33
|
next if final_node.dstr_type? # nested, fixed in next iteration
|
|
34
|
+
next if follows_literal_interpolation?(begin_node)
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
final_node.source.delete_prefix('"').delete_suffix('"')
|
|
46
|
-
else
|
|
47
|
-
expanded_value
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
corrector.replace(final_node.parent, replacement)
|
|
36
|
+
corrector.replace(final_node.parent, replacement(final_node, expanded_value))
|
|
51
37
|
end
|
|
52
38
|
end
|
|
53
39
|
|
|
@@ -73,6 +59,22 @@ module RuboCop
|
|
|
73
59
|
node.array_type? && grandparent.regexp_type?
|
|
74
60
|
end
|
|
75
61
|
|
|
62
|
+
def correctable_value(begin_node, final_node)
|
|
63
|
+
value = autocorrected_value(final_node)
|
|
64
|
+
value = handle_special_regexp_chars(begin_node, value)
|
|
65
|
+
return if starts_interpolation_after_hash_sign?(begin_node, value)
|
|
66
|
+
|
|
67
|
+
value = escape_trailing_hash_sign(begin_node, value)
|
|
68
|
+
|
|
69
|
+
# %W and %I split the content into words before expansion
|
|
70
|
+
# treating each interpolation as a word component, so
|
|
71
|
+
# interpolation should not be removed if the expanded value
|
|
72
|
+
# contains a space character.
|
|
73
|
+
return if in_array_percent_literal?(begin_node) && /\s|\A\z/.match?(value)
|
|
74
|
+
|
|
75
|
+
value
|
|
76
|
+
end
|
|
77
|
+
|
|
76
78
|
# rubocop:disable-next Metrics/MethodLength, Metrics/CyclomaticComplexity
|
|
77
79
|
def autocorrected_value(node)
|
|
78
80
|
case node.type
|
|
@@ -116,6 +118,17 @@ module RuboCop
|
|
|
116
118
|
end
|
|
117
119
|
end
|
|
118
120
|
|
|
121
|
+
def starts_interpolation_after_hash_sign?(begin_node, value)
|
|
122
|
+
source = processed_source.buffer.source
|
|
123
|
+
range = begin_node.source_range
|
|
124
|
+
# Removing an empty value puts the `#` right before what follows the interpolation.
|
|
125
|
+
following = "#{value}#{source[range.end_pos]}"
|
|
126
|
+
return false unless following.start_with?('{', '@', '$')
|
|
127
|
+
return false unless source[range.begin_pos - 1] == '#'
|
|
128
|
+
|
|
129
|
+
unescaped_hash_sign?(source[0...range.begin_pos])
|
|
130
|
+
end
|
|
131
|
+
|
|
119
132
|
def autocorrected_value_for_string(node)
|
|
120
133
|
return node.source.delete_prefix('"').delete_suffix('"') unless node.value.valid_encoding?
|
|
121
134
|
|
|
@@ -180,7 +193,9 @@ module RuboCop
|
|
|
180
193
|
end
|
|
181
194
|
|
|
182
195
|
def space_literal?(node)
|
|
183
|
-
|
|
196
|
+
# `String#blank?` misses Unicode spaces such as U+3000 that
|
|
197
|
+
# `Layout/TrailingWhitespace` treats as trailing whitespace.
|
|
198
|
+
node.str_type? && node.value.valid_encoding? && node.value.match?(/\A[[:space:]]*\z/)
|
|
184
199
|
end
|
|
185
200
|
|
|
186
201
|
def ends_heredoc_line?(node)
|
|
@@ -188,7 +203,8 @@ module RuboCop
|
|
|
188
203
|
return false unless grandparent&.dstr_type? && grandparent.heredoc?
|
|
189
204
|
|
|
190
205
|
line = processed_source.lines[node.last_line - 1]
|
|
191
|
-
|
|
206
|
+
|
|
207
|
+
line.size == node.parent.source_range.last_column
|
|
192
208
|
end
|
|
193
209
|
|
|
194
210
|
def in_array_percent_literal?(node)
|
|
@@ -198,6 +214,34 @@ module RuboCop
|
|
|
198
214
|
grandparent = parent.parent
|
|
199
215
|
grandparent&.array_type? && grandparent.percent_literal?
|
|
200
216
|
end
|
|
217
|
+
|
|
218
|
+
def escape_trailing_hash_sign(begin_node, value)
|
|
219
|
+
following = processed_source.buffer.source[begin_node.source_range.end_pos]
|
|
220
|
+
return value unless %w[{ @ $].include?(following) && unescaped_hash_sign?(value)
|
|
221
|
+
|
|
222
|
+
"#{value.delete_suffix('#')}\\#"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def unescaped_hash_sign?(text)
|
|
226
|
+
text.end_with?('#') && text.delete_suffix('#')[/\\*\z/].length.even?
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Each interpolation is checked against the original source, so correcting it in the
|
|
230
|
+
# same pass as a preceding one that leaves a `#` (or nothing) could join that `#` and
|
|
231
|
+
# a `{` from this one. It's left for the next iteration instead.
|
|
232
|
+
def follows_literal_interpolation?(begin_node)
|
|
233
|
+
previous = begin_node.left_sibling
|
|
234
|
+
return false unless previous&.begin_type? && offending?(previous.children.last)
|
|
235
|
+
|
|
236
|
+
value = correctable_value(previous, previous.children.last)
|
|
237
|
+
value && (value.empty? || value.end_with?('#'))
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def replacement(node, expanded_value)
|
|
241
|
+
return expanded_value unless node.str_type? && !node.value.valid_encoding?
|
|
242
|
+
|
|
243
|
+
node.source.delete_prefix('"').delete_suffix('"')
|
|
244
|
+
end
|
|
201
245
|
end
|
|
202
246
|
end
|
|
203
247
|
end
|
|
@@ -42,6 +42,8 @@ module RuboCop
|
|
|
42
42
|
# break if some_condition
|
|
43
43
|
# end
|
|
44
44
|
class Loop < Base
|
|
45
|
+
include Alignment
|
|
46
|
+
include RangeHelp
|
|
45
47
|
extend AutoCorrector
|
|
46
48
|
|
|
47
49
|
MSG = 'Use `Kernel#loop` with `break` rather than `begin/end/until`(or `while`).'
|
|
@@ -62,7 +64,8 @@ module RuboCop
|
|
|
62
64
|
add_offense(node.loc.keyword) do |corrector|
|
|
63
65
|
corrector.replace(body.loc.begin, 'loop do')
|
|
64
66
|
corrector.remove(keyword_and_condition_range(node))
|
|
65
|
-
|
|
67
|
+
|
|
68
|
+
insert_break_line(corrector, node, body)
|
|
66
69
|
end
|
|
67
70
|
end
|
|
68
71
|
|
|
@@ -70,9 +73,28 @@ module RuboCop
|
|
|
70
73
|
node.body.loc.end.end.join(node.source_range.end)
|
|
71
74
|
end
|
|
72
75
|
|
|
76
|
+
def insert_break_line(corrector, node, body)
|
|
77
|
+
if body.single_line?
|
|
78
|
+
corrector.replace(space_before_end(body), build_break_line(node))
|
|
79
|
+
else
|
|
80
|
+
corrector.insert_before(body.loc.end, build_break_line(node))
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def space_before_end(body)
|
|
85
|
+
range = range_with_surrounding_space(range: body.loc.end, side: :left, newlines: false)
|
|
86
|
+
|
|
87
|
+
range.with(end_pos: body.loc.end.begin_pos)
|
|
88
|
+
end
|
|
89
|
+
|
|
73
90
|
def build_break_line(node)
|
|
74
91
|
conditional_keyword = node.while_post_type? ? 'unless' : 'if'
|
|
75
|
-
"break #{conditional_keyword} #{node.condition.source}\n#{indent(node)}"
|
|
92
|
+
break_line = "break #{conditional_keyword} #{node.condition.source}\n#{indent(node)}"
|
|
93
|
+
return break_line unless node.body.single_line?
|
|
94
|
+
|
|
95
|
+
# A single-line `begin ... end` keeps its body and `end` on one line,
|
|
96
|
+
# so the `break` has to start a line of its own.
|
|
97
|
+
"\n#{indent(node, offset: configured_indentation_width)}#{break_line}"
|
|
76
98
|
end
|
|
77
99
|
end
|
|
78
100
|
end
|