rubocop 1.89.0 → 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.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +74 -7
  3. data/exe/rubocop +1 -1
  4. data/lib/rubocop/cached_data.rb +7 -2
  5. data/lib/rubocop/cli/command/execute_runner.rb +31 -0
  6. data/lib/rubocop/cli.rb +2 -4
  7. data/lib/rubocop/comment_config/directive_range.rb +32 -0
  8. data/lib/rubocop/comment_config/disable_next.rb +97 -0
  9. data/lib/rubocop/comment_config.rb +46 -30
  10. data/lib/rubocop/config.rb +23 -5
  11. data/lib/rubocop/config_loader.rb +1 -2
  12. data/lib/rubocop/config_loader_resolver.rb +1 -2
  13. data/lib/rubocop/config_validator.rb +1 -2
  14. data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
  15. data/lib/rubocop/cop/base.rb +34 -2
  16. data/lib/rubocop/cop/corrector.rb +3 -3
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -2
  18. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
  19. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  20. data/lib/rubocop/cop/layout/class_structure.rb +102 -26
  21. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
  22. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
  23. data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
  24. data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
  25. data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
  26. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
  27. data/lib/rubocop/cop/layout/line_length.rb +1 -2
  28. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
  29. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -5
  30. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
  31. data/lib/rubocop/cop/layout/space_around_operators.rb +7 -0
  32. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +2 -4
  33. data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
  34. data/lib/rubocop/cop/lint/argument_mismatch.rb +89 -0
  35. data/lib/rubocop/cop/lint/constant_reassignment.rb +17 -11
  36. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -8
  37. data/lib/rubocop/cop/lint/deprecated_reference.rb +11 -2
  38. data/lib/rubocop/cop/lint/duplicate_methods.rb +112 -14
  39. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
  40. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
  41. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
  42. data/lib/rubocop/cop/lint/literal_as_condition.rb +3 -6
  43. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +3 -6
  44. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
  45. data/lib/rubocop/cop/lint/name_typo.rb +53 -6
  46. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -0
  47. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +98 -19
  48. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
  49. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
  50. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
  51. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
  52. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
  53. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
  54. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -2
  55. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
  56. data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
  57. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
  58. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -2
  59. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
  60. data/lib/rubocop/cop/lint/unused_private_method.rb +94 -17
  61. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
  62. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
  63. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
  64. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  65. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
  66. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
  67. data/lib/rubocop/cop/lint/void.rb +1 -2
  68. data/lib/rubocop/cop/lint.rb +2 -0
  69. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
  70. data/lib/rubocop/cop/migration/department_name.rb +1 -2
  71. data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
  72. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
  73. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -2
  74. data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
  75. data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
  76. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
  77. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +51 -6
  78. data/lib/rubocop/cop/mixin/range_help.rb +1 -2
  79. data/lib/rubocop/cop/mixin/statement_modifier.rb +13 -7
  80. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
  81. data/lib/rubocop/cop/mixin.rb +4 -0
  82. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
  83. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -4
  84. data/lib/rubocop/cop/naming/method_name.rb +5 -2
  85. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
  86. data/lib/rubocop/cop/offense.rb +27 -4
  87. data/lib/rubocop/cop/registry.rb +34 -10
  88. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
  89. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -6
  90. data/lib/rubocop/cop/style/array_first_last.rb +1 -2
  91. data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
  92. data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
  93. data/lib/rubocop/cop/style/case_like_if.rb +2 -4
  94. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -2
  95. data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
  96. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
  97. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -2
  98. data/lib/rubocop/cop/style/constant_visibility.rb +1 -2
  99. data/lib/rubocop/cop/style/copyright.rb +1 -2
  100. data/lib/rubocop/cop/style/directive_scope.rb +171 -0
  101. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +36 -10
  102. data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
  103. data/lib/rubocop/cop/style/format_string_token.rb +1 -2
  104. data/lib/rubocop/cop/style/guard_clause.rb +1 -2
  105. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
  106. data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
  107. data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
  108. data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
  109. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -2
  110. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
  111. data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
  112. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  113. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
  114. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
  115. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
  116. data/lib/rubocop/cop/style/negative_array_index.rb +2 -4
  117. data/lib/rubocop/cop/style/nil_comparison.rb +1 -2
  118. data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
  119. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
  120. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -2
  121. data/lib/rubocop/cop/style/redundant_condition.rb +2 -4
  122. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +1 -2
  123. data/lib/rubocop/cop/style/redundant_each.rb +1 -2
  124. data/lib/rubocop/cop/style/redundant_format.rb +2 -4
  125. data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -4
  126. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
  127. data/lib/rubocop/cop/style/redundant_return.rb +1 -2
  128. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
  129. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
  130. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
  131. data/lib/rubocop/cop/style/safe_navigation.rb +1 -2
  132. data/lib/rubocop/cop/style/sample.rb +11 -6
  133. data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
  134. data/lib/rubocop/cop/style/semicolon.rb +2 -4
  135. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  136. data/lib/rubocop/cop/style/sole_nested_conditional.rb +1 -2
  137. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
  138. data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
  139. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  140. data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
  141. data/lib/rubocop/cop/style/time_now.rb +41 -0
  142. data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
  143. data/lib/rubocop/cop/style.rb +2 -0
  144. data/lib/rubocop/cop/team.rb +11 -3
  145. data/lib/rubocop/cop/util.rb +2 -4
  146. data/lib/rubocop/cop/variable_force/variable.rb +1 -2
  147. data/lib/rubocop/cops_documentation_generator.rb +3 -6
  148. data/lib/rubocop/directive_comment.rb +37 -3
  149. data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
  150. data/lib/rubocop/formatter/html_formatter.rb +2 -4
  151. data/lib/rubocop/formatter/json_formatter.rb +11 -6
  152. data/lib/rubocop/formatter/junit_formatter.rb +26 -9
  153. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
  154. data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
  155. data/lib/rubocop/formatter/tap_formatter.rb +3 -1
  156. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
  157. data/lib/rubocop/lockfile.rb +18 -0
  158. data/lib/rubocop/lsp/diagnostic.rb +4 -8
  159. data/lib/rubocop/lsp/routes.rb +48 -19
  160. data/lib/rubocop/mcp/server.rb +1 -2
  161. data/lib/rubocop/options.rb +10 -6
  162. data/lib/rubocop/path_util.rb +1 -2
  163. data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
  164. data/lib/rubocop/rake_task.rb +1 -2
  165. data/lib/rubocop/rspec/expect_offense.rb +2 -4
  166. data/lib/rubocop/rspec/shared_contexts.rb +4 -2
  167. data/lib/rubocop/runner.rb +26 -23
  168. data/lib/rubocop/server/cache.rb +1 -2
  169. data/lib/rubocop/server/cli.rb +2 -4
  170. data/lib/rubocop/server.rb +14 -0
  171. data/lib/rubocop/target_finder.rb +4 -1
  172. data/lib/rubocop/todo_audit.rb +111 -0
  173. data/lib/rubocop/version.rb +3 -5
  174. data/lib/rubocop.rb +3 -0
  175. data/lib/ruby_lsp/rubocop/addon.rb +1 -2
  176. metadata +13 -5
