rubocop 1.89.0 → 1.91.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +309 -14
  3. data/config/obsoletion.yml +9 -0
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop/cached_data.rb +7 -2
  6. data/lib/rubocop/changed_files.rb +112 -0
  7. data/lib/rubocop/cli/command/execute_runner.rb +52 -1
  8. data/lib/rubocop/cli.rb +11 -7
  9. data/lib/rubocop/comment_config/directive_range.rb +34 -0
  10. data/lib/rubocop/comment_config/disable_next.rb +143 -0
  11. data/lib/rubocop/comment_config/push_pop.rb +55 -0
  12. data/lib/rubocop/comment_config.rb +50 -64
  13. data/lib/rubocop/config.rb +33 -5
  14. data/lib/rubocop/config_loader.rb +13 -6
  15. data/lib/rubocop/config_loader_resolver.rb +50 -9
  16. data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
  17. data/lib/rubocop/config_obsoletion/removed_cop.rb +8 -0
  18. data/lib/rubocop/config_obsoletion.rb +1 -1
  19. data/lib/rubocop/config_validator.rb +19 -5
  20. data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
  21. data/lib/rubocop/cop/badge.rb +1 -1
  22. data/lib/rubocop/cop/base.rb +49 -5
  23. data/lib/rubocop/cop/corrector.rb +3 -3
  24. data/lib/rubocop/cop/correctors.rb +2 -2
  25. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  26. data/lib/rubocop/cop/generator.rb +1 -1
  27. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -2
  28. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
  29. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  30. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
  31. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  32. data/lib/rubocop/cop/layout/class_structure.rb +102 -26
  33. data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
  34. data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
  35. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
  36. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
  37. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  38. data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
  39. data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
  40. data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
  41. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
  42. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
  43. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -5
  44. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  45. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
  46. data/lib/rubocop/cop/layout/line_length.rb +20 -3
  47. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
  48. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -5
  49. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
  50. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +14 -3
  51. data/lib/rubocop/cop/layout/space_around_operators.rb +12 -1
  52. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +2 -4
  53. data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
  54. data/lib/rubocop/cop/lint/argument_mismatch.rb +99 -0
  55. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
  56. data/lib/rubocop/cop/lint/constant_reassignment.rb +17 -11
  57. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +165 -15
  58. data/lib/rubocop/cop/lint/deprecated_reference.rb +11 -2
  59. data/lib/rubocop/cop/lint/duplicate_methods.rb +113 -15
  60. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  61. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
  62. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
  63. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
  64. data/lib/rubocop/cop/lint/literal_as_condition.rb +11 -8
  65. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +3 -6
  66. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +185 -0
  67. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
  68. data/lib/rubocop/cop/lint/name_typo.rb +53 -6
  69. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  70. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  71. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -0
  72. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
  73. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +122 -29
  74. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
  75. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
  76. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
  77. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
  78. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
  79. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
  80. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -2
  81. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
  82. data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
  83. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
  84. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -2
  85. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
  86. data/lib/rubocop/cop/lint/unused_private_method.rb +94 -17
  87. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
  88. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
  89. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
  90. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  91. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
  92. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
  93. data/lib/rubocop/cop/lint/void.rb +1 -2
  94. data/lib/rubocop/cop/lint.rb +3 -0
  95. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
  96. data/lib/rubocop/cop/migration/department_name.rb +1 -2
  97. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  98. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  99. data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
  100. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
  101. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +11 -1
  102. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +7 -4
  103. data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
  104. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  105. data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
  106. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
  107. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +62 -6
  108. data/lib/rubocop/cop/mixin/range_help.rb +1 -2
  109. data/lib/rubocop/cop/mixin/statement_modifier.rb +13 -7
  110. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
  111. data/lib/rubocop/cop/mixin.rb +6 -2
  112. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
  113. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
  114. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  115. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -4
  116. data/lib/rubocop/cop/naming/method_name.rb +5 -2
  117. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
  118. data/lib/rubocop/cop/offense.rb +27 -4
  119. data/lib/rubocop/cop/registry.rb +43 -11
  120. data/lib/rubocop/cop/severity.rb +12 -0
  121. data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -2
  122. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
  123. data/lib/rubocop/cop/style/arguments_forwarding.rb +5 -8
  124. data/lib/rubocop/cop/style/array_first_last.rb +1 -2
  125. data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
  126. data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
  127. data/lib/rubocop/cop/style/case_like_if.rb +5 -7
  128. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -2
  129. data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
  130. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
  131. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -2
  132. data/lib/rubocop/cop/style/constant_visibility.rb +6 -3
  133. data/lib/rubocop/cop/style/copyright.rb +1 -2
  134. data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
  135. data/lib/rubocop/cop/style/directive_scope.rb +304 -0
  136. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +83 -13
  137. data/lib/rubocop/cop/style/documentation_method.rb +1 -9
  138. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  139. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  140. data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
  141. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  142. data/lib/rubocop/cop/style/empty_method.rb +12 -2
  143. data/lib/rubocop/cop/style/endless_method.rb +31 -12
  144. data/lib/rubocop/cop/style/file_null.rb +2 -2
  145. data/lib/rubocop/cop/style/float_division.rb +10 -16
  146. data/lib/rubocop/cop/style/for.rb +5 -0
  147. data/lib/rubocop/cop/style/format_string_token.rb +1 -2
  148. data/lib/rubocop/cop/style/guard_clause.rb +5 -2
  149. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
  150. data/lib/rubocop/cop/style/hash_lookup_method.rb +3 -0
  151. data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
  152. data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
  153. data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
  154. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -2
  155. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
  156. data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
  157. data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
  158. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  159. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
  160. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
  161. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
  162. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
  163. data/lib/rubocop/cop/style/negative_array_index.rb +2 -4
  164. data/lib/rubocop/cop/style/nested_modifier.rb +9 -4
  165. data/lib/rubocop/cop/style/nil_comparison.rb +36 -9
  166. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  167. data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
  168. data/lib/rubocop/cop/style/not.rb +3 -1
  169. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
  170. data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
  171. data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
  172. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
  173. data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
  174. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -2
  175. data/lib/rubocop/cop/style/redundant_condition.rb +6 -8
  176. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +11 -3
  177. data/lib/rubocop/cop/style/redundant_each.rb +1 -2
  178. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  179. data/lib/rubocop/cop/style/redundant_format.rb +2 -4
  180. data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -5
  181. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
  182. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
  183. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +6 -6
  184. data/lib/rubocop/cop/style/redundant_return.rb +1 -2
  185. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
  186. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
  187. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
  188. data/lib/rubocop/cop/style/safe_navigation.rb +1 -2
  189. data/lib/rubocop/cop/style/sample.rb +11 -6
  190. data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
  191. data/lib/rubocop/cop/style/semicolon.rb +2 -4
  192. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  193. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  194. data/lib/rubocop/cop/style/sole_nested_conditional.rb +1 -2
  195. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  196. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
  197. data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
  198. data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
  199. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  200. data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
  201. data/lib/rubocop/cop/style/time_now.rb +41 -0
  202. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  203. data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
  204. data/lib/rubocop/cop/style.rb +2 -1
  205. data/lib/rubocop/cop/team.rb +11 -3
  206. data/lib/rubocop/cop/util.rb +3 -5
  207. data/lib/rubocop/cop/variable_force/variable.rb +1 -2
  208. data/lib/rubocop/cops_documentation_generator.rb +26 -8
  209. data/lib/rubocop/directive_comment.rb +73 -10
  210. data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
  211. data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
  212. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  213. data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
  214. data/lib/rubocop/formatter/html_formatter.rb +2 -4
  215. data/lib/rubocop/formatter/json_formatter.rb +11 -6
  216. data/lib/rubocop/formatter/junit_formatter.rb +26 -9
  217. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
  218. data/lib/rubocop/formatter/sarif_formatter.rb +181 -0
  219. data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
  220. data/lib/rubocop/formatter/tap_formatter.rb +3 -1
  221. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
  222. data/lib/rubocop/formatter.rb +1 -0
  223. data/lib/rubocop/lockfile.rb +18 -0
  224. data/lib/rubocop/lsp/diagnostic.rb +4 -8
  225. data/lib/rubocop/lsp/routes.rb +48 -19
  226. data/lib/rubocop/magic_comment.rb +27 -0
  227. data/lib/rubocop/mcp/server.rb +1 -2
  228. data/lib/rubocop/options.rb +69 -7
  229. data/lib/rubocop/path_util.rb +2 -3
  230. data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
  231. data/lib/rubocop/plugin/loader.rb +1 -1
  232. data/lib/rubocop/rake_task.rb +1 -2
  233. data/lib/rubocop/remote_config.rb +4 -4
  234. data/lib/rubocop/result_cache.rb +16 -5
  235. data/lib/rubocop/rspec/expect_offense.rb +2 -4
  236. data/lib/rubocop/rspec/shared_contexts.rb +4 -2
  237. data/lib/rubocop/runner.rb +90 -44
  238. data/lib/rubocop/server/cache.rb +1 -2
  239. data/lib/rubocop/server/cli.rb +2 -4
  240. data/lib/rubocop/server.rb +14 -0
  241. data/lib/rubocop/target_finder.rb +4 -1
  242. data/lib/rubocop/todo_audit.rb +111 -0
  243. data/lib/rubocop/unified_diff.rb +257 -0
  244. data/lib/rubocop/version.rb +3 -5
  245. data/lib/rubocop.rb +7 -1
  246. data/lib/ruby_lsp/rubocop/addon.rb +1 -2
  247. metadata +18 -6
  248. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
