rubocop 1.88.2 → 1.90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +139 -16
  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 +81 -49
  10. data/lib/rubocop/config.rb +76 -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 +39 -3
  14. data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
  15. data/lib/rubocop/cop/badge.rb +8 -0
  16. data/lib/rubocop/cop/base.rb +40 -6
  17. data/lib/rubocop/cop/bundler.rb +19 -0
  18. data/lib/rubocop/cop/corrector.rb +4 -4
  19. data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
  20. data/lib/rubocop/cop/gemspec.rb +22 -0
  21. data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
  22. data/lib/rubocop/cop/generator.rb +4 -0
  23. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -2
  24. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
  25. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  26. data/lib/rubocop/cop/layout/class_structure.rb +108 -26
  27. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
  28. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
  29. data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
  30. data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
  31. data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
  32. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  33. data/lib/rubocop/cop/layout/indentation_width.rb +3 -1
  34. data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
  35. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
  36. data/lib/rubocop/cop/layout/line_length.rb +84 -12
  37. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
  38. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +12 -2
  39. data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
  40. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
  41. data/lib/rubocop/cop/layout/space_around_operators.rb +7 -0
  42. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +31 -34
  43. data/lib/rubocop/cop/layout.rb +114 -0
  44. data/lib/rubocop/cop/lazy_loader.rb +41 -0
  45. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
  46. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
  47. data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
  48. data/lib/rubocop/cop/lint/argument_mismatch.rb +89 -0
  49. data/lib/rubocop/cop/lint/constant_reassignment.rb +18 -21
  50. data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
  51. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -8
  52. data/lib/rubocop/cop/lint/deprecated_reference.rb +194 -0
  53. data/lib/rubocop/cop/lint/duplicate_methods.rb +300 -22
  54. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
  55. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
  56. data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
  57. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
  58. data/lib/rubocop/cop/lint/literal_as_condition.rb +8 -6
  59. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -7
  60. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
  61. data/lib/rubocop/cop/lint/missing_super.rb +31 -1
  62. data/lib/rubocop/cop/lint/name_typo.rb +270 -0
  63. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -4
  64. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +98 -19
  65. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
  66. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
  67. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
  68. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
  69. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
  70. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
  71. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -2
  72. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
  73. data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
  74. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
  75. data/lib/rubocop/cop/lint/to_enum_arguments.rb +38 -3
  76. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
  77. data/lib/rubocop/cop/lint/unused_private_method.rb +234 -0
  78. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
  79. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
  80. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
  81. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  82. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
  83. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -0
  84. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
  85. data/lib/rubocop/cop/lint/void.rb +18 -8
  86. data/lib/rubocop/cop/lint.rb +178 -0
  87. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
  88. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  89. data/lib/rubocop/cop/metrics.rb +31 -0
  90. data/lib/rubocop/cop/migration/department_name.rb +1 -6
  91. data/lib/rubocop/cop/migration.rb +13 -0
  92. data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -5
  93. data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
  94. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
  95. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
  96. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
  97. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -2
  98. data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
  99. data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
  100. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
  101. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +51 -6
  102. data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
  103. data/lib/rubocop/cop/mixin/range_help.rb +1 -2
  104. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
  105. data/lib/rubocop/cop/mixin/statement_modifier.rb +46 -4
  106. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
  107. data/lib/rubocop/cop/mixin.rb +82 -77
  108. data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
  109. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
  110. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -4
  111. data/lib/rubocop/cop/naming/method_name.rb +5 -2
  112. data/lib/rubocop/cop/naming/predicate_prefix.rb +33 -5
  113. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
  114. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  115. data/lib/rubocop/cop/naming.rb +31 -0
  116. data/lib/rubocop/cop/offense.rb +37 -10
  117. data/lib/rubocop/cop/registry.rb +180 -46
  118. data/lib/rubocop/cop/security.rb +19 -0
  119. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
  120. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -6
  121. data/lib/rubocop/cop/style/array_first_last.rb +1 -2
  122. data/lib/rubocop/cop/style/array_intersect.rb +24 -5
  123. data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
  124. data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
  125. data/lib/rubocop/cop/style/case_like_if.rb +2 -4
  126. data/lib/rubocop/cop/style/class_and_module_children.rb +79 -20
  127. data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
  128. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
  129. data/lib/rubocop/cop/style/conditional_assignment.rb +23 -2
  130. data/lib/rubocop/cop/style/constant_visibility.rb +1 -2
  131. data/lib/rubocop/cop/style/copyright.rb +5 -1
  132. data/lib/rubocop/cop/style/directive_scope.rb +171 -0
  133. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +76 -12
  134. data/lib/rubocop/cop/style/documentation.rb +47 -0
  135. data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
  136. data/lib/rubocop/cop/style/endless_method.rb +0 -10
  137. data/lib/rubocop/cop/style/format_string_token.rb +1 -2
  138. data/lib/rubocop/cop/style/guard_clause.rb +1 -2
  139. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
  140. data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
  141. data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
  142. data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
  143. data/lib/rubocop/cop/style/if_unless_modifier.rb +8 -50
  144. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
  145. data/lib/rubocop/cop/style/lambda.rb +4 -0
  146. data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
  147. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +22 -7
  148. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
  149. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +30 -0
  150. data/lib/rubocop/cop/style/multiline_if_then.rb +7 -1
  151. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
  152. data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
  153. data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
  154. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
  155. data/lib/rubocop/cop/style/negative_array_index.rb +3 -3
  156. data/lib/rubocop/cop/style/nil_comparison.rb +1 -2
  157. data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
  158. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
  159. data/lib/rubocop/cop/style/redundant_assignment.rb +12 -2
  160. data/lib/rubocop/cop/style/redundant_condition.rb +2 -4
  161. data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
  162. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +1 -2
  163. data/lib/rubocop/cop/style/redundant_each.rb +1 -2
  164. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
  165. data/lib/rubocop/cop/style/redundant_format.rb +14 -7
  166. data/lib/rubocop/cop/style/redundant_line_continuation.rb +78 -135
  167. data/lib/rubocop/cop/style/redundant_parentheses.rb +65 -67
  168. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
  169. data/lib/rubocop/cop/style/redundant_return.rb +1 -2
  170. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
  171. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
  172. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
  173. data/lib/rubocop/cop/style/safe_navigation.rb +11 -2
  174. data/lib/rubocop/cop/style/sample.rb +11 -6
  175. data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
  176. data/lib/rubocop/cop/style/semicolon.rb +2 -4
  177. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  178. data/lib/rubocop/cop/style/sole_nested_conditional.rb +21 -5
  179. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
  180. data/lib/rubocop/cop/style/static_class.rb +21 -0
  181. data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
  182. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  183. data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
  184. data/lib/rubocop/cop/style/time_now.rb +41 -0
  185. data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
  186. data/lib/rubocop/cop/style.rb +312 -0
  187. data/lib/rubocop/cop/team.rb +59 -6
  188. data/lib/rubocop/cop/util.rb +2 -4
  189. data/lib/rubocop/cop/variable_force/variable.rb +19 -8
  190. data/lib/rubocop/cops_documentation_generator.rb +4 -7
  191. data/lib/rubocop/directive_comment.rb +39 -3
  192. data/lib/rubocop/feature_loader.rb +0 -6
  193. data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
  194. data/lib/rubocop/formatter/html_formatter.rb +2 -4
  195. data/lib/rubocop/formatter/json_formatter.rb +11 -6
  196. data/lib/rubocop/formatter/junit_formatter.rb +26 -9
  197. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
  198. data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
  199. data/lib/rubocop/formatter/tap_formatter.rb +3 -1
  200. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
  201. data/lib/rubocop/lockfile.rb +18 -0
  202. data/lib/rubocop/lsp/diagnostic.rb +4 -8
  203. data/lib/rubocop/lsp/routes.rb +89 -22
  204. data/lib/rubocop/lsp/runtime.rb +4 -0
  205. data/lib/rubocop/lsp/server.rb +4 -0
  206. data/lib/rubocop/lsp/stdin_runner.rb +20 -0
  207. data/lib/rubocop/mcp/server.rb +1 -2
  208. data/lib/rubocop/options.rb +10 -6
  209. data/lib/rubocop/path_util.rb +1 -2
  210. data/lib/rubocop/pending_cops_reporter.rb +1 -6
  211. data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
  212. data/lib/rubocop/project_index_loader.rb +16 -0
  213. data/lib/rubocop/rake_task.rb +1 -2
  214. data/lib/rubocop/result_cache.rb +6 -4
  215. data/lib/rubocop/rspec/expect_offense.rb +2 -4
  216. data/lib/rubocop/rspec/shared_contexts.rb +16 -2
  217. data/lib/rubocop/runner.rb +71 -43
  218. data/lib/rubocop/server/cache.rb +11 -7
  219. data/lib/rubocop/server/cli.rb +2 -4
  220. data/lib/rubocop/server.rb +14 -0
  221. data/lib/rubocop/target_finder.rb +4 -1
  222. data/lib/rubocop/target_ruby.rb +1 -1
  223. data/lib/rubocop/todo_audit.rb +111 -0
  224. data/lib/rubocop/version.rb +3 -5
  225. data/lib/rubocop.rb +20 -626
  226. data/lib/ruby_lsp/rubocop/addon.rb +1 -2
  227. metadata +28 -5