@@ -49,7 +49,7 @@ module RuboCop
49
49
 
50
50
  private
51
51
 
52
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
52
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
53
53
  def complexity_score_for(node)
54
54
  case node.type
55
55
  when :case
@@ -74,7 +74,6 @@ module RuboCop
74
74
  super
75
75
  end
76
76
  end
77
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
78
77
 
79
78
  def simple_in_pattern?(in_pattern_node)
80
79
  # `in_pattern_node.children[1]` is the guard (`if`/`unless`), or `nil`.
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Lint/RedundantCopDisableDirective needs to be disabled so as
4
4
  # to be able to provide examples of rubocop:disable comments.
5
- # rubocop:disable Lint/RedundantCopDisableDirective
5
+ # rubocop:disable-next Lint/RedundantCopDisableDirective
6
6
  module RuboCop
7
7
  module Cop
8
8
  module Migration
@@ -85,4 +85,3 @@ module RuboCop
85
85
  end
86
86
  end
87
87
  end
88
- # rubocop:enable Lint/RedundantCopDisableDirective
@@ -64,7 +64,7 @@ module RuboCop
64
64
  # Returns the end line of a node, which might be a comment and not part of the AST
65
65
  # End line is considered either the line at which another node starts, or
66
66
  # the line at which the parent node ends.
67
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
67
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
68
68
  def find_end_line(node)
69
69
  if node.if_type?
70
70
  if node.else?
@@ -89,7 +89,6 @@ module RuboCop
89
89
  end
90
90
  end || node.loc.end.line
91
91
  end
92
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
93
92
  end
94
93
  end
95
94
  end
