rubocop 1.88.2 → 1.90.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 +139 -16
- data/exe/rubocop +1 -1
- data/lib/rubocop/cached_data.rb +7 -2
- data/lib/rubocop/cli/command/execute_runner.rb +31 -0
- data/lib/rubocop/cli.rb +2 -4
- data/lib/rubocop/comment_config/directive_range.rb +32 -0
- data/lib/rubocop/comment_config/disable_next.rb +97 -0
- data/lib/rubocop/comment_config.rb +81 -49
- data/lib/rubocop/config.rb +76 -5
- data/lib/rubocop/config_loader.rb +1 -2
- data/lib/rubocop/config_loader_resolver.rb +1 -2
- data/lib/rubocop/config_validator.rb +39 -3
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
- data/lib/rubocop/cop/badge.rb +8 -0
- data/lib/rubocop/cop/base.rb +40 -6
- data/lib/rubocop/cop/bundler.rb +19 -0
- data/lib/rubocop/cop/corrector.rb +4 -4
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
- data/lib/rubocop/cop/gemspec.rb +22 -0
- data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
- data/lib/rubocop/cop/generator.rb +4 -0
- 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/layout/block_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/class_structure.rb +108 -26
- 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/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 +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +3 -1
- data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/line_length.rb +84 -12
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +12 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/space_around_operators.rb +7 -0
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +31 -34
- data/lib/rubocop/cop/layout.rb +114 -0
- data/lib/rubocop/cop/lazy_loader.rb +41 -0
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
- data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
- data/lib/rubocop/cop/lint/argument_mismatch.rb +89 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +18 -21
- data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -8
- data/lib/rubocop/cop/lint/deprecated_reference.rb +194 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +300 -22
- 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/inherit_exception.rb +53 -7
- data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
- data/lib/rubocop/cop/lint/literal_as_condition.rb +8 -6
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -7
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
- data/lib/rubocop/cop/lint/missing_super.rb +31 -1
- data/lib/rubocop/cop/lint/name_typo.rb +270 -0
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -4
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +98 -19
- 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 +8 -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 +38 -3
- data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
- data/lib/rubocop/cop/lint/unused_private_method.rb +234 -0
- 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/useless_ruby2_keywords.rb +2 -0
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
- data/lib/rubocop/cop/lint/void.rb +18 -8
- data/lib/rubocop/cop/lint.rb +178 -0
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/metrics.rb +31 -0
- data/lib/rubocop/cop/migration/department_name.rb +1 -6
- data/lib/rubocop/cop/migration.rb +13 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -5
- 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/endless_method_rewriter.rb +13 -5
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
- 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 +51 -6
- data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
- data/lib/rubocop/cop/mixin/range_help.rb +1 -2
- data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +46 -4
- data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
- data/lib/rubocop/cop/mixin.rb +82 -77
- data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
- 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/predicate_prefix.rb +33 -5
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
- data/lib/rubocop/cop/naming/variable_number.rb +3 -1
- data/lib/rubocop/cop/naming.rb +31 -0
- data/lib/rubocop/cop/offense.rb +37 -10
- data/lib/rubocop/cop/registry.rb +180 -46
- data/lib/rubocop/cop/security.rb +19 -0
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -6
- data/lib/rubocop/cop/style/array_first_last.rb +1 -2
- data/lib/rubocop/cop/style/array_intersect.rb +24 -5
- 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 +2 -4
- data/lib/rubocop/cop/style/class_and_module_children.rb +79 -20
- 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 +23 -2
- data/lib/rubocop/cop/style/constant_visibility.rb +1 -2
- data/lib/rubocop/cop/style/copyright.rb +5 -1
- data/lib/rubocop/cop/style/directive_scope.rb +171 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +76 -12
- data/lib/rubocop/cop/style/documentation.rb +47 -0
- data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
- data/lib/rubocop/cop/style/endless_method.rb +0 -10
- data/lib/rubocop/cop/style/format_string_token.rb +1 -2
- data/lib/rubocop/cop/style/guard_clause.rb +1 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
- 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 +8 -50
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
- data/lib/rubocop/cop/style/lambda.rb +4 -0
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +22 -7
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +30 -0
- data/lib/rubocop/cop/style/multiline_if_then.rb +7 -1
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
- data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
- data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
- data/lib/rubocop/cop/style/negative_array_index.rb +3 -3
- data/lib/rubocop/cop/style/nil_comparison.rb +1 -2
- 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/redundant_assignment.rb +12 -2
- data/lib/rubocop/cop/style/redundant_condition.rb +2 -4
- data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +1 -2
- data/lib/rubocop/cop/style/redundant_each.rb +1 -2
- data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
- data/lib/rubocop/cop/style/redundant_format.rb +14 -7
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +78 -135
- data/lib/rubocop/cop/style/redundant_parentheses.rb +65 -67
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
- 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 +11 -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_do_end_block.rb +1 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +21 -5
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
- data/lib/rubocop/cop/style/static_class.rb +21 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
- 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/yoda_condition.rb +1 -2
- data/lib/rubocop/cop/style.rb +312 -0
- data/lib/rubocop/cop/team.rb +59 -6
- data/lib/rubocop/cop/util.rb +2 -4
- data/lib/rubocop/cop/variable_force/variable.rb +19 -8
- data/lib/rubocop/cops_documentation_generator.rb +4 -7
- data/lib/rubocop/directive_comment.rb +39 -3
- data/lib/rubocop/feature_loader.rb +0 -6
- data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
- 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/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/lockfile.rb +18 -0
- data/lib/rubocop/lsp/diagnostic.rb +4 -8
- data/lib/rubocop/lsp/routes.rb +89 -22
- data/lib/rubocop/lsp/runtime.rb +4 -0
- data/lib/rubocop/lsp/server.rb +4 -0
- data/lib/rubocop/lsp/stdin_runner.rb +20 -0
- data/lib/rubocop/mcp/server.rb +1 -2
- data/lib/rubocop/options.rb +10 -6
- data/lib/rubocop/path_util.rb +1 -2
- data/lib/rubocop/pending_cops_reporter.rb +1 -6
- data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
- data/lib/rubocop/project_index_loader.rb +16 -0
- data/lib/rubocop/rake_task.rb +1 -2
- data/lib/rubocop/result_cache.rb +6 -4
- data/lib/rubocop/rspec/expect_offense.rb +2 -4
- data/lib/rubocop/rspec/shared_contexts.rb +16 -2
- data/lib/rubocop/runner.rb +71 -43
- data/lib/rubocop/server/cache.rb +11 -7
- 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/target_ruby.rb +1 -1
- data/lib/rubocop/todo_audit.rb +111 -0
- data/lib/rubocop/version.rb +3 -5
- data/lib/rubocop.rb +20 -626
- data/lib/ruby_lsp/rubocop/addon.rb +1 -2
- metadata +28 -5
data/lib/rubocop/cop/base.rb
CHANGED
|
@@ -127,6 +127,8 @@ module RuboCop
|
|
|
127
127
|
# in the `on_new_investigation` callback.
|
|
128
128
|
# If your cop does autocorrections, be aware that your instance may be called
|
|
129
129
|
# multiple times with the same `processed_source.path` but different content.
|
|
130
|
+
# Note that under `--parallel` each worker process has its own cop
|
|
131
|
+
# instances, so state persists only within a worker's share of the files.
|
|
130
132
|
def self.support_multiple_source?
|
|
131
133
|
false
|
|
132
134
|
end
|
|
@@ -208,13 +210,15 @@ module RuboCop
|
|
|
208
210
|
severity = find_severity(range_to_pass, severity)
|
|
209
211
|
message = find_message(range_to_pass, message)
|
|
210
212
|
|
|
211
|
-
status, corrector =
|
|
213
|
+
status, corrector = enabled_lines?(range) ? correct(range, &block) : :disabled
|
|
214
|
+
justification = suppression_reason(range) if status == :disabled
|
|
212
215
|
|
|
213
216
|
# Since this range may be generated from Ruby code embedded in some
|
|
214
217
|
# template file, we convert it to location info in the original file.
|
|
215
218
|
range = range_for_original(range)
|
|
216
219
|
|
|
217
|
-
current_offenses << Offense.new(severity, range, message, name, status, corrector
|
|
220
|
+
current_offenses << Offense.new(severity, range, message, name, status, corrector,
|
|
221
|
+
justification: justification)
|
|
218
222
|
end
|
|
219
223
|
|
|
220
224
|
# This method should be overridden when a cop's behavior depends
|
|
@@ -372,6 +376,12 @@ module RuboCop
|
|
|
372
376
|
|
|
373
377
|
private
|
|
374
378
|
|
|
379
|
+
### Reserved for Commissioner
|
|
380
|
+
|
|
381
|
+
private_class_method def self.restrict_on_send
|
|
382
|
+
@restrict_on_send ||= self::RESTRICT_ON_SEND.to_a.freeze
|
|
383
|
+
end
|
|
384
|
+
|
|
375
385
|
### Reserved for Cop::Cop
|
|
376
386
|
|
|
377
387
|
def callback_argument(range)
|
|
@@ -400,10 +410,6 @@ module RuboCop
|
|
|
400
410
|
@current_offenses ||= []
|
|
401
411
|
end
|
|
402
412
|
|
|
403
|
-
private_class_method def self.restrict_on_send
|
|
404
|
-
@restrict_on_send ||= self::RESTRICT_ON_SEND.to_a.freeze
|
|
405
|
-
end
|
|
406
|
-
|
|
407
413
|
EMPTY_OFFENSES = [].freeze
|
|
408
414
|
private_constant :EMPTY_OFFENSES
|
|
409
415
|
# Called to complete an investigation
|
|
@@ -443,6 +449,10 @@ module RuboCop
|
|
|
443
449
|
def use_corrector(range, corrector)
|
|
444
450
|
if autocorrect?
|
|
445
451
|
attempt_correction(range, corrector)
|
|
452
|
+
elsif skipped_unsafe_correction_with_disable_uncorrectable?
|
|
453
|
+
# The unsafe correction will not run, so the offense is
|
|
454
|
+
# uncorrectable for this run and gets a todo comment.
|
|
455
|
+
attempt_correction(range, nil)
|
|
446
456
|
elsif corrector && (always_autocorrect? || (contextual_autocorrect? && !LSP.enabled?))
|
|
447
457
|
:uncorrected
|
|
448
458
|
else
|
|
@@ -522,6 +532,30 @@ module RuboCop
|
|
|
522
532
|
@processed_source.comment_config.cop_enabled_at_line?(self, line_number)
|
|
523
533
|
end
|
|
524
534
|
|
|
535
|
+
# A multi-line offense is suppressed by a directive on any line of its
|
|
536
|
+
# range, not only its first line, matching the intuition that the
|
|
537
|
+
# directive is attached to the offending code.
|
|
538
|
+
def enabled_lines?(range)
|
|
539
|
+
return true if @options[:ignore_disable_comments] || !@processed_source
|
|
540
|
+
|
|
541
|
+
comment_config = @processed_source.comment_config
|
|
542
|
+
comment_config.cop_enabled_at_lines?(self, range.first_line, range.last_line)
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# The `--` reason on the directive that suppresses offenses on this
|
|
546
|
+
# range, or `nil` when the directive carries none.
|
|
547
|
+
def suppression_reason(range)
|
|
548
|
+
covering = covering_disabled_range(range)
|
|
549
|
+
covering.directive.reason if covering.respond_to?(:directive)
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def covering_disabled_range(range)
|
|
553
|
+
disabled_ranges = @processed_source.comment_config.cop_disabled_line_ranges[cop_name]
|
|
554
|
+
disabled_ranges&.find do |disabled_range|
|
|
555
|
+
disabled_range.end >= range.first_line && disabled_range.begin <= range.last_line
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
|
|
525
559
|
def find_severity(_range, severity)
|
|
526
560
|
custom_severity || severity || default_severity
|
|
527
561
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Cops for the `Bundler` department. The department's cops are registered for lazy loading
|
|
6
|
+
# and their files are loaded on demand.
|
|
7
|
+
module Bundler
|
|
8
|
+
extend LazyLoader
|
|
9
|
+
|
|
10
|
+
register_cop :DuplicatedGem, "#{__dir__}/bundler/duplicated_gem"
|
|
11
|
+
register_cop :DuplicatedGroup, "#{__dir__}/bundler/duplicated_group"
|
|
12
|
+
register_cop :GemComment, "#{__dir__}/bundler/gem_comment"
|
|
13
|
+
register_cop :GemFilename, "#{__dir__}/bundler/gem_filename"
|
|
14
|
+
register_cop :GemVersion, "#{__dir__}/bundler/gem_version"
|
|
15
|
+
register_cop :InsecureProtocolSource, "#{__dir__}/bundler/insecure_protocol_source"
|
|
16
|
+
register_cop :OrderedGems, "#{__dir__}/bundler/ordered_gems"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -46,7 +46,7 @@ module RuboCop
|
|
|
46
46
|
|
|
47
47
|
# Removes `size` characters prior to the source range.
|
|
48
48
|
#
|
|
49
|
-
# @param [Parser::Source::Range, RuboCop::AST::Node]
|
|
49
|
+
# @param [Parser::Source::Range, RuboCop::AST::Node] node_or_range
|
|
50
50
|
# @param [Integer] size
|
|
51
51
|
def remove_preceding(node_or_range, size)
|
|
52
52
|
range = to_range(node_or_range)
|
|
@@ -58,7 +58,7 @@ module RuboCop
|
|
|
58
58
|
# If `size` is greater than the size of `range`, the removed region can
|
|
59
59
|
# overrun the end of `range`.
|
|
60
60
|
#
|
|
61
|
-
# @param [Parser::Source::Range, RuboCop::AST::Node]
|
|
61
|
+
# @param [Parser::Source::Range, RuboCop::AST::Node] node_or_range
|
|
62
62
|
# @param [Integer] size
|
|
63
63
|
def remove_leading(node_or_range, size)
|
|
64
64
|
range = to_range(node_or_range)
|
|
@@ -70,7 +70,7 @@ module RuboCop
|
|
|
70
70
|
# If `size` is greater than the size of `range`, the removed region can
|
|
71
71
|
# overrun the beginning of `range`.
|
|
72
72
|
#
|
|
73
|
-
# @param [Parser::Source::Range, RuboCop::AST::Node]
|
|
73
|
+
# @param [Parser::Source::Range, RuboCop::AST::Node] node_or_range
|
|
74
74
|
# @param [Integer] size
|
|
75
75
|
def remove_trailing(node_or_range, size)
|
|
76
76
|
range = to_range(node_or_range)
|
|
@@ -103,7 +103,7 @@ module RuboCop
|
|
|
103
103
|
# :nodoc:
|
|
104
104
|
def to_range(node_or_range)
|
|
105
105
|
range = case node_or_range
|
|
106
|
-
when
|
|
106
|
+
when RuboCop::AST::Node, ::Parser::Source::Comment
|
|
107
107
|
node_or_range.source_range
|
|
108
108
|
when ::Parser::Source::Range
|
|
109
109
|
node_or_range
|
|
@@ -12,20 +12,28 @@ module RuboCop
|
|
|
12
12
|
class << self
|
|
13
13
|
attr_reader :processed_source
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
# `tab_indentation` may only be set by callers whose `column_delta` represents
|
|
16
|
+
# whole indentation levels (e.g. `Layout/IndentationWidth`).
|
|
17
|
+
# Alignment to an arbitrary column cannot be expressed with tabs, so those callers
|
|
18
|
+
# leave tab correction disabled to avoid producing a never-converging correction.
|
|
19
|
+
def correct(corrector, processed_source, node, column_delta, tab_indentation: false)
|
|
16
20
|
return unless node
|
|
17
21
|
|
|
18
22
|
@processed_source = processed_source
|
|
19
|
-
# Disable autocorrection for tabs as it requires special handling
|
|
20
|
-
return if using_tabs?
|
|
21
23
|
|
|
22
24
|
expr = node.respond_to?(:loc) ? node.source_range : node
|
|
23
25
|
return if block_comment_within?(expr)
|
|
24
26
|
|
|
25
27
|
taboo_ranges = inside_string_ranges(node)
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
if using_tabs?
|
|
30
|
+
return unless tab_indentation
|
|
31
|
+
|
|
32
|
+
correct_tab_indentation(corrector, expr, column_delta, taboo_ranges)
|
|
33
|
+
else
|
|
34
|
+
each_line(expr) do |line_begin_pos|
|
|
35
|
+
autocorrect_line(corrector, line_begin_pos, expr, column_delta, taboo_ranges)
|
|
36
|
+
end
|
|
29
37
|
end
|
|
30
38
|
end
|
|
31
39
|
|
|
@@ -58,6 +66,48 @@ module RuboCop
|
|
|
58
66
|
end
|
|
59
67
|
end
|
|
60
68
|
|
|
69
|
+
# Tab indentation is corrected by rewriting each line's leading whitespace to
|
|
70
|
+
# the target number of tabs. Working in whole tabs rather than applying a column delta
|
|
71
|
+
# keeps the result idempotent, which avoids the infinite loops that delta-based correction
|
|
72
|
+
# caused for tabs.
|
|
73
|
+
def correct_tab_indentation(corrector, expr, column_delta, taboo_ranges)
|
|
74
|
+
buffer = expr.source_buffer
|
|
75
|
+
|
|
76
|
+
each_line(expr) do |line_begin_pos|
|
|
77
|
+
line_range = buffer.line_range(buffer.line_for_position(line_begin_pos))
|
|
78
|
+
correct_tab_line(corrector, line_range, column_delta, taboo_ranges)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def correct_tab_line(corrector, line_range, column_delta, taboo_ranges)
|
|
83
|
+
leading = line_range.source[/\A[ \t]*/]
|
|
84
|
+
return if leading.length == line_range.source.length # blank line
|
|
85
|
+
|
|
86
|
+
leading_range = range_between(line_range.begin_pos, line_range.begin_pos + leading.length)
|
|
87
|
+
return if taboo_ranges.any? { |t| within?(leading_range, t) }
|
|
88
|
+
|
|
89
|
+
target = target_tab_indentation(leading, column_delta)
|
|
90
|
+
corrector.replace(leading_range, target) unless leading == target
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def target_tab_indentation(leading, column_delta)
|
|
94
|
+
width = configured_indentation_width
|
|
95
|
+
return leading unless width.positive?
|
|
96
|
+
|
|
97
|
+
visual_width = leading.chars.sum { |char| char == "\t" ? width : 1 }
|
|
98
|
+
|
|
99
|
+
"\t" * ([visual_width + column_delta, 0].max / width)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# The number of columns a single tab spans. This must match the width
|
|
103
|
+
# `Layout/IndentationWidth` uses to compute `column_delta`, otherwise
|
|
104
|
+
# the division below would not land on a tab boundary. `Layout/IndentationStyle`
|
|
105
|
+
# has its own `IndentationWidth`, but that only governs how that cop replaces tabs
|
|
106
|
+
# with spaces, so it must not be consulted here.
|
|
107
|
+
def configured_indentation_width
|
|
108
|
+
processed_source.config.for_cop('Layout/IndentationWidth')['Width'] || 2
|
|
109
|
+
end
|
|
110
|
+
|
|
61
111
|
def inside_string_ranges(node)
|
|
62
112
|
return [] unless node.is_a?(Parser::AST::Node)
|
|
63
113
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Cops for the `Gemspec` department. The department's cops are registered for lazy loading
|
|
6
|
+
# and their files are loaded on demand.
|
|
7
|
+
module Gemspec
|
|
8
|
+
extend LazyLoader
|
|
9
|
+
|
|
10
|
+
register_cop :AddRuntimeDependency, "#{__dir__}/gemspec/add_runtime_dependency"
|
|
11
|
+
register_cop :AttributeAssignment, "#{__dir__}/gemspec/attribute_assignment"
|
|
12
|
+
register_cop :DependencyVersion, "#{__dir__}/gemspec/dependency_version"
|
|
13
|
+
register_cop :DeprecatedAttributeAssignment, "#{__dir__}/gemspec/deprecated_attribute_assignment"
|
|
14
|
+
register_cop :DevelopmentDependencies, "#{__dir__}/gemspec/development_dependencies"
|
|
15
|
+
register_cop :DuplicatedAssignment, "#{__dir__}/gemspec/duplicated_assignment"
|
|
16
|
+
register_cop :OrderedDependencies, "#{__dir__}/gemspec/ordered_dependencies"
|
|
17
|
+
register_cop :RequireMFA, "#{__dir__}/gemspec/require_mfa"
|
|
18
|
+
register_cop :RequiredRubyVersion, "#{__dir__}/gemspec/required_ruby_version"
|
|
19
|
+
register_cop :RubyVersionGlobalsUsage, "#{__dir__}/gemspec/ruby_version_globals_usage"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
class Generator
|
|
6
|
+
# A class that injects a `register_cop` directive into the cop's department module
|
|
7
|
+
# so that the new cop is registered for lazy loading.
|
|
8
|
+
# The directive is injected in alphabetical order among the existing directives without
|
|
9
|
+
# reordering them, since the registration order defines the cop execution order.
|
|
10
|
+
# When the department module file does not exist yet, it is created, and a require for
|
|
11
|
+
# it is injected into the root RuboCop file.
|
|
12
|
+
#
|
|
13
|
+
# Anything unexpected in the department module, such as a commented-out or otherwise
|
|
14
|
+
# unparsable `register_cop` line, raises an error instead of silently producing
|
|
15
|
+
# a broken registration.
|
|
16
|
+
class RegistrationInjector
|
|
17
|
+
EXTEND_PATTERN = /\A(?<indent>\s*)extend LazyLoader\n\z/.freeze
|
|
18
|
+
ENTRY_PATTERN = %r{
|
|
19
|
+
\A(?<indent>\s*)register_cop\s
|
|
20
|
+
:(?<constant>[A-Za-z0-9]+),\s
|
|
21
|
+
"\#\{__dir__\}/(?<path>[a-z0-9_/]+)"\n\z
|
|
22
|
+
}x.freeze
|
|
23
|
+
DEPARTMENT_REQUIRE_PATTERN = %r{\Arequire_relative 'rubocop/cop/[a-z0-9_/]+'\n\z}.freeze
|
|
24
|
+
|
|
25
|
+
def initialize(source_path:, badge:, root_file_path: 'lib/rubocop.rb', output: $stdout)
|
|
26
|
+
@source_path = Pathname(source_path)
|
|
27
|
+
@badge = badge
|
|
28
|
+
@root_file_path = root_file_path
|
|
29
|
+
@department_file_path = Pathname("#{@source_path.dirname}.rb")
|
|
30
|
+
@output = output
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def inject
|
|
34
|
+
if File.exist?(department_file_path)
|
|
35
|
+
inject_directive
|
|
36
|
+
else
|
|
37
|
+
create_department_file
|
|
38
|
+
inject_department_require
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
attr_reader :source_path, :badge, :root_file_path, :department_file_path, :output
|
|
45
|
+
|
|
46
|
+
def inject_directive
|
|
47
|
+
lines = File.readlines(department_file_path)
|
|
48
|
+
entries = parse_entries(lines)
|
|
49
|
+
return if entries.any? { |entry| entry[:path] == cop_path }
|
|
50
|
+
|
|
51
|
+
line, indent = insertion_point(lines, entries)
|
|
52
|
+
lines.insert(line, "#{indent}#{directive}\n")
|
|
53
|
+
|
|
54
|
+
write_department_file(lines)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def create_department_file
|
|
58
|
+
File.write(department_file_path, department_module_source)
|
|
59
|
+
|
|
60
|
+
output.puts "[create] #{department_file_path}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def inject_department_require
|
|
64
|
+
require_path = department_file_path.sub_ext('').to_s.delete_prefix('lib/')
|
|
65
|
+
require_directive = "require_relative '#{require_path}'\n"
|
|
66
|
+
|
|
67
|
+
lines = File.readlines(root_file_path)
|
|
68
|
+
return if lines.include?(require_directive)
|
|
69
|
+
|
|
70
|
+
lines.insert(department_require_index(lines, require_directive), require_directive)
|
|
71
|
+
File.write(root_file_path, lines.join)
|
|
72
|
+
|
|
73
|
+
output.puts "[modify] #{root_file_path} - `#{require_directive.strip}` was injected."
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def write_department_file(lines)
|
|
77
|
+
File.write(department_file_path, lines.join)
|
|
78
|
+
|
|
79
|
+
output.puts "[modify] #{department_file_path} - `#{directive}` was injected."
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @return [Array<Hash>] the `register_cop` entries with their line numbers
|
|
83
|
+
def parse_entries(lines)
|
|
84
|
+
lines.filter_map.with_index do |line, index|
|
|
85
|
+
match = ENTRY_PATTERN.match(line)
|
|
86
|
+
|
|
87
|
+
if match
|
|
88
|
+
{ line: index, indent: match[:indent], constant: match[:constant],
|
|
89
|
+
path: match[:path] }
|
|
90
|
+
elsif line.include?('register_cop')
|
|
91
|
+
raise Error, "unexpected `register_cop` line in #{department_file_path}:" \
|
|
92
|
+
"#{index + 1}; fix it or register the cop manually: #{line}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The directive path is relative to the department module file's directory,
|
|
98
|
+
# which `__dir__` resolves to at load time.
|
|
99
|
+
def cop_path
|
|
100
|
+
source_path.relative_path_from(department_file_path.dirname).to_s.delete_suffix('.rb')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def insertion_point(lines, entries)
|
|
104
|
+
if entries.empty?
|
|
105
|
+
first_directive_position(lines)
|
|
106
|
+
else
|
|
107
|
+
successor = entries.find { |entry| entry[:path] > cop_path }
|
|
108
|
+
entry = successor || entries.last
|
|
109
|
+
|
|
110
|
+
[entry[:line] + (successor ? 0 : 1), entry[:indent]]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def directive
|
|
115
|
+
"register_cop :#{badge.cop_name}, \"\#{__dir__}/#{cop_path}\""
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# The department requires form the contiguous block right before
|
|
119
|
+
# the `rubocop/cop/team` require in the root RuboCop file.
|
|
120
|
+
def department_require_index(lines, require_directive)
|
|
121
|
+
anchor = lines.index("require_relative 'rubocop/cop/team'\n")
|
|
122
|
+
unless anchor
|
|
123
|
+
raise Error, "could not find the department require block in #{root_file_path}"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
block_end = anchor - 1
|
|
127
|
+
block_end -= 1 while lines[block_end] == "\n"
|
|
128
|
+
block_start = block_end
|
|
129
|
+
block_start -= 1 while lines[block_start - 1]&.match?(DEPARTMENT_REQUIRE_PATTERN)
|
|
130
|
+
|
|
131
|
+
(block_start..block_end).find do |index|
|
|
132
|
+
lines[index] > require_directive
|
|
133
|
+
end || (block_end + 1)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def first_directive_position(lines)
|
|
137
|
+
extend_lines = lines.filter_map.with_index do |line, index|
|
|
138
|
+
[index, EXTEND_PATTERN.match(line)[:indent]] if EXTEND_PATTERN.match?(line)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
unless extend_lines.size == 1
|
|
142
|
+
raise Error, "expected `extend LazyLoader` in #{department_file_path}; " \
|
|
143
|
+
'add it or register the cop manually'
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
index, indent = extend_lines.first
|
|
147
|
+
blank_after_extend = lines[index + 1] == "\n"
|
|
148
|
+
|
|
149
|
+
[index + (blank_after_extend ? 2 : 1), indent]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def department_module_source
|
|
153
|
+
modules = %w[RuboCop Cop] + badge.department.to_s.split('/')
|
|
154
|
+
body_indent = ' ' * modules.size
|
|
155
|
+
|
|
156
|
+
lines = ['# frozen_string_literal: true', '']
|
|
157
|
+
lines.concat(module_opening_lines(modules))
|
|
158
|
+
lines << "#{body_indent}extend LazyLoader"
|
|
159
|
+
lines << ''
|
|
160
|
+
lines << "#{body_indent}#{directive}"
|
|
161
|
+
lines.concat(module_closing_lines(modules))
|
|
162
|
+
|
|
163
|
+
"#{lines.join("\n")}\n"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def module_opening_lines(modules)
|
|
167
|
+
modules.flat_map.with_index do |mod, nesting|
|
|
168
|
+
indent = ' ' * nesting
|
|
169
|
+
lines = []
|
|
170
|
+
|
|
171
|
+
if mod == modules.last
|
|
172
|
+
lines << "#{indent}# Cops for the `#{badge.department}` department. " \
|
|
173
|
+
"The department's cops are"
|
|
174
|
+
lines << "#{indent}# registered for lazy loading and their files are " \
|
|
175
|
+
'loaded on demand.'
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
lines << "#{indent}module #{mod}"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def module_closing_lines(modules)
|
|
183
|
+
(modules.size - 1).downto(0).map { |nesting| "#{' ' * nesting}end" }
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -136,6 +136,10 @@ module RuboCop
|
|
|
136
136
|
RequireFileInjector.new(source_path: source_path, root_file_path: root_file_path).inject
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
+
def inject_registration
|
|
140
|
+
RegistrationInjector.new(source_path: source_path, badge: badge).inject
|
|
141
|
+
end
|
|
142
|
+
|
|
139
143
|
def inject_config(config_file_path: 'config/default.yml',
|
|
140
144
|
version_added: '<<next>>')
|
|
141
145
|
injector =
|
|
@@ -146,7 +146,7 @@ module RuboCop
|
|
|
146
146
|
end
|
|
147
147
|
end
|
|
148
148
|
|
|
149
|
-
# rubocop:disable Metrics/MethodLength
|
|
149
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
150
150
|
def formatted_message(offense_type, directive, actual_name, method_name)
|
|
151
151
|
case offense_type
|
|
152
152
|
when :wrong_name
|
|
@@ -166,7 +166,6 @@ module RuboCop
|
|
|
166
166
|
format(MSG, method: method_name)
|
|
167
167
|
end
|
|
168
168
|
end
|
|
169
|
-
# rubocop:enable Metrics/MethodLength
|
|
170
169
|
|
|
171
170
|
def remove_receiver(current)
|
|
172
171
|
current.delete_prefix('self.')
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module InternalAffairs
|
|
6
|
-
# rubocop:disable InternalAffairs/RedundantSourceRange -- node here is a `NodePattern::Node`
|
|
6
|
+
# rubocop:disable-next InternalAffairs/RedundantSourceRange -- node here is a `NodePattern::Node`
|
|
7
7
|
class NodePatternGroups
|
|
8
8
|
# Walks an AST that has been processed by `InternalAffairs::NodePatternGroups::Processor`
|
|
9
9
|
# in order to find `node_type` and `node_sequence` nodes that can be replaced with a node
|
|
@@ -125,7 +125,6 @@ module RuboCop
|
|
|
125
125
|
end
|
|
126
126
|
end
|
|
127
127
|
end
|
|
128
|
-
# rubocop:enable InternalAffairs/RedundantSourceRange
|
|
129
128
|
end
|
|
130
129
|
end
|
|
131
130
|
end
|
|
@@ -281,7 +281,7 @@ module RuboCop
|
|
|
281
281
|
end
|
|
282
282
|
end
|
|
283
283
|
|
|
284
|
-
# rubocop:disable Metrics/AbcSize
|
|
284
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
285
285
|
def do_line_begins_inside_argument?(node, do_loc)
|
|
286
286
|
line_begin_pos = do_loc.begin_pos - do_loc.column
|
|
287
287
|
first_char_pos = line_begin_pos + (do_loc.source_line =~ /\S/)
|
|
@@ -292,7 +292,6 @@ module RuboCop
|
|
|
292
292
|
first_char_pos < argument.source_range.end_pos
|
|
293
293
|
end
|
|
294
294
|
end
|
|
295
|
-
# rubocop:enable Metrics/AbcSize
|
|
296
295
|
|
|
297
296
|
# The continuation line indentation is only an unmeaningful alignment target when
|
|
298
297
|
# it is dictated by an opening delimiter, i.e. the line begins inside `(` or `[`.
|