@@ -13,6 +13,11 @@ module RuboCop
13
13
  # a `#:nodoc:` comment next to it. Likewise, `#:nodoc: all` does the
14
14
  # same for all its children.
15
15
  #
16
+ # When `AllCops/UseProjectIndex` is enabled and the `rubydex` gem is
17
+ # installed, a reopened class or module is not reported when any of its
18
+ # other definition sites (in the same or another file) carries a
19
+ # documentation comment.
20
+ #
16
21
  # @example
17
22
  # # bad
18
23
  # class Person
@@ -71,10 +76,15 @@ module RuboCop
71
76
  #
72
77
  class Documentation < Base
73
78
  include DocumentationComment
79
+ include ProjectIndexHelp
74
80
  include RangeHelp
75
81
 
76
82
  MSG = 'Missing top-level documentation comment for `%<type>s %<identifier>s`.'
77
83
 
84
+ # Comments that configure tooling rather than document the code.
85
+ DIRECTIVE_COMMENT_REGEXP = /\A\#\s*(rubocop:|frozen_string_literal:|encoding:|
86
+ shareable_constant_value:|typed:|-\*-)/x.freeze
87
+
78
88
  # @!method constant_definition?(node)
79
89
  def_node_matcher :constant_definition?, '{class module casgn}'
