rubocop 1.67.0 → 1.76.1

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 (488) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +22 -16
  4. data/config/default.yml +315 -53
  5. data/config/internal_affairs.yml +20 -0
  6. data/config/obsoletion.yml +8 -3
  7. data/lib/rubocop/cached_data.rb +12 -4
  8. data/lib/rubocop/cli/command/execute_runner.rb +4 -4
  9. data/lib/rubocop/cli/command/show_cops.rb +24 -2
  10. data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
  11. data/lib/rubocop/cli/command/version.rb +2 -2
  12. data/lib/rubocop/cli.rb +1 -1
  13. data/lib/rubocop/comment_config.rb +2 -2
  14. data/lib/rubocop/config.rb +52 -10
  15. data/lib/rubocop/config_loader.rb +52 -9
  16. data/lib/rubocop/config_loader_resolver.rb +36 -10
  17. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -3
  18. data/lib/rubocop/config_obsoletion/renamed_cop.rb +18 -3
  19. data/lib/rubocop/config_obsoletion.rb +46 -2
  20. data/lib/rubocop/config_validator.rb +25 -14
  21. data/lib/rubocop/cop/autocorrect_logic.rb +51 -26
  22. data/lib/rubocop/cop/base.rb +7 -1
  23. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
  24. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  25. data/lib/rubocop/cop/bundler/gem_filename.rb +0 -1
  26. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +0 -1
  27. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  28. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
  29. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +1 -1
  30. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
  31. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -2
  32. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +50 -6
  33. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  34. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +0 -2
  35. data/lib/rubocop/cop/generator.rb +6 -0
  36. data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
  37. data/lib/rubocop/cop/internal_affairs/example_description.rb +8 -4
  38. data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
  39. data/lib/rubocop/cop/internal_affairs/location_expression.rb +2 -1
  40. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -4
  41. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +3 -2
  42. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  43. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +63 -0
  44. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +131 -0
  45. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +231 -0
  46. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +91 -0
  47. data/lib/rubocop/cop/internal_affairs/node_type_multiple_predicates.rb +126 -0
  48. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
  49. data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +1 -1
  50. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +90 -0
  51. data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +48 -0
  52. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  53. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
  54. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +3 -1
  55. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
  56. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +0 -2
  57. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +13 -2
  58. data/lib/rubocop/cop/internal_affairs.rb +7 -16
  59. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +1 -1
  60. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -9
  61. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  62. data/lib/rubocop/cop/layout/begin_end_alignment.rb +0 -1
  63. data/lib/rubocop/cop/layout/block_alignment.rb +3 -2
  64. data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
  65. data/lib/rubocop/cop/layout/class_structure.rb +44 -9
  66. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +4 -4
  67. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  68. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  69. data/lib/rubocop/cop/layout/else_alignment.rb +2 -2
  70. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -3
  71. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +7 -11
  72. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +36 -6
  73. data/lib/rubocop/cop/layout/empty_lines_around_begin_body.rb +5 -6
  74. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -0
  75. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +4 -5
  76. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +23 -1
  77. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  78. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  79. data/lib/rubocop/cop/layout/first_argument_indentation.rb +4 -9
  80. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
  81. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -7
  82. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +1 -1
  83. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +2 -2
  84. data/lib/rubocop/cop/layout/hash_alignment.rb +8 -6
  85. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -1
  86. data/lib/rubocop/cop/layout/indentation_width.rb +8 -7
  87. data/lib/rubocop/cop/layout/leading_comment_space.rb +57 -2
  88. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +11 -2
  89. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  90. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -2
  91. data/lib/rubocop/cop/layout/line_length.rb +123 -4
  92. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -0
  93. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +1 -1
  94. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +25 -0
  95. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +2 -1
  96. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +4 -4
  97. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
  98. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +1 -0
  99. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +3 -4
  100. data/lib/rubocop/cop/layout/parameter_alignment.rb +3 -4
  101. data/lib/rubocop/cop/layout/redundant_line_break.rb +19 -46
  102. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +6 -7
  103. data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -1
  104. data/lib/rubocop/cop/layout/space_after_colon.rb +2 -2
  105. data/lib/rubocop/cop/layout/space_after_comma.rb +1 -1
  106. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
  107. data/lib/rubocop/cop/layout/space_after_semicolon.rb +11 -1
  108. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -1
  109. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  110. data/lib/rubocop/cop/layout/space_around_operators.rb +23 -21
  111. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -0
  112. data/lib/rubocop/cop/layout/space_before_brackets.rb +8 -34
  113. data/lib/rubocop/cop/layout/space_before_comma.rb +1 -1
  114. data/lib/rubocop/cop/layout/space_before_semicolon.rb +1 -1
  115. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +11 -1
  116. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +5 -0
  117. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +7 -0
  118. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +0 -1
  119. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
  120. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  121. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +118 -0
  122. data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -3
  123. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +10 -12
  124. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  125. data/lib/rubocop/cop/lint/circular_argument_reference.rb +4 -1
  126. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +3 -3
  127. data/lib/rubocop/cop/lint/constant_reassignment.rb +148 -0
  128. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
  129. data/lib/rubocop/cop/lint/debugger.rb +3 -3
  130. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  131. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +3 -2
  132. data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
  133. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +1 -1
  134. data/lib/rubocop/cop/lint/duplicate_methods.rb +86 -19
  135. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +1 -1
  136. data/lib/rubocop/cop/lint/duplicate_set_element.rb +20 -7
  137. data/lib/rubocop/cop/lint/empty_conditional_body.rb +14 -64
  138. data/lib/rubocop/cop/lint/empty_ensure.rb +1 -1
  139. data/lib/rubocop/cop/lint/empty_expression.rb +0 -2
  140. data/lib/rubocop/cop/lint/empty_file.rb +0 -2
  141. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  142. data/lib/rubocop/cop/lint/ensure_return.rb +1 -1
  143. data/lib/rubocop/cop/lint/erb_new_arguments.rb +0 -6
  144. data/lib/rubocop/cop/lint/float_comparison.rb +47 -14
  145. data/lib/rubocop/cop/lint/float_out_of_range.rb +2 -4
  146. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +2 -2
  147. data/lib/rubocop/cop/lint/hash_new_with_keyword_arguments_as_default.rb +55 -0
  148. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  149. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
  150. data/lib/rubocop/cop/lint/interpolation_check.rb +9 -0
  151. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +3 -0
  152. data/lib/rubocop/cop/lint/literal_as_condition.rb +110 -9
  153. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +1 -1
  154. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +24 -6
  155. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
  156. data/lib/rubocop/cop/lint/missing_super.rb +2 -2
  157. data/lib/rubocop/cop/lint/mixed_case_range.rb +5 -8
  158. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -1
  159. data/lib/rubocop/cop/lint/nested_method_definition.rb +10 -6
  160. data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -1
  161. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +2 -2
  162. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +12 -3
  163. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -3
  164. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -3
  165. data/lib/rubocop/cop/lint/number_conversion.rb +0 -1
  166. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -2
  167. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +93 -0
  168. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +2 -3
  169. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +3 -2
  170. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -5
  171. data/lib/rubocop/cop/lint/raise_exception.rb +29 -10
  172. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  173. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  174. data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
  175. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +12 -7
  176. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -7
  177. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +2 -2
  178. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +261 -0
  179. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
  180. data/lib/rubocop/cop/lint/redundant_with_object.rb +3 -0
  181. data/lib/rubocop/cop/lint/refinement_import_methods.rb +1 -1
  182. data/lib/rubocop/cop/lint/regexp_as_condition.rb +0 -1
  183. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
  184. data/lib/rubocop/cop/lint/rescue_type.rb +3 -7
  185. data/lib/rubocop/cop/lint/return_in_void_context.rb +9 -11
  186. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -1
  187. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +5 -1
  188. data/lib/rubocop/cop/lint/self_assignment.rb +8 -10
  189. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  190. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -1
  191. data/lib/rubocop/cop/lint/shared_mutable_default.rb +76 -0
  192. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  193. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
  194. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  195. data/lib/rubocop/cop/lint/syntax.rb +4 -1
  196. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -1
  197. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  198. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
  199. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +3 -1
  200. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -1
  201. data/lib/rubocop/cop/lint/unreachable_code.rb +52 -2
  202. data/lib/rubocop/cop/lint/unreachable_loop.rb +6 -6
  203. data/lib/rubocop/cop/lint/unused_method_argument.rb +18 -2
  204. data/lib/rubocop/cop/lint/useless_access_modifier.rb +5 -4
  205. data/lib/rubocop/cop/lint/useless_assignment.rb +3 -1
  206. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +71 -0
  207. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +87 -0
  208. data/lib/rubocop/cop/lint/useless_defined.rb +55 -0
  209. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +4 -0
  210. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  211. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +2 -1
  212. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  213. data/lib/rubocop/cop/lint/useless_rescue.rb +2 -2
  214. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -2
  215. data/lib/rubocop/cop/lint/useless_setter_call.rb +14 -25
  216. data/lib/rubocop/cop/lint/void.rb +16 -12
  217. data/lib/rubocop/cop/message_annotator.rb +7 -3
  218. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  219. data/lib/rubocop/cop/metrics/block_length.rb +1 -0
  220. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  221. data/lib/rubocop/cop/metrics/class_length.rb +9 -9
  222. data/lib/rubocop/cop/metrics/collection_literal_length.rb +7 -0
  223. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +5 -2
  224. data/lib/rubocop/cop/metrics/method_length.rb +9 -1
  225. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  226. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  227. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  228. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +3 -4
  229. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  230. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  231. data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
  232. data/lib/rubocop/cop/mixin/check_assignment.rb +4 -12
  233. data/lib/rubocop/cop/mixin/check_line_breakable.rb +22 -12
  234. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +49 -0
  235. data/lib/rubocop/cop/mixin/comments_help.rb +8 -3
  236. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  237. data/lib/rubocop/cop/mixin/dig_help.rb +27 -0
  238. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  239. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
  240. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +20 -0
  241. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +16 -0
  242. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +3 -2
  243. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
  244. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +22 -22
  245. data/lib/rubocop/cop/mixin/hash_subset.rb +203 -0
  246. data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
  247. data/lib/rubocop/cop/mixin/line_length_help.rb +5 -4
  248. data/lib/rubocop/cop/mixin/method_complexity.rb +2 -1
  249. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +7 -9
  250. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  251. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  252. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +68 -30
  253. data/lib/rubocop/cop/mixin/range_help.rb +15 -4
  254. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  255. data/lib/rubocop/cop/mixin/statement_modifier.rb +8 -3
  256. data/lib/rubocop/cop/mixin/string_help.rb +2 -2
  257. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  258. data/lib/rubocop/cop/mixin/target_ruby_version.rb +17 -1
  259. data/lib/rubocop/cop/mixin/trailing_comma.rb +21 -5
  260. data/lib/rubocop/cop/naming/accessor_method_name.rb +6 -6
  261. data/lib/rubocop/cop/naming/block_forwarding.rb +20 -16
  262. data/lib/rubocop/cop/naming/constant_name.rb +6 -7
  263. data/lib/rubocop/cop/naming/file_name.rb +0 -2
  264. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +12 -13
  265. data/lib/rubocop/cop/naming/method_name.rb +64 -8
  266. data/lib/rubocop/cop/naming/predicate_method.rb +216 -0
  267. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +46 -2
  268. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +6 -14
  269. data/lib/rubocop/cop/naming/variable_name.rb +50 -6
  270. data/lib/rubocop/cop/naming/variable_number.rb +2 -3
  271. data/lib/rubocop/cop/offense.rb +2 -3
  272. data/lib/rubocop/cop/registry.rb +9 -6
  273. data/lib/rubocop/cop/security/compound_hash.rb +2 -0
  274. data/lib/rubocop/cop/security/yaml_load.rb +3 -2
  275. data/lib/rubocop/cop/style/access_modifier_declarations.rb +114 -34
  276. data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
  277. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
  278. data/lib/rubocop/cop/style/and_or.rb +1 -1
  279. data/lib/rubocop/cop/style/arguments_forwarding.rb +47 -28
  280. data/lib/rubocop/cop/style/array_first_last.rb +18 -2
  281. data/lib/rubocop/cop/style/array_intersect.rb +42 -30
  282. data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
  283. data/lib/rubocop/cop/style/block_delimiters.rb +43 -25
  284. data/lib/rubocop/cop/style/case_like_if.rb +8 -11
  285. data/lib/rubocop/cop/style/class_and_module_children.rb +52 -11
  286. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  287. data/lib/rubocop/cop/style/collection_methods.rb +2 -1
  288. data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
  289. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  290. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  291. data/lib/rubocop/cop/style/commented_keyword.rb +20 -3
  292. data/lib/rubocop/cop/style/comparable_between.rb +78 -0
  293. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
  294. data/lib/rubocop/cop/style/conditional_assignment.rb +39 -27
  295. data/lib/rubocop/cop/style/constant_visibility.rb +3 -12
  296. data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
  297. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  298. data/lib/rubocop/cop/style/dig_chain.rb +89 -0
  299. data/lib/rubocop/cop/style/documentation.rb +1 -1
  300. data/lib/rubocop/cop/style/double_negation.rb +4 -4
  301. data/lib/rubocop/cop/style/each_for_simple_loop.rb +4 -7
  302. data/lib/rubocop/cop/style/each_with_object.rb +2 -3
  303. data/lib/rubocop/cop/style/empty_else.rb +4 -2
  304. data/lib/rubocop/cop/style/empty_literal.rb +5 -1
  305. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  306. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  307. data/lib/rubocop/cop/style/endless_method.rb +150 -18
  308. data/lib/rubocop/cop/style/eval_with_location.rb +4 -4
  309. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -3
  310. data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -7
  311. data/lib/rubocop/cop/style/explicit_block_argument.rb +16 -3
  312. data/lib/rubocop/cop/style/exponential_notation.rb +3 -3
  313. data/lib/rubocop/cop/style/fetch_env_var.rb +2 -1
  314. data/lib/rubocop/cop/style/file_null.rb +89 -0
  315. data/lib/rubocop/cop/style/file_touch.rb +75 -0
  316. data/lib/rubocop/cop/style/float_division.rb +8 -4
  317. data/lib/rubocop/cop/style/for.rb +1 -1
  318. data/lib/rubocop/cop/style/format_string_token.rb +38 -11
  319. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -2
  320. data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
  321. data/lib/rubocop/cop/style/global_vars.rb +1 -3
  322. data/lib/rubocop/cop/style/guard_clause.rb +17 -3
  323. data/lib/rubocop/cop/style/hash_conversion.rb +1 -2
  324. data/lib/rubocop/cop/style/hash_each_methods.rb +6 -8
  325. data/lib/rubocop/cop/style/hash_except.rb +35 -147
  326. data/lib/rubocop/cop/style/hash_fetch_chain.rb +104 -0
  327. data/lib/rubocop/cop/style/hash_slice.rb +80 -0
  328. data/lib/rubocop/cop/style/hash_syntax.rb +9 -3
  329. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  330. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  331. data/lib/rubocop/cop/style/identical_conditional_branches.rb +25 -6
  332. data/lib/rubocop/cop/style/if_inside_else.rb +10 -14
  333. data/lib/rubocop/cop/style/if_unless_modifier.rb +25 -7
  334. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
  335. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +3 -4
  336. data/lib/rubocop/cop/style/if_with_semicolon.rb +20 -9
  337. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  338. data/lib/rubocop/cop/style/inverse_methods.rb +15 -12
  339. data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
  340. data/lib/rubocop/cop/style/ip_addresses.rb +2 -2
  341. data/lib/rubocop/cop/style/it_assignment.rb +36 -0
  342. data/lib/rubocop/cop/style/it_block_parameter.rb +119 -0
  343. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
  344. data/lib/rubocop/cop/style/keyword_parameters_order.rb +14 -8
  345. data/lib/rubocop/cop/style/lambda.rb +1 -0
  346. data/lib/rubocop/cop/style/lambda_call.rb +10 -4
  347. data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
  348. data/lib/rubocop/cop/style/map_into_array.rb +11 -3
  349. data/lib/rubocop/cop/style/map_to_hash.rb +12 -1
  350. data/lib/rubocop/cop/style/map_to_set.rb +3 -2
  351. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +27 -17
  352. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -0
  353. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +8 -11
  354. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +3 -4
  355. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  356. data/lib/rubocop/cop/style/missing_else.rb +2 -0
  357. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +33 -3
  358. data/lib/rubocop/cop/style/multiline_block_chain.rb +3 -2
  359. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
  360. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  361. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -9
  362. data/lib/rubocop/cop/style/multiple_comparison.rb +52 -51
  363. data/lib/rubocop/cop/style/mutable_constant.rb +7 -8
  364. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -5
  365. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  366. data/lib/rubocop/cop/style/nested_ternary_operator.rb +5 -4
  367. data/lib/rubocop/cop/style/next.rb +44 -0
  368. data/lib/rubocop/cop/style/not.rb +1 -1
  369. data/lib/rubocop/cop/style/object_then.rb +15 -15
  370. data/lib/rubocop/cop/style/one_line_conditional.rb +25 -4
  371. data/lib/rubocop/cop/style/open_struct_use.rb +5 -5
  372. data/lib/rubocop/cop/style/operator_method_call.rb +5 -6
  373. data/lib/rubocop/cop/style/or_assignment.rb +3 -6
  374. data/lib/rubocop/cop/style/parallel_assignment.rb +9 -18
  375. data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
  376. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  377. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  378. data/lib/rubocop/cop/style/proc.rb +2 -2
  379. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  380. data/lib/rubocop/cop/style/raise_args.rb +15 -13
  381. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  382. data/lib/rubocop/cop/style/redundant_argument.rb +3 -1
  383. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  384. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -1
  385. data/lib/rubocop/cop/style/redundant_begin.rb +2 -1
  386. data/lib/rubocop/cop/style/redundant_condition.rb +95 -23
  387. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +16 -5
  388. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -10
  389. data/lib/rubocop/cop/style/redundant_each.rb +1 -1
  390. data/lib/rubocop/cop/style/redundant_exception.rb +2 -2
  391. data/lib/rubocop/cop/style/redundant_format.rb +262 -0
  392. data/lib/rubocop/cop/style/redundant_freeze.rb +3 -3
  393. data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
  394. data/lib/rubocop/cop/style/redundant_line_continuation.rb +54 -18
  395. data/lib/rubocop/cop/style/redundant_parentheses.rb +68 -26
  396. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
  397. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
  398. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  399. data/lib/rubocop/cop/style/redundant_return.rb +2 -2
  400. data/lib/rubocop/cop/style/redundant_self.rb +9 -15
  401. data/lib/rubocop/cop/style/redundant_self_assignment.rb +20 -32
  402. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +4 -4
  403. data/lib/rubocop/cop/style/redundant_sort.rb +3 -3
  404. data/lib/rubocop/cop/style/redundant_sort_by.rb +17 -1
  405. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -2
  406. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  407. data/lib/rubocop/cop/style/rescue_modifier.rb +5 -3
  408. data/lib/rubocop/cop/style/return_nil.rb +2 -2
  409. data/lib/rubocop/cop/style/safe_navigation.rb +56 -16
  410. data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
  411. data/lib/rubocop/cop/style/select_by_regexp.rb +5 -2
  412. data/lib/rubocop/cop/style/self_assignment.rb +11 -17
  413. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  414. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
  415. data/lib/rubocop/cop/style/signal_exception.rb +2 -3
  416. data/lib/rubocop/cop/style/single_argument_dig.rb +9 -5
  417. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  418. data/lib/rubocop/cop/style/single_line_do_end_block.rb +15 -4
  419. data/lib/rubocop/cop/style/single_line_methods.rb +6 -7
  420. data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
  421. data/lib/rubocop/cop/style/sole_nested_conditional.rb +42 -106
  422. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  423. data/lib/rubocop/cop/style/string_concatenation.rb +15 -15
  424. data/lib/rubocop/cop/style/string_literals.rb +1 -1
  425. data/lib/rubocop/cop/style/string_methods.rb +1 -1
  426. data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
  427. data/lib/rubocop/cop/style/super_arguments.rb +66 -19
  428. data/lib/rubocop/cop/style/swap_values.rb +4 -15
  429. data/lib/rubocop/cop/style/symbol_proc.rb +2 -0
  430. data/lib/rubocop/cop/style/ternary_parentheses.rb +25 -4
  431. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -1
  432. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +11 -2
  433. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
  434. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
  435. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +4 -4
  436. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  437. data/lib/rubocop/cop/style/variable_interpolation.rb +1 -2
  438. data/lib/rubocop/cop/style/while_until_modifier.rb +0 -1
  439. data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
  440. data/lib/rubocop/cop/style/yoda_expression.rb +2 -1
  441. data/lib/rubocop/cop/team.rb +1 -1
  442. data/lib/rubocop/cop/util.rb +12 -5
  443. data/lib/rubocop/cop/utils/format_string.rb +10 -5
  444. data/lib/rubocop/cop/variable_force/assignment.rb +24 -5
  445. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  446. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  447. data/lib/rubocop/cop/variable_force/variable.rb +14 -3
  448. data/lib/rubocop/cop/variable_force/variable_table.rb +5 -5
  449. data/lib/rubocop/cop/variable_force.rb +5 -11
  450. data/lib/rubocop/cops_documentation_generator.rb +50 -25
  451. data/lib/rubocop/directive_comment.rb +45 -11
  452. data/lib/rubocop/ext/regexp_node.rb +0 -1
  453. data/lib/rubocop/formatter/disabled_config_formatter.rb +3 -2
  454. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  455. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  456. data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
  457. data/lib/rubocop/lsp/diagnostic.rb +189 -0
  458. data/lib/rubocop/lsp/logger.rb +2 -2
  459. data/lib/rubocop/lsp/routes.rb +7 -23
  460. data/lib/rubocop/lsp/runtime.rb +18 -50
  461. data/lib/rubocop/lsp/server.rb +0 -2
  462. data/lib/rubocop/lsp/stdin_runner.rb +85 -0
  463. data/lib/rubocop/magic_comment.rb +11 -3
  464. data/lib/rubocop/options.rb +28 -12
  465. data/lib/rubocop/path_util.rb +15 -8
  466. data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
  467. data/lib/rubocop/plugin/load_error.rb +26 -0
  468. data/lib/rubocop/plugin/loader.rb +100 -0
  469. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  470. data/lib/rubocop/plugin.rb +46 -0
  471. data/lib/rubocop/rake_task.rb +4 -1
  472. data/lib/rubocop/result_cache.rb +13 -13
  473. data/lib/rubocop/rspec/cop_helper.rb +13 -1
  474. data/lib/rubocop/rspec/expect_offense.rb +15 -5
  475. data/lib/rubocop/rspec/shared_contexts.rb +38 -1
  476. data/lib/rubocop/rspec/support.rb +4 -2
  477. data/lib/rubocop/runner.rb +26 -15
  478. data/lib/rubocop/server/cache.rb +47 -11
  479. data/lib/rubocop/server/cli.rb +2 -2
  480. data/lib/rubocop/target_finder.rb +7 -2
  481. data/lib/rubocop/target_ruby.rb +17 -2
  482. data/lib/rubocop/version.rb +53 -12
  483. data/lib/rubocop.rb +38 -2
  484. data/lib/ruby_lsp/rubocop/addon.rb +75 -0
  485. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +65 -0
  486. metadata +85 -21
  487. data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
  488. data/lib/rubocop/rspec/host_environment_simulation_helper.rb +0 -28
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Use `File::NULL` instead of hardcoding the null device (`/dev/null` on Unix-like
7
+ # OSes, `NUL` or `NUL:` on Windows), so that code is platform independent.
8
+ # Only looks for full string matches, substrings within a longer string are not
9
+ # considered.
10
+ #
11
+ # However, only files that use the string `'/dev/null'` are targeted for detection.
12
+ # This is because the string `'NUL'` is not limited to the null device.
13
+ # This behavior results in false negatives when the `'/dev/null'` string is not used,
14
+ # but it is a trade-off to avoid false positives. `NULL:`
15
+ # Unlike `'NUL'`, `'NUL:'` is regarded as something like `C:` and is always detected.
16
+ #
17
+ # NOTE: Uses inside arrays and hashes are ignored.
18
+ #
19
+ # @safety
20
+ # It is possible for a string value to be changed if code is being run
21
+ # on multiple platforms and was previously hardcoded to a specific null device.
22
+ #
23
+ # For example, the following string will change on Windows when changed to
24
+ # `File::NULL`:
25
+ #
26
+ # [source,ruby]
27
+ # ----
28
+ # path = "/dev/null"
29
+ # ----
30
+ #
31
+ # @example
32
+ # # bad
33
+ # '/dev/null'
34
+ # 'NUL'
35
+ # 'NUL:'
36
+ #
37
+ # # good
38
+ # File::NULL
39
+ #
40
+ # # ok - inside an array
41
+ # null_devices = %w[/dev/null nul]
42
+ #
43
+ # # ok - inside a hash
44
+ # { unix: "/dev/null", windows: "nul" }
45
+ class FileNull < Base
46
+ extend AutoCorrector
47
+
48
+ REGEXP = %r{\A(/dev/null|NUL:?)\z}i.freeze
49
+ MSG = 'Use `File::NULL` instead of `%<source>s`.'
50
+
51
+ def on_new_investigation
52
+ return unless (ast = processed_source.ast)
53
+
54
+ @contain_dev_null_string_in_file = ast.each_descendant(:str).any? do |str|
55
+ content = str.str_content
56
+
57
+ valid_string?(content) && content.downcase == '/dev/null' # rubocop:disable Style/FileNull
58
+ end
59
+ end
60
+
61
+ def on_str(node)
62
+ value = node.value
63
+ return unless valid_string?(value)
64
+ return if acceptable?(node)
65
+ return if value.downcase == 'nul' && !@contain_dev_null_string_in_file # rubocop:disable Style/FileNull
66
+ return unless REGEXP.match?(value)
67
+
68
+ add_offense(node, message: format(MSG, source: value)) do |corrector|
69
+ corrector.replace(node, 'File::NULL')
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def valid_string?(value)
76
+ !value.empty? && value.valid_encoding?
77
+ end
78
+
79
+ def acceptable?(node)
80
+ # Using a hardcoded null device is acceptable when inside an array or
81
+ # inside a hash to ensure behavior doesn't change.
82
+ return false unless node.parent
83
+
84
+ node.parent.type?(:array, :pair)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for usage of `File.open` in append mode with empty block.
7
+ #
8
+ # Such a usage only creates a new file, but it doesn't update
9
+ # timestamps for an existing file, which might have been the intention.
10
+ #
11
+ # For example, for an existing file `foo.txt`:
12
+ #
13
+ # ruby -e "puts File.mtime('foo.txt')"
14
+ # # 2024-11-26 12:17:23 +0100
15
+ #
16
+ # ruby -e "File.open('foo.txt', 'a') {}"
17
+ #
18
+ # ruby -e "puts File.mtime('foo.txt')"
19
+ # # 2024-11-26 12:17:23 +0100 -> unchanged
20
+ #
21
+ # If the intention was to update timestamps, `FileUtils.touch('foo.txt')`
22
+ # should be used instead.
23
+ #
24
+ # @safety
25
+ # Autocorrection is unsafe for this cop because unlike `File.open`,
26
+ # `FileUtils.touch` updates an existing file's timestamps.
27
+ #
28
+ # @example
29
+ # # bad
30
+ # File.open(filename, 'a') {}
31
+ # File.open(filename, 'a+') {}
32
+ #
33
+ # # good
34
+ # FileUtils.touch(filename)
35
+ #
36
+ class FileTouch < Base
37
+ extend AutoCorrector
38
+
39
+ MSG = 'Use `FileUtils.touch(%<argument>s)` instead of `File.open` in ' \
40
+ 'append mode with empty block.'
41
+
42
+ RESTRICT_ON_SEND = %i[open].freeze
43
+
44
+ APPEND_FILE_MODES = %w[a a+ ab a+b at a+t].to_set.freeze
45
+
46
+ # @!method file_open?(node)
47
+ def_node_matcher :file_open?, <<~PATTERN
48
+ (send
49
+ (const {nil? cbase} :File) :open
50
+ $(...)
51
+ (str %APPEND_FILE_MODES))
52
+ PATTERN
53
+
54
+ def on_send(node)
55
+ filename = file_open?(node)
56
+ parent = node.parent
57
+
58
+ return unless filename
59
+ return unless parent && empty_block?(parent)
60
+
61
+ message = format(MSG, argument: filename.source)
62
+ add_offense(parent, message: message) do |corrector|
63
+ corrector.replace(parent, "FileUtils.touch(#{filename.source})")
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def empty_block?(node)
70
+ node.block_type? && !node.body
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -65,19 +65,23 @@ module RuboCop
65
65
 
66
66
  # @!method right_coerce?(node)
67
67
  def_node_matcher :right_coerce?, <<~PATTERN
68
- (send _ :/ (send _ :to_f))
68
+ (send _ :/ #to_f_method?)
69
69
  PATTERN
70
70
  # @!method left_coerce?(node)
71
71
  def_node_matcher :left_coerce?, <<~PATTERN
72
- (send (send _ :to_f) :/ _)
72
+ (send #to_f_method? :/ _)
73
73
  PATTERN
74
74
  # @!method both_coerce?(node)
75
75
  def_node_matcher :both_coerce?, <<~PATTERN
76
- (send (send _ :to_f) :/ (send _ :to_f))
76
+ (send #to_f_method? :/ #to_f_method?)
77
77
  PATTERN
78
78
  # @!method any_coerce?(node)
79
79
  def_node_matcher :any_coerce?, <<~PATTERN
80
- {(send _ :/ (send _ :to_f)) (send (send _ :to_f) :/ _)}
80
+ {(send _ :/ #to_f_method?) (send #to_f_method? :/ _)}
81
+ PATTERN
82
+ # @!method to_f_method?(node)
83
+ def_node_matcher :to_f_method?, <<~PATTERN
84
+ (send !nil? :to_f)
81
85
  PATTERN
82
86
 
83
87
  def on_send(node)
@@ -44,7 +44,6 @@ module RuboCop
44
44
  #
45
45
  class For < Base
46
46
  include ConfigurableEnforcedStyle
47
- include RangeHelp
48
47
  extend AutoCorrector
49
48
 
50
49
  EACH_LENGTH = 'each'.length
@@ -78,6 +77,7 @@ module RuboCop
78
77
  end
79
78
 
80
79
  alias on_numblock on_block
80
+ alias on_itblock on_block
81
81
 
82
82
  private
83
83
 
@@ -3,16 +3,24 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Use a consistent style for named format string tokens.
6
+ # Use a consistent style for tokens within a format string.
7
7
  #
8
- # NOTE: `unannotated` style cop only works for strings
9
- # which are passed as arguments to those methods:
10
- # `printf`, `sprintf`, `format`, `%`.
11
- # The reason is that _unannotated_ format is very similar
12
- # to encoded URLs or Date/Time formatting strings.
8
+ # By default, all strings are evaluated. In some cases, this may be undesirable,
9
+ # as they could be used as arguments to a method that does not consider
10
+ # them to be tokens, but rather other identifiers or just part of the string.
13
11
  #
14
- # This cop's allowed methods can be customized with `AllowedMethods`.
15
- # By default, there are no allowed methods.
12
+ # `AllowedMethods` or `AllowedPatterns` can be configured with in order to mark specific
13
+ # methods as always allowed, thereby avoiding an offense from the cop. By default, there
14
+ # are no allowed methods.
15
+ #
16
+ # Additionally, the cop can be made conservative by configuring it with
17
+ # `Mode: conservative` (default `aggressive`). In this mode, tokens (regardless
18
+ # of `EnforcedStyle`) are only considered if used in the format string argument to the
19
+ # methods `printf`, `sprintf`, `format` and `%`.
20
+ #
21
+ # NOTE: Tokens in the `unannotated` style (eg. `%s`) are always treated as if
22
+ # configured with `Conservative: true`. This is done in order to prevent false positives,
23
+ # because this format is very similar to encoded URLs or Date/Time formatting strings.
16
24
  #
17
25
  # @example EnforcedStyle: annotated (default)
18
26
  #
@@ -82,6 +90,20 @@ module RuboCop
82
90
  # # good
83
91
  # redirect('foo/%{bar_id}')
84
92
  #
93
+ # @example Mode: conservative, EnforcedStyle: annotated
94
+ # # In `conservative` mode, offenses are only registered for strings
95
+ # # given to a known formatting method.
96
+ #
97
+ # # good
98
+ # "%{greeting}"
99
+ # foo("%{greeting}")
100
+ #
101
+ # # bad
102
+ # format("%{greeting}", greeting: 'Hello')
103
+ # printf("%{greeting}", greeting: 'Hello')
104
+ # sprintf("%{greeting}", greeting: 'Hello')
105
+ # "%{greeting}" % { greeting: 'Hello' }
106
+ #
85
107
  class FormatStringToken < Base
86
108
  include ConfigurableEnforcedStyle
87
109
  include AllowedMethods
@@ -153,8 +175,9 @@ module RuboCop
153
175
  corrector.replace(token_range, correction)
154
176
  end
155
177
 
156
- def unannotated_format?(node, detected_style)
157
- detected_style == :unannotated && !format_string_in_typical_context?(node)
178
+ def allowed_string?(node, detected_style)
179
+ (detected_style == :unannotated || conservative?) &&
180
+ !format_string_in_typical_context?(node)
158
181
  end
159
182
 
160
183
  def message(detected_style)
@@ -203,7 +226,7 @@ module RuboCop
203
226
  def collect_detections(node)
204
227
  detections = []
205
228
  tokens(node) do |detected_sequence, token_range|
206
- unless unannotated_format?(node, detected_sequence.style)
229
+ unless allowed_string?(node, detected_sequence.style)
207
230
  detections << [detected_sequence, token_range]
208
231
  end
209
232
  end
@@ -222,6 +245,10 @@ module RuboCop
222
245
  def max_unannotated_placeholders_allowed
223
246
  cop_config['MaxUnannotatedPlaceholdersAllowed']
224
247
  end
248
+
249
+ def conservative?
250
+ cop_config.fetch('Mode', :aggressive).to_sym == :conservative
251
+ end
225
252
  end
226
253
  end
227
254
  end
@@ -151,7 +151,7 @@ module RuboCop
151
151
 
152
152
  def frozen_string_literal_comment(processed_source)
153
153
  processed_source.tokens.find do |token|
154
- token.text.start_with?(FROZEN_STRING_LITERAL)
154
+ MagicComment.parse(token.text).frozen_string_literal_specified?
155
155
  end
156
156
  end
157
157
 
@@ -189,8 +189,9 @@ module RuboCop
189
189
 
190
190
  def enable_comment(corrector)
191
191
  comment = frozen_string_literal_comment(processed_source)
192
+ replacement = MagicComment.parse(comment.text).new_frozen_string_literal(true)
192
193
 
193
- corrector.replace(line_range(comment.line), FROZEN_STRING_LITERAL_ENABLED)
194
+ corrector.replace(line_range(comment.line), replacement)
194
195
  end
195
196
 
196
197
  def insert_comment(corrector)
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # reassign (possibly to redirect some stream) constants in Ruby, you'll get
9
9
  # an interpreter warning if you do so.
10
10
  #
11
+ # Additionally, `$stdout/$stderr/$stdin` can safely be accessed in a Ractor because they
12
+ # are ractor-local, while `STDOUT/STDERR/STDIN` will raise `Ractor::IsolationError`.
13
+ #
11
14
  # @safety
12
15
  # Autocorrection is unsafe because `STDOUT` and `$stdout` may point to different
13
16
  # objects, for example.
@@ -70,9 +70,7 @@ module RuboCop
70
70
  end
71
71
 
72
72
  def check(node)
73
- global_var, = *node
74
-
75
- add_offense(node.loc.name) unless allowed_var?(global_var)
73
+ add_offense(node.loc.name) unless allowed_var?(node.name)
76
74
  end
77
75
  end
78
76
  end
@@ -135,6 +135,7 @@ module RuboCop
135
135
  on_def(node)
136
136
  end
137
137
  alias on_numblock on_block
138
+ alias on_itblock on_block
138
139
 
139
140
  def on_if(node)
140
141
  return if accepted_form?(node)
@@ -213,7 +214,7 @@ module RuboCop
213
214
  if_branch = node.if_branch
214
215
  else_branch = node.else_branch
215
216
 
216
- corrector.replace(node.loc.begin, "\n") if node.loc.begin&.is?('then')
217
+ corrector.replace(node.loc.begin, "\n") if node.then?
217
218
 
218
219
  if if_branch&.send_type? && heredoc?(if_branch.last_argument)
219
220
  autocorrect_heredoc_argument(corrector, node, if_branch, else_branch, guard)
@@ -264,7 +265,7 @@ module RuboCop
264
265
 
265
266
  def and_or_guard_clause?(guard_clause)
266
267
  parent = guard_clause.parent
267
- parent.and_type? || parent.or_type?
268
+ parent.operator_keyword?
268
269
  end
269
270
 
270
271
  def too_long_for_single_line?(node, example)
@@ -283,7 +284,8 @@ module RuboCop
283
284
  end
284
285
 
285
286
  def accepted_if?(node, ending)
286
- return true if node.modifier_form? || node.ternary? || node.elsif_conditional?
287
+ return true if node.modifier_form? || node.ternary? || node.elsif_conditional? ||
288
+ assigned_lvar_used_in_if_branch?(node)
287
289
 
288
290
  if ending
289
291
  node.else?
@@ -292,6 +294,18 @@ module RuboCop
292
294
  end
293
295
  end
294
296
 
297
+ def assigned_lvar_used_in_if_branch?(node)
298
+ return false unless (if_branch = node.if_branch)
299
+
300
+ assigned_lvars_in_condition = node.condition.each_descendant(:lvasgn).map do |lvasgn|
301
+ lvar_name, = *lvasgn
302
+ lvar_name.to_s
303
+ end
304
+ used_lvars_in_branch = if_branch.each_descendant(:lvar).map(&:source) || []
305
+
306
+ (assigned_lvars_in_condition & used_lvars_in_branch).any?
307
+ end
308
+
295
309
  def remove_whole_lines(corrector, range)
296
310
  corrector.remove(range_by_whole_lines(range, include_final_newline: true))
297
311
  end
@@ -111,8 +111,7 @@ module RuboCop
111
111
  end
112
112
 
113
113
  def requires_parens?(node)
114
- (node.call_type? && node.arguments.any? && !node.parenthesized?) ||
115
- node.or_type? || node.and_type?
114
+ (node.call_type? && node.arguments.any? && !node.parenthesized?) || node.operator_keyword?
116
115
  end
117
116
 
118
117
  def multi_argument(node)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of `each_key` and `each_value` Hash methods.
6
+ # Checks for uses of `each_key` and `each_value` `Hash` methods.
7
7
  #
8
8
  # NOTE: If you have an array of two-element arrays, you can put
9
9
  # parentheses around the block arguments to indicate that you're not
@@ -44,7 +44,7 @@ module RuboCop
44
44
 
45
45
  # @!method kv_each(node)
46
46
  def_node_matcher :kv_each, <<~PATTERN
47
- ({block numblock} $(call (call _ ${:keys :values}) :each) ...)
47
+ (any_block $(call (call _ ${:keys :values}) :each) ...)
48
48
  PATTERN
49
49
 
50
50
  # @!method each_arguments(node)
@@ -74,6 +74,7 @@ module RuboCop
74
74
  check_unused_block_args(node, key, value)
75
75
  end
76
76
  alias on_numblock on_block
77
+ alias on_itblock on_block
77
78
 
78
79
  # rubocop:disable Metrics/AbcSize
79
80
  def check_unused_block_args(node, key, value)
@@ -128,8 +129,8 @@ module RuboCop
128
129
  lvar_sources = node.body.each_descendant(:lvar).map(&:source)
129
130
 
130
131
  if block_arg.mlhs_type?
131
- block_arg.each_descendant(:arg, :restarg).all? do |block_arg|
132
- lvar_sources.none?(block_arg.source.delete_prefix('*'))
132
+ block_arg.each_descendant(:arg, :restarg).all? do |descendant|
133
+ lvar_sources.none?(descendant.source.delete_prefix('*'))
133
134
  end
134
135
  else
135
136
  lvar_sources.none?(block_arg.source.delete_prefix('*'))
@@ -162,10 +163,7 @@ module RuboCop
162
163
 
163
164
  def use_array_converter_method_as_preceding?(node)
164
165
  return false unless (preceding_method = node.children.first.children.first)
165
- unless preceding_method.call_type? ||
166
- preceding_method.block_type? || preceding_method.numblock_type?
167
- return false
168
- end
166
+ return false unless preceding_method.type?(:call, :any_block)
169
167
 
170
168
  ARRAY_CONVERTER_METHODS.include?(preceding_method.method_name)
171
169
  end
@@ -10,8 +10,10 @@ module RuboCop
10
10
  # (`Hash#except` was added in Ruby 3.0.)
11
11
  #
12
12
  # For safe detection, it is limited to commonly used string and symbol comparisons
13
- # when used `==`.
14
- # And do not check `Hash#delete_if` and `Hash#keep_if` to change receiver object.
13
+ # when using `==` or `!=`.
14
+ #
15
+ # This cop doesn't check for `Hash#delete_if` and `Hash#keep_if` because they
16
+ # modify the receiver.
15
17
  #
16
18
  # @safety
17
19
  # This cop is unsafe because it cannot be guaranteed that the receiver
@@ -23,6 +25,9 @@ module RuboCop
23
25
  # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k == :bar }
24
26
  # {foo: 1, bar: 2, baz: 3}.select {|k, v| k != :bar }
25
27
  # {foo: 1, bar: 2, baz: 3}.filter {|k, v| k != :bar }
28
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k.eql?(:bar) }
29
+ #
30
+ # # bad
26
31
  # {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[bar].include?(k) }
27
32
  # {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[bar].include?(k) }
28
33
  # {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[bar].include?(k) }
@@ -30,161 +35,44 @@ module RuboCop
30
35
  # # good
31
36
  # {foo: 1, bar: 2, baz: 3}.except(:bar)
32
37
  #
38
+ # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
39
+ #
40
+ # # good
41
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| !%i[bar].exclude?(k) }
42
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| %i[bar].exclude?(k) }
43
+ #
44
+ # # good
45
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k.in?(%i[bar]) }
46
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| !k.in?(%i[bar]) }
47
+ #
48
+ # @example AllCops:ActiveSupportExtensionsEnabled: true
49
+ #
50
+ # # bad
51
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| !%i[bar].exclude?(k) }
52
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| %i[bar].exclude?(k) }
53
+ #
54
+ # # bad
55
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k.in?(%i[bar]) }
56
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| !k.in?(%i[bar]) }
57
+ #
58
+ # # good
59
+ # {foo: 1, bar: 2, baz: 3}.except(:bar)
60
+ #
33
61
  class HashExcept < Base