@@ -25,7 +25,7 @@ module RuboCop
25
25
  end
26
26
  private_constant :SYMBOL_TO_STRING_CACHE
27
27
 
28
- # rubocop:disable Metrics
28
+ # rubocop:disable-next Metrics
29
29
  def style_detected(detected)
30
30
  return if no_acceptable_style?
31
31
 
@@ -51,7 +51,6 @@ module RuboCop
51
51
  config_to_allow_offenses[style_parameter_name] = updated_list.first
52
52
  end
53
53
  end
54
- # rubocop:enable Metrics
55
54
 
56
55
  def no_acceptable_style?
57
56
  config_to_allow_offenses['Enabled'] == false
@@ -117,7 +117,7 @@ module RuboCop
117
117
  use_modifier_form_without_parenthesized_method_call?(method_dispatch_node)
118
118
  end
119
119
 
120
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
120
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
121
121
  def def_node_that_require_parentheses(node)
122
122
  last_pair = node.parent.pairs.last
123
123
  return unless last_pair.key.source == last_pair.value.source
@@ -131,7 +131,6 @@ module RuboCop
131
131
 
132
132
  DefNode.new(def_node) unless def_node && def_node.arguments.empty?
133
133
  end
134
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
135
134
 
136
135
  def find_ancestor_method_dispatch_node(node)
137
136
  return unless (ancestor = node.parent.parent)
@@ -5,7 +5,7 @@ module RuboCop
5
5
  # Common functionality for Style/HashExcept and Style/HashSlice cops.
6
6
  # It registers an offense on methods with blocks that are equivalent
7
7
  # to Hash#except or Hash#slice.
8
- # rubocop:disable Metrics/ModuleLength
8
+ # rubocop:disable-next Metrics/ModuleLength
9
9
  module HashSubset
10
10
  include RangeHelp
11
11
  extend NodePattern::Macros
@@ -206,6 +206,5 @@ module RuboCop
206
206
  range_between(node.loc.selector.begin_pos, node.parent.loc.end.end_pos)
207
207
  end
208
208
  end
209
- # rubocop:enable Metrics/ModuleLength
210
209
  end
211
210
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Shared machinery for cops that compare a call site's arguments against
6
+ # a method signature from the project index (`Rubydex::Signature`).
7
+ #
8
+ # A signature is reduced to a shape - `[min, max, has_keywords]` where
9
+ # `max` is nil for a rest parameter - and the number of positional
10
+ # arguments a call passes is counted with Ruby's semantics for trailing
11
+ # keyword hashes: keywords passed to a method with no keyword parameters
12
+ # collapse into a single positional hash.
13
+ module IndexedMethodArity
14
+ private
15
+
16
+ def arity_satisfied?(given, min, max)
17
+ given >= min && (max.nil? || given <= max)
18
+ end
19
+
20
+ def expected_range(min, max)
21
+ return "#{min}+" if max.nil?
22
+ return min.to_s if min == max
23
+
24
+ "#{min}..#{max}"
25
+ end
26
+
27
+ # The agreed `[min, max, has_keywords]` shape of an indexed member's
28
+ # method definitions, or nil when the member is not backed by plain
29
+ # method definitions (attr, alias), has no signature, forwards
30
+ # arguments, or its definitions disagree on arity (e.g. reopened with
31
+ # a different one).
32
+ def indexed_member_shape(member)
33
+ definitions = member.definitions.grep(Rubydex::MethodDefinition)
34
+ return nil if definitions.empty?
35
+
36
+ signature_shape(definitions.flat_map(&:signatures))
37
+ end
38
+
39
+ def signature_shape(signatures)
40
+ shapes = signatures.filter_map { |signature| shape_for(signature) }.uniq
41
+ shapes.one? ? shapes.first : nil
42
+ end
43
+
44
+ def shape_for(signature)
45
+ return nil if signature.forward_parameter
46
+
47
+ min = signature.positional_parameters.size + signature.post_parameters.size
48
+
49
+ [min, maximum_positional(signature, min), keyword_parameters?(signature)]
50
+ end
51
+
52
+ def maximum_positional(signature, min)
53
+ return nil if signature.rest_positional_parameter
54
+
55
+ min + signature.optional_positional_parameters.size
56
+ end
57
+
58
+ def keyword_parameters?(signature)
59
+ !signature.keyword_parameters.empty? ||
60
+ !signature.optional_keyword_parameters.empty? ||
61
+ !signature.rest_keyword_parameter.nil?
62
+ end
63
+
64
+ # The number of positional arguments the call passes, or nil when it
65
+ # cannot be determined statically (argument forwarding).
66
+ def positional_argument_count(node, callee_has_keywords)
67
+ arguments = node.arguments
68
+ return nil if forwards_arguments?(arguments)
69
+
70
+ arguments = arguments[0...-1] if arguments.last&.block_pass_type?
71
+ return arguments.size unless (keyword_hash = keyword_hash(arguments.last))
72
+ return nil if double_splat?(keyword_hash)
73
+
74
+ # A keyword-style hash passed to a method with no keyword parameters is
75
+ # received as a single positional hash argument.
76
+ callee_has_keywords ? arguments.size - 1 : arguments.size
77
+ end
78
+
79
+ def forwards_arguments?(arguments)
80
+ arguments.any? do |argument|
81
+ argument.type?(:splat, :forwarded_args, :forwarded_restarg, :forwarded_kwrestarg)
82
+ end
83
+ end
84
+
85
+ def keyword_hash(node)
86
+ node if node&.hash_type? && !node.braces?
87
+ end
88
+
89
+ def double_splat?(hash_node)
90
+ hash_node.each_child_node.any?(&:kwsplat_type?)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -128,7 +128,13 @@ module RuboCop
128
128
  # Additionally, the RFC2396_PARSER alias is only available on 3.4 for now.