80
90
 
@@ -109,12 +119,49 @@ module RuboCop
109
119
  return if constant_allowed?(node)
110
120
  return if nodoc_self_or_outer_module?(node)
111
121
  return if include_statement_only?(body)
122
+ return if documented_elsewhere?(node)
123
+
124
+ add_documentation_offense(node)
125
+ end
112
126
 
127
+ def add_documentation_offense(node)
113
128
  range = range_between(node.source_range.begin_pos, node.loc.name.end_pos)
114
129
  message = format(MSG, type: node.type, identifier: identifier(node))
115
130
  add_offense(range, message: message)
116
131
  end
117
132
 
133
+ # A reopened class or module is typically documented at a single
134
+ # definition site. When `AllCops/UseProjectIndex` is enabled, the
135
+ # documentation requirement is satisfied by a comment on any other
136
+ # definition of the same class or module.
137
+ def documented_elsewhere?(node)
138
+ return false unless project_index
139
+
140
+ declaration = resolve_constant_in_index(node.identifier)
141
+ return false unless declaration.is_a?(Rubydex::Namespace)
142
+
143
+ declaration.definitions.any? do |definition|
144
+ other_definition?(definition, node) && documenting_comments?(definition)
145
+ end
146
+ rescue StandardError
147
+ false
148
+ end
149
+
150
+ def other_definition?(definition, node)
151
+ location = definition.location
152
+ return false unless location.uri.start_with?(FILE_URI_PREFIX)
153
+
154
+ !same_file?(location.to_file_path, processed_source.file_path) ||
155
+ location.to_display.start_line != node.first_line
156
+ end
157
+
158
+ def documenting_comments?(definition)
159
+ definition.comments.to_a.any? do |comment|
160
+ text = comment.string.strip
161
+ !text.empty? && !DIRECTIVE_COMMENT_REGEXP.match?(text)
162
+ end
163
+ end
164
+
118
165
  def nodoc_self_or_outer_module?(node)
119
166
  nodoc_comment?(node) ||
120
167
  (compact_namespace?(node) && nodoc_comment?(outer_module(node).first))
@@ -42,7 +42,7 @@ module RuboCop
42
42
  MSG = 'Do not use empty `case` condition, instead use an `if` expression.'
43
43
  NOT_SUPPORTED_PARENT_TYPES = %i[return break next send csend yield super].freeze
44
44
 
45
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
45
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
46
46
  def on_case(case_node)
47
47
  if case_node.condition || NOT_SUPPORTED_PARENT_TYPES.include?(case_node.parent&.type)
48
48
  return
@@ -56,7 +56,6 @@ module RuboCop
56
56
 
57
57
  add_offense(case_node.loc.keyword) { |corrector| autocorrect(corrector, case_node) }
58
58
  end
59
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
60
59
 
61
60
  private
62
61
 
@@ -206,16 +206,6 @@ module RuboCop
206
206
  body.each_descendant(:str).any?(&:heredoc?)