@@ -5,6 +5,8 @@ module RuboCop
5
5
  # and provides a way to check if each cop is enabled at arbitrary line.
6
6
  class CommentConfig
7
7
  extend SimpleForwardable
8
+ include DisableNext
9
+ include PushPop
8
10
 
9
11
  CONFIG_DISABLED_LINE_RANGE_MIN = -Float::INFINITY
10
12
 
@@ -25,8 +27,6 @@ module RuboCop
25
27
  end
26
28
  end
27
29
 
28
- CopAnalysis = Struct.new(:line_ranges, :start_line_number)
29
-
30
30
  attr_reader :processed_source
31
31
 
32
32
  def_delegators :@processed_source, :config, :registry
@@ -35,14 +35,25 @@ module RuboCop
35
35
  @processed_source = processed_source
36
36
  @no_directives = !processed_source.raw_source.include?('rubocop')
37
37
  @stack = []
38
+ @detached_next_directives = []
38
39
  end
39
40
 
40
41
  def cop_enabled_at_line?(cop, line_number)
42
+ cop_enabled_at_lines?(cop, line_number, line_number)
43
+ end
44
+
45
+ # Whether the cop is enabled for all of the given line span. The cop
46
+ # counts as disabled for the span when any of its disabled ranges
47
+ # overlaps it, so that a directive on any line of a multi-line offense
48
+ # suppresses the offense.
49
+ def cop_enabled_at_lines?(cop, first_line, last_line)
41
50
  cop = cop.cop_name if cop.respond_to?(:cop_name)