129
129
  # Extra info at https://github.com/ruby/uri/issues/118
130
130
  parser = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
131
- parser.make_regexp(config.for_cop('Layout/LineLength')['URISchemes'])
131
+ schemes = config.for_cop('Layout/LineLength')['URISchemes']
132
+ regexp = parser.make_regexp(schemes)
133
+
134
+ # `make_regexp` in uri 1.1.0+ matches schemes case-insensitively, which makes
135
+ # constant paths like `Http::UploadedFile` look like URIs. Require the exact case
136
+ # given in `URISchemes`.
137
+ schemes ? /(?=#{Regexp.union(schemes)}:)#{regexp}/ : regexp
132
138
  end
133
139
  end
134
140
 
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  # Common functionality for checking whether an AST node/token is aligned
6
6
  # with something on a preceding or following line
7
- # rubocop:disable Metrics/ModuleLength
7
+ # rubocop:disable-next Metrics/ModuleLength
8
8
  module PrecedingFollowingAlignment
9
9
  # Tokens that end with an `=`, as well as `<<`, that can be aligned together:
10
10
  # `=`, `==`, `===`, `!=`, `<=`, `>=`, `<<` and operator assignment (`+=`, etc).
@@ -69,7 +69,15 @@ module RuboCop
69
69
  line = processed_source.lines[lineno]
70
70
  index = line =~ /\S/
71
71
  next unless index
72
- next if indent && indent != index
72
+
73
+ if indent
74
+ # When searching for the nearest line with the same indentation,
75
+ # more deeply indented lines are nested content of the current group
76
+ # and are skipped, but a less deeply indented line ends the enclosing block:
77
+ # an alignment anchor beyond it would be coincidental.
78
+ break if index < indent
79
+ next if index > indent
80
+ end
73
81
 
74
82
  return yield(range, line, lineno + 1)
75
83
  end
@@ -174,20 +182,58 @@ module RuboCop
174
182
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
175
183
  # rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength
176
184
  def relevant_assignment_lines(line_range)
185
+ relevant_lines(line_range) { |line_number| assignment_lines.include?(line_number) }
186
+ end
187
+
188
+ def alignment_lines(line_number)
189
+ @alignment_lines_by_line ||= {}
190
+ return @alignment_lines_by_line[line_number] if @alignment_lines_by_line.key?(line_number)
191
+
192
+ lines = alignment_line_ranges(line_number).flatten.uniq.sort.freeze
193
+
194
+ lines.each { |line| @alignment_lines_by_line[line] = lines }
195
+ end
196
+
197
+ def alignment_line_ranges(line_number)
198
+ last_line = processed_source.lines.length
199
+
200
+ [
201
+ relevant_lines(line_number.downto(1), definition_boundary_lines) do |line|
202
+ !aligned_comment_lines.include?(line)
203
+ end,
204
+ relevant_lines(line_number.upto(last_line), definition_boundary_lines) do |line|
205
+ !aligned_comment_lines.include?(line)
206
+ end
207
+ ]
208
+ end
209
+
210
+ def definition_boundary_lines
211
+ @definition_boundary_lines ||= begin
212
+ nodes = processed_source.ast&.each_node(:any_def) || []
213
+
214
+ nodes.each_with_object(Set.new) do |node, lines|
215
+ lines << node.first_line << node.last_line
216
+ end
217
+ end
218
+ end
219
+
220
+ def relevant_lines(line_range, boundary_lines = [])
177
221
  result = []
178
- original_line_indent = processed_source.line_indentation(line_range.first)
222
+ original_line = line_range.first
223
+ original_line_indent = processed_source.line_indentation(original_line)
179
224
  relevant_line_indent_at_level = true
180
225
 
181
226
  line_range.each do |line_number|
182
227
  current_line_indent = processed_source.line_indentation(line_number)
183
228
  blank_line = processed_source.lines[line_number - 1].blank?
184
229
 
185
- if (current_line_indent < original_line_indent && !blank_line) ||
230
+ if (line_number != original_line && boundary_lines.include?(line_number)) ||
231
+ (current_line_indent < original_line_indent && !blank_line) ||
186
232
  (relevant_line_indent_at_level && blank_line)
187
233
  break
188
234
  end
189
235
 
190
- result << line_number if assignment_lines.include?(line_number) &&
236
+ result << line_number if yield(line_number) &&
191
237
  current_line_indent == original_line_indent
192
238
 
193
239
  unless blank_line
@@ -214,6 +260,5 @@ module RuboCop
214
260
  asgn_tokens.reject { |t| eqls_to_ignore.include?(t.begin_pos) }
215
261
  end
216
262
  end
217
- # rubocop:enable Metrics/ModuleLength
218
263
  end
219
264
  end
@@ -116,14 +116,13 @@ module RuboCop
116
116
  end
117
117
  end
118
118
 
119
- # rubocop:disable Metrics/ParameterLists
119
+ # rubocop:disable-next Metrics/ParameterLists
120
120
  def final_pos(src, pos, increment, continuations, newlines, whitespace)
121
121
  pos = move_pos(src, pos, increment, true, /[ \t]/)
122
122
  pos = move_pos_str(src, pos, increment, continuations, "\\\n")
123
123
  pos = move_pos(src, pos, increment, newlines, /\n/)
124
124
  move_pos(src, pos, increment, whitespace, /\s/)
125
125
  end
126
- # rubocop:enable Metrics/ParameterLists
127
126
 
128
127
  def move_pos(src, pos, step, condition, regexp)
129
128
  offset = step == -1 ? -1 : 0
@@ -35,8 +35,14 @@ module RuboCop
35
35
  condition.each_node.any?(&:lvasgn_type?)
36
36
  end
37
37
 
38
+ # The rendered modifier form is compared against the bare maximum, not
39
+ # `acceptable_line_length?`: `Layout/LineLength`'s exemptions describe
40
+ # long lines the user tolerates, not permission to manufacture new ones,
41
+ # so collapsing to modifier form must never push a line past the maximum.
38
42
  def modifier_fits_on_single_line?(node)
39
- acceptable_line_length?(line_in_modifier_form(node), node.first_line)
43
+ return true unless max_line_length
44
+
45
+ line_length(line_in_modifier_form(node)) <= max_line_length
40
46
  end
41
47
 
42
48
  def line_in_modifier_form(node)
@@ -46,12 +52,12 @@ module RuboCop
46
52
  "#{code_before}#{to_modifier_form(node)}#{code_after(node)}"
47
53
  end
48
54
 
49
- # Rather than comparing the rendered modifier form against the bare
50
- # maximum, ask the question `Layout/LineLength` would: a line the user's
51
- # configuration exempts (`Layout/LineLength` disabled at that line, an
52
- # allowed pattern, an allowed cop directive, an allowed URI) fits by
53
- # definition. This keeps every modifier cop consistent with
54
- # `Layout/LineLength` instead of each reimplementing part of its policy.
55
+ # Whether an existing line is acceptable to `Layout/LineLength`,
56
+ # exemptions (the cop disabled at that line, an allowed pattern, an
57
+ # allowed cop directive, an allowed URI) included. Only for judging
58
+ # lines already in the source as too long - see
59
+ # `modifier_fits_on_single_line?` for why the exemptions must not apply
60
+ # when deciding whether a modifier line may be created.
55
61
  def acceptable_line_length?(line, line_number)
56
62
  return true unless max_line_length
57
63
  return true if line_length(line) <= max_line_length
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  # Common methods shared by Style/TrailingCommaInArguments,
6
6
  # Style/TrailingCommaInArrayLiteral and Style/TrailingCommaInHashLiteral
7
- # rubocop:disable Metrics/ModuleLength
7
+ # rubocop:disable-next Metrics/ModuleLength
8
8
  module TrailingComma
9
9
  include ConfigurableEnforcedStyle
10
10
  include RangeHelp
@@ -95,7 +95,7 @@ module RuboCop
95
95
  node.multiline? && !allowed_multiline_argument?(node)
96
96
  end
97
97
 
98
- # rubocop:disable Metrics/AbcSize
98
+ # rubocop:disable-next Metrics/AbcSize
99
99
  def method_name_and_arguments_on_same_line?(node)
100
100
  return false if !node.call_type? || node.last_line != node.last_argument.last_line
101
101
  return true if node.last_argument.hash_type? && node.last_argument.braces?
@@ -104,7 +104,6 @@ module RuboCop
104
104
 
105
105
  line == node.last_argument.last_line
106
106
  end
107
- # rubocop:enable Metrics/AbcSize
108
107
 
109
108
  # A single argument with the closing bracket on the same line as the end
110
109
  # of the argument is not considered multiline, even if the argument
@@ -223,6 +222,5 @@ module RuboCop
223
222
  false
224
223
  end
225
224
  end
226
- # rubocop:enable Metrics/ModuleLength
227
225
  end
228
226
  end
@@ -41,6 +41,10 @@ module RuboCop
41
41
  autoload :HashAlignmentStyles, "#{__dir__}/mixin/hash_alignment_styles"
42
42
  autoload :HashSubset, "#{__dir__}/mixin/hash_subset"
43
43
  autoload :HashTransformMethod, "#{__dir__}/mixin/hash_transform_method"
44
+ # Deprecated aliases of `AllowedMethods` and `AllowedPattern`, defined in those files.
45
+ autoload :IgnoredMethods, "#{__dir__}/mixin/allowed_methods"
46
+ autoload :IgnoredPattern, "#{__dir__}/mixin/allowed_pattern"
47
+ autoload :IndexedMethodArity, "#{__dir__}/mixin/indexed_method_arity"
44
48
  autoload :IntegerNode, "#{__dir__}/mixin/integer_node"
45
49
  autoload :Interpolation, "#{__dir__}/mixin/interpolation"
46
50
  autoload :LineLengthHelp, "#{__dir__}/mixin/line_length_help"
@@ -33,6 +33,7 @@ module RuboCop
33
33
  node.each_descendant(:lvar, :lvasgn) do |lvar|
34
34
  lvar_location = lvar.loc.name
35
35
  next unless lvar_location.source == arg.source
36
+ next if shadowed?(lvar, arg.source, node)
36
37
 
37
38
  corrector.replace(lvar_location, 'other')
38
39
  end
@@ -47,6 +48,21 @@ module RuboCop
47
48
 
48
49
  !/\A[[:word:]]/.match?(name) || OP_LIKE_METHODS.include?(name)
49
50
  end
51
+
52
+ # A reference is shadowed when an enclosing block within the method redeclares the
53
+ # parameter name (as a block parameter or block-local). Such a reference points to
54
+ # the block's variable, not the operator's parameter, so it must not be renamed.
55
+ def shadowed?(node, name, def_node)
56
+ node.each_ancestor(:block).any? do |block|
57
+ def_node.source_range.contains?(block.source_range) && redeclares?(block, name)
58
+ end
59
+ end
60
+
61
+ def redeclares?(block, name)
62
+ block.arguments.any? do |argument|
63
+ argument.respond_to?(:name) && argument.name.to_s == name
64
+ end
65
+ end
50
66
  end
51
67
  end
52
68
  end
@@ -167,7 +167,7 @@ module RuboCop
167
167
  PATTERN
168
168
 
169
169
  # rubocop:disable Metrics/AbcSize
170
- # rubocop:disable Metrics/MethodLength
170
+ # rubocop:disable-next Metrics/MethodLength
171
171
  def on_or_asgn(node)
172
172
  lhs = node.lhs
173
173
  return unless lhs.ivasgn_type?
@@ -192,7 +192,6 @@ module RuboCop
192
192
  corrector.replace(lhs.loc.name, "@#{suggested_var}")
193
193
  end
194
194
  end
195
- # rubocop:enable Metrics/MethodLength
196
195
  # rubocop:enable Metrics/AbcSize
197
196
 
198
197
  # @!method defined_memoized?(node, ivar)
@@ -203,7 +202,7 @@ module RuboCop
203
202
  $(ivasgn %1 _))