207
207
  end
208
208
 
209
- def correct_to_multiline(corrector, node)
210
- replacement = <<~RUBY.strip
211
- def #{receiver(node)}#{node.method_name}#{arguments(node)}
212
- #{node.body.source}
213
- end
214
- RUBY
215
-
216
- corrector.replace(node, replacement)
217
- end
218
-
219
209
  def endless_replacement(node)
220
210
  <<~RUBY.strip
221
211
  def #{receiver(node)}#{node.method_name}#{arguments(node)} = #{node.body.source}
@@ -228,7 +228,7 @@ module RuboCop
228
228
  "Prefer #{message_text(style)} over #{message_text(detected_style)}."
229
229
  end
230
230
 
231
- # rubocop:disable Style/FormatStringToken
231
+ # rubocop:disable-next Style/FormatStringToken
232
232
  def message_text(style)
233
233
  {
234
234
  annotated: 'annotated tokens (like `%<foo>s`)',
@@ -236,7 +236,6 @@ module RuboCop
236
236
  unannotated: 'unannotated tokens (like `%s`)'
237
237
  }[style]
238
238
  end
239
- # rubocop:enable Style/FormatStringToken
240
239
 
241
240
  def tokens(str_node, &block)
242
241
  return if str_node.source == '__FILE__'
@@ -196,7 +196,7 @@ module RuboCop
196
196
  end
197
197
  end
198
198
 
199
- # rubocop:disable Metrics/AbcSize
199
+ # rubocop:disable-next Metrics/AbcSize
200
200
  def autocorrect(corrector, node, condition, replacement, guard)
201
201
  corrector.replace(node.loc.keyword.join(condition.source_range), replacement)
202
202
 
@@ -217,7 +217,6 @@ module RuboCop
217
217
  corrector.remove(range_of_branch_to_remove(node, guard))
218
218
  end
219
219
  end
220
- # rubocop:enable Metrics/AbcSize
221
220
 
222
221
  def heredoc?(argument)
223
222
  argument.respond_to?(:heredoc?) && argument.heredoc?
@@ -76,7 +76,7 @@ module RuboCop
76
76
  alias on_numblock on_block
77
77
  alias on_itblock on_block
78
78
 
79
- # rubocop:disable Metrics/AbcSize
79
+ # rubocop:disable-next Metrics/AbcSize
80
80
  def check_unused_block_args(node, key, value)
81
81
  return if node.body.nil?
82
82
 
@@ -96,7 +96,6 @@ module RuboCop
96
96
  register_each_args_offense(node, message, 'each_value', unused_range)
97
97
  end
98
98
  end
99
- # rubocop:enable Metrics/AbcSize
100
99
 
101
100
  def on_block_pass(node)
102
101
  kv_each_with_block_pass(node.parent) do |target, method|
@@ -217,7 +217,7 @@ module RuboCop
217
217
  acceptable_19_syntax_symbol?(pair.key.source)
218
218
  end
219
219
 
220
- # rubocop:disable Metrics/CyclomaticComplexity
220
+ # rubocop:disable-next Metrics/CyclomaticComplexity
221
221
  def acceptable_19_syntax_symbol?(sym_name)
222
222
  sym_name.delete_prefix!(':')
223
223
 
@@ -237,7 +237,6 @@ module RuboCop
237
237
  (sym_name.start_with?("'") && sym_name.end_with?("'")) ||
238
238
  (sym_name.start_with?('"') && sym_name.end_with?('"'))
239
239
  end
240
- # rubocop:enable Metrics/CyclomaticComplexity
241
240
 
242
241
  def check(pairs, delim, msg)
243
242
  pairs.each do |pair|
@@ -136,7 +136,7 @@ module RuboCop
136
136
 
137
137
  private
138
138
 
139
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
139
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
140
140
  def check_branches(node, branches)
141
141
  # return if any branch is empty. An empty branch can be an `if`
142
142
  # without an `else` or a branch that contains only comments.
@@ -168,7 +168,6 @@ module RuboCop
168
168
 
169
169
  check_expressions(node, heads, :before_condition)
170
170
  end
171
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
172
171
 
173
172
  def duplicated_expressions?(node, expressions)
174
173
  unique_expressions = expressions.uniq
@@ -189,7 +188,7 @@ module RuboCop
189
188
  end
190
189
  end
191
190
 
192
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
191
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
193
192
  def check_expressions(node, expressions, insert_position)
194
193
  return if expressions.any?(&:nil?)
195
194
 
@@ -213,7 +212,6 @@ module RuboCop
213
212
  end
214
213
  end