34
- include RangeHelp
62
+ include HashSubset
35
63
  extend TargetRubyVersion
36
64
  extend AutoCorrector
37
65
 
38
66
  minimum_target_ruby_version 3.0
39
67
 
40
- MSG = 'Use `%<prefer>s` instead.'
41
- RESTRICT_ON_SEND = %i[reject select filter].freeze
42
-
43
- # @!method bad_method_with_poro?(node)
44
- def_node_matcher :bad_method_with_poro?, <<~PATTERN
45
- (block
46
- (call _ _)
47
- (args
48
- $(arg _)
49
- (arg _))
50
- {
51
- $(send
52
- _ {:== :!= :eql? :include?} _)
53
- (send
54
- $(send
55
- _ {:== :!= :eql? :include?} _) :!)
56
- })
57
- PATTERN
58
-
59
- # @!method bad_method_with_active_support?(node)
60
- def_node_matcher :bad_method_with_active_support?, <<~PATTERN
61
- (block
62
- (send _ _)
63
- (args
64
- $(arg _)
65
- (arg _))
66
- {
67
- $(send
68
- _ {:== :!= :eql? :in? :include? :exclude?} _)
69
- (send
70
- $(send
71
- _ {:== :!= :eql? :in? :include? :exclude?} _) :!)
72
- })
73
- PATTERN
74
-
75
- def on_send(node)
76
- method_name = node.method_name
77
- block = node.parent
78
- return unless bad_method?(method_name, block) && semantically_except_method?(node, block)
79
-
80
- except_key = except_key(block)
81
- return if except_key.nil? || !safe_to_register_offense?(block, except_key)
82
-
83
- range = offense_range(node)
84
- preferred_method = "except(#{except_key_source(except_key)})"
85
-
86
- add_offense(range, message: format(MSG, prefer: preferred_method)) do |corrector|
87
- corrector.replace(range, preferred_method)
88
- end
89
- end
90
- alias on_csend on_send
91
-
92
68
  private