42
51
  disabled_line_ranges = cop_disabled_line_ranges[cop]
43
52
  return true unless disabled_line_ranges
44
53
 
45
- disabled_line_ranges.none? { |range| range.include?(line_number) }
54
+ disabled_line_ranges.none? do |range|
55
+ range.end >= first_line && range.begin <= last_line
56
+ end
46
57
  end
47
58
 
48
59
  def cop_opted_in?(cop)
@@ -65,8 +76,9 @@ module RuboCop
65
76
  non_comment_token_line_numbers.none?(line_number)
66
77
  end
67
78
 
68
- # The names of the cops that are opted in by an `enable` comment directive,
69
- # used to mobilize cops disabled in the config on demand.
79
+ # The names of the cops that are opted in by an `enable` comment directive
80
+ # or a `+` argument of a `push`/`next` directive, used to mobilize cops
81
+ # disabled in the config on demand.
70
82
  #
71
83
  # @api private
72
84
  # @return [Set<String>]
@@ -74,10 +86,11 @@ module RuboCop
74
86
  @opt_in_cops ||= begin
75
87
  cops = Set.new
76
88
  each_directive do |directive|
77
- next unless directive.enabled?
78
- next if directive.all_cops?
79
-
80
- cops.merge(directive.raw_cop_names)
89
+ if directive.enabled?
90
+ cops.merge(directive.raw_cop_names) unless directive.all_cops?
91
+ elsif directive.push? || directive.next?
92
+ cops.merge(directive.signed_args.fetch('+', []))
93
+ end
81
94
  end
82
95
  cops
83
96
  end
@@ -88,7 +101,7 @@ module RuboCop
88
101
  def extra_enabled_comments_with_names(extras:, names:)
89
102
  each_directive do |directive|
90
103
  next unless comment_only_line?(directive.line_number)
91
- next if directive.push? || directive.pop?
104
+ next if self_closing_directive?(directive)
92
105
 
93
106
  if directive.enabled_all?
94
107
  handle_enable_all(directive, names, extras)
@@ -100,7 +113,7 @@ module RuboCop
100
113
  extras
101
114
  end
102
115
 
103
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
116
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
104
117
  def analyze
105
118
  return {} if @no_directives
106
119
 
@@ -111,9 +124,15 @@ module RuboCop
111
124
  if directive.push?
112
125
  restore_point = analyses.transform_values(&:dup)
113
126
  @stack.push(restore_point)
114
- apply_push(analyses, resolve_push_cops(directive), directive.line_number)
127
+ apply_push(analyses, resolve_push_cops(directive), directive)
115
128
  elsif directive.pop?
116
129
  pop_state(analyses, directive.line_number) if @stack.any?