215
214
  end
216
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
217
215
 
218
216
  def correct_assignment(corrector, node, expression, insert_position)
219
217
  indentation = indentation_of(node.parent)
@@ -64,7 +64,7 @@ module RuboCop
64
64
 
65
65
  MSG = 'Convert `if` nested inside `else` to `elsif`.'
66
66
 
67
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
67
+ # rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
68
68
  def on_if(node)
69
69
  return if node.ternary? || node.unless?
70
70
 
@@ -81,7 +81,6 @@ module RuboCop
81
81
  ignore_node(node)
82
82
  end
83
83
  end
84
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
85
84
 
86
85
  private
87
86
 
@@ -72,10 +72,8 @@ module RuboCop
72
72
  # do_something
73
73
  # end
74
74
  #
75
- class IfUnlessModifier < Base # rubocop:disable Metrics/ClassLength
75
+ class IfUnlessModifier < Base
76
76
  include StatementModifier
77
- include LineLengthHelp
78
- include AllowedPattern
79
77
  include RangeHelp
80
78
  include CommentsHelp
81
79
  extend AutoCorrector
@@ -83,13 +81,17 @@ module RuboCop
83
81
  MSG_USE_MODIFIER = 'Favor modifier `%<keyword>s` usage when having a ' \
84
82
  'single-line body. Another good alternative is ' \
85
83
  'the usage of control flow `&&`/`||`.'
84
+ MSG_USE_MODIFIER_PARENS = 'Favor modifier `%<keyword>s` usage when having a ' \
85
+ 'single-line body. Wrap the expression in parentheses ' \
86
+ 'to keep the current behavior, as it is part of a ' \
87
+ 'larger expression.'
86
88
  MSG_USE_NORMAL = 'Modifier form of `%<keyword>s` makes the line too long.'
87
89
 
88
90
  def self.autocorrect_incompatible_with
89
91
  [Style::Next, Style::SoleNestedConditional]
90
92
  end
91
93
 
92
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
94
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
93
95
  def on_if(node)
94
96
  return if endless_method?(node.body) || node.each_ancestor(:dstr).any?
95
97
 
@@ -107,7 +109,6 @@ module RuboCop
107
109
  ignore_node(node)
108
110
  end
109
111
  end
110
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
111
112
 
112
113
  private
113
114
 
@@ -143,7 +144,7 @@ module RuboCop
143
144
 
144
145
  def message(node)
145
146
  if single_line_as_modifier?(node) && !named_capture_in_condition?(node)
146
- MSG_USE_MODIFIER
147
+ parenthesize?(node) ? MSG_USE_MODIFIER_PARENS : MSG_USE_MODIFIER
147
148
  elsif too_long_due_to_modifier?(node)
148
149
  MSG_USE_NORMAL
149
150
  end
@@ -188,52 +189,9 @@ module RuboCop
188
189
  max_line_length.between?(source_length - comment.source_range.length, source_length)
189
190
  end
190
191
 
191
- def allowed_patterns
192
- line_length_config = config.for_cop('Layout/LineLength')
193
- line_length_config['AllowedPatterns'] || line_length_config['IgnoredPatterns'] || []
194
- end
195
-
196
192
  def too_long_single_line?(node)
197
- return false unless max_line_length
198
-
199
193
  range = node.source_range
200
- return false unless range.single_line?
201
- return false unless line_length_enabled_at_line?(range.first_line)
202
-
203
- line = range.source_line
204
- return false if line_length(line) <= max_line_length
205
-
206
- too_long_line_based_on_config?(range, line)
207
- end
208
-
209
- def too_long_line_based_on_config?(range, line)
210
- return false if matches_allowed_pattern?(line)
211
-
212
- too_long = too_long_line_based_on_allow_cop_directives?(range, line)
213
- return too_long unless too_long == :undetermined
214
-
215
- too_long_line_based_on_allow_uri?(line)
216
- end
217
-
218
- def too_long_line_based_on_allow_cop_directives?(range, line)
219
- if allow_cop_directives? && directive_on_source_line?(range.line - 1)
220
- return line_length_without_directive(line) > max_line_length
221
- end
222
-
223
- :undetermined
224
- end
225
-
226
- def too_long_line_based_on_allow_uri?(line)
227
- if allow_uri?
228
- uri_range = find_excessive_range(line, :uri)
229
- return false if uri_range && allowed_position?(line, uri_range)
230
- end
231
-
232
- true
233
- end
234
-
235
- def line_length_enabled_at_line?(line)
236
- processed_source.comment_config.cop_enabled_at_line?('Layout/LineLength', line)
194
+ range.single_line? && !acceptable_line_length?(range.source_line, range.first_line)
237
195
  end