204
203
  PATTERN
205
204
 
206
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
205
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
207
206
  def on_defined?(node)
208
207
  arg = node.first_argument
209
208
  return false unless arg.ivar_type?
@@ -233,7 +232,6 @@ module RuboCop
233
232
  end
234
233
  end
235
234
  end
236
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
237
235
 
238
236
  private
239
237
 
@@ -105,6 +105,10 @@ module RuboCop
105
105
 
106
106
  MSG = 'Use %<style>s for method names.'
107
107
  MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another method name instead.'
108
+ RESTRICT_ON_SEND = %i[
109
+ define_method define_singleton_method new define alias_method
110
+ attr attr_reader attr_writer attr_accessor
111
+ ].freeze
108
112
 
109
113
  OPERATOR_METHODS = %i[| ^ & <=> == === =~ > >= < <= << >> + - * /
110
114
  % ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].to_set.freeze
@@ -209,7 +213,7 @@ module RuboCop
209
213
  forbidden_identifier?(name) || forbidden_pattern?(name)
210
214
  end
211
215
 
212
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
216
+ # rubocop:disable-next Metrics/MethodLength, Metrics/AbcSize
213
217
  def register_forbidden_name(node)
214
218
  if node.any_def_type?
215
219
  name_node = node.loc.name
@@ -227,7 +231,6 @@ module RuboCop
227
231
  message = format(MSG_FORBIDDEN, identifier: method_name)