130
+ elsif directive.disable_next?
131
+ apply_disable_next(analyses, directive)
132
+ elsif directive.next?
133
+ apply_next_directive(analyses, resolve_push_cops(directive), directive)
134
+ elsif directive.enable_next?
135
+ apply_enable_next(analyses, directive)
117
136
  else
118
137
  directive.cop_names.each do |cop_name|
119
138
  cop_name = qualified_cop_name(cop_name)
@@ -129,45 +148,6 @@ module RuboCop
129
148
  hash[cop_name] = cop_line_ranges(analysis)
130
149
  end
131
150
  end
132
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
133
-
134
- def resolve_push_cops(directive)
135
- directive.push_args.transform_values do |names|
136
- names.flat_map { |name| expand_cop_name(name) }
137
- end
138
- end
139
-
140
- def expand_cop_name(name)
141
- registry = Cop::Registry.global
142
- cops = registry.department?(name) ? registry.names_for_department(name) : [name]
143
- cops.map { |c| qualified_cop_name(c) }
144
- end
145
-
146
- def apply_push(analyses, resolved_cops, line)
147
- resolved_cops.each do |op, cops|
148
- cops.each { |cop| apply_cop_op(analyses, op, cop, line) }
149
- end
150
- end
151
-
152
- def apply_cop_op(analyses, operation, cop, line)
153
- analysis = analyses[cop]
154
- if operation == '-' && !analysis.start_line_number
155
- analyses[cop] = CopAnalysis.new(analysis.line_ranges, line)
156
- elsif operation == '+' && analysis.start_line_number
157
- analyses[cop] =
158
- CopAnalysis.new(analysis.line_ranges + [analysis.start_line_number..line], nil)
159
- end
160
- end
161
-
162
- def pop_state(analyses, line)
163
- restore_point = @stack.pop
164
- (restore_point.keys | analyses.keys).each do |cop|
165
- current = analyses[cop]
166
- new_range = current.start_line_number ? [current.start_line_number..(line - 1)] : []
167
- new_start = restore_point[cop]&.start_line_number ? line : nil
168
- analyses[cop] = CopAnalysis.new(current.line_ranges + new_range, new_start)
169
- end
170
- end
171
151
 
172
152
  def inject_disabled_cops_directives(analyses)
173
153
  registry.disabled_names(config).each do |cop_name|
@@ -193,27 +173,21 @@ module RuboCop
193
173
  return analysis unless directive.disabled?
194
174
 
195
175
  line = directive.line_number
196
- CopAnalysis.new(analysis.line_ranges + [(line..line)], analysis.start_line_number)
176
+ range = DirectiveRange.new(line, line, directive)
177
+ CopAnalysis.new(analysis.line_ranges + [range], analysis.start_line_number,
178
+ analysis.start_directive)
197
179
  end
198
180
 
199
181
  def analyze_disabled(analysis, directive)
200
- line = directive.line_number
201
- start_line = analysis.start_line_number
202
- new_ranges = start_line ? analysis.line_ranges + [start_line..line] : analysis.line_ranges
203
- CopAnalysis.new(new_ranges, line)
182
+ CopAnalysis.new(analysis.close(directive.line_number), directive.line_number, directive)
204
183
  end
205
184
 
206
185
  def analyze_rest(analysis, directive)
207
- line = directive.line_number
208
- start_line = analysis.start_line_number
209
- new_ranges = start_line ? analysis.line_ranges + [start_line..line] : analysis.line_ranges
210
- CopAnalysis.new(new_ranges, nil)
186
+ CopAnalysis.new(analysis.close(directive.line_number), nil)
211
187
  end
212
188
 
213
189
  def cop_line_ranges(analysis)
214
- return analysis.line_ranges unless analysis.start_line_number
215
-
216
- analysis.line_ranges + [(analysis.start_line_number..Float::INFINITY)]
190
+ analysis.close(Float::INFINITY)
217
191
  end
218
192
 
219
193
  def each_directive
@@ -225,8 +199,20 @@ module RuboCop
225
199
  end
226
200
  end
227
201
 
202
+ # Push/pop and next-statement directives close themselves, so they
203
+ # play no part in the disable/enable pairing.
204
+ def self_closing_directive?(directive)
205
+ directive.push? || directive.pop? || directive.disable_next? ||
206
+ directive.next? || directive.enable_next?
207
+ end
208
+
228
209
  def qualified_cop_name(cop_name)
229
- Cop::Registry.qualified_cop_name(cop_name.strip, processed_source.file_path)
210
+ # A directive naming a cop under the wrong department is not honored -
211
+ # the mistake is surfaced by `Lint/RedundantCopDisableDirective` (with a
212
+ # did-you-mean hint) instead of a stderr warning that is easy to miss
213
+ # and disappears entirely on cached runs.
214
+ Cop::Registry.qualified_cop_name(cop_name.strip, processed_source.file_path,
215
+ correct_namespace: false)
230
216
  end
