rubocop 1.91.0 → 1.92.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +4 -1
- data/lib/rubocop/cached_data.rb +47 -7
- data/lib/rubocop/changed_files.rb +35 -13
- data/lib/rubocop/cli/command/cop_names.rb +57 -0
- data/lib/rubocop/cli/command/execute_runner.rb +2 -2
- data/lib/rubocop/cli/command/explain.rb +34 -0
- data/lib/rubocop/cli/command/show_cops.rb +6 -0
- data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
- data/lib/rubocop/cli.rb +3 -2
- data/lib/rubocop/comment_config.rb +17 -8
- data/lib/rubocop/config.rb +0 -4
- data/lib/rubocop/config_loader.rb +6 -3
- data/lib/rubocop/config_loader_resolver.rb +37 -12
- data/lib/rubocop/config_obsoletion/removed_cop.rb +1 -1
- data/lib/rubocop/config_obsoletion.rb +9 -0
- data/lib/rubocop/config_validator.rb +11 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +42 -21
- data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
- data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
- data/lib/rubocop/cop/cop_documentation.rb +81 -0
- data/lib/rubocop/cop/cop_explanation.rb +140 -0
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +37 -7
- data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +25 -4
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
- data/lib/rubocop/cop/documentation.rb +1 -1
- data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
- data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
- data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
- data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
- data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
- data/lib/rubocop/cop/layout/class_structure.rb +29 -7
- data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/dot_position.rb +25 -15
- data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
- data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +9 -3
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +21 -25
- data/lib/rubocop/cop/layout/extra_spacing.rb +21 -6
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -1
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +16 -8
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
- data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +16 -12
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +7 -3
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
- data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
- data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
- data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
- data/lib/rubocop/cop/layout/line_length.rb +176 -26
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +9 -0
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +30 -5
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
- data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +16 -3
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
- data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +6 -11
- data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +31 -4
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +16 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +32 -9
- data/lib/rubocop/cop/layout/space_inside_range_literal.rb +29 -11
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
- data/lib/rubocop/cop/lint/interpolation_check.rb +20 -17
- data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
- data/lib/rubocop/cop/lint/loop.rb +24 -2
- data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +71 -11
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +3 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +3 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +22 -1
- data/lib/rubocop/cop/lint/percent_string_array.rb +75 -9
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +39 -9
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +11 -4
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
- data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
- data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
- data/lib/rubocop/cop/lint/void.rb +8 -1
- data/lib/rubocop/cop/metrics/class_length.rb +4 -2
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
- data/lib/rubocop/cop/metrics/module_length.rb +4 -2
- data/lib/rubocop/cop/mixin/alignment.rb +4 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -19
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +8 -2
- data/lib/rubocop/cop/mixin/code_length.rb +10 -2
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
- data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
- data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +41 -6
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +26 -2
- data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
- data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +17 -2
- data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
- data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
- data/lib/rubocop/cop/mixin/statement_modifier.rb +39 -5
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
- data/lib/rubocop/cop/mixin.rb +2 -0
- data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
- data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
- data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +59 -3
- data/lib/rubocop/cop/registry.rb +1 -1
- data/lib/rubocop/cop/security/json_load.rb +16 -4
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +37 -2
- data/lib/rubocop/cop/style/alias.rb +18 -5
- data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
- data/lib/rubocop/cop/style/and_or.rb +37 -10
- data/lib/rubocop/cop/style/arguments_forwarding.rb +32 -3
- data/lib/rubocop/cop/style/attr.rb +3 -4
- data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
- data/lib/rubocop/cop/style/block_delimiters.rb +35 -3
- data/lib/rubocop/cop/style/case_equality.rb +14 -3
- data/lib/rubocop/cop/style/case_like_if.rb +6 -3
- data/lib/rubocop/cop/style/class_and_module_children.rb +57 -16
- data/lib/rubocop/cop/style/class_methods_definitions.rb +15 -0
- data/lib/rubocop/cop/style/collection_querying.rb +79 -46
- data/lib/rubocop/cop/style/combinable_loops.rb +89 -11
- data/lib/rubocop/cop/style/concat_array_literals.rb +58 -7
- data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +65 -9
- data/lib/rubocop/cop/style/double_negation.rb +11 -0
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +3 -1
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
- data/lib/rubocop/cop/style/endless_method.rb +104 -11
- data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
- data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
- data/lib/rubocop/cop/style/float_division.rb +37 -2
- data/lib/rubocop/cop/style/for.rb +53 -2
- data/lib/rubocop/cop/style/format_string.rb +28 -6
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
- data/lib/rubocop/cop/style/guard_clause.rb +31 -22
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
- data/lib/rubocop/cop/style/hash_lookup_method.rb +42 -9
- data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
- data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -26
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
- data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
- data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
- data/lib/rubocop/cop/style/it_block_parameter.rb +94 -4
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
- data/lib/rubocop/cop/style/lambda.rb +34 -1
- data/lib/rubocop/cop/style/lambda_call.rb +7 -2
- data/lib/rubocop/cop/style/map_into_array.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +80 -13
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +26 -2
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
- data/lib/rubocop/cop/style/method_def_parentheses.rb +38 -4
- data/lib/rubocop/cop/style/missing_else.rb +7 -1
- data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
- data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
- data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
- data/lib/rubocop/cop/style/mutable_constant.rb +52 -7
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
- data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +27 -8
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
- data/lib/rubocop/cop/style/next.rb +24 -10
- data/lib/rubocop/cop/style/nil_comparison.rb +16 -4
- data/lib/rubocop/cop/style/non_nil_check.rb +1 -1
- data/lib/rubocop/cop/style/not.rb +10 -5
- data/lib/rubocop/cop/style/numeric_predicate.rb +28 -5
- data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +19 -1
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
- data/lib/rubocop/cop/style/quoted_symbols.rb +5 -7
- data/lib/rubocop/cop/style/raise_args.rb +80 -11
- data/lib/rubocop/cop/style/reduce_to_hash.rb +47 -33
- data/lib/rubocop/cop/style/redundant_begin.rb +45 -5
- data/lib/rubocop/cop/style/redundant_condition.rb +30 -13
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -0
- data/lib/rubocop/cop/style/redundant_exception.rb +27 -11
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +9 -1
- data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +12 -1
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +20 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
- data/lib/rubocop/cop/style/redundant_self.rb +9 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
- data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
- data/lib/rubocop/cop/style/require_order.rb +25 -8
- data/lib/rubocop/cop/style/rescue_modifier.rb +11 -9
- data/lib/rubocop/cop/style/return_nil.rb +17 -2
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
- data/lib/rubocop/cop/style/sample.rb +9 -8
- data/lib/rubocop/cop/style/self_assignment.rb +12 -15
- data/lib/rubocop/cop/style/semicolon.rb +16 -5
- data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +42 -4
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
- data/lib/rubocop/cop/style/string_concatenation.rb +21 -4
- data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
- data/lib/rubocop/cop/style/super_arguments.rb +3 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +24 -6
- data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
- data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
- data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
- data/lib/rubocop/cop/team.rb +10 -5
- data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +14 -1
- data/lib/rubocop/formatter/base_formatter.rb +22 -0
- data/lib/rubocop/formatter/formatter_set.rb +4 -0
- data/lib/rubocop/formatter/json_formatter.rb +71 -1
- data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
- data/lib/rubocop/formatter/sarif_formatter.rb +12 -7
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
- data/lib/rubocop/lsp/routes.rb +23 -23
- data/lib/rubocop/lsp/runtime.rb +47 -11
- data/lib/rubocop/lsp/stdin_runner.rb +11 -5
- data/lib/rubocop/lsp.rb +5 -2
- data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
- data/lib/rubocop/mcp/explain_tool.rb +68 -0
- data/lib/rubocop/mcp/scope.rb +65 -0
- data/lib/rubocop/mcp/server.rb +184 -76
- data/lib/rubocop/mcp/source_files.rb +35 -0
- data/lib/rubocop/offense_limit.rb +30 -0
- data/lib/rubocop/options.rb +45 -10
- data/lib/rubocop/plugin/configuration_integrator.rb +36 -8
- data/lib/rubocop/remote_config.rb +4 -1
- data/lib/rubocop/result_cache.rb +20 -13
- data/lib/rubocop/runner.rb +110 -42
- data/lib/rubocop/server/cache.rb +6 -6
- data/lib/rubocop/target_ruby.rb +21 -9
- data/lib/rubocop/util.rb +69 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +5 -0
- metadata +16 -5
|
@@ -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
|
|
@@ -116,31 +116,88 @@ module RuboCop
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
def first_code_token
|
|
119
|
-
|
|
119
|
+
processed_source.sorted_tokens.find { |token| !token.comment? }
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
def check_encoding_comment(comment)
|
|
123
|
+
return unless misplaced_encoding_position?(comment)
|
|
124
|
+
|
|
125
|
+
add_offense(comment, message: MSG_ENCODING) do |corrector|
|
|
126
|
+
correct_encoding_comment(corrector, comment)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def misplaced_encoding_position?(comment)
|
|
123
131
|
line = comment.source_range.line
|
|
124
|
-
return if line == effective_encoding_line && comment_starts_line?(comment)
|
|
132
|
+
return false if line == effective_encoding_line && comment_starts_line?(comment)
|
|
133
|
+
|
|
125
134
|
# Leave `# frozen_string_literal: true` + `# encoding: x` runs at the
|
|
126
135
|
# very top to Lint/OrderedMagicComments, which already reorders them.
|
|
127
|
-
|
|
136
|
+
!preceded_only_by_magic_comments?(comment)
|
|
137
|
+
end
|
|
128
138
|
|
|
129
|
-
|
|
139
|
+
# With an effective encoding comment already in place, moving another
|
|
140
|
+
# one in would override it. Drop an identical duplicate and leave a
|
|
141
|
+
# conflicting one for a human to resolve.
|
|
142
|
+
def correct_encoding_comment(corrector, comment)
|
|
143
|
+
effective_comment = effective_encoding_comment
|
|
144
|
+
|
|
145
|
+
if effective_comment.nil?
|
|
130
146
|
move_comment(corrector, comment, effective_encoding_line)
|
|
147
|
+
elsif comment_encoding(effective_comment) == comment_encoding(comment)
|
|
148
|
+
corrector.remove(comment_removal_range(comment))
|
|
131
149
|
end
|
|
132
150
|
end
|
|
133
151
|
|
|
152
|
+
def comment_encoding(comment)
|
|
153
|
+
Encoding.find(MagicComment.parse(comment.text).encoding)
|
|
154
|
+
rescue ArgumentError
|
|
155
|
+
nil
|
|
156
|
+
end
|
|
157
|
+
|
|
134
158
|
def check_top_block_comment(comment, directive)
|
|
135
159
|
return unless first_code_token
|
|
136
160
|
return unless comment.source_range.begin_pos > first_code_token.begin_pos
|
|
137
161
|
|
|
138
162
|
message = format(MSG_AFTER_CODE, directive: directive)
|
|
139
163
|
add_offense(comment, message: message) do |corrector|
|
|
140
|
-
|
|
164
|
+
# Both would be inserted at the top, in no reliable order. Leave
|
|
165
|
+
# this one to the next pass, once the encoding comment is in place.
|
|
166
|
+
next if encoding_comment_moving_in?
|
|
167
|
+
|
|
168
|
+
move_comment(corrector, comment, top_block_insertion_line)
|
|
141
169
|
end
|
|
142
170
|
end
|
|
143
171
|
|
|
172
|
+
def encoding_comment_moving_in?
|
|
173
|
+
return false if effective_encoding_comment
|
|
174
|
+
|
|
175
|
+
processed_source.comments.any? do |comment|
|
|
176
|
+
known_encoding_comment?(comment) && misplaced_encoding_position?(comment)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def effective_encoding_comment
|
|
181
|
+
processed_source.comments.find do |comment|
|
|
182
|
+
comment.source_range.line == effective_encoding_line &&
|
|
183
|
+
comment_starts_line?(comment) &&
|
|
184
|
+
MagicComment.parse(comment.text).encoding_specified?
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def known_encoding_comment?(comment)
|
|
189
|
+
return false unless magic_comment_shaped?(comment.text)
|
|
190
|
+
|
|
191
|
+
magic_comment = MagicComment.parse(comment.text)
|
|
192
|
+
magic_comment.encoding_specified? && known_encoding?(magic_comment.encoding)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Insert below an effective encoding comment rather than above it,
|
|
196
|
+
# which would push it off the only lines where Ruby honors it.
|
|
197
|
+
def top_block_insertion_line
|
|
198
|
+
effective_encoding_comment ? effective_encoding_line + 1 : effective_encoding_line
|
|
199
|
+
end
|
|
200
|
+
|
|
144
201
|
def check_magic_comment_above_shebang
|
|
145
202
|
shebang_comment = processed_source.comments.find do |comment|
|
|
146
203
|
comment.source_range.line > 1 && comment.text.start_with?('#!') &&
|
|
@@ -166,12 +223,7 @@ module RuboCop
|
|
|
166
223
|
end
|
|
167
224
|
|
|
168
225
|
def move_comment(corrector, comment, target_line)
|
|
169
|
-
|
|
170
|
-
range_by_whole_lines(comment.source_range, include_final_newline: true)
|
|
171
|
-
else
|
|
172
|
-
range_with_surrounding_space(comment.source_range, side: :left)
|
|
173
|
-
end
|
|
174
|
-
corrector.remove(removal_range)
|
|
226
|
+
corrector.remove(comment_removal_range(comment))
|
|
175
227
|
target_range = processed_source.buffer.line_range(target_line)
|
|
176
228
|
if comment.source_range.line < target_line
|
|
177
229
|
corrector.insert_after(target_range, "\n#{comment.text}")
|
|
@@ -179,6 +231,14 @@ module RuboCop
|
|
|
179
231
|
corrector.insert_before(target_range, "#{comment.text}\n")
|
|
180
232
|
end
|
|
181
233
|
end
|
|
234
|
+
|
|
235
|
+
def comment_removal_range(comment)
|
|
236
|
+
if comment_starts_line?(comment)
|
|
237
|
+
range_by_whole_lines(comment.source_range, include_final_newline: true)
|
|
238
|
+
else
|
|
239
|
+
range_with_surrounding_space(comment.source_range, side: :left)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
182
242
|
end
|
|
183
243
|
end
|
|
184
244
|
end
|
|
@@ -62,7 +62,7 @@ module RuboCop
|
|
|
62
62
|
each_missing_enable do |cop, line_range|
|
|
63
63
|
next if acceptable_range?(cop, line_range)
|
|
64
64
|
|
|
65
|
-
comment =
|
|
65
|
+
comment = opening_comment(line_range)
|
|
66
66
|
|
|
67
67
|
add_offense(comment, message: message(cop, comment))
|
|
68
68
|
end
|
|
@@ -73,14 +73,29 @@ module RuboCop
|
|
|
73
73
|
def each_missing_enable
|
|
74
74
|
processed_source.disabled_line_ranges.each do |cop, line_ranges|
|
|
75
75
|
line_ranges.each do |line_range|
|
|
76
|
-
# A `disable-next` scope closes itself with its statement.
|
|
77
|
-
next if
|
|
76
|
+
# A `disable-next` or `next` scope closes itself with its statement.
|
|
77
|
+
next if statement_scoped?(line_range)
|
|
78
78
|
|
|
79
79
|
yield cop, line_range
|
|
80
80
|
end
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
def statement_scoped?(line_range)
|
|
85
|
+
directive = line_range.respond_to?(:directive) && line_range.directive
|
|
86
|
+
directive && (directive.disable_next? || directive.next?)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# A disable suspended by `enable-next` or `next +Cop` reopens below the
|
|
90
|
+
# statement, on a code line, so the comment that opened it has to come
|
|
91
|
+
# from the range rather than from its first line.
|
|
92
|
+
def opening_comment(line_range)
|
|
93
|
+
directive = line_range.respond_to?(:directive) && line_range.directive
|
|
94
|
+
return directive.comment if directive
|
|
95
|
+
|
|
96
|
+
processed_source.comment_at_line(line_range.begin)
|
|
97
|
+
end
|
|
98
|
+
|
|
84
99
|
def acceptable_range?(cop, line_range)
|
|
85
100
|
# This has to remain a strict inequality to handle
|
|
86
101
|
# the case when max_range is Float::INFINITY
|
|
@@ -40,6 +40,7 @@ module RuboCop
|
|
|
40
40
|
# NestedPercentLiteral violation to be emitted.
|
|
41
41
|
PERCENT_LITERAL_TYPES = PreferredDelimiters::PERCENT_LITERAL_TYPES
|
|
42
42
|
REGEXES = PERCENT_LITERAL_TYPES.map { |percent_literal| /\A#{percent_literal}\W/ }.freeze
|
|
43
|
+
FORMAT_SEQUENCE = /\A#{RuboCop::Cop::Utils::FormatString::SEQUENCE}\z/.freeze
|
|
43
44
|
|
|
44
45
|
def on_array(node)
|
|
45
46
|
process(node, *PERCENT_LITERAL_TYPES)
|
|
@@ -54,6 +55,8 @@ module RuboCop
|
|
|
54
55
|
def contains_percent_literals?(node)
|
|
55
56
|
node.each_child_node.any? do |child|
|
|
56
57
|
literal = child.children.first.to_s.scrub
|
|
58
|
+
next false if literal.match?(FORMAT_SEQUENCE)
|
|
59
|
+
|
|
57
60
|
REGEXES.any? { |regex| literal.match?(regex) }
|
|
58
61
|
end
|
|
59
62
|
end
|
|
@@ -132,7 +132,7 @@ module RuboCop
|
|
|
132
132
|
|
|
133
133
|
def handle_as_symbol(node)
|
|
134
134
|
to_method_symbol(node) do |receiver, sym_node, to_method|
|
|
135
|
-
next if receiver.nil? || !node.arguments.one?
|
|
135
|
+
next if receiver.nil? || !node.arguments.one? || node.operator_method?
|
|
136
136
|
|
|
137
137
|
message = format(
|
|
138
138
|
MSG,
|
|
@@ -140,6 +140,8 @@ module RuboCop
|
|
|
140
140
|
corrected_method: correct_sym_method(to_method)
|
|
141
141
|
)
|
|
142
142
|
add_offense(node, message: message) do |corrector|
|
|
143
|
+
next if part_of_ignored_node?(node)
|
|
144
|
+
|
|
143
145
|
remove_parentheses(corrector, node) if node.parenthesized?
|
|
144
146
|
|
|
145
147
|
corrector.replace(sym_node, correct_sym_method(to_method))
|
|
@@ -57,7 +57,7 @@ module RuboCop
|
|
|
57
57
|
node = node.children.first while node&.begin_type? && node.children.one?
|
|
58
58
|
return false unless node
|
|
59
59
|
|
|
60
|
-
keyword_operator?(node) || modifier_expression?(node)
|
|
60
|
+
keyword_operator?(node) || modifier_expression?(node) || command_call_operand?(node)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def keyword_operator?(node)
|
|
@@ -69,6 +69,27 @@ module RuboCop
|
|
|
69
69
|
node.type?(:if, :while, :until) && node.modifier_form?
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
def command_call_operand?(node)
|
|
73
|
+
if node.assignment? || (node.call_type? && node.assignment_method?)
|
|
74
|
+
command_call_value?(node.children.last)
|
|
75
|
+
elsif node.send_type? && node.prefix_bang?
|
|
76
|
+
command_call_value?(node.receiver)
|
|
77
|
+
else
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def command_call_value?(node)
|
|
83
|
+
command_call?(node) || command_call_operand?(node)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def command_call?(node)
|
|
87
|
+
node = node.send_node if node.any_block_type?
|
|
88
|
+
return false unless node.type?(:call, :super, :yield)
|
|
89
|
+
|
|
90
|
+
node.arguments? && !node.parenthesized? && !node.operator_method?
|
|
91
|
+
end
|
|
92
|
+
|
|
72
93
|
def chained_calls?(node)
|
|
73
94
|
first_argument = node.first_argument
|
|
74
95
|
first_argument.call_type? && (node.children.last&.children&.count || 0) > 1
|
|
@@ -9,6 +9,11 @@ module RuboCop
|
|
|
9
9
|
# example, mistranslating an array of literals to percent string notation)
|
|
10
10
|
# rather than meant to be part of the resulting strings.
|
|
11
11
|
#
|
|
12
|
+
# When a quoted string contains whitespace, `%w` splits it into separate values,
|
|
13
|
+
# so removing the quotes would not restore the intended string.
|
|
14
|
+
# Such an array is corrected to an array literal instead, as long as its content
|
|
15
|
+
# reads as one with only string elements.
|
|
16
|
+
#
|
|
12
17
|
# @safety
|
|
13
18
|
# The cop is unsafe because the correction changes the values in the array
|
|
14
19
|
# and that might have been done purposely.
|
|
@@ -26,6 +31,12 @@ module RuboCop
|
|
|
26
31
|
#
|
|
27
32
|
# # good
|
|
28
33
|
# %w(foo bar)
|
|
34
|
+
#
|
|
35
|
+
# # bad - the quoted strings are split at whitespace
|
|
36
|
+
# %w('foo bar', 'baz')
|
|
37
|
+
#
|
|
38
|
+
# # good
|
|
39
|
+
# ['foo bar', 'baz']
|
|
29
40
|
class PercentStringArray < Base
|
|
30
41
|
include PercentLiteral
|
|
31
42
|
extend AutoCorrector
|
|
@@ -36,27 +47,69 @@ module RuboCop
|
|
|
36
47
|
|
|
37
48
|
MSG = "Within `%w`/`%W`, quotes and ',' are unnecessary and may be " \
|
|
38
49
|
'unwanted in the resulting strings.'
|
|
50
|
+
MSG_SPLIT_STRING = 'Within `%w`/`%W`, quoted strings are split at whitespace; ' \
|
|
51
|
+
'use an array literal instead.'
|
|
39
52
|
|
|
40
53
|
def on_array(node)
|
|
41
54
|
process(node, '%w', '%W')
|
|
42
55
|
end
|
|
43
56
|
|
|
44
57
|
def on_percent_literal(node)
|
|
45
|
-
|
|
58
|
+
if split_quoted_string?(node)
|
|
59
|
+
register_split_quoted_string(node)
|
|
60
|
+
elsif contains_quotes_or_commas?(node)
|
|
61
|
+
register_quotes_or_commas(node)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
46
64
|
|
|
47
|
-
|
|
48
|
-
node.each_value do |value|
|
|
49
|
-
range = value.source_range
|
|
65
|
+
private
|
|
50
66
|
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
def split_quoted_string?(node)
|
|
68
|
+
sources = node.values.map { |value| value.source.delete_suffix(',') }
|
|
53
69
|
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
sources.each_with_index.any? do |source, index|
|
|
71
|
+
next false unless (quote = unclosed_quote(source))
|
|
72
|
+
|
|
73
|
+
sources.drop(index + 1).any? { |rest| closing_quote?(rest, quote) }
|
|
56
74
|
end
|
|
57
75
|
end
|
|
58
76
|
|
|
59
|
-
|
|
77
|
+
def unclosed_quote(source)
|
|
78
|
+
quote = source[0]
|
|
79
|
+
return unless LEADING_QUOTE.match?(quote)
|
|
80
|
+
|
|
81
|
+
quote if source.length > 1 && !source.end_with?(quote)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def closing_quote?(source, quote)
|
|
85
|
+
source.end_with?(quote) && (source.length == 1 || !source.start_with?(quote))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def register_split_quoted_string(node)
|
|
89
|
+
add_offense(node, message: MSG_SPLIT_STRING) do |corrector|
|
|
90
|
+
next unless (array_literal = array_literal_source(node))
|
|
91
|
+
|
|
92
|
+
corrector.replace(node, array_literal)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def array_literal_source(node)
|
|
97
|
+
content = range_between(node.loc.begin.end_pos, node.loc.end.begin_pos).source
|
|
98
|
+
array_literal = "[#{content}]"
|
|
99
|
+
|
|
100
|
+
array_literal if string_array?(parse(array_literal))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def string_array?(processed_source)
|
|
104
|
+
return false unless processed_source.valid_syntax? && processed_source.comments.empty?
|
|
105
|
+
return false unless (array = processed_source.ast).array_type?
|
|
106
|
+
|
|
107
|
+
array.children.all? { |child| quoted_string?(child) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def quoted_string?(node)
|
|
111
|
+
node.type?(:str, :dstr) && node.loc?(:begin)
|
|
112
|
+
end
|
|
60
113
|
|
|
61
114
|
def contains_quotes_or_commas?(node)
|
|
62
115
|
node.values.any? do |value|
|
|
@@ -68,6 +121,19 @@ module RuboCop
|
|
|
68
121
|
QUOTES_AND_COMMAS.any? { |pat| literal.match?(pat) }
|
|
69
122
|
end
|
|
70
123
|
end
|
|
124
|
+
|
|
125
|
+
def register_quotes_or_commas(node)
|
|
126
|
+
add_offense(node) do |corrector|
|
|
127
|
+
node.each_value do |value|
|
|
128
|
+
range = value.source_range
|
|
129
|
+
|
|
130
|
+
match = range.source.match(TRAILING_QUOTE)
|
|
131
|
+
corrector.remove_trailing(range, match[0].length) if match
|
|
132
|
+
|
|
133
|
+
corrector.remove_leading(range, 1) if LEADING_QUOTE.match?(range.source)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
71
137
|
end
|
|
72
138
|
end
|
|
73
139
|
end
|
|
@@ -110,7 +110,7 @@ module RuboCop
|
|
|
110
110
|
# A pending cop that is not enabled in this run produces no
|
|
111
111
|
# offenses, so its directives cannot be judged - they typically
|
|
112
112
|
# prepare the code for the moment the cop gets enabled.
|
|
113
|
-
next if pending_cop_not_run?(cop)
|
|
113
|
+
next if pending_cop_not_run?(cop) || disabled_by_preview?(cop)
|
|
114
114
|
|
|
115
115
|
each_already_disabled(cop, line_ranges, &block)
|
|
116
116
|
each_line_range(cop, line_ranges, &block)
|
|
@@ -128,6 +128,17 @@ module RuboCop
|
|
|
128
128
|
!processed_source.registry.enabled_pending_cop?(cop_cfg, config, cop)
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
+
# A cop that only the `Preview` defaults disable runs again as soon as
|
|
132
|
+
# preview is turned off, so its directives are still needed.
|
|
133
|
+
def disabled_by_preview?(cop)
|
|
134
|
+
return false unless preview? && all_cop_names.include?(cop)
|
|
135
|
+
|
|
136
|
+
preview_defaults = ConfigLoader.default_configuration[cop]&.fetch('Preview', nil)
|
|
137
|
+
return false unless preview_defaults.is_a?(Hash) && preview_defaults['Enabled'] == false
|
|
138
|
+
|
|
139
|
+
!config.cop_enabled?(cop)
|
|
140
|
+
end
|
|
141
|
+
|
|
131
142
|
def each_line_range(cop, line_ranges)
|
|
132
143
|
line_ranges.each_with_index do |line_range, line_range_index|
|
|
133
144
|
next if should_skip_line_range?(cop, line_range)
|
|
@@ -135,8 +146,8 @@ module RuboCop
|
|
|
135
146
|
comment = directive_comment(line_range)
|
|
136
147
|
next if skip_directive?(comment)
|
|
137
148
|
|
|
138
|
-
|
|
139
|
-
redundant = find_redundant_directive(cop, comment, line_range,
|
|
149
|
+
following_ranges = line_ranges[(line_range_index + 1)..]
|
|
150
|
+
redundant = find_redundant_directive(cop, comment, line_range, following_ranges)
|
|
140
151
|
yield comment, redundant if redundant
|
|
141
152
|
end
|
|
142
153
|
end
|
|
@@ -180,16 +191,35 @@ module RuboCop
|
|
|
180
191
|
directive.push? || directive.pop? || directive.next?
|
|
181
192
|
end
|
|
182
193
|
|
|
183
|
-
def find_redundant_directive(cop, comment, line_range,
|
|
184
|
-
if all_disabled?(comment)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
194
|
+
def find_redundant_directive(cop, comment, line_range, following_ranges)
|
|
195
|
+
return find_redundant_all(line_range, following_ranges.first) if all_disabled?(comment)
|
|
196
|
+
|
|
197
|
+
range = line_range_through_repeated_disables(cop, line_range, following_ranges)
|
|
198
|
+
if department_disabled?(cop, comment)
|
|
199
|
+
find_redundant_department(cop, range)
|
|
188
200
|
else
|
|
189
|
-
find_redundant_cop(cop,
|
|
201
|
+
find_redundant_cop(cop, range)
|
|
190
202
|
end
|
|
191
203
|
end
|
|
192
204
|
|
|
205
|
+
def line_range_through_repeated_disables(cop, line_range, following_ranges)
|
|
206
|
+
last_range = line_range
|
|
207
|
+
following_ranges.each do |range|
|
|
208
|
+
break unless followed_ranges?(last_range, range) && repeated_disable?(cop, range)
|
|
209
|
+
|
|
210
|
+
last_range = range
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
line_range.begin..last_range.end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def repeated_disable?(cop, range)
|
|
217
|
+
comment = directive_comment(range)
|
|
218
|
+
return false if comment.nil? || skip_directive?(comment)
|
|
219
|
+
|
|
220
|
+
!all_disabled?(comment) && !department_disabled?(cop, comment)
|
|
221
|
+
end
|
|
222
|
+
|
|
193
223
|
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
194
224
|
def each_already_disabled(cop, line_ranges)
|
|
195
225
|
line_ranges.each_cons(2) do |previous_range, range|
|
|
@@ -28,6 +28,7 @@ module RuboCop
|
|
|
28
28
|
# end
|
|
29
29
|
#
|
|
30
30
|
class RedundantDirGlobSort < Base
|
|
31
|
+
include RangeHelp
|
|
31
32
|
extend AutoCorrector
|
|
32
33
|
extend TargetRubyVersion
|
|
33
34
|
|
|
@@ -46,8 +47,7 @@ module RuboCop
|
|
|
46
47
|
selector = node.loc.selector
|
|
47
48
|
|
|
48
49
|
add_offense(selector) do |corrector|
|
|
49
|
-
corrector.
|
|
50
|
-
corrector.remove(node.loc.dot)
|
|
50
|
+
autocorrect(corrector, node.loc.dot, selector)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -67,6 +67,17 @@ module RuboCop
|
|
|
67
67
|
def sort_with_comparator?(node)
|
|
68
68
|
node.parent&.any_block_type? || node.last_argument&.block_pass_type?
|
|
69
69
|
end
|
|
70
|
+
|
|
71
|
+
def autocorrect(corrector, dot, selector)
|
|
72
|
+
range = dot.join(selector)
|
|
73
|
+
|
|
74
|
+
if processed_source.contains_comment?(range)
|
|
75
|
+
corrector.remove(dot)
|
|
76
|
+
corrector.remove(selector)
|
|
77
|
+
else
|
|
78
|
+
corrector.remove(range_with_surrounding_space(range, side: :left))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
70
81
|
end
|
|
71
82
|
end
|
|
72
83
|
end
|
|
@@ -118,10 +118,7 @@ module RuboCop
|
|
|
118
118
|
def redundant_splat_expansion(node)
|
|
119
119
|
literal_expansion(node) do |expanded_item|
|
|
120
120
|
if expanded_item.send_type?
|
|
121
|
-
return
|
|
122
|
-
|
|
123
|
-
grandparent = node.parent.parent
|
|
124
|
-
return if grandparent && !ASSIGNMENT_TYPES.include?(grandparent.type)
|
|
121
|
+
return unless redundant_array_new_splat?(node, expanded_item)
|
|
125
122
|
# An empty array/percent literal (`*[]`, `*%w()`, ...) expands to nothing, so
|
|
126
123
|
# removing the splat would produce invalid or semantically different code.
|
|
127
124
|
elsif expanded_item.array_type? && expanded_item.children.empty?
|
|
@@ -132,6 +129,16 @@ module RuboCop
|
|
|
132
129
|
end
|
|
133
130
|
end
|
|
134
131
|
|
|
132
|
+
# A splatted `Array.new` passed as a method argument provides the arguments,
|
|
133
|
+
# so the splat is only redundant directly inside an array.
|
|
134
|
+
def redundant_array_new_splat?(node, array_new_node)
|
|
135
|
+
return false unless node.parent.array_type?
|
|
136
|
+
return false if array_new_inside_array_literal?(array_new_node)
|
|
137
|
+
|
|
138
|
+
grandparent = node.parent.parent
|
|
139
|
+
!grandparent || ASSIGNMENT_TYPES.include?(grandparent.type)
|
|
140
|
+
end
|
|
141
|
+
|
|
135
142
|
def array_new_inside_array_literal?(array_new_node)
|
|
136
143
|
return false unless array_new?(array_new_node)
|
|
137
144
|
|
|
@@ -60,7 +60,7 @@ module RuboCop
|
|
|
60
60
|
|
|
61
61
|
rest_operands = grouped_operands[begin_of_rest_operands..]
|
|
62
62
|
rest_operands.each do |operand|
|
|
63
|
-
next
|
|
63
|
+
next unless correction_needed?(operand, dot_op, grouped_operands)
|
|
64
64
|
|
|
65
65
|
register_offense(operand, dot_op)
|
|
66
66
|
end
|
|
@@ -100,12 +100,58 @@ module RuboCop
|
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
def correction_needed?(operand, dot_op, grouped_operands)
|
|
104
|
+
return false if already_appropriate_call?(operand, dot_op)
|
|
105
|
+
|
|
106
|
+
dot_op == '&.' || receiver_guaranteed?(operand, grouped_operands)
|
|
107
|
+
end
|
|
108
|
+
|
|
103
109
|
def already_appropriate_call?(operand, dot_op)
|
|
104
110
|
return true if operand.safe_navigation? && dot_op == '&.'
|
|
105
111
|
|
|
106
112
|
(operand.dot? || operand.operator_method?) && dot_op == '.'
|
|
107
113
|
end
|
|
108
114
|
|
|
115
|
+
def receiver_guaranteed?(operand, grouped_operands)
|
|
116
|
+
grouped_operands.take_while { |guard| !guard.equal?(operand) }.any? do |guard|
|
|
117
|
+
guaranteed_by?(guard, operand)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def guaranteed_by?(guard, operand)
|
|
122
|
+
return false unless (path = logical_path(guard, operand))
|
|
123
|
+
|
|
124
|
+
truthiness = path.last.first.and_type? ? :truthy : :falsey
|
|
125
|
+
path[0...-1].reverse_each do |node, child|
|
|
126
|
+
return false unless (truthiness = child_truthiness(node, child, truthiness))
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
!guard.csend_type? || truthiness == :truthy
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def logical_path(guard, operand)
|
|
133
|
+
path = []
|
|
134
|
+
child = guard
|
|
135
|
+
|
|
136
|
+
guard.each_ancestor do |node|
|
|
137
|
+
unless node.begin_type?
|
|
138
|
+
return nil unless node.operator_keyword?
|
|
139
|
+
|
|
140
|
+
path << [node, child]
|
|
141
|
+
end
|
|
142
|
+
return path if operand.each_ancestor.any? { |ancestor| ancestor.equal?(node) }
|
|
143
|
+
|
|
144
|
+
child = node
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def child_truthiness(node, child, truthiness)
|
|
149
|
+
deciding = node.and_type? ? :truthy : :falsey
|
|
150
|
+
return truthiness == deciding ? deciding : :unknown if node.lhs.equal?(child)
|
|
151
|
+
|
|
152
|
+
deciding if truthiness == deciding
|
|
153
|
+
end
|
|
154
|
+
|
|
109
155
|
def register_offense(operand, dot_operator)
|
|
110
156
|
offense_range = operand.operator_method? ? operand : operand.loc.dot
|
|
111
157
|
message = dot_operator == '.' ? USE_DOT_MSG : USE_SAFE_NAVIGATION_MSG
|
|
@@ -53,12 +53,22 @@ module RuboCop
|
|
|
53
53
|
return if node.each_descendant(:dstr).any?
|
|
54
54
|
|
|
55
55
|
regexp_constructor(node) do |text|
|
|
56
|
+
next unless verbatim_string?(text)
|
|
57
|
+
|
|
56
58
|
detect_offenses_in_tree(text, parse_regexp(text.value))
|
|
57
59
|
end
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
private
|
|
61
63
|
|
|
64
|
+
# Offsets into the string's value only point at the right place in the source
|
|
65
|
+
# when the string has no escape sequences, so anything else is left alone.
|
|
66
|
+
def verbatim_string?(node)
|
|
67
|
+
return false unless node.loc?(:begin) && node.loc?(:end)
|
|
68
|
+
|
|
69
|
+
node.loc.begin.end.join(node.loc.end.begin).source == node.value
|
|
70
|
+
end
|
|
71
|
+
|
|
62
72
|
# When a character class opens with a bare `]` (e.g. `[^]]`), `regexp_parser` parses
|
|
63
73
|
# `[^]` / `[]` as an empty set and reports the closing `]` as a separate literal.
|
|
64
74
|
# Ruby treats that `]` as the end of the class, not as an unescaped bracket,
|
|
@@ -97,7 +107,7 @@ module RuboCop
|
|
|
97
107
|
location = range_at_index(node, expr.ts, pos)
|
|
98
108
|
|
|
99
109
|
add_offense(location) do |corrector|
|
|
100
|
-
corrector.replace(location,
|
|
110
|
+
corrector.replace(location, escaped_bracket(node))
|
|
101
111
|
end
|
|
102
112
|
end
|
|
103
113
|
|
|
@@ -108,6 +118,13 @@ module RuboCop
|
|
|
108
118
|
adjustment = index + offset
|
|
109
119
|
node.loc.begin.end.adjust(begin_pos: adjustment, end_pos: adjustment + 1)
|
|
110
120
|
end
|
|
121
|
+
|
|
122
|
+
# A double-quoted string turns `\]` into `]`, so its backslash needs escaping too.
|
|
123
|
+
def escaped_bracket(node)
|
|
124
|
+
return '\]' if node.regexp_type? || node.single_quoted? || node.percent_literal?(:q)
|
|
125
|
+
|
|
126
|
+
'\\\\]'
|
|
127
|
+
end
|
|
111
128
|
end
|
|
112
129
|
end
|
|
113
130
|
end
|