228
232
  add_offense(name_node, message: message)
229
233
  end
230
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
231
234
 
232
235
  def attr_name(name_item)
233
236
  sym_name(name_item) || str_name(name_item)
@@ -118,7 +118,7 @@ module RuboCop
118
118
 
119
119
  # Returns the reassignment node once the exception variable is reassigned (a truthy
120
120
  # signal to stop correcting later references), or `nil` when no reassignment is found.
121
- # rubocop:disable Metrics/MethodLength
121
+ # rubocop:disable-next Metrics/MethodLength
122
122
  def correct_node(corrector, node, offending_name, preferred_name)
123
123
  return unless node
124
124
 
@@ -141,7 +141,6 @@ module RuboCop
141
141
  end
142
142
  nil
143
143
  end
144
- # rubocop:enable Metrics/MethodLength
145
144
 
146
145
  # If the exception variable is reassigned, that assignment needs to be corrected.
147
146
  # Further `lvar` nodes will not be corrected though since they now refer to a
@@ -86,9 +86,19 @@ module RuboCop
86
86
 
87
87
  NO_LOCATION = PseudoSourceRange.new(1, 0, '', 0, 0).freeze
88
88
 
89
+ # @api public
90
+ #
91
+ # @!attribute [r] justification
92
+ #
93
+ # @return [String, nil]
94
+ # the reason given on the directive that suppressed this offense
95
+ # (the text after `--`), or `nil` when the offense is not suppressed
96
+ # or the directive carries no reason
97
+ attr_reader :justification
98
+
89
99
  # @api private