238
196
 
239
197
  def named_capture_in_condition?(node)
@@ -28,7 +28,7 @@ module RuboCop
28
28
 
29
29
  MSG = 'Avoid modifier `%<keyword>s` after another conditional.'
30
30
 
31
- # rubocop:disable Metrics/AbcSize
31
+ # rubocop:disable-next Metrics/AbcSize
32
32
  def on_if(node)
33
33
  return unless node.modifier_form? && node.body.if_type?
34
34
 
@@ -37,7 +37,6 @@ module RuboCop
37
37
  corrector.remove(node.if_branch.source_range.end.join(node.condition.source_range.end))
38
38
  end
39
39
  end
40
- # rubocop:enable Metrics/AbcSize
41
40
  end
42
41
  end
43
42
  end
@@ -61,6 +61,10 @@ module RuboCop
61
61
  }
62
62
  }.freeze
63
63
 
64
+ def self.autocorrect_incompatible_with
65
+ [Style::SymbolProc]
66
+ end
67
+
64
68
  def on_block(node)
65
69
  return unless node.lambda?
66
70
 
@@ -67,7 +67,7 @@ module RuboCop
67
67
 
68
68
  private
69
69
 
70
- # rubocop:disable Metrics/AbcSize
70
+ # rubocop:disable-next Metrics/AbcSize
71
71
  def autocorrect(corrector, to_h, map)
72
72
  removal_range = range_between(to_h.loc.dot.begin_pos, to_h.loc.selector.end_pos)
73
73
 
@@ -83,7 +83,6 @@ module RuboCop
83
83
  corrector.replace(argument, argument.source[1..-2])
84
84
  end
85
85
  end
86
- # rubocop:enable Metrics/AbcSize
87
86
  end
88
87
  end
89
88
  end
@@ -5,14 +5,29 @@ module RuboCop
5
5
  module Style
6
6
  class MethodCallWithArgsParentheses
7
7
  # Style omit_parentheses
8
- # rubocop:disable Metrics/ModuleLength, Metrics/CyclomaticComplexity
8
+ # rubocop:disable-next Metrics/ModuleLength, Metrics/CyclomaticComplexity
9
9
  module OmitParentheses
10
10
  include RangeHelp
11
+ include ReparsedEquivalence
11
12
 
12
13
  TRAILING_WHITESPACE_REGEX = /\s+\Z/.freeze
13
14
  OMIT_MSG = 'Omit parentheses for method calls with arguments.'
14
15
  private_constant :OMIT_MSG
15
16
 
17
+ def on_investigation_end
18
+ # Each candidate's exact correction is verified by reparsing before
19
+ # the offense is registered, so an omission that would change how
20
+ # the code parses is never reported or offered.
21
+ verified_by_reparse(@pending_omit_offenses || []).each do |node|
22
+ add_offense(offense_range(node), message: OMIT_MSG) do |corrector|
23
+ autocorrect(corrector, node)
24
+ end
25
+ end
26
+ @pending_omit_offenses = []
27
+
28
+ super
29
+ end
30
+
16
31
  private
17
32
 
18
33
  def omit_parentheses(node) # rubocop:disable Metrics/PerceivedComplexity
@@ -26,9 +41,11 @@ module RuboCop
26
41
  return if allowed_camel_case_method_call?(node)
27
42
  return if allowed_string_interpolation_method_call?(node)
28
43
 
29
- add_offense(offense_range(node), message: OMIT_MSG) do |corrector|
30
- autocorrect(corrector, node)
31
- end
44
+ (@pending_omit_offenses ||= []) << node
45
+ end
46
+
47
+ def apply_reparse_correction(corrector, node)
48
+ autocorrect(corrector, node)
32
49
  end
33
50
 
34
51
  def autocorrect(corrector, node)
@@ -133,7 +150,7 @@ module RuboCop
133
150
  node.parent&.class_type? && node.parent.single_line?
134
151
  end
135
152
 
136
- # rubocop:disable Metrics/PerceivedComplexity
153
+ # rubocop:disable-next Metrics/PerceivedComplexity
137
154
  def call_with_ambiguous_arguments?(node)
138
155
  call_with_braced_block?(node) ||
139
156
  call_in_argument_with_block?(node) ||
@@ -146,7 +163,6 @@ module RuboCop
146
163
  ambiguous_literal?(n) || logical_operator?(n)
147
164
  end
148
165
  end
149
- # rubocop:enable Metrics/PerceivedComplexity
150
166
 