93
69
 
94
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
95
- def bad_method?(method_name, block)
96
- if active_support_extensions_enabled?
97
- bad_method_with_active_support?(block) do |key_arg, send_node|
98
- if send_node.method?(:in?) && send_node.receiver&.source != key_arg.source
99
- return false
100
- end
101
- return true if !send_node.method?(:include?) && !send_node.method?(:exclude?)
102
-
103
- send_node.first_argument&.source == key_arg.source
104
- end
105
- else
106
- bad_method_with_poro?(block) do |key_arg, send_node|
107
- return false if method_name == :reject && block.body.method?(:!)
108
-
109
- !send_node.method?(:include?) || send_node.first_argument&.source == key_arg.source
110
- end
111
- end
112
- end
113
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
114
-
115
- def semantically_except_method?(send, block)
116
- body = block.body
117
-
118
- negated = body.method?('!')
119
- body = body.receiver if negated
120
-
121
- case send.method_name
122
- when :reject
123
- body.method?('==') || body.method?('eql?') || included?(negated, body)
124
- when :select, :filter
125
- body.method?('!=') || not_included?(negated, body)
126
- else
127
- false
128
- end
129
- end
130
-
131
- def included?(negated, body)
132
- body.method?('include?') || body.method?('in?') || (negated && body.method?('exclude?'))
133
- end
134
-
135
- def not_included?(negated, body)
136
- body.method?('exclude?') || (negated && (body.method?('include?') || body.method?('in?')))
137
- end
138
-
139
- def safe_to_register_offense?(block, except_key)
140
- extracted = extract_body_if_negated(block.body)
141
- if extracted.method?('in?') || extracted.method?('include?') ||
142
- extracted.method?('exclude?')
143
- return true
144
- end
145
- return true if block.body.method?('eql?')
146
-
147
- except_key.sym_type? || except_key.str_type?
148
- end
149
-
150
- def extract_body_if_negated(body)
151
- return body unless body.method?('!')
152
-
153
- body.receiver
154
- end
155
-
156
- def except_key_source(key)
157
- if key.array_type?
158
- key = if key.percent_literal?
159
- key.each_value.map { |v| decorate_source(v) }
160
- else
161
- key.each_value.map(&:source)
162
- end
163
- return key.join(', ')
164
- end
165
-
166
- key.literal? ? key.source : "*#{key.source}"
167
- end
168
-
169
- def decorate_source(value)
170
- return ":\"#{value.source}\"" if value.dsym_type?
171
- return "\"#{value.source}\"" if value.dstr_type?
172
- return ":#{value.source}" if value.sym_type?
173
-
174
- "'#{value.source}'"
175
- end
176
-
177
- def except_key(node)
178
- key_argument = node.argument_list.first.source
179
- body = extract_body_if_negated(node.body)
180
- lhs, _method_name, rhs = *body
181
- return if [lhs, rhs].map(&:source).none?(key_argument)
182
-
183
- [lhs, rhs].find { |operand| operand.source != key_argument }
70
+ def semantically_subset_method?(node)
71
+ semantically_except_method?(node)
184
72
  end
185
73
 
186
- def offense_range(node)
187
- range_between(node.loc.selector.begin_pos, node.parent.loc.end.end_pos)
74
+ def preferred_method_name
75
+ 'except'
188
76
  end
189
77
  end
190
78
  end