90
100
  def initialize(severity, location, message, cop_name, # rubocop:disable Metrics/ParameterLists
91
- status = :uncorrected, corrector = nil)
101
+ status = :uncorrected, corrector = nil, justification: nil)
92
102
  @severity = RuboCop::Cop::Severity.new(severity)
93
103
  @location = location
94
104
 
@@ -101,15 +111,16 @@ module RuboCop
101
111
  @cop_name = cop_name.freeze
102
112
  @status = status
103
113
  @corrector = corrector
114
+ @justification = justification.freeze
104
115
  freeze
105
116
  end
106
117
 
107
118
  def marshal_dump
108
- [@severity, @location, @message, @cop_name, @status]
119
+ [@severity, @location, @message, @cop_name, @status, @justification]
109
120
  end
110
121
 
111
122
  def marshal_load(array)
112
- @severity, @location, @message, @cop_name, @status = array
123
+ @severity, @location, @message, @cop_name, @status, @justification = array
113
124
  @line = @location.line
114
125
  @column = @location.column
115
126
  end
@@ -121,8 +132,10 @@ module RuboCop
121
132
  # @return [Boolean]
122
133
  # whether this offense can be automatically corrected via autocorrect.
123
134
  # This includes todo comments, for example when requested with `--disable-uncorrectable`.
