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
@@ -149,11 +149,7 @@ module RuboCop
149
149
  def adjust_str(part)
150
150
  case part.type
151
151
  when :str
152
- if single_quoted?(part)
153
- part.value.gsub(/(\\|"|#\{|#@|#\$)/, '\\\\\&')
154
- else
155
- part.value.inspect[1..-2]
156
- end
152
+ adjust_str_literal(part)
157
153
  when :dstr, :begin
158
154
  part.children.map do |child|
159
155
  adjust_str(child)
@@ -163,6 +159,19 @@ module RuboCop
163
159
  end
164
160
  end
165
161
 
162
+ def adjust_str_literal(part)
163
+ if single_quoted?(part)
164
+ part.value.gsub(/(\\|"|#\{|#@|#\$)/, '\\\\\&')
165
+ elsif double_quoted?(part)
166
+ # Reuse the source as written - rebuilding it from the value via
167
+ # `inspect` would rewrite the author's escape notation
168
+ # (e.g. `\x0a` into `\n`).
169
+ part.source[1..-2]
170
+ else
171
+ part.value.inspect[1..-2]
172
+ end
173
+ end
174
+
166
175
  def handle_quotes(parts)
167
176
  parts.map do |part|
168
177
  part == '"' ? '\"' : part
@@ -173,6 +182,10 @@ module RuboCop
173
182
  str_node.source.start_with?("'")
174
183
  end
175
184
 
185
+ def double_quoted?(str_node)
186
+ str_node.source.start_with?('"')
187
+ end
188
+
176
189
  def mode
177
190
  cop_config['Mode'].to_sym
178
191
  end
@@ -103,7 +103,7 @@ module RuboCop
103
103
  end
104
104
  end
105
105
 
106
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
106
+ # rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
107
107
  def arguments_identical?(def_node, super_node, def_args, super_args)
108
108
  return false if argument_list_size_differs?(def_args, super_args, super_node)
109
109
 
@@ -120,7 +120,6 @@ module RuboCop
120
120
 
121
121
  true
122
122
  end
123
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
124
123
 
125
124
  def argument_list_size_differs?(def_args, super_args, super_node)
126
125
  # If the def node has a block argument and the super node has an explicit block,
@@ -167,7 +167,7 @@ module RuboCop
167
167
  [Layout::SpaceBeforeBlockBraces]
168
168
  end
169
169
 
170
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
170
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
171
171
  def on_block(node)
172
172
  symbol_proc?(node) do |dispatch_node, arguments_node, method_name|
173
173
  if active_support_extensions_enabled?
@@ -184,7 +184,6 @@ module RuboCop
184
184
  register_offense(node, method_name, dispatch_node.method_name)
185
185
  end
186
186
  end
187
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
188
187
  alias on_numblock on_block
189
188
  alias on_itblock on_block
190
189
 
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for `Time.new` without arguments, which is an implicit way
7
+ # to retrieve the current system time. Prefer the more explicit `Time.now`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # Time.new
12
+ #
13
+ # # good
14
+ # Time.now
15
+ #
16
+ # # good - `Time.new` with arguments constructs a specific time
17
+ # Time.new(2026, 8, 19)
18
+ #
19
+ class TimeNow < Base
20
+ extend AutoCorrector
21
+
22
+ MSG = 'Prefer `Time.now` over `Time.new` to retrieve the current time.'
23
+ RESTRICT_ON_SEND = %i[new].freeze
24
+
25
+ # @!method time_new?(node)
26
+ def_node_matcher :time_new?, <<~PATTERN
27
+ (call (const {nil? cbase} :Time) :new)
28
+ PATTERN
29
+
30
+ def on_send(node)
31
+ return unless time_new?(node)
32
+
33
+ add_offense(node) do |corrector|
34
+ corrector.replace(node.loc.selector.join(node.source_range.end), 'now')
35
+ end
36
+ end
37
+ alias on_csend on_send
38
+ end
39
+ end
40
+ end
41
+ end
@@ -122,7 +122,7 @@ module RuboCop
122
122
  node.comparison_method? && !noncommutative_operator?(node)
123
123
  end
124
124
 
125
- # rubocop:disable Metrics/CyclomaticComplexity
125
+ # rubocop:disable-next Metrics/CyclomaticComplexity
126
126
  def valid_yoda?(node)
127
127
  return true unless (rhs = node.first_argument)
128
128
 
@@ -133,7 +133,6 @@ module RuboCop
133
133
 
134
134
  enforce_yoda? ? constant_portion?(lhs) : constant_portion?(rhs)
135
135
  end
136
- # rubocop:enable Metrics/CyclomaticComplexity
137
136
 
138
137
  def message(node)
139
138
  format(MSG, source: node.source)
@@ -58,6 +58,7 @@ module RuboCop
58
58
  register_cop :DigChain, "#{__dir__}/style/dig_chain"
59
59
  register_cop :Dir, "#{__dir__}/style/dir"
60
60
  register_cop :DirEmpty, "#{__dir__}/style/dir_empty"
61
+ register_cop :DirectiveScope, "#{__dir__}/style/directive_scope"
61
62
  register_cop :DisableCopsWithinSourceCodeDirective, "#{__dir__}/style/disable_cops_within_source_code_directive"
62
63
  register_cop :DocumentationMethod, "#{__dir__}/style/documentation_method"
63
64
  register_cop :Documentation, "#{__dir__}/style/documentation"
@@ -282,6 +283,7 @@ module RuboCop
282
283
  register_cop :SymbolProc, "#{__dir__}/style/symbol_proc"
283
284
  register_cop :TallyMethod, "#{__dir__}/style/tally_method"
284
285
  register_cop :TernaryParentheses, "#{__dir__}/style/ternary_parentheses"
286
+ register_cop :TimeNow, "#{__dir__}/style/time_now"
285
287
  register_cop :TopLevelMethodDefinition, "#{__dir__}/style/top_level_method_definition"
286
288
  register_cop :TrailingBodyOnClass, "#{__dir__}/style/trailing_body_on_class"
287
289
  register_cop :TrailingBodyOnMethodDefinition, "#{__dir__}/style/trailing_body_on_method_definition"
@@ -9,7 +9,7 @@ module RuboCop
9
9
  # For performance reasons, Team will first dispatch cops & forces in two groups,
10
10
  # first the ones needed for autocorrection (if any), then the rest
11
11
  # (unless autocorrections happened).
12
- # rubocop:disable Metrics/ClassLength
12
+ # rubocop:disable-next Metrics/ClassLength
13
13
  class Team
14
14
  InvestigationResult = Struct.new(:report, :corrector)
15
15
  private_constant :InvestigationResult
@@ -211,7 +211,7 @@ module RuboCop
211
211
  # run the other cops when no corrections are left
212
212
  on_duty = roundup_relevant_cops(processed_source)
213
213
 
214
- autocorrect_cops, other_cops = on_duty.partition(&:autocorrect?)
214
+ autocorrect_cops, other_cops = partition_by_correcting(on_duty)
215
215
  report = investigate_partial(autocorrect_cops, processed_source,
216
216
  offset: offset, original: original)
217
217
 
@@ -251,6 +251,15 @@ module RuboCop
251
251
  end
252
252
 
253
253
  # @return [Array<cop>]
254
+ # A cop whose unsafe correction is skipped still inserts todo comments
255
+ # under `--disable-uncorrectable`, so it must run with the correcting
256
+ # cops - correctors of the later `other_cops` round are never applied.
257
+ def partition_by_correcting(cops)
258
+ cops.partition do |cop|
259
+ cop.autocorrect? || cop.skipped_unsafe_correction_with_disable_uncorrectable?
260
+ end
261
+ end
262
+
254
263
  def roundup_relevant_cops(processed_source)
255
264
  (cops + opted_in_standby_cops(processed_source)).select do |cop|
256
265
  next false if cop.excluded_file?(processed_source.file_path)
@@ -395,6 +404,5 @@ module RuboCop
395
404
  end
396
405
  end
397
406
  end
398
- # rubocop:enable Metrics/ClassLength
399
407
  end
400
408
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  # This module contains a collection of useful utility methods.
6
- # rubocop:disable Metrics/ModuleLength
6
+ # rubocop:disable-next Metrics/ModuleLength
7
7
  module Util
8
8
  include PathUtil
9
9
 
@@ -35,7 +35,7 @@ module RuboCop
35
35
  node.loc_is?(:end, ')')
36
36
  end
37
37
 
38
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
38
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
39
39
  def add_parentheses(node, corrector)
40
40
  if node.args_type?
41
41
  arguments_range = node.source_range
@@ -55,7 +55,6 @@ module RuboCop
55
55
  corrector.insert_after(args_end(node), ')')
56
56
  end
57
57
  end
58
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
59
58
 
60
59
  def any_descendant?(node, *types)
61
60
  if block_given?
@@ -210,6 +209,5 @@ module RuboCop
210
209
  source == target || (source.is_a?(Array) && source.include?(target))
211
210
  end
212
211
  end
213
- # rubocop:enable Metrics/ModuleLength
214
212
  end
215
213
  end
@@ -47,7 +47,7 @@ module RuboCop
47
47
  !@references.empty?
48
48
  end
49
49
 
50
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
50
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
51
51
  def reference!(node)
52
52
  reference = Reference.new(node, @scope)
53
53
  @references << reference
@@ -72,7 +72,6 @@ module RuboCop
72
72
  end
73
73
  end
74
74
  end
75
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
76
75
 
77
76
  def in_modifier_conditional?(assignment, reference_node)
78
77
  conditional = modifier_conditional_of(assignment.node)
@@ -137,7 +137,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
137
137
  "NOTE: Requires Ruby version #{requirement}\n\n"
138
138
  end
139
139
 
140
- # rubocop:disable Metrics/MethodLength
140
+ # rubocop:disable-next Metrics/MethodLength
141
141
  def properties(cop)
142
142
  header = [
143
143
  'Enabled by default', 'Safe', 'Supports autocorrection', 'Version Added',
@@ -160,7 +160,6 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
160
160
  ]]
161
161
  "#{to_table(header, content)}\n"
162
162
  end
163
- # rubocop:enable Metrics/MethodLength
164
163
 
165
164
  def cop_header(cop)
166
165
  content = +"\n"
@@ -217,7 +216,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
217
216
  "xref:#{filename}#allowmultilinefinalelement[AllowMultilineFinalElement]"
218
217
  end
219
218
 
220
- # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
219
+ # rubocop:disable-next Metrics/CyclomaticComplexity,Metrics/MethodLength
221
220
  def configurable_values(cop_config, name)
222
221
  case name
223
222
  when /^Enforced/
@@ -244,7 +243,6 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
244
243
  end
245
244
  end
246
245
  end
247
- # rubocop:enable Metrics/CyclomaticComplexity,Metrics/MethodLength
248
246
 
249
247
  def to_table(header, content)
250
248
  table = ['|===', "| #{header.join(' | ')}\n\n"].join("\n")
@@ -303,7 +301,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
303
301
  "\ninclude::../partials/#{filename}[]\n"
304
302
  end
305
303
 
306
- # rubocop:disable Metrics/MethodLength
304
+ # rubocop:disable-next Metrics/MethodLength
307
305
  def print_cops_of_department(department)
308
306
  selected_cops = cops_of_department(department)
309
307
  content = +<<~HEADER
@@ -323,7 +321,6 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
323
321
  file.write("#{content.strip}\n")
324
322
  end
325
323
  end
326
- # rubocop:enable Metrics/MethodLength
327
324
 
328
325
  def print_cop_with_doc(cop) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
329
326
  cop_config = config.for_cop(cop)
@@ -26,13 +26,17 @@ module RuboCop
26
26
  # @api private
27
27
  PUSH_POP_ARGS_PATTERN = "([+\\-]#{COP_NAME_PATTERN_NC}(?:\\s+[+\\-]#{COP_NAME_PATTERN_NC})*)"
28
28
  # @api private
29
- AVAILABLE_MODES = %w[disable enable todo push pop].freeze
29
+ AVAILABLE_MODES = %w[disable enable todo push pop disable-next todo-next].freeze
30
+ # @api private
31
+ # Longest first, so a `-next` mode is not matched as its prefix
32
+ # (`-` is a word boundary).
33
+ MODES_PATTERN = AVAILABLE_MODES.sort_by { |mode| -mode.length }.join('|').freeze
30
34
  # @api private
31
35
  DIRECTIVE_MARKER_PATTERN = '# rubocop : '
32
36
  # @api private
33
37
  DIRECTIVE_MARKER_REGEXP = Regexp.new(DIRECTIVE_MARKER_PATTERN.gsub(' ', '\s*'))
34
38
  # @api private
35
- DIRECTIVE_HEADER_PATTERN = "#{DIRECTIVE_MARKER_PATTERN}((?:#{AVAILABLE_MODES.join('|')}))\\b"
39
+ DIRECTIVE_HEADER_PATTERN = "#{DIRECTIVE_MARKER_PATTERN}((?:#{MODES_PATTERN}))\\b"
36
40
  # @api private
37
41
  DIRECTIVE_COMMENT_REGEXP = Regexp.new(
38
42
  "#{DIRECTIVE_HEADER_PATTERN}(?:\\s+#{COPS_PATTERN}|\\s+#{PUSH_POP_ARGS_PATTERN})?"
@@ -80,6 +84,18 @@ module RuboCop
80
84
  MALFORMED_DIRECTIVE_WITHOUT_COP_NAME_REGEXP.match?(comment.text)
81
85
  end
82
86
 
87
+ # The text of the directive's optional `--` trailing comment, or `nil`
88
+ # when there is none.
89
+ def reason
90
+ return unless @match_data
91
+
92
+ tail = @match_data.post_match.lstrip
93
+ return unless tail.start_with?(TRAILING_COMMENT_MARKER)
94
+
95
+ reason = tail.delete_prefix(TRAILING_COMMENT_MARKER).strip
96
+ reason unless reason.empty?
97
+ end
98
+
83
99
  # Checks if this directive relates to single line
84
100
  def single_line?
85
101
  !comment.text.start_with?(DIRECTIVE_COMMENT_REGEXP)
@@ -98,6 +114,19 @@ module RuboCop
98
114
  )
99
115
  end
100
116
 
117
+ # `#range` stops at the cop list, so a `--` reason sits outside it. The reason documents the
118
+ # directive and means nothing once the directive is gone, so removal has to cover both. Any
119
+ # other trailing text is an ordinary comment and is left alone.
120
+ def range_with_reason
121
+ directive_range = range
122
+ trailing = Parser::Source::Range.new(
123
+ comment.source_range.source_buffer, directive_range.end_pos, comment.source_range.end_pos
124
+ )
125
+ return directive_range unless trailing.source.lstrip.start_with?(TRAILING_COMMENT_MARKER)
126
+
127
+ directive_range.with(end_pos: comment.source_range.end_pos)
128
+ end
129
+
101
130
  # Returns match captures to directive comment pattern
102
131
  def match_captures
103
132
  @match_captures ||= @match_data && begin
@@ -111,7 +140,12 @@ module RuboCop
111
140
 
112
141
  # Checks if this directive disables cops
113
142
  def disabled?
114
- %w[disable todo].include?(mode)
143
+ %w[disable todo].include?(mode) || disable_next?
144
+ end
145
+
146
+ # Checks if this directive disables cops for the next statement only
147
+ def disable_next?
148
+ %w[disable-next todo-next].include?(mode)
115
149
  end
116
150
 
117
151
  # Checks if this directive enables cops
@@ -23,7 +23,9 @@ module RuboCop
23
23
 
24
24
  def message(offense)
25
25
  message =
26
- if offense.corrected_with_todo?
26
+ if offense.disabled?
27
+ "[Suppressed] #{offense.message}"
28
+ elsif offense.corrected_with_todo?
27
29
  "[Todo] #{offense.message}"
28
30
  elsif offense.corrected?
29
31
  "[Corrected] #{offense.message}"
@@ -74,11 +74,10 @@ module RuboCop
74
74
  end
75
75
 
76
76
  # Make Kernel#binding public.
77
- # rubocop:disable Lint/UselessMethodDefinition
77
+ # rubocop:disable-next Lint/UselessMethodDefinition
78
78
  def binding
79
79
  super
80
80
  end
81
- # rubocop:enable Lint/UselessMethodDefinition
82
81
 
83
82
  def decorated_message(offense)
84
83
  offense.message.gsub(/`(.+?)`/) { "<code>#{escape(Regexp.last_match(1))}</code>" }
@@ -144,11 +143,10 @@ module RuboCop
144
143
  }.freeze
145
144
 
146
145
  # Make Kernel#binding public.
147
- # rubocop:disable Lint/UselessMethodDefinition
146
+ # rubocop:disable-next Lint/UselessMethodDefinition
148
147
  def binding
149
148
  super
150
149
  end
151
- # rubocop:enable Lint/UselessMethodDefinition
152
150
  end
153
151
  end
154
152
  end
@@ -47,7 +47,7 @@ module RuboCop
47
47
  end
48
48
 
49
49
  def hash_for_offense(offense)
50
- {
50
+ hash = {
51
51
  severity: offense.severity.name,
52
52
  message: offense.message,
53
53
  cop_name: offense.cop_name,
@@ -55,18 +55,23 @@ module RuboCop
55
55
  correctable: offense.correctable?,
56
56
  location: hash_for_location(offense)
57
57
  }
58
+
59
+ # Suppressed offenses appear only under `--display-suppressed`, so
60
+ # these keys are additive for existing consumers.
61
+ if offense.disabled?
62
+ hash[:suppressed] = true
63
+ hash[:justification] = offense.justification
64
+ end
65
+
66
+ hash
58
67
  end
59
68
 
60
- # TODO: Consider better solution for Offense#real_column.
61
- # The minimum value of `start_column: real_column` is 1.
62
- # So, the minimum value of `last_column` should be 1.
63
- # And non-zero value of `last_column` should be used as is.
64
69
  def hash_for_location(offense)
65
70
  {
66
71
  start_line: offense.line,
67
72
  start_column: offense.real_column,
68
73
  last_line: offense.last_line,
69
- last_column: offense.last_column.zero? ? 1 : offense.last_column,
74
+ last_column: offense.real_last_column,
70
75
  length: offense.location.length,
71
76
  # `line` and `column` exist for compatibility.
72
77
  # Use `start_line` and `start_column` instead.
@@ -25,21 +25,21 @@ module RuboCop
25
25
  super
26
26
 
27
27
  @test_case_elements = []
28
+ @config_store = nil
28
29
 
29
30
  reset_count
30
31
  end
31
32
 
33
+ def file_started(_file, options)
34
+ @config_store = options[:config_store]
35
+ end
36
+
32
37
  def file_finished(file, offenses)
33
38
  @inspected_file_count += 1
39
+ @offense_count += offenses.count
34
40
 
35
- # TODO: Returns all cops with the same behavior as
36
- # the original rubocop-junit-formatter.
37
- # https://github.com/mikian/rubocop-junit-formatter/blob/v0.1.4/lib/rubocop/formatter/junit_formatter.rb#L9
38
- #
39
- # In the future, it would be preferable to return only enabled cops.
40
- Cop::Registry.all.each do |cop|
41
+ cops_for(file).each do |cop|
41
42
  target_offenses = offenses_for_cop(offenses, cop)
42
- @offense_count += target_offenses.count
43
43
 
44
44
  next unless relevant_for_output?(options, target_offenses)
45
45
 
@@ -47,7 +47,7 @@ module RuboCop
47
47
  end
48
48
  end
49
49
 
50
- # rubocop:disable Layout/LineLength,Metrics/AbcSize,Metrics/MethodLength
50
+ # rubocop:disable-next Layout/LineLength,Metrics/AbcSize,Metrics/MethodLength
51
51
  def finished(_inspected_files)
52
52
  output.puts %(<?xml version='1.0'?>)
53
53
  output.puts %(<testsuites>)
@@ -70,10 +70,17 @@ module RuboCop
70
70
  output.puts %( </testsuite>)
71
71
  output.puts %(</testsuites>)
72
72
  end
73
- # rubocop:enable Layout/LineLength,Metrics/AbcSize,Metrics/MethodLength
74
73
 
75
74
  private
76
75
 
76
+ def cops_for(file)
77
+ # `file_started` is not called when this formatter is used directly through the API,
78
+ # in which case all cops are returned for compatibility.
79
+ return Cop::Registry.all if @config_store.nil?
80
+
81
+ registry.enabled(@config_store.for_file(file))
82
+ end
83
+
77
84
  def relevant_for_output?(options, target_offenses)
78
85
  !options[:display_only_failed] || target_offenses.any?
79
86
  end
@@ -91,6 +98,16 @@ module RuboCop
91
98
  end
92
99
  end
93
100
 
101
+ # Filtering by badge keeps lazy-loaded cops unloaded; `Registry#enabled` then loads only
102
+ # the enabled cops, the same set the runner mobilizes.
103
+ def registry
104
+ @registry ||= Cop::Registry.global.filter_by_badge(options) do |badge|
105
+ next false if badge.department == :Test || badge.match_name?(options[:except])
106
+
107
+ options[:only].nil? || badge.match_name?(options[:only])
108
+ end
109
+ end
110
+
94
111
  def classname_attribute_value(file)
95
112
  @classname_attribute_value_cache ||= Hash.new do |hash, key|
96
113
  hash[key] = key.delete_suffix('.rb').gsub("#{PathUtil.pwd}/", '').tr('/', '.')
@@ -52,7 +52,7 @@ module RuboCop
52
52
  report_summary(@offense_counts, @offending_files_count)
53
53
  end
54
54
 
55
- # rubocop:disable Metrics/AbcSize
55
+ # rubocop:disable-next Metrics/AbcSize
56
56
  def report_summary(offense_counts, offending_files_count)
57
57
  per_cop_counts = ordered_offense_counts(offense_counts)
58
58
  total_count = total_offense_count(offense_counts)
@@ -68,7 +68,6 @@ module RuboCop
68
68
 
69
69
  output.puts
70
70
  end
71
- # rubocop:enable Metrics/AbcSize
72
71
 
73
72
  def ordered_offense_counts(offense_counts)
74
73
  offense_counts.sort_by { |k, v| [-v, k] }.to_h
@@ -86,7 +86,9 @@ module RuboCop
86
86
 
87
87
  def message(offense)
88
88
  message =
89
- if offense.corrected_with_todo?
89
+ if offense.disabled?
90
+ magenta('[Suppressed] ')
91
+ elsif offense.corrected_with_todo?
90
92
  green('[Todo] ')
91
93
  elsif offense.corrected?
92
94
  green('[Corrected] ')
@@ -104,7 +106,7 @@ module RuboCop
104
106
  include Colorizable
105
107
  include TextUtil
106
108
 
107
- # rubocop:disable Metrics/ParameterLists
109
+ # rubocop:disable-next Metrics/ParameterLists
108
110
  def initialize(
109
111
  file_count, offense_count, correction_count, correctable_count, rainbow,
110
112
  safe_autocorrect: false
@@ -116,7 +118,6 @@ module RuboCop
116
118
  @rainbow = rainbow
117
119
  @safe_autocorrect = safe_autocorrect
118
120
  end
119
- # rubocop:enable Metrics/ParameterLists
120
121
 
121
122
  def summary
122
123
  if @correction_count.positive?
@@ -68,7 +68,9 @@ module RuboCop
68
68
 
69
69
  def message(offense)
70
70
  message =
71
- if offense.corrected_with_todo?
71
+ if offense.disabled?
72
+ '[Suppressed] '
73
+ elsif offense.corrected_with_todo?
72
74
  '[Todo] '
73
75
  elsif offense.corrected?
74
76
  '[Corrected] '
@@ -32,7 +32,7 @@ module RuboCop
32
32
  report_summary(@offense_counts)
33
33
  end
34
34
 
35
- # rubocop:disable Metrics/AbcSize
35
+ # rubocop:disable-next Metrics/AbcSize
36
36
  def report_summary(offense_counts)
37
37
  per_file_counts = ordered_offense_counts(offense_counts)
38
38
  total_count = total_offense_count(offense_counts)
@@ -50,7 +50,6 @@ module RuboCop
50
50
 
51
51
  output.puts
52
52
  end
53
- # rubocop:enable Metrics/AbcSize
54
53
 
55
54
  def ordered_offense_counts(offense_counts)
56
55
  offense_counts.sort_by { |k, v| [-v, k] }.to_h
@@ -59,6 +59,18 @@ module RuboCop
59
59
  end
60
60
  end
61
61
 
62
+ # Names of gems sourced from a local path, i.e. `path:` dependencies and the
63
+ # project's own gem when the `Gemfile` uses `gemspec`. Git sources are not
64
+ # included: their code lives outside the project, like a released gem's.
65
+ # @return [Array<String>]
66
+ def path_sourced_gem_names
67
+ return [] unless parser
68
+
69
+ parser.specs.filter_map do |spec|
70
+ spec.name if path_source?(spec.source)
71
+ end
72
+ end
73
+
62
74
  # Whether this lockfile includes the named gem, directly or indirectly.
63
75
  # @param [String] name
64
76
  # @return [Boolean]
@@ -68,6 +80,12 @@ module RuboCop
68
80
 
69
81
  private
70
82
 
83
+ # `Bundler::Source::Git` is a subclass of `Bundler::Source::Path`, but a git
84
+ # source is a remote one.
85
+ def path_source?(source)
86
+ source.is_a?(::Bundler::Source::Path) && !source.is_a?(::Bundler::Source::Git)
87
+ end
88
+
71
89
  # @return [Bundler::LockfileParser, nil]
72
90
  def parser
73
91
  return @parser if defined?(@parser)