151
167
  def call_with_braced_block?(node)
152
168
  node.type?(:call, :super) && node.block_node&.braces?
@@ -252,7 +268,6 @@ module RuboCop
252
268
  last_argument.hash_type? && last_argument.children.any?(&:forwarded_kwrestarg_type?)
253
269
  end
254
270
  end
255
- # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
256
271
  end
257
272
  end
258
273
  end
@@ -33,7 +33,7 @@ module RuboCop
33
33
 
34
34
  MSG = 'Do not use parentheses for method calls with no arguments.'
35
35
 
36
- # rubocop:disable Metrics/CyclomaticComplexity
36
+ # rubocop:disable-next Metrics/CyclomaticComplexity
37
37
  def on_send(node)
38
38
  return unless !node.arguments? && node.parenthesized?
39
39
  return if ineligible_node?(node)
@@ -44,7 +44,6 @@ module RuboCop
44
44
 
45
45
  register_offense(node)
46
46
  end
47
- # rubocop:enable Metrics/CyclomaticComplexity
48
47
  alias on_csend on_send
49
48
 
50
49
  private
@@ -28,6 +28,11 @@ module RuboCop
28
28
  # delegator.upcase # => FOO
29
29
  # ----
30
30
  #
31
+ # When `AllCops/UseProjectIndex` is enabled and the `rubydex` gem is
32
+ # installed, `respond_to_missing?` defined in another definition of the
33
+ # same class or module (e.g. a reopening in another file) also
34
+ # satisfies the check.
35
+ #
31
36
  # @example
32
37
  # # bad
33
38
  # def method_missing(name, *args)
@@ -52,11 +57,14 @@ module RuboCop
52
57
  # end
53
58
  #
54
59
  class MissingRespondToMissing < Base
60
+ include ProjectIndexHelp
61
+
55
62
  MSG = 'When using `method_missing`, define `respond_to_missing?`.'
56
63
 
57
64
  def on_def(node)
58
65
  return unless node.method?(:method_missing)
59
66
  return if implements_respond_to_missing?(node)
67
+ return if respond_to_missing_elsewhere?(node)
60
68
 
61
69
  add_offense(node)
62
70
  end
@@ -64,6 +72,28 @@ module RuboCop
64
72
 
65
73
  private
66
74
 
75
+ # When `AllCops/UseProjectIndex` is enabled, `respond_to_missing?`
76
+ # defined in another definition of the same class or module (e.g. a
77
+ # reopening in another file) also satisfies the check. Ancestors are
78
+ # not consulted: overriding `method_missing` warrants a matching
79
+ # `respond_to_missing?` for the same class.
80
+ def respond_to_missing_elsewhere?(node)
81
+ return false unless project_index
82
+ return false unless (namespace_node = node.each_ancestor(:class, :module).first)
83
+
84
+ declaration = resolve_constant_in_index(namespace_node.identifier)
85
+ return false unless declaration.is_a?(Rubydex::Namespace)
86
+
87
+ scope = singleton_definition?(node) ? indexed_singleton_of(declaration) : declaration
88
+ !scope&.member('respond_to_missing?()').nil?
89
+ rescue StandardError
90
+ false
91
+ end
92
+
93
+ def singleton_definition?(node)
94
+ node.defs_type? || node.each_ancestor(:sclass, :class, :module).first&.sclass_type?
95
+ end
96
+
67
97
  def implements_respond_to_missing?(node)
68
98
  scope = enclosing_scope(node)
69
99
  search_root = scope || node.parent
@@ -36,7 +36,13 @@ module RuboCop
36
36
  private
37
37
 
38
38
  def non_modifier_then?(node)
39
- node.multiline? && node.then? && node.loc.begin.line != node.if_branch&.loc&.line
39
+ node.multiline? && own_then?(node) && node.loc.begin.line != node.if_branch&.loc&.line
40
+ end
41
+
42
+ # Prism gives an `elsif` with no `then` of its own the previous branch's `then` location,
43
+ # which lies outside the node.
44
+ def own_then?(node)
45
+ node.then? && node.source_range.contains?(node.loc.begin)
40
46
  end
41
47
  end
42
48
  end
@@ -38,7 +38,7 @@ module RuboCop
38
38
 
39
39
  private
40
40
 
41
- # rubocop:disable Metrics/AbcSize
41
+ # rubocop:disable-next Metrics/AbcSize
42
42
  def autocorrect(corrector, node, begin_of_arguments)
43
43
  arguments = node.arguments
44
44
  joined_arguments = arguments.map(&:source).join(', ')