231
217
 
232
218
  # `Style/DisableCopsWithinSourceCodeDirective` cannot be disabled via
@@ -2,7 +2,7 @@
2
2
 
3
3
  # FIXME: Moving Rails department code to RuboCop Rails will remove
4
4
  # the following rubocop:disable comment.
5
- # rubocop:disable Metrics/ClassLength
5
+ # rubocop:disable-next Metrics/ClassLength
6
6
  module RuboCop
7
7
  # This class represents the configuration of the RuboCop application
8
8
  # and all its cops. A Config is associated with a YAML configuration
@@ -27,7 +27,7 @@ module RuboCop
27
27
  config
28
28
  end
29
29
 
30
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
30
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
31
31
  def initialize(hash = RuboCop::ConfigLoader.default_configuration, loaded_path = nil)
32
32
  @loaded_path = loaded_path
33
33
  @for_cop = Hash.new do |h, cop|
@@ -72,7 +72,6 @@ module RuboCop
72
72
  @badge_config_cache = {}.compare_by_identity
73
73
  @clusivity_config_exists_cache = {}
74
74
  end
75
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
76
75
 
77
76
  def loaded_plugins
78
77
  @loaded_plugins ||= ConfigLoader.loaded_plugins
@@ -168,7 +167,11 @@ module RuboCop
168
167
  department_config = self[badge.department_name]
169
168
  cop_config = for_cop(badge.to_s)
170
169
  if department_config
171
- department_config.merge(cop_config)
170
+ merged_config = department_config.merge(cop_config)
171
+ if department_config['Exclude'] && cop_config['Exclude']
172
+ merged_config['Exclude'] = department_config['Exclude'] | cop_config['Exclude']
173
+ end
174
+ merged_config
172
175
  else
173
176
  cop_config
174
177
  end
@@ -223,6 +226,16 @@ module RuboCop
223
226
  end
224
227
  end
225
228
 
229
+ # Whether preview behavior is on, for cops that gate an unstable change
230
+ # behind it and for cops that are themselves `Enabled: preview`.
231
+ # `--preview` / `--no-preview` win over `AllCops: Preview`.
232
+ def preview?(options = {})
233
+ preview = options[:preview]
234
+ return preview unless preview.nil?
235
+
236
+ for_all_cops['Preview'] == true
237
+ end
238
+
226
239
  def active_support_extensions_enabled?
227
240
  for_all_cops['ActiveSupportExtensionsEnabled']
228
241
  end
@@ -352,6 +365,14 @@ module RuboCop
352
365
  @gem_versions_in_target ||= read_gem_versions_from_target_lockfile
353
366
  end
354
367
 
368
+ # Returns the names of the target's gems that are sourced from a local path
369
+ # (i.e. `path:` dependencies and the project's own gem when the `Gemfile`
370
+ # uses `gemspec`), whose code therefore lives in the project itself.
371
+ # @returns [Array<String>, nil] The gem names, or nil without a lockfile.
372
+ def path_sourced_gems_in_target
373
+ @path_sourced_gems_in_target ||= read_path_sourced_gems_from_target_lockfile
374
+ end
375
+
355
376
  def inspect # :nodoc:
356
377
  "#<#{self.class.name}:#{object_id} @loaded_path=#{loaded_path}>"
357
378
  end
@@ -396,6 +417,14 @@ module RuboCop
396
417
  Lockfile.new(lockfile_path).gem_versions
397
418
  end
398
419
 
420
+ # @returns [Array<String>, nil] The names of the gems sourced from a local path.
421
+ def read_path_sourced_gems_from_target_lockfile
422
+ lockfile_path = bundler_lock_file_path
423
+ return nil unless lockfile_path
424
+
425
+ Lockfile.new(lockfile_path).path_sourced_gem_names
426
+ end
427
+
399
428
  def enable_cop?(qualified_cop_name, cop_options)
400
429
  # If the cop is explicitly enabled or `Lint/Syntax`, the other checks can be skipped.
401
430
  return true if cop_options['Enabled'] == true || qualified_cop_name == 'Lint/Syntax'
@@ -454,4 +483,3 @@ module RuboCop
454
483
  end
455
484
  end
456
485
  end
457
- # rubocop:enable Metrics/ClassLength
@@ -24,7 +24,8 @@ module RuboCop
24
24
  include FileFinder
25
25
 
26
26
  attr_accessor :debug, :ignore_parent_exclusion, :disable_pending_cops, :enable_pending_cops,