135
+ # An offense suppressed by a directive comment is not correctable -
136
+ # autocorrect will not touch it.
124
137
  def correctable?
125
- @status != :unsupported
138
+ @status != :unsupported && @status != :disabled
126
139
  end
127
140
 
128
141
  # @api public
@@ -223,6 +236,16 @@ module RuboCop
223
236
  column + 1
224
237
  end
225
238
 
239
+ # @api private
240
+ #
241
+ # The minimum value of `real_column` is 1, so the minimum value of
242
+ # `real_last_column` is also 1. A non-zero `last_column` is used as is,
243
+ # because the 0-based end-exclusive column is the same as the 1-based
244
+ # column of the last character.
245
+ def real_last_column
246
+ last_column.zero? ? 1 : last_column
247
+ end
248
+
226
249
  # @api public
227
250
  #
228
251
  # @return [Boolean]
@@ -23,8 +23,8 @@ module RuboCop
23
23
  global.without_department(:Test).cops
24
24
  end
25
25
 
26
- def self.qualified_cop_name(name, origin, warn: true)
27
- global.qualified_cop_name(name, origin, warn: warn)
26
+ def self.qualified_cop_name(name, origin, warn: true, correct_namespace: true)
27
+ global.qualified_cop_name(name, origin, warn: warn, correct_namespace: correct_namespace)
28
28
  end
29
29
 
30
30
  # Changes momentarily the global registry
@@ -171,11 +171,19 @@ module RuboCop
171
171
  # @note Emits a warning if the provided name has an incorrect namespace
172
172
  #
173
173
  # @return [String] Qualified cop name
174
- def qualified_cop_name(name, path, warn: true)
174
+ def qualified_cop_name(name, path, warn: true, correct_namespace: true)
175
175
  badge = Badge.parse(name)
176
176
  print_department_missing_warning(name, path) if warn && department_missing?(badge, name)
177
177
  return name if registered?(badge)
178
+ # A name qualified with the wrong department is returned as is when
179
+ # namespace correction is not wanted, so the caller can surface the
180
+ # mistake instead of silently acting on the corrected name.
181
+ return name if badge.qualified? && !correct_namespace
178
182
 
183
+ resolve_cop_name(badge, name, path, warn)
184
+ end
185
+
186
+ def resolve_cop_name(badge, name, path, warn)
179
187
  potential_badges = qualify_badge(badge)
180
188
 
181
189
  case potential_badges.size
@@ -325,10 +333,33 @@ module RuboCop
325
333
  cop ? [cop] : cops_for_department(directive)
326
334
  end
327
335
 
336
+ # Loads all cops that are registered for lazy loading. Useful when lazily registered cops
337
+ # must not outlive the current global registry, e.g. before a temporary global registry is
338
+ # discarded in tests.
339
+ #
340
+ # @api private
341
+ def load_all_lazy_cops
342
+ # Take a snapshot because loading a cop can load (and thereby register)
343
+ # another lazy-loaded cop, e.g. its superclass.
344
+ badges = @cops_by_badge.keys
345
+
346
+ badges.each do |badge|
347
+ load_lazy_cop(badge) if @cops_by_badge[badge].is_a?(String)
348
+ end
349
+ end
350
+
328
351
  def freeze
329
352
  clear_enrollment_queue
330
353
  load_all_lazy_cops
331
354
  unqualified_cop_names # build cache
355
+
356
+ # Freeze the structural collections as well, so that a cop class defined after
357
+ # the freeze fails fast at its `enlist`/`lazy_load` call site instead of corrupting
358
+ # the registry and failing much later.
359
+ @cops_by_badge.freeze
360
+ @departments.freeze
361
+ @enrollment_queue.freeze
362
+
332
363
  super
333
364
  end
334
365
 
@@ -398,13 +429,6 @@ module RuboCop
398
429
  end
399
430
  end
400
431
 
401
- def load_all_lazy_cops
402
- # Take a snapshot because loading a cop can load (and thereby register)
403
- # another lazy-loaded cop, e.g. its superclass.
404
- badges = @cops_by_badge.keys
405
- badges.each { |badge| load_lazy_cop(badge) if @cops_by_badge[badge].is_a?(String) }
406
- end
407
-
408
432
  # @return [Class, nil] the loaded cop class, or nil if the cop excluded
409
433
  # itself from the registry while being loaded
410
434
  def load_lazy_cop(badge)