@@ -62,7 +62,6 @@ module RuboCop
62
62
  corrector.remove(arguments_range)
63
63
  corrector.insert_after(begin_of_arguments, joined_arguments)
64
64
  end
65
- # rubocop:enable Metrics/AbcSize
66
65
 
67
66
  def last_line_source_of_arguments(arguments)
68
67
  processed_source[arguments.last_line - 1].strip
@@ -70,48 +70,59 @@ module RuboCop
70
70
  (send $_ :== ${lvar call})
71
71
  PATTERN
72
72
 
73
- # rubocop:disable Metrics/AbcSize
74
73
  def on_or(node)
75
- root_of_or_node = root_of_or_node(node)
76
- return unless node == root_of_or_node
74
+ return unless node == root_of_or_node(node)
77
75
  return unless nested_comparison?(node)
78
-
79
- return unless (variable, values = find_offending_var(node))
76
+ return unless (variable, values, skipped = find_offending_var(node))
80
77
  return if values.size < comparisons_threshold
81
78
 
82
79
  range = offense_range(values)
80
+ # Bail when an allowed method comparison sits between the collected
81
+ # values: collapsing them into a single `include?` would drop it.
82
+ return if skipped_within_range?(skipped, range)
83
83
 
84
84
  add_offense(range) do |corrector|
85
- elements = values.map(&:source).join(', ')
86
- argument = variable.lvar_type? ? variable_name(variable) : variable.source
87
- prefer_method = "[#{elements}].include?(#{argument})"
88
-
89
- corrector.replace(range, prefer_method)
85
+ corrector.replace(range, preferred_method(variable, values))
90
86
  end
91
87
  end
92
- # rubocop:enable Metrics/AbcSize
93
88
 
94
89
  private
95
90
 
96
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
97
- def find_offending_var(node, variables = Set.new, values = [])
91
+ def skipped_within_range?(skipped, range)
92
+ skipped.any? { |node_part| range.contains?(node_part.source_range) }
93
+ end
94
+
95
+ def preferred_method(variable, values)
96
+ elements = values.map(&:source).join(', ')
97
+ argument = variable.lvar_type? ? variable_name(variable) : variable.source
98
+ "[#{elements}].include?(#{argument})"
99
+ end
100
+
101
+ def find_offending_var(node, variables = Set.new, values = [], skipped = [])
98
102
  if node.or_type?
99
- find_offending_var(node.lhs, variables, values)
100
- find_offending_var(node.rhs, variables, values)
103
+ find_offending_var(node.lhs, variables, values, skipped)
104
+ find_offending_var(node.rhs, variables, values, skipped)
101
105
  elsif simple_double_comparison?(node)
102
106
  return
103
- elsif (var, obj = simple_comparison(node))
104
- return if allow_method_comparison? && obj.call_type?
107
+ elsif (comparison = simple_comparison(node))
108
+ collect_comparison(node, comparison, variables, values, skipped)
109
+ end
105
110
 
106
- variables << var
107
- return if variables.size > 1
111
+ [variables.first, values, skipped] if variables.any?
112
+ end
108
113
 
109
- values << obj
114
+ def collect_comparison(node, comparison, variables, values, skipped)
115
+ var, obj = comparison
116
+ if allow_method_comparison? && obj.call_type?
117
+ skipped << node
118
+ return
110
119
  end
111
120
 
112
- [variables.first, values] if variables.any?
121
+ variables << var
122
+ return if variables.size > 1
123
+
124
+ values << obj
113
125
  end
114
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
115
126
 
116
127
  def offense_range(values)
117
128
  values.first.parent.source_range.begin.join(values.last.parent.source_range.end)
@@ -89,6 +89,9 @@ module RuboCop
89
89
  # end
90
90
  # end.freeze
91
91
  #
92
+ # # good - `Data.define` declares an immutable value type
93
+ # CONST = Data.define(:foo, :bar)
94
+ #
92
95
  # @example
93
96
  # # Magic comment - shareable_constant_value: literal
94
97
  #
@@ -319,6 +322,8 @@ module RuboCop
319
322
  (const _ _)
320
323
  (send (const {nil? cbase} :Struct) :new ...)
321
324
  (block (send (const {nil? cbase} :Struct) :new ...) ...)
325
+ (send (const {nil? cbase} :Data) :define ...)
326
+ (block (send (const {nil? cbase} :Data) :define ...) ...)
322
327
  (send _ :freeze)
323
328
  (send {float int} {:+ :- :* :** :/ :% :<<} _)
324
329
  (send _ {:+ :- :* :** :/ :%} {float int})