27
- :enabled_by_default, :disabled_by_default, :ignore_unrecognized_cops
27
+ :enabled_by_default, :disabled_by_default, :ignore_unrecognized_cops,
28
+ :preview
28
29
  attr_writer :default_configuration, :cache_root
29
30
  attr_reader :loaded_plugins, :loaded_features
30
31
 
@@ -41,6 +42,7 @@ module RuboCop
41
42
  @loaded_features = Set.new
42
43
  @disable_pending_cops = nil
43
44
  @enable_pending_cops = nil
45
+ @preview = nil
44
46
  @enabled_by_default = nil
45
47
  @disabled_by_default = nil
46
48
  @ignore_parent_exclusion = nil
@@ -49,7 +51,7 @@ module RuboCop
49
51
  FileFinder.root_level = nil
50
52
  end
51
53
 
52
- # rubocop:disable Metrics/AbcSize
54
+ # rubocop:disable-next Metrics/AbcSize
53
55
  def load_file(file, check: true)
54
56
  path = file_path(file)
55
57
 
@@ -73,7 +75,6 @@ module RuboCop
73
75
 
74
76
  Config.create(hash, path, check: check)
75
77
  end
76
- # rubocop:enable Metrics/AbcSize
77
78
 
78
79
  def load_yaml_configuration(absolute_path)
79
80
  file_contents = read_file(absolute_path)
@@ -196,12 +197,18 @@ module RuboCop
196
197
  # `AllCops/DisabledByDefault` to the given configuration. Used when the
197
198
  # configuration would otherwise be returned without going through
198
199
  # `merge_with_default` (e.g. there is no user-supplied `.rubocop.yml`).
200
+ # Used when there is no configuration file, so the defaults are the whole
201
+ # configuration and still need the same treatment `merge_with_default`
202
+ # would have given them.
199
203
  def apply_default_overrides(config)
200
- return config if @enabled_by_default.nil? && @disabled_by_default.nil?
204
+ hash = resolver.apply_preview_defaults(config, preview == true)
201
205
 
202
- hash = config.transform_values do |params|
203
- params.is_a?(Hash) ? params.merge('Enabled' => !@disabled_by_default) : params
206
+ unless @enabled_by_default.nil? && @disabled_by_default.nil?
207
+ hash = hash.transform_values do |params|
208
+ params.is_a?(Hash) ? params.merge('Enabled' => !@disabled_by_default) : params
209
+ end
204
210
  end
211
+
205
212
  Config.new(hash, config.loaded_path)
206
213
  end
207
214
 
@@ -95,19 +95,22 @@ module RuboCop
95
95
  # When the `--disable-all-cops` or `--enable-all-cops` CLI option is given,
96
96
  # it takes precedence over the configuration values.
97
97
  def merge_with_default(config, config_file, unset_nil:)
98
- default_configuration = ConfigLoader.default_configuration
98
+ base_defaults = apply_preview_defaults(ConfigLoader.default_configuration, preview?(config))
99
+ default_configuration = base_defaults
99
100
  disabled_by_default, enabled_by_default = resolve_default_overrides(config)
100
101
 
101
102
  if disabled_by_default || enabled_by_default
102
- default_configuration = transform(default_configuration) do |params|
103
+ default_configuration = transform(base_defaults) do |params|
103
104
  params.merge('Enabled' => !disabled_by_default)
104
105
  end
105
106
  end
106
107
 
107
- config = handle_disabled_by_default(config, default_configuration) if disabled_by_default
108
+ if disabled_by_default
109
+ config = handle_disabled_by_default(config, default_configuration, base_defaults)
110
+ end
108
111
  override_enabled_for_disabled_departments(default_configuration, config)
109
112
 
110
- opts = { inherit_mode: config['inherit_mode'] || {}, unset_nil: unset_nil }
113
+ opts = { inherit_mode: inherit_mode_for_default(config), unset_nil: unset_nil }
111
114
  Config.new(merge(default_configuration, config, **opts), config_file)
112
115
  end
113
116
 
@@ -115,7 +118,7 @@ module RuboCop
115
118
  # with the addition that any value that is a hash, and occurs in both
116
119
  # arguments, will also be merged. And so on.
117
120
  #
118
- # rubocop:disable Metrics/AbcSize
121
+ # rubocop:disable-next Metrics/AbcSize
119
122
  def merge(base_hash, derived_hash, **opts)
120
123
  result = base_hash.merge(derived_hash)
121
124
  keys_appearing_in_both = base_hash.keys & derived_hash.keys
@@ -132,7 +135,6 @@ module RuboCop
132
135
  end
133
136
  result
134
137
  end
135
- # rubocop:enable Metrics/AbcSize
136
138
 
137
139
  # An `Enabled: true` setting in user configuration for a cop overrides an
138
140
  # `Enabled: false` setting for its department.
@@ -167,8 +169,47 @@ module RuboCop
167
169
  end
168
170
  end
169
171
 
172
+ # A cop's entry in the default configuration may carry a `Preview` section
173
+ # with the defaults it is expected to adopt in the next major release. Under
174
+ # `Preview` those replace the current defaults. The section is dropped either
175
+ # way, so the resolved configuration only ever shows what is in effect.
176
+ def apply_preview_defaults(default_configuration, preview)
177
+ transform(default_configuration) do |params|
178
+ next params unless params['Preview'].is_a?(Hash)
179
+
180
+ params = params.dup
181
+ preview_params = params.delete('Preview')
182
+ preview ? params.merge(preview_params) : params
183
+ end
184
+ end
185
+
170
186
  private
171
187
 
188
+ def inherit_mode_for_default(config)
189
+ with_preview_exclude_merge(config['inherit_mode'] || {}, config)
190
+ end
191
+
192
+ # Under `Preview`, `Exclude` is merged rather than replaced, so that excluding
193
+ # one directory does not silently drop the excludes it would have inherited -
194
+ # from the default configuration or from a file named in `inherit_from`. An
195
+ # explicit `inherit_mode` still wins, in either direction.
196
+ def with_preview_exclude_merge(mode, config)
197
+ return mode unless preview?(config)
198
+ return mode if Array(mode['override']).include?('Exclude')
199
+ return mode if Array(mode['merge']).include?('Exclude')
200
+
201
+ mode.merge('merge' => Array(mode['merge']) + ['Exclude'])
202
+ end
203
+
204
+ # `config` has already been through `handle_disabled_by_default` by this
205
+ # point, which returns a plain hash, so read `AllCops` directly rather than
206
+ # going through `Config#for_all_cops`.
207
+ def preview?(config)
208
+ return ConfigLoader.preview unless ConfigLoader.preview.nil?
209
+
210
+ (config['AllCops'] || {})['Preview'] == true
211
+ end
212
+
172
213
  def resolve_default_overrides(config)
173
214
  if ConfigLoader.disabled_by_default || ConfigLoader.enabled_by_default
174
215
  [ConfigLoader.disabled_by_default, ConfigLoader.enabled_by_default]
@@ -211,7 +252,7 @@ module RuboCop
211
252
  def determine_inherit_mode(hash, key)
212
253
  cop_cfg = hash[key]
213
254
  local_inherit = cop_cfg['inherit_mode'] if cop_cfg.is_a?(Hash)
214
- local_inherit || hash['inherit_mode'] || {}
255
+ with_preview_exclude_merge(local_inherit || hash['inherit_mode'] || {}, hash)
215
256
  end
216
257
 
217
258
  def should_union?(derived_hash, base_hash, root_mode, key)
@@ -277,7 +318,7 @@ module RuboCop
277
318
  file.is_a?(RemoteConfig)
278
319
  end
279
320
 
280
- def handle_disabled_by_default(config, new_default_configuration)
321
+ def handle_disabled_by_default(config, new_default_configuration, base_defaults)
281
322
  department_config = config.to_hash.reject { |cop| cop.include?('/') }
282
323
  department_config.each do |dept, dept_params|
283
324
  next unless dept_params['Enabled']
@@ -286,7 +327,7 @@ module RuboCop
286
327
  next unless cop.start_with?("#{dept}/")
287
328
 
288
329
  # Retain original default configuration for cops in the department.
289
- params['Enabled'] = ConfigLoader.default_configuration[cop]['Enabled']
330
+ params['Enabled'] = base_defaults[cop]['Enabled']
290
331
  end
291
332
  end
292
333
 
@@ -20,7 +20,8 @@ module RuboCop
20
20
  rule_message + "\n(obsolete configuration found in #{smart_loaded_path}, please update it)"
21
21
  end
22
22
 
23
- # Cop rules currently can only be failures, not warnings
23
+ # Failure by default; rules that accept `severity: warning` say so
24
+ # themselves.
24
25
  def warning?
25
26
  false
26
27
  end
@@ -27,8 +27,16 @@ module RuboCop
27
27
  end
28
28
  end
29
29
 
30
+ def warning?
31
+ severity == 'warning'
32
+ end
33
+
30
34
  private
31
35
 
36
+ def severity
37
+ metadata['severity']
38
+ end
39
+
32
40
  def reason
33
41
  metadata['reason']
34
42
  end
@@ -15,7 +15,7 @@ module RuboCop
15
15
  'changed_parameters' => ChangedParameter,
16
16
  'changed_enforced_styles' => ChangedEnforcedStyles
17
17
  }.freeze
18
- LOAD_RULES_CACHE = {} # rubocop:disable Style/MutableConstant
18
+ LOAD_RULES_CACHE = {} # rubocop:disable Style/MutableConstant -- a cache, written to at runtime
19
19
  private_constant :LOAD_RULES_CACHE
20
20
 
21
21
  attr_reader :rules, :warnings
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # Handles validation of configuration, for example cop names, parameter
5
5
  # names, and Ruby versions.
6
- # rubocop:disable Metrics/ClassLength
6
+ # rubocop:disable-next Metrics/ClassLength
7
7
  class ConfigValidator
8
8
  extend SimpleForwardable
9
9
 
@@ -11,7 +11,7 @@ module RuboCop
11
11
  COMMON_PARAMS = %w[Exclude Include Severity inherit_mode AutoCorrect StyleGuide Details
12
12
  Enabled Reference References Safe SafeAutoCorrect].freeze
13
13
  # @api private
14
- INTERNAL_PARAMS = %w[Description StyleGuide
14
+ INTERNAL_PARAMS = %w[Description StyleGuide Preview
15
15
  VersionAdded VersionChanged VersionRemoved
16
16
  Reference References Safe SafeAutoCorrect].freeze
17
17
  # @api private
@@ -21,7 +21,7 @@ module RuboCop
21
21
 
22
22
  # @api private
23
23
  CONFIG_CHECK_KEYS = %w[Enabled Safe SafeAutoCorrect AutoCorrect References].to_set.freeze
24
- CONFIG_CHECK_DEPARTMENTS = %w[pending override_department].freeze
24
+ CONFIG_CHECK_DEPARTMENTS = %w[pending preview override_department].freeze
25
25
  CONFIG_CHECK_AUTOCORRECTS = %w[always contextual disabled].freeze
26
26
  private_constant :CONFIG_CHECK_KEYS, :CONFIG_CHECK_DEPARTMENTS
27
27
 
@@ -48,7 +48,7 @@ module RuboCop
48
48
 
49
49
  check_obsoletions
50
50
  alert_about_unrecognized_cops(invalid_cop_names)
51
- validate_new_cops_parameter
51
+ validate_all_cops_parameters
52
52
  validate_parameter_names(valid_cop_names)
53
53
  validate_enforced_styles(valid_cop_names)
54
54
  validate_syntax_cop
@@ -170,6 +170,21 @@ module RuboCop
170
170
  validate_department_new_cops_parameters
171
171
  end
172
172
 
173
+ def validate_all_cops_parameters
174
+ validate_new_cops_parameter
175
+ validate_fail_level_parameter
176
+ end
177
+
178
+ def validate_fail_level_parameter
179
+ fail_level = @config.for_all_cops['FailLevel']
180
+ return if fail_level.nil? || Cop::Severity::NAMES.include?(fail_level.to_sym)
181
+
182
+ message = "invalid #{fail_level} for `FailLevel` found in #{smart_loaded_path}\n" \
183
+ "Valid choices are: #{Cop::Severity::NAMES.join(', ')}"
184
+
185
+ raise ValidationError, message
186
+ end
187
+
173
188
  def validate_all_cops_new_cops_parameter
174
189
  new_cop_parameter = @config.for_all_cops['NewCops']
175
190
  return if new_cop_parameter.nil? || NEW_COPS_VALUES.include?(new_cop_parameter)
@@ -324,5 +339,4 @@ module RuboCop
324
339
  "is supposed to be #{supposed_values} and #{Rainbow(value).yellow} is not."
325
340
  end
326
341
  end
327
- # rubocop:enable Metrics/ClassLength
328
342
  end
@@ -12,6 +12,14 @@ module RuboCop
12
12
  autocorrect_requested? && disable_uncorrectable? && autocorrect_enabled?
13
13
  end
14
14
 
15
+ # Whether a correction exists but is skipped because it is unsafe in a
16
+ # safe autocorrect run, while `--disable-uncorrectable` asks for a todo
17
+ # comment on everything this run cannot correct.
18
+ def skipped_unsafe_correction_with_disable_uncorrectable?
19
+ autocorrect_requested? && disable_uncorrectable? &&
20
+ @options.fetch(:safe_autocorrect, false) && !safe_autocorrect?
21
+ end
22
+
15
23
  def autocorrect_requested?
16
24
  @options.fetch(:autocorrect, false)
17
25
  end
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  def hash
47
47
  # Do hashing manually to reduce Array allocations.
48
- department.hash ^ cop_name.hash # rubocop:disable Security/CompoundHash
48
+ department.hash ^ cop_name.hash # rubocop:disable Security/CompoundHash -- hashing manually avoids the Array allocation
49
49
  end
50
50
 
51
51
  def match?(other)