rubocop 1.89.0 → 1.91.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/config/default.yml +309 -14
- data/config/obsoletion.yml +9 -0
- data/exe/rubocop +1 -1
- data/lib/rubocop/cached_data.rb +7 -2
- data/lib/rubocop/changed_files.rb +112 -0
- data/lib/rubocop/cli/command/execute_runner.rb +52 -1
- data/lib/rubocop/cli.rb +11 -7
- data/lib/rubocop/comment_config/directive_range.rb +34 -0
- data/lib/rubocop/comment_config/disable_next.rb +143 -0
- data/lib/rubocop/comment_config/push_pop.rb +55 -0
- data/lib/rubocop/comment_config.rb +50 -64
- data/lib/rubocop/config.rb +33 -5
- data/lib/rubocop/config_loader.rb +13 -6
- data/lib/rubocop/config_loader_resolver.rb +50 -9
- data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
- data/lib/rubocop/config_obsoletion/removed_cop.rb +8 -0
- data/lib/rubocop/config_obsoletion.rb +1 -1
- data/lib/rubocop/config_validator.rb +19 -5
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +49 -5
- data/lib/rubocop/cop/corrector.rb +3 -3
- data/lib/rubocop/cop/correctors.rb +2 -2
- 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 +1 -2
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/class_structure.rb +102 -26
- data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
- data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +6 -5
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/line_length.rb +20 -3
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -5
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +14 -3
- data/lib/rubocop/cop/layout/space_around_operators.rb +12 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +2 -4
- data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
- data/lib/rubocop/cop/lint/argument_mismatch.rb +99 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +17 -11
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +165 -15
- data/lib/rubocop/cop/lint/deprecated_reference.rb +11 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +113 -15
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
- data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
- data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
- data/lib/rubocop/cop/lint/literal_as_condition.rb +11 -8
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +3 -6
- data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +185 -0
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
- data/lib/rubocop/cop/lint/name_typo.rb +53 -6
- 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/out_of_range_regexp_ref.rb +15 -0
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +122 -29
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -2
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
- data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -2
- data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
- data/lib/rubocop/cop/lint/unused_private_method.rb +94 -17
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
- data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
- data/lib/rubocop/cop/lint/void.rb +1 -2
- data/lib/rubocop/cop/lint.rb +3 -0
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
- data/lib/rubocop/cop/migration/department_name.rb +1 -2
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +11 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +7 -4
- data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
- data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +62 -6
- data/lib/rubocop/cop/mixin/range_help.rb +1 -2
- data/lib/rubocop/cop/mixin/statement_modifier.rb +13 -7
- data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
- data/lib/rubocop/cop/mixin.rb +6 -2
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
- 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/memoized_instance_variable_name.rb +2 -4
- data/lib/rubocop/cop/naming/method_name.rb +5 -2
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
- data/lib/rubocop/cop/offense.rb +27 -4
- data/lib/rubocop/cop/registry.rb +43 -11
- data/lib/rubocop/cop/severity.rb +12 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +5 -8
- data/lib/rubocop/cop/style/array_first_last.rb +1 -2
- data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
- data/lib/rubocop/cop/style/case_like_if.rb +5 -7
- data/lib/rubocop/cop/style/class_and_module_children.rb +1 -2
- data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -2
- data/lib/rubocop/cop/style/constant_visibility.rb +6 -3
- data/lib/rubocop/cop/style/copyright.rb +1 -2
- data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/directive_scope.rb +304 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +83 -13
- data/lib/rubocop/cop/style/documentation_method.rb +1 -9
- 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_case_condition.rb +1 -2
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +12 -2
- data/lib/rubocop/cop/style/endless_method.rb +31 -12
- data/lib/rubocop/cop/style/file_null.rb +2 -2
- data/lib/rubocop/cop/style/float_division.rb +10 -16
- data/lib/rubocop/cop/style/for.rb +5 -0
- data/lib/rubocop/cop/style/format_string_token.rb +1 -2
- data/lib/rubocop/cop/style/guard_clause.rb +5 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
- data/lib/rubocop/cop/style/hash_lookup_method.rb +3 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
- data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -2
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
- data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
- data/lib/rubocop/cop/style/negative_array_index.rb +2 -4
- data/lib/rubocop/cop/style/nested_modifier.rb +9 -4
- data/lib/rubocop/cop/style/nil_comparison.rb +36 -9
- 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 +3 -1
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
- data/lib/rubocop/cop/style/redundant_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_condition.rb +6 -8
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +11 -3
- data/lib/rubocop/cop/style/redundant_each.rb +1 -2
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +2 -4
- data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -5
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +6 -6
- data/lib/rubocop/cop/style/redundant_return.rb +1 -2
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
- data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -2
- data/lib/rubocop/cop/style/sample.rb +11 -6
- data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
- data/lib/rubocop/cop/style/semicolon.rb +2 -4
- data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +1 -2
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
- data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/super_arguments.rb +1 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
- data/lib/rubocop/cop/style/time_now.rb +41 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
- data/lib/rubocop/cop/style.rb +2 -1
- data/lib/rubocop/cop/team.rb +11 -3
- data/lib/rubocop/cop/util.rb +3 -5
- data/lib/rubocop/cop/variable_force/variable.rb +1 -2
- data/lib/rubocop/cops_documentation_generator.rb +26 -8
- data/lib/rubocop/directive_comment.rb +73 -10
- data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
- data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
- data/lib/rubocop/formatter/html_formatter.rb +2 -4
- data/lib/rubocop/formatter/json_formatter.rb +11 -6
- data/lib/rubocop/formatter/junit_formatter.rb +26 -9
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
- data/lib/rubocop/formatter/sarif_formatter.rb +181 -0
- data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
- data/lib/rubocop/formatter/tap_formatter.rb +3 -1
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
- data/lib/rubocop/formatter.rb +1 -0
- data/lib/rubocop/lockfile.rb +18 -0
- data/lib/rubocop/lsp/diagnostic.rb +4 -8
- data/lib/rubocop/lsp/routes.rb +48 -19
- data/lib/rubocop/magic_comment.rb +27 -0
- data/lib/rubocop/mcp/server.rb +1 -2
- data/lib/rubocop/options.rb +69 -7
- data/lib/rubocop/path_util.rb +2 -3
- data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/rake_task.rb +1 -2
- data/lib/rubocop/remote_config.rb +4 -4
- data/lib/rubocop/result_cache.rb +16 -5
- data/lib/rubocop/rspec/expect_offense.rb +2 -4
- data/lib/rubocop/rspec/shared_contexts.rb +4 -2
- data/lib/rubocop/runner.rb +90 -44
- data/lib/rubocop/server/cache.rb +1 -2
- data/lib/rubocop/server/cli.rb +2 -4
- data/lib/rubocop/server.rb +14 -0
- data/lib/rubocop/target_finder.rb +4 -1
- data/lib/rubocop/todo_audit.rb +111 -0
- data/lib/rubocop/unified_diff.rb +257 -0
- data/lib/rubocop/version.rb +3 -5
- data/lib/rubocop.rb +7 -1
- data/lib/ruby_lsp/rubocop/addon.rb +1 -2
- metadata +18 -6
- data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
|
24
24
|
MSG = 'Literal interpolation detected.'
|
|
25
25
|
COMPOSITE = %i[array hash pair irange erange].freeze
|
|
26
26
|
|
|
27
|
-
# rubocop:disable Metrics/AbcSize
|
|
27
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
28
28
|
def on_interpolation(begin_node)
|
|
29
29
|
final_node = begin_node.children.last
|
|
30
30
|
return unless offending?(final_node)
|
|
@@ -50,7 +50,6 @@ module RuboCop
|
|
|
50
50
|
corrector.replace(final_node.parent, replacement)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
-
# rubocop:enable Metrics/AbcSize
|
|
54
53
|
|
|
55
54
|
private
|
|
56
55
|
|
|
@@ -74,7 +73,7 @@ module RuboCop
|
|
|
74
73
|
node.array_type? && grandparent.regexp_type?
|
|
75
74
|
end
|
|
76
75
|
|
|
77
|
-
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
|
76
|
+
# rubocop:disable-next Metrics/MethodLength, Metrics/CyclomaticComplexity
|
|
78
77
|
def autocorrected_value(node)
|
|
79
78
|
case node.type
|
|
80
79
|
when :int
|
|
@@ -95,7 +94,6 @@ module RuboCop
|
|
|
95
94
|
node.source.gsub('"', '\"')
|
|
96
95
|
end
|
|
97
96
|
end
|
|
98
|
-
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
|
99
97
|
|
|
100
98
|
def handle_special_regexp_chars(begin_node, value)
|
|
101
99
|
parent_node = begin_node.parent
|
|
@@ -155,7 +153,7 @@ module RuboCop
|
|
|
155
153
|
"{#{hash_string}}"
|
|
156
154
|
end
|
|
157
155
|
|
|
158
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
156
|
+
# rubocop:disable-next Metrics/MethodLength, Metrics/AbcSize
|
|
159
157
|
def autocorrected_value_in_hash(node)
|
|
160
158
|
case node.type
|
|
161
159
|
when :int
|
|
@@ -174,7 +172,6 @@ module RuboCop
|
|
|
174
172
|
node.source.gsub('"', '\"')
|
|
175
173
|
end
|
|
176
174
|
end
|
|
177
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
|
178
175
|
|
|
179
176
|
# Does node print its own source when converted to a string?
|
|
180
177
|
def prints_as_self?(node)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# Checks for magic comments placed where Ruby silently ignores them.
|
|
7
|
+
#
|
|
8
|
+
# The `encoding` magic comment is only honored on the first line of a file,
|
|
9
|
+
# or on the second line when the first line is a shebang. Anywhere else it
|
|
10
|
+
# is silently ignored - even directly below another comment or a blank line.
|
|
11
|
+
#
|
|
12
|
+
# Other magic comments (such as `frozen_string_literal`) are honored
|
|
13
|
+
# anywhere before the first token of code, but are ignored after any code,
|
|
14
|
+
# with a warning emitted only when running Ruby with `-w`.
|
|
15
|
+
#
|
|
16
|
+
# A magic comment misplaced ahead of a shebang also renders the shebang
|
|
17
|
+
# ineffective, since a shebang is only recognized on the first line.
|
|
18
|
+
#
|
|
19
|
+
# NOTE: An `encoding` comment that is only preceded by other magic
|
|
20
|
+
# comments is not flagged by this cop; that case is handled by
|
|
21
|
+
# `Lint/OrderedMagicComments`. `shareable_constant_value` is never
|
|
22
|
+
# flagged, as Ruby intentionally allows it mid-file with block scoping.
|
|
23
|
+
#
|
|
24
|
+
# @safety
|
|
25
|
+
# This cop's autocorrection is unsafe because moving the magic comment
|
|
26
|
+
# to its effective position activates it, which changes runtime
|
|
27
|
+
# behavior (e.g. the source encoding or string mutability).
|
|
28
|
+
#
|
|
29
|
+
# @example
|
|
30
|
+
# # bad
|
|
31
|
+
# # Documentation comment
|
|
32
|
+
# # encoding: ascii-8bit
|
|
33
|
+
# puts 'hello'
|
|
34
|
+
#
|
|
35
|
+
# # good
|
|
36
|
+
# # encoding: ascii-8bit
|
|
37
|
+
# # Documentation comment
|
|
38
|
+
# puts 'hello'
|
|
39
|
+
#
|
|
40
|
+
# # bad
|
|
41
|
+
# require 'foo'
|
|
42
|
+
# # frozen_string_literal: true
|
|
43
|
+
#
|
|
44
|
+
# # good
|
|
45
|
+
# # frozen_string_literal: true
|
|
46
|
+
# require 'foo'
|
|
47
|
+
#
|
|
48
|
+
# # bad
|
|
49
|
+
# # frozen_string_literal: true
|
|
50
|
+
# #!/usr/bin/env ruby
|
|
51
|
+
# puts 'hello'
|
|
52
|
+
#
|
|
53
|
+
# # good
|
|
54
|
+
# #!/usr/bin/env ruby
|
|
55
|
+
# # frozen_string_literal: true
|
|
56
|
+
# puts 'hello'
|
|
57
|
+
class MisplacedMagicComment < Base
|
|
58
|
+
include RangeHelp
|
|
59
|
+
extend AutoCorrector
|
|
60
|
+
|
|
61
|
+
MSG_ENCODING = 'The `encoding` magic comment is ignored unless placed on the first ' \
|
|
62
|
+
'line (or below a shebang on the first line).'
|
|
63
|
+
MSG_AFTER_CODE = 'The `%<directive>s` magic comment is ignored after any code.'
|
|
64
|
+
MSG_ABOVE_SHEBANG = 'A magic comment above a shebang renders the shebang ineffective.'
|
|
65
|
+
|
|
66
|
+
def on_new_investigation
|
|
67
|
+
return if processed_source.buffer.source.empty?
|
|
68
|
+
|
|
69
|
+
check_magic_comment_above_shebang
|
|
70
|
+
processed_source.comments.each { |comment| check_comment(comment) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def check_comment(comment)
|
|
76
|
+
return unless magic_comment_shaped?(comment.text)
|
|
77
|
+
|
|
78
|
+
magic_comment = MagicComment.parse(comment.text)
|
|
79
|
+
return unless magic_comment.valid?
|
|
80
|
+
|
|
81
|
+
if magic_comment.encoding_specified?
|
|
82
|
+
return unless known_encoding?(magic_comment.encoding)
|
|
83
|
+
|
|
84
|
+
check_encoding_comment(comment)
|
|
85
|
+
elsif magic_comment.frozen_string_literal_specified?
|
|
86
|
+
check_top_block_comment(comment, 'frozen_string_literal')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The Emacs and Vim regexps in `MagicComment` match anywhere inside a
|
|
91
|
+
# comment, which would flag documentation that merely quotes a magic
|
|
92
|
+
# comment (e.g. `# # -*- coding: UTF-8 -*-`). Require the magic
|
|
93
|
+
# comment to start right after the `#`.
|
|
94
|
+
def magic_comment_shaped?(text)
|
|
95
|
+
/\A#(?![^#]*#)/.match?(text)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Prose that happens to open with `Encoding:` parses as an encoding
|
|
99
|
+
# comment (`# Encoding: force given encoding` yields `force`). Ruby
|
|
100
|
+
# ignores a misplaced encoding comment entirely, so a name no encoding
|
|
101
|
+
# answers to means the line is a comment, not a directive.
|
|
102
|
+
def known_encoding?(name)
|
|
103
|
+
Encoding.find(name)
|
|
104
|
+
true
|
|
105
|
+
rescue ArgumentError
|
|
106
|
+
false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def shebang?
|
|
110
|
+
processed_source.lines.first.to_s.start_with?('#!')
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# First line that may carry an effective encoding comment.
|
|
114
|
+
def effective_encoding_line
|
|
115
|
+
shebang? ? 2 : 1
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def first_code_token
|
|
119
|
+
@first_code_token ||= processed_source.sorted_tokens.find { |token| !token.comment? }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def check_encoding_comment(comment)
|
|
123
|
+
line = comment.source_range.line
|
|
124
|
+
return if line == effective_encoding_line && comment_starts_line?(comment)
|
|
125
|
+
# Leave `# frozen_string_literal: true` + `# encoding: x` runs at the
|
|
126
|
+
# very top to Lint/OrderedMagicComments, which already reorders them.
|
|
127
|
+
return if preceded_only_by_magic_comments?(comment)
|
|
128
|
+
|
|
129
|
+
add_offense(comment, message: MSG_ENCODING) do |corrector|
|
|
130
|
+
move_comment(corrector, comment, effective_encoding_line)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def check_top_block_comment(comment, directive)
|
|
135
|
+
return unless first_code_token
|
|
136
|
+
return unless comment.source_range.begin_pos > first_code_token.begin_pos
|
|
137
|
+
|
|
138
|
+
message = format(MSG_AFTER_CODE, directive: directive)
|
|
139
|
+
add_offense(comment, message: message) do |corrector|
|
|
140
|
+
move_comment(corrector, comment, effective_encoding_line)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def check_magic_comment_above_shebang
|
|
145
|
+
shebang_comment = processed_source.comments.find do |comment|
|
|
146
|
+
comment.source_range.line > 1 && comment.text.start_with?('#!') &&
|
|
147
|
+
comment.source_range.column.zero?
|
|
148
|
+
end
|
|
149
|
+
return unless shebang_comment
|
|
150
|
+
# Only flag when what precedes the shebang is magic comments -
|
|
151
|
+
# a `#!` comment deep inside a file is not an intended shebang.
|
|
152
|
+
return unless preceded_only_by_magic_comments?(shebang_comment)
|
|
153
|
+
|
|
154
|
+
add_offense(shebang_comment, message: MSG_ABOVE_SHEBANG)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def preceded_only_by_magic_comments?(comment)
|
|
158
|
+
(1...comment.source_range.line).all? do |line_number|
|
|
159
|
+
text = processed_source.lines[line_number - 1]
|
|
160
|
+
(line_number == 1 && text.start_with?('#!')) || MagicComment.parse(text).valid?
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def comment_starts_line?(comment)
|
|
165
|
+
processed_source.lines[comment.source_range.line - 1].lstrip.start_with?('#')
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def move_comment(corrector, comment, target_line)
|
|
169
|
+
removal_range = if comment_starts_line?(comment)
|
|
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)
|
|
175
|
+
target_range = processed_source.buffer.line_range(target_line)
|
|
176
|
+
if comment.source_range.line < target_line
|
|
177
|
+
corrector.insert_after(target_range, "\n#{comment.text}")
|
|
178
|
+
else
|
|
179
|
+
corrector.insert_before(target_range, "#{comment.text}\n")
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# rubocop:disable Lint/RedundantCopDisableDirective
|
|
3
|
+
# rubocop:disable-next Lint/RedundantCopDisableDirective -- the examples below read as real directives
|
|
4
4
|
module RuboCop
|
|
5
5
|
module Cop
|
|
6
6
|
module Lint
|
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
|
55
55
|
class MissingCopEnableDirective < Base
|
|
56
56
|
include RangeHelp
|
|
57
57
|
|
|
58
|
-
MSG = 'Re-enable %<cop>s %<type>s with
|
|
58
|
+
MSG = 'Re-enable %<cop>s %<type>s with `%<directive>s` after disabling it.'
|
|
59
59
|
MSG_BOUND = 'Re-enable %<cop>s %<type>s within %<max_range>s lines after disabling it.'
|
|
60
60
|
|
|
61
61
|
def on_new_investigation
|
|
@@ -72,7 +72,12 @@ module RuboCop
|
|
|
72
72
|
|
|
73
73
|
def each_missing_enable
|
|
74
74
|
processed_source.disabled_line_ranges.each do |cop, line_ranges|
|
|
75
|
-
line_ranges.each
|
|
75
|
+
line_ranges.each do |line_range|
|
|
76
|
+
# A `disable-next` scope closes itself with its statement.
|
|
77
|
+
next if line_range.respond_to?(:directive) && line_range.directive.disable_next?
|
|
78
|
+
|
|
79
|
+
yield cop, line_range
|
|
80
|
+
end
|
|
76
81
|
end
|
|
77
82
|
end
|
|
78
83
|
|
|
@@ -80,17 +85,22 @@ module RuboCop
|
|
|
80
85
|
# This has to remain a strict inequality to handle
|
|
81
86
|
# the case when max_range is Float::INFINITY
|
|
82
87
|
return true if line_range.max - line_range.min < max_range + 2
|
|
88
|
+
# A name that is not registered as written (e.g. a wrongly-namespaced
|
|
89
|
+
# cop) suppresses nothing; `Lint/RedundantCopDisableDirective` reports
|
|
90
|
+
# it, so demanding its re-enablement would only add noise.
|
|
91
|
+
return true unless Registry.global.names.include?(cop)
|
|
92
|
+
|
|
93
|
+
expected_open_range?(cop, line_range)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def expected_open_range?(cop, line_range)
|
|
83
97
|
# This cop is disabled in the config, it is not expected to be re-enabled
|
|
84
98
|
return true if line_range.min == CommentConfig::CONFIG_DISABLED_LINE_RANGE_MIN
|
|
85
99
|
|
|
86
|
-
cop_class = RuboCop::Cop::Registry.global.find_by_cop_name
|
|
87
|
-
|
|
100
|
+
cop_class = RuboCop::Cop::Registry.global.find_by_cop_name(cop)
|
|
101
|
+
!!(cop_class &&
|
|
88
102
|
!processed_source.registry.enabled?(cop_class, config) &&
|
|
89
|
-
line_range.max == Float::INFINITY
|
|
90
|
-
return true
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
false
|
|
103
|
+
line_range.max == Float::INFINITY)
|
|
94
104
|
end
|
|
95
105
|
|
|
96
106
|
def max_range
|
|
@@ -98,13 +108,17 @@ module RuboCop
|
|
|
98
108
|
end
|
|
99
109
|
|
|
100
110
|
def message(cop, comment, type = 'cop')
|
|
111
|
+
directive = DirectiveComment.new(comment)
|
|
101
112
|
if department_enabled?(cop, comment)
|
|
102
113
|
type = 'department'
|
|
103
114
|
cop = cop.split('/').first
|
|
104
115
|
end
|
|
105
116
|
|
|
106
117
|
if max_range == Float::INFINITY
|
|
107
|
-
|
|
118
|
+
# A range opened by `rubocop:push` is closed by `rubocop:pop`,
|
|
119
|
+
# not by `rubocop:enable`.
|
|
120
|
+
enabling_directive = directive.push? ? '# rubocop:pop' : '# rubocop:enable'
|
|
121
|
+
format(MSG, cop: cop, type: type, directive: enabling_directive)
|
|
108
122
|
else
|
|
109
123
|
format(MSG_BOUND, cop: cop, type: type, max_range: max_range)
|
|
110
124
|
end
|
|
@@ -117,4 +131,3 @@ module RuboCop
|
|
|
117
131
|
end
|
|
118
132
|
end
|
|
119
133
|
end
|
|
120
|
-
# rubocop:enable Lint/RedundantCopDisableDirective
|
|
@@ -27,6 +27,13 @@ module RuboCop
|
|
|
27
27
|
# alone is not reported, since the index does not see gems or the
|
|
28
28
|
# standard library.
|
|
29
29
|
#
|
|
30
|
+
# For the same reason, a namespace whose root segment names a gem in the
|
|
31
|
+
# bundle (`Flipper` for the `flipper` gem) is left alone: a project that
|
|
32
|
+
# reopens it (`module Flipper; module Adapters; ...`) makes it resolve in
|
|
33
|
+
# the index, while the members the gem itself defines stay invisible and
|
|
34
|
+
# would look like typos. Enabling `AllCops/ProjectIndexIncludesGems`
|
|
35
|
+
# indexes those sources and restores the check.
|
|
36
|
+
#
|
|
30
37
|
# Names that appear as symbols or inside string literals in the same
|
|
31
38
|
# file are never reported, since they usually belong to runtime
|
|
32
39
|
# definitions the index cannot see (`stub_const`, `const_set`,
|
|
@@ -129,26 +136,61 @@ module RuboCop
|
|
|
129
136
|
end
|
|
130
137
|
|
|
131
138
|
def constant_typo_suggestion(node)
|
|
132
|
-
return nil if literal_names.include?(node.short_name.to_s)
|
|
133
|
-
|
|
134
139
|
namespace = resolve_constant_in_index(node.namespace)
|
|
135
140
|
return nil unless namespace.is_a?(Rubydex::Namespace)
|
|
136
141
|
return nil if namespace.find_member(node.short_name.to_s)
|
|
142
|
+
return nil unless complete_index_members?(namespace)
|
|
143
|
+
return nil if literal_names.include?(node.short_name.to_s)
|
|
137
144
|
|
|
138
145
|
spell_check(node.short_name, constant_member_names(namespace))
|
|
139
146
|
rescue StandardError
|
|
140
147
|
nil
|
|
141
148
|
end
|
|
142
149
|
|
|
150
|
+
# Whether the index can be trusted to list everything the namespace
|
|
151
|
+
# defines. A name missing from an incomplete member list is not
|
|
152
|
+
# evidence of a typo, so both gaps have to be ruled out first:
|
|
153
|
+
# an ancestor that does not resolve may contribute the name, and so
|
|
154
|
+
# may a gem that reopens the namespace.
|
|
155
|
+
def complete_index_members?(declaration)
|
|
156
|
+
fully_resolved_index_ancestry?(declaration) && !gem_owned_namespace?(declaration)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Whether the namespace's root segment names a gem in the bundle,
|
|
160
|
+
# which is then free to define members the index never sees: gem
|
|
161
|
+
# sources are only indexed when `AllCops/ProjectIndexIncludesGems`
|
|
162
|
+
# is enabled. A project that reopens such a namespace to add its own
|
|
163
|
+
# members (`module Flipper; module Adapters; ...`) makes it resolve
|
|
164
|
+
# in the index while most of what it holds stays invisible.
|
|
165
|
+
def gem_owned_namespace?(declaration)
|
|
166
|
+
return false if config.for_all_cops['ProjectIndexIncludesGems']
|
|
167
|
+
|
|
168
|
+
root = declaration.name.to_s.split('::').first.to_s
|
|
169
|
+
bundled_gem_namespaces.include?(root.downcase)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Gem names normalized towards the constant they conventionally
|
|
173
|
+
# provide: `flipper` for `Flipper`, `activerecord` for `ActiveRecord`.
|
|
174
|
+
def bundled_gem_namespaces
|
|
175
|
+
@bundled_gem_namespaces ||= bundled_gem_names.to_set { |name| name.delete('-_').downcase }
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# The bundle's gems except those sourced from a local path, whose code
|
|
179
|
+
# lives in the project and is indexed like the rest of it — among them
|
|
180
|
+
# the project's own gem, which its lockfile lists in the `PATH` section.
|
|
181
|
+
def bundled_gem_names
|
|
182
|
+
(config.gem_versions_in_target || {}).keys - (config.path_sourced_gems_in_target || [])
|
|
183
|
+
end
|
|
184
|
+
|
|
143
185
|
# Writers are indexed under the reader's name, so setter calls are
|
|
144
186
|
# verified and spell-checked through the base name.
|
|
145
187
|
def method_typo_suggestion(node)
|
|
146
188
|
setter = setter_call?(node)
|
|
147
189
|
base = setter ? node.method_name.to_s.delete_suffix('=') : node.method_name.to_s
|
|
148
|
-
return nil if literal_names.include?(base)
|
|
149
190
|
|
|
150
191
|
declaration = unknown_method_owner(node, base)
|
|
151
192
|
return nil unless declaration
|
|
193
|
+
return nil if literal_names.include?(base)
|
|
152
194
|
|
|
153
195
|
suggestion = spell_check(base, method_member_names(declaration))
|
|
154
196
|
suggestion && setter ? "#{suggestion}=" : suggestion
|
|
@@ -156,13 +198,15 @@ module RuboCop
|
|
|
156
198
|
nil
|
|
157
199
|
end
|
|
158
200
|
|
|
159
|
-
# The receiver's declaration when it resolves in the index,
|
|
160
|
-
#
|
|
201
|
+
# The receiver's declaration when it resolves in the index, everything
|
|
202
|
+
# it can inherit or be reopened with is visible there, and the method
|
|
203
|
+
# is not found — nil otherwise.
|
|
161
204
|
def unknown_method_owner(node, base)
|
|
162
205
|
declaration = resolve_constant_in_index(node.receiver)
|
|
163
206
|
return nil unless declaration.is_a?(Rubydex::Namespace)
|
|
164
207
|
return nil if responds_in_index?(declaration, node.method_name.to_s, base)
|
|
165
208
|
return nil unless fully_resolved_index_ancestry?(declaration)
|
|
209
|
+
return nil if gem_owned_namespace?(declaration)
|
|
166
210
|
|
|
167
211
|
declaration
|
|
168
212
|
end
|
|
@@ -191,7 +235,10 @@ module RuboCop
|
|
|
191
235
|
if literal.sym_type?
|
|
192
236
|
names << literal.value.to_s
|
|
193
237
|
else
|
|
194
|
-
|
|
238
|
+
# `scan` raises on binary string literals with invalid byte sequences.
|
|
239
|
+
value = literal.value
|
|
240
|
+
value = value.scrub unless value.valid_encoding?
|
|
241
|
+
value.scan(LITERAL_IDENTIFIER_PATTERN) { |token| names << token }
|
|
195
242
|
end
|
|
196
243
|
end
|
|
197
244
|
end
|
|
@@ -95,7 +95,7 @@ module RuboCop
|
|
|
95
95
|
MSG = 'Method definitions must not be nested. Use `lambda` instead.'
|
|
96
96
|
|
|
97
97
|
def on_def(node)
|
|
98
|
-
subject, = *node # rubocop:disable InternalAffairs/NodeDestructuring
|
|
98
|
+
subject, = *node # rubocop:disable InternalAffairs/NodeDestructuring -- the first child differs between `def` and `defs`
|
|
99
99
|
return if node.defs_type? && allowed_subject_type?(subject)
|
|
100
100
|
|
|
101
101
|
def_ancestor = node.each_ancestor(:any_def).first
|
|
@@ -67,7 +67,7 @@ module RuboCop
|
|
|
67
67
|
|
|
68
68
|
# NOTE: itblock is not handled because this cop is limited to Ruby <= 2.7
|
|
69
69
|
# via `maximum_target_ruby_version`, so itblock nodes (Ruby 3.4+) are never encountered.
|
|
70
|
-
def on_block(node) # rubocop:disable InternalAffairs/ItblockHandler
|
|
70
|
+
def on_block(node) # rubocop:disable InternalAffairs/ItblockHandler -- limited to Ruby <= 2.7, where `it` blocks do not exist
|
|
71
71
|
return unless node.body
|
|
72
72
|
return unless unsorted_dir_loop?(node.send_node)
|
|
73
73
|
|
|
@@ -53,6 +53,8 @@ module RuboCop
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def after_send(node)
|
|
56
|
+
return if backrefs_unaffected?(node)
|
|
57
|
+
|
|
56
58
|
@valid_ref = nil
|
|
57
59
|
|
|
58
60
|
if regexp_first_argument?(node)
|
|
@@ -111,6 +113,19 @@ module RuboCop
|
|
|
111
113
|
end
|
|
112
114
|
end
|
|
113
115
|
|
|
116
|
+
# A call such as `hash[:key]` or `array[0]` cannot involve a regexp match:
|
|
117
|
+
# a non-string literal cannot act as a pattern (whereas `sub`, `gsub`,
|
|
118
|
+
# and `match` treat a string one as a pattern and do set backreferences),
|
|
119
|
+
# and `$~` is frame-local, so a user-defined method cannot change
|
|
120
|
+
# the caller's backreferences either.
|
|
121
|
+
def backrefs_unaffected?(send_node)
|
|
122
|
+
first_argument = send_node.first_argument
|
|
123
|
+
return false unless first_argument&.basic_literal?
|
|
124
|
+
return false if first_argument.str_type?
|
|
125
|
+
|
|
126
|
+
!regexp_receiver?(send_node)
|
|
127
|
+
end
|
|
128
|
+
|
|
114
129
|
def regexp_first_argument?(send_node)
|
|
115
130
|
send_node.first_argument&.regexp_type? \
|
|
116
131
|
&& REGEXP_ARGUMENT_METHODS.include?(send_node.method_name)
|
|
@@ -46,13 +46,29 @@ module RuboCop
|
|
|
46
46
|
|
|
47
47
|
def valid_first_argument?(first_arg)
|
|
48
48
|
first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg) ||
|
|
49
|
-
compound_range?(first_arg)
|
|
49
|
+
compound_range?(first_arg) || invalid_bare_argument?(first_arg)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def compound_range?(first_arg)
|
|
53
53
|
first_arg.range_type? && first_arg.parenthesized_call?
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def invalid_bare_argument?(node)
|
|
57
|
+
node = node.children.first while node&.begin_type? && node.children.one?
|
|
58
|
+
return false unless node
|
|
59
|
+
|
|
60
|
+
keyword_operator?(node) || modifier_expression?(node)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def keyword_operator?(node)
|
|
64
|
+
(node.operator_keyword? && node.semantic_operator?) ||
|
|
65
|
+
node.rescue_type? || (node.send_type? && node.prefix_not?)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def modifier_expression?(node)
|
|
69
|
+
node.type?(:if, :while, :until) && node.modifier_form?
|
|
70
|
+
end
|
|
71
|
+
|
|
56
72
|
def chained_calls?(node)
|
|
57
73
|
first_argument = node.first_argument
|
|
58
74
|
first_argument.call_type? && (node.children.last&.children&.count || 0) > 1
|