rubocop 1.69.0 → 1.79.2

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 (448) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +23 -19
  4. data/config/default.yml +290 -65
  5. data/config/internal_affairs.yml +20 -0
  6. data/config/obsoletion.yml +8 -3
  7. data/lib/rubocop/cli/command/execute_runner.rb +3 -3
  8. data/lib/rubocop/cli/command/show_cops.rb +24 -2
  9. data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
  10. data/lib/rubocop/cli.rb +13 -2
  11. data/lib/rubocop/comment_config.rb +2 -2
  12. data/lib/rubocop/config.rb +52 -10
  13. data/lib/rubocop/config_loader.rb +53 -47
  14. data/lib/rubocop/config_loader_resolver.rb +36 -10
  15. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -3
  16. data/lib/rubocop/config_obsoletion/renamed_cop.rb +18 -3
  17. data/lib/rubocop/config_obsoletion.rb +46 -2
  18. data/lib/rubocop/config_validator.rb +25 -14
  19. data/lib/rubocop/cop/autocorrect_logic.rb +44 -39
  20. data/lib/rubocop/cop/base.rb +6 -0
  21. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
  22. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  23. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  24. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  25. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  26. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +37 -15
  27. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  28. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  29. data/lib/rubocop/cop/generator.rb +6 -0
  30. data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
  31. data/lib/rubocop/cop/internal_affairs/example_description.rb +9 -5
  32. data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
  33. data/lib/rubocop/cop/internal_affairs/location_expression.rb +2 -1
  34. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  35. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +3 -2
  36. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +5 -5
  37. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +63 -0
  38. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +131 -0
  39. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +231 -0
  40. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +92 -0
  41. data/lib/rubocop/cop/internal_affairs/node_type_multiple_predicates.rb +126 -0
  42. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
  43. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +90 -0
  44. data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +4 -2
  45. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  46. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
  47. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +3 -1
  48. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
  49. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +13 -2
  50. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  51. data/lib/rubocop/cop/internal_affairs.rb +6 -16
  52. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +1 -1
  53. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -8
  54. data/lib/rubocop/cop/layout/block_alignment.rb +3 -1
  55. data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
  56. data/lib/rubocop/cop/layout/class_structure.rb +44 -9
  57. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +5 -5
  58. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  59. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  60. data/lib/rubocop/cop/layout/else_alignment.rb +2 -2
  61. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +2 -2
  62. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +7 -11
  63. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  64. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +35 -4
  65. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  66. data/lib/rubocop/cop/layout/empty_lines_around_begin_body.rb +5 -6
  67. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -0
  68. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
  69. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -1
  70. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
  71. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  72. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  73. data/lib/rubocop/cop/layout/first_argument_indentation.rb +4 -9
  74. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
  75. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -7
  76. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +1 -1
  77. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +2 -2
  78. data/lib/rubocop/cop/layout/hash_alignment.rb +8 -6
  79. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -1
  80. data/lib/rubocop/cop/layout/indentation_width.rb +1 -0
  81. data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
  82. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +11 -2
  83. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  84. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -2
  85. data/lib/rubocop/cop/layout/line_length.rb +35 -9
  86. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -0
  87. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +1 -1
  88. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +25 -0
  89. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -0
  90. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +4 -4
  91. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +1 -0
  92. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +1 -1
  93. data/lib/rubocop/cop/layout/redundant_line_break.rb +16 -11
  94. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -5
  95. data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -1
  96. data/lib/rubocop/cop/layout/space_after_colon.rb +2 -2
  97. data/lib/rubocop/cop/layout/space_after_comma.rb +1 -1
  98. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
  99. data/lib/rubocop/cop/layout/space_after_semicolon.rb +11 -1
  100. data/lib/rubocop/cop/layout/space_around_keyword.rb +7 -1
  101. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  102. data/lib/rubocop/cop/layout/space_around_operators.rb +15 -4
  103. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -0
  104. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -38
  105. data/lib/rubocop/cop/layout/space_before_comma.rb +1 -1
  106. data/lib/rubocop/cop/layout/space_before_semicolon.rb +1 -1
  107. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +12 -3
  108. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -0
  109. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
  110. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
  111. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  112. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  113. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +118 -0
  114. data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -3
  115. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -3
  116. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  117. data/lib/rubocop/cop/lint/circular_argument_reference.rb +4 -3
  118. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +3 -3
  119. data/lib/rubocop/cop/lint/constant_reassignment.rb +148 -0
  120. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
  121. data/lib/rubocop/cop/lint/debugger.rb +3 -3
  122. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  123. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +2 -1
  124. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +1 -1
  125. data/lib/rubocop/cop/lint/duplicate_methods.rb +111 -23
  126. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +1 -1
  127. data/lib/rubocop/cop/lint/duplicate_set_element.rb +20 -7
  128. data/lib/rubocop/cop/lint/empty_conditional_body.rb +14 -64
  129. data/lib/rubocop/cop/lint/empty_expression.rb +0 -2
  130. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  131. data/lib/rubocop/cop/lint/erb_new_arguments.rb +0 -6
  132. data/lib/rubocop/cop/lint/float_comparison.rb +37 -12
  133. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  134. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +2 -2
  135. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  136. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
  137. data/lib/rubocop/cop/lint/literal_as_condition.rb +125 -10
  138. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +24 -6
  139. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
  140. data/lib/rubocop/cop/lint/missing_super.rb +2 -2
  141. data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -3
  142. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -1
  143. data/lib/rubocop/cop/lint/nested_method_definition.rb +9 -5
  144. data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -1
  145. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +4 -2
  146. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -3
  147. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -3
  148. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +19 -31
  149. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
  150. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -1
  151. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -5
  152. data/lib/rubocop/cop/lint/raise_exception.rb +29 -10
  153. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +2 -2
  154. data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
  155. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  156. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +2 -2
  157. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +261 -0
  158. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
  159. data/lib/rubocop/cop/lint/redundant_with_object.rb +3 -0
  160. data/lib/rubocop/cop/lint/refinement_import_methods.rb +1 -1
  161. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  162. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
  163. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  164. data/lib/rubocop/cop/lint/return_in_void_context.rb +9 -11
  165. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -1
  166. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  167. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -1
  168. data/lib/rubocop/cop/lint/shared_mutable_default.rb +76 -0
  169. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  170. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
  171. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  172. data/lib/rubocop/cop/lint/syntax.rb +4 -1
  173. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -1
  174. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  175. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -1
  176. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +3 -1
  177. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -1
  178. data/lib/rubocop/cop/lint/unreachable_code.rb +52 -2
  179. data/lib/rubocop/cop/lint/unreachable_loop.rb +6 -6
  180. data/lib/rubocop/cop/lint/useless_access_modifier.rb +34 -8
  181. data/lib/rubocop/cop/lint/useless_assignment.rb +3 -1
  182. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +71 -0
  183. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  184. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +4 -0
  185. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  186. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +3 -1
  187. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  188. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
  189. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +5 -5
  190. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  191. data/lib/rubocop/cop/lint/void.rb +14 -11
  192. data/lib/rubocop/cop/message_annotator.rb +7 -3
  193. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  194. data/lib/rubocop/cop/metrics/block_length.rb +1 -0
  195. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  196. data/lib/rubocop/cop/metrics/class_length.rb +9 -9
  197. data/lib/rubocop/cop/metrics/collection_literal_length.rb +7 -0
  198. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
  199. data/lib/rubocop/cop/metrics/method_length.rb +9 -1
  200. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  201. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  202. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  203. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  204. data/lib/rubocop/cop/mixin/alignment.rb +3 -3
  205. data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
  206. data/lib/rubocop/cop/mixin/check_line_breakable.rb +13 -13
  207. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
  208. data/lib/rubocop/cop/mixin/comments_help.rb +8 -3
  209. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  210. data/lib/rubocop/cop/mixin/dig_help.rb +1 -1
  211. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  212. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +20 -0
  213. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +16 -0
  214. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -2
  215. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  216. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
  217. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +22 -22
  218. data/lib/rubocop/cop/mixin/hash_subset.rb +203 -0
  219. data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
  220. data/lib/rubocop/cop/mixin/line_length_help.rb +27 -10
  221. data/lib/rubocop/cop/mixin/method_complexity.rb +2 -1
  222. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -0
  223. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  224. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  225. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +68 -30
  226. data/lib/rubocop/cop/mixin/range_help.rb +15 -3
  227. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  228. data/lib/rubocop/cop/mixin/statement_modifier.rb +8 -3
  229. data/lib/rubocop/cop/mixin/string_help.rb +2 -2
  230. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  231. data/lib/rubocop/cop/mixin/target_ruby_version.rb +1 -1
  232. data/lib/rubocop/cop/mixin/trailing_comma.rb +21 -5
  233. data/lib/rubocop/cop/naming/accessor_method_name.rb +6 -6
  234. data/lib/rubocop/cop/naming/block_forwarding.rb +19 -15
  235. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  236. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  237. data/lib/rubocop/cop/naming/method_name.rb +185 -15
  238. data/lib/rubocop/cop/naming/predicate_method.rb +306 -0
  239. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +48 -4
  240. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +4 -4
  241. data/lib/rubocop/cop/naming/variable_name.rb +51 -6
  242. data/lib/rubocop/cop/registry.rb +9 -6
  243. data/lib/rubocop/cop/security/compound_hash.rb +2 -0
  244. data/lib/rubocop/cop/security/eval.rb +2 -1
  245. data/lib/rubocop/cop/security/open.rb +1 -0
  246. data/lib/rubocop/cop/security/yaml_load.rb +3 -2
  247. data/lib/rubocop/cop/style/access_modifier_declarations.rb +66 -15
  248. data/lib/rubocop/cop/style/accessor_grouping.rb +32 -6
  249. data/lib/rubocop/cop/style/and_or.rb +1 -1
  250. data/lib/rubocop/cop/style/arguments_forwarding.rb +57 -44
  251. data/lib/rubocop/cop/style/array_first_last.rb +18 -2
  252. data/lib/rubocop/cop/style/array_intersect.rb +81 -40
  253. data/lib/rubocop/cop/style/block_delimiters.rb +27 -24
  254. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  255. data/lib/rubocop/cop/style/class_and_module_children.rb +52 -11
  256. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  257. data/lib/rubocop/cop/style/collection_methods.rb +2 -1
  258. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  259. data/lib/rubocop/cop/style/combinable_defined.rb +1 -1
  260. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  261. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  262. data/lib/rubocop/cop/style/commented_keyword.rb +12 -5
  263. data/lib/rubocop/cop/style/comparable_between.rb +78 -0
  264. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
  265. data/lib/rubocop/cop/style/conditional_assignment.rb +23 -7
  266. data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
  267. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  268. data/lib/rubocop/cop/style/dig_chain.rb +6 -7
  269. data/lib/rubocop/cop/style/documentation.rb +1 -1
  270. data/lib/rubocop/cop/style/double_negation.rb +4 -4
  271. data/lib/rubocop/cop/style/each_for_simple_loop.rb +4 -7
  272. data/lib/rubocop/cop/style/each_with_object.rb +2 -3
  273. data/lib/rubocop/cop/style/empty_else.rb +4 -2
  274. data/lib/rubocop/cop/style/empty_literal.rb +5 -1
  275. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  276. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  277. data/lib/rubocop/cop/style/endless_method.rb +163 -18
  278. data/lib/rubocop/cop/style/eval_with_location.rb +4 -4
  279. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -3
  280. data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -7
  281. data/lib/rubocop/cop/style/explicit_block_argument.rb +16 -3
  282. data/lib/rubocop/cop/style/exponential_notation.rb +6 -5
  283. data/lib/rubocop/cop/style/fetch_env_var.rb +34 -7
  284. data/lib/rubocop/cop/style/file_null.rb +20 -4
  285. data/lib/rubocop/cop/style/float_division.rb +8 -4
  286. data/lib/rubocop/cop/style/for.rb +1 -0
  287. data/lib/rubocop/cop/style/format_string_token.rb +38 -11
  288. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -2
  289. data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
  290. data/lib/rubocop/cop/style/guard_clause.rb +2 -1
  291. data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
  292. data/lib/rubocop/cop/style/hash_each_methods.rb +6 -8
  293. data/lib/rubocop/cop/style/hash_except.rb +35 -147
  294. data/lib/rubocop/cop/style/hash_fetch_chain.rb +104 -0
  295. data/lib/rubocop/cop/style/hash_slice.rb +80 -0
  296. data/lib/rubocop/cop/style/hash_syntax.rb +9 -3
  297. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  298. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  299. data/lib/rubocop/cop/style/identical_conditional_branches.rb +25 -6
  300. data/lib/rubocop/cop/style/if_inside_else.rb +10 -13
  301. data/lib/rubocop/cop/style/if_unless_modifier.rb +36 -9
  302. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
  303. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -2
  304. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  305. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  306. data/lib/rubocop/cop/style/inverse_methods.rb +16 -12
  307. data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
  308. data/lib/rubocop/cop/style/ip_addresses.rb +2 -2
  309. data/lib/rubocop/cop/style/it_assignment.rb +93 -0
  310. data/lib/rubocop/cop/style/it_block_parameter.rb +121 -0
  311. data/lib/rubocop/cop/style/keyword_parameters_order.rb +14 -8
  312. data/lib/rubocop/cop/style/lambda.rb +1 -0
  313. data/lib/rubocop/cop/style/lambda_call.rb +10 -3
  314. data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
  315. data/lib/rubocop/cop/style/map_into_array.rb +5 -2
  316. data/lib/rubocop/cop/style/map_to_hash.rb +13 -4
  317. data/lib/rubocop/cop/style/map_to_set.rb +4 -5
  318. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +27 -19
  319. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +18 -0
  320. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +3 -2
  321. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +3 -4
  322. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  323. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  324. data/lib/rubocop/cop/style/missing_else.rb +2 -0
  325. data/lib/rubocop/cop/style/multiline_block_chain.rb +3 -2
  326. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
  327. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -9
  328. data/lib/rubocop/cop/style/multiple_comparison.rb +34 -22
  329. data/lib/rubocop/cop/style/mutable_constant.rb +3 -3
  330. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -1
  331. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  332. data/lib/rubocop/cop/style/next.rb +44 -0
  333. data/lib/rubocop/cop/style/object_then.rb +15 -15
  334. data/lib/rubocop/cop/style/open_struct_use.rb +5 -5
  335. data/lib/rubocop/cop/style/parallel_assignment.rb +33 -25
  336. data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
  337. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  338. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  339. data/lib/rubocop/cop/style/proc.rb +2 -2
  340. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  341. data/lib/rubocop/cop/style/raise_args.rb +14 -12
  342. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  343. data/lib/rubocop/cop/style/redundant_argument.rb +3 -1
  344. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  345. data/lib/rubocop/cop/style/redundant_begin.rb +2 -1
  346. data/lib/rubocop/cop/style/redundant_condition.rb +59 -2
  347. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +16 -5
  348. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -10
  349. data/lib/rubocop/cop/style/redundant_each.rb +1 -1
  350. data/lib/rubocop/cop/style/redundant_exception.rb +2 -2
  351. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  352. data/lib/rubocop/cop/style/redundant_format.rb +262 -0
  353. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -4
  354. data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
  355. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  356. data/lib/rubocop/cop/style/redundant_line_continuation.rb +39 -22
  357. data/lib/rubocop/cop/style/redundant_parentheses.rb +85 -17
  358. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +3 -0
  359. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
  360. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  361. data/lib/rubocop/cop/style/redundant_self.rb +10 -6
  362. data/lib/rubocop/cop/style/redundant_self_assignment.rb +14 -28
  363. data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
  364. data/lib/rubocop/cop/style/redundant_sort_by.rb +17 -1
  365. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -2
  366. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  367. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -0
  368. data/lib/rubocop/cop/style/return_nil.rb +2 -2
  369. data/lib/rubocop/cop/style/safe_navigation.rb +46 -16
  370. data/lib/rubocop/cop/style/select_by_regexp.rb +4 -1
  371. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  372. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
  373. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  374. data/lib/rubocop/cop/style/single_line_do_end_block.rb +4 -3
  375. data/lib/rubocop/cop/style/single_line_methods.rb +13 -11
  376. data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
  377. data/lib/rubocop/cop/style/sole_nested_conditional.rb +68 -101
  378. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  379. data/lib/rubocop/cop/style/string_concatenation.rb +15 -14
  380. data/lib/rubocop/cop/style/string_literals.rb +1 -1
  381. data/lib/rubocop/cop/style/string_methods.rb +1 -1
  382. data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
  383. data/lib/rubocop/cop/style/super_arguments.rb +66 -19
  384. data/lib/rubocop/cop/style/symbol_proc.rb +3 -1
  385. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  386. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -1
  387. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +11 -2
  388. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
  389. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  390. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
  391. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  392. data/lib/rubocop/cop/style/while_until_modifier.rb +0 -1
  393. data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
  394. data/lib/rubocop/cop/style/yoda_expression.rb +2 -1
  395. data/lib/rubocop/cop/team.rb +1 -1
  396. data/lib/rubocop/cop/util.rb +12 -5
  397. data/lib/rubocop/cop/utils/format_string.rb +10 -5
  398. data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
  399. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  400. data/lib/rubocop/cop/variable_force/variable.rb +10 -3
  401. data/lib/rubocop/cop/variable_force/variable_table.rb +3 -3
  402. data/lib/rubocop/cop/variable_force.rb +23 -8
  403. data/lib/rubocop/cops_documentation_generator.rb +32 -16
  404. data/lib/rubocop/directive_comment.rb +45 -11
  405. data/lib/rubocop/ext/regexp_node.rb +0 -1
  406. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
  407. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  408. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  409. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  410. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  411. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  412. data/lib/rubocop/formatter/pacman_formatter.rb +2 -1
  413. data/lib/rubocop/lsp/diagnostic.rb +189 -0
  414. data/lib/rubocop/lsp/logger.rb +2 -2
  415. data/lib/rubocop/lsp/routes.rb +10 -26
  416. data/lib/rubocop/lsp/runtime.rb +18 -50
  417. data/lib/rubocop/lsp/server.rb +0 -2
  418. data/lib/rubocop/lsp/stdin_runner.rb +85 -0
  419. data/lib/rubocop/magic_comment.rb +11 -3
  420. data/lib/rubocop/options.rb +28 -12
  421. data/lib/rubocop/path_util.rb +15 -8
  422. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  423. data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
  424. data/lib/rubocop/plugin/load_error.rb +26 -0
  425. data/lib/rubocop/plugin/loader.rb +100 -0
  426. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  427. data/lib/rubocop/plugin.rb +46 -0
  428. data/lib/rubocop/rake_task.rb +4 -1
  429. data/lib/rubocop/result_cache.rb +26 -24
  430. data/lib/rubocop/rspec/cop_helper.rb +13 -1
  431. data/lib/rubocop/rspec/expect_offense.rb +15 -5
  432. data/lib/rubocop/rspec/shared_contexts.rb +38 -1
  433. data/lib/rubocop/rspec/support.rb +4 -2
  434. data/lib/rubocop/runner.rb +10 -7
  435. data/lib/rubocop/server/cache.rb +51 -13
  436. data/lib/rubocop/server/cli.rb +2 -2
  437. data/lib/rubocop/server/client_command/base.rb +10 -0
  438. data/lib/rubocop/server/client_command/exec.rb +2 -1
  439. data/lib/rubocop/server/client_command/start.rb +11 -1
  440. data/lib/rubocop/target_finder.rb +7 -2
  441. data/lib/rubocop/target_ruby.rb +16 -1
  442. data/lib/rubocop/version.rb +30 -8
  443. data/lib/rubocop.rb +27 -2
  444. data/lib/ruby_lsp/rubocop/addon.rb +75 -0
  445. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +65 -0
  446. metadata +72 -19
  447. data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
  448. data/lib/rubocop/rspec/host_environment_simulation_helper.rb +0 -28
@@ -13,6 +13,20 @@ module RuboCop
13
13
  # The default variable name is `block`. If the name is already in use, it will not be
14
14
  # autocorrected.
15
15
  #
16
+ # [NOTE]
17
+ # ====
18
+ # Because of a bug in Ruby 3.3.0, when a block is referenced inside of another block,
19
+ # no offense will be registered until Ruby 3.4:
20
+ #
21
+ # [source,ruby]
22
+ # ----
23
+ # def foo(&block)
24
+ # # Using an anonymous block would be a syntax error on Ruby 3.3.0
25
+ # block_method { bar(&block) }
26
+ # end
27
+ # ----
28
+ # ====
29
+ #
16
30
  # @example EnforcedStyle: anonymous (default)
17
31
  #
18
32
  # # bad
@@ -90,21 +104,11 @@ module RuboCop
90
104
  last_argument.source == block_pass_node.source
91
105
  end
92
106
 
93
- # Prevents the following syntax error:
94
- #
95
- # # foo.rb
96
- # def foo(&)
97
- # block_method do
98
- # bar(&)
99
- # end
100
- # end
101
- #
102
- # $ ruby -vc foo.rb
103
- # ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22]
104
- # foo.rb: foo.rb:4: anonymous block parameter is also used within block (SyntaxError)
105
- #
107
+ # Ruby 3.3.0 had a bug where accessing an anonymous block argument inside of a block
108
+ # was a syntax error in unambiguous cases: https://bugs.ruby-lang.org/issues/20090
109
+ # We disallow this also for earlier Ruby versions so that code is forwards compatible.
106
110
  def invalidates_syntax?(block_pass_node)
107
- block_pass_node.each_ancestor(:block, :numblock).any?
111
+ target_ruby_version <= 3.3 && block_pass_node.each_ancestor(:any_block).any?
108
112
  end
109
113
 
110
114
  def use_kwarg_in_method_definition?(node)
@@ -138,7 +142,7 @@ module RuboCop
138
142
  def use_block_argument_as_local_variable?(node, last_argument)
139
143
  return false if node.body.nil?
140
144
 
141
- node.body.each_descendant(:lvar, :lvasgn).any? do |lvar|
145
+ node.body.each_node(:lvar, :lvasgn).any? do |lvar|
142
146
  !lvar.parent.block_pass_type? && lvar.node_parts[0].to_s == last_argument
143
147
  end
144
148
  end
@@ -152,7 +152,7 @@ module RuboCop
152
152
 
153
153
  const_namespace, const_name = *const
154
154
  next if name != const_name && !match_acronym?(name, const_name)
155
- next unless namespace.empty? || match_namespace(child, const_namespace, namespace)
155
+ next unless namespace.empty? || namespace_matches?(child, const_namespace, namespace)
156
156
 
157
157
  return node
158
158
  end
@@ -169,7 +169,7 @@ module RuboCop
169
169
  s(:const, namespace, name) if name
170
170
  end
171
171
 
172
- def match_namespace(node, namespace, expected)
172
+ def namespace_matches?(node, namespace, expected)
173
173
  match_partial = partial_matcher!(expected)
174
174
 
175
175
  match_partial.call(namespace)
@@ -242,7 +242,7 @@ module RuboCop
242
242
  def find_definition(node)
243
243
  # Methods can be defined in a `def` or `defs`,
244
244
  # or dynamically via a `block` node.
245
- node.each_ancestor(:def, :defs, :block).each do |ancestor|
245
+ node.each_ancestor(:any_def, :block).each do |ancestor|
246
246
  method_node, method_name = method_definition?(ancestor)
247
247
  return [method_node, method_name] if method_node
248
248
  end
@@ -6,14 +6,31 @@ module RuboCop
6
6
  # Makes sure that all methods use the configured style,
7
7
  # snake_case or camelCase, for their names.
8
8
  #
9
- # This cop has `AllowedPatterns` configuration option.
9
+ # Method names matching patterns are always allowed.
10
10
  #
11
- # Naming/MethodName:
12
- # AllowedPatterns:
13
- # - '\AonSelectionBulkChange\z'
14
- # - '\AonSelectionCleared\z'
11
+ # The cop can be configured with `AllowedPatterns` to allow certain regexp patterns:
15
12
  #
16
- # Method names matching patterns are always allowed.
13
+ # [source,yaml]
14
+ # ----
15
+ # Naming/MethodName:
16
+ # AllowedPatterns:
17
+ # - '\AonSelectionBulkChange\z'
18
+ # - '\AonSelectionCleared\z'
19
+ # ----
20
+ #
21
+ # As well, you can also forbid specific method names or regexp patterns
22
+ # using `ForbiddenIdentifiers` or `ForbiddenPatterns`:
23
+ #
24
+ # [source,yaml]
25
+ # ----
26
+ # Naming/MethodName:
27
+ # ForbiddenIdentifiers:
28
+ # - 'def'
29
+ # - 'super'
30
+ # ForbiddenPatterns:
31
+ # - '_v1\z'
32
+ # - '_gen1\z'
33
+ # ----
17
34
  #
18
35
  # @example EnforcedStyle: snake_case (default)
19
36
  # # bad
@@ -22,18 +39,75 @@ module RuboCop
22
39
  # # good
23
40
  # def foo_bar; end
24
41
  #
42
+ # # bad
43
+ # define_method :fooBar do
44
+ # end
45
+ #
46
+ # # good
47
+ # define_method :foo_bar do
48
+ # end
49
+ #
50
+ # # bad
51
+ # Struct.new(:fooBar)
52
+ #
53
+ # # good
54
+ # Struct.new(:foo_bar)
55
+ #
56
+ # # bad
57
+ # alias_method :fooBar, :some_method
58
+ #
59
+ # # good
60
+ # alias_method :foo_bar, :some_method
61
+ #
25
62
  # @example EnforcedStyle: camelCase
26
63
  # # bad
27
64
  # def foo_bar; end
28
65
  #
29
66
  # # good
30
67
  # def fooBar; end
68
+ #
69
+ # # bad
70
+ # define_method :foo_bar do
71
+ # end
72
+ #
73
+ # # good
74
+ # define_method :fooBar do
75
+ # end
76
+ #
77
+ # # bad
78
+ # Struct.new(:foo_bar)
79
+ #
80
+ # # good
81
+ # Struct.new(:fooBar)
82
+ #
83
+ # # bad
84
+ # alias_method :foo_bar, :some_method
85
+ #
86
+ # # good
87
+ # alias_method :fooBar, :some_method
88
+ #
89
+ # @example ForbiddenIdentifiers: ['def', 'super']
90
+ # # bad
91
+ # def def; end
92
+ # def super; end
93
+ #
94
+ # @example ForbiddenPatterns: ['_v1\z', '_gen1\z']
95
+ # # bad
96
+ # def release_v1; end
97
+ # def api_gen1; end
98
+ #
31
99
  class MethodName < Base
32
100
  include ConfigurableNaming
33
101
  include AllowedPattern
34
102
  include RangeHelp
103
+ include ForbiddenIdentifiers
104
+ include ForbiddenPattern
35
105
 
36
106
  MSG = 'Use %<style>s for method names.'
107
+ MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another method name instead.'
108
+
109
+ OPERATOR_METHODS = %i[| ^ & <=> == === =~ > >= < <= << >> + - * /
110
+ % ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].to_set.freeze
37
111
 
38
112
  # @!method sym_name(node)
39
113
  def_node_matcher :sym_name, '(sym $_name)'
@@ -41,35 +115,131 @@ module RuboCop
41
115
  # @!method str_name(node)
42
116
  def_node_matcher :str_name, '(str $_name)'
43
117
 
118
+ # @!method new_struct?(node)
119
+ def_node_matcher :new_struct?, '(send (const {nil? cbase} :Struct) :new ...)'
120
+
121
+ # @!method define_data?(node)
122
+ def_node_matcher :define_data?, '(send (const {nil? cbase} :Data) :define ...)'
123
+
44
124
  def on_send(node)
125
+ if node.method?(:define_method) || node.method?(:define_singleton_method)
126
+ handle_define_method(node)
127
+ elsif new_struct?(node)
128
+ handle_new_struct(node)
129
+ elsif define_data?(node)
130
+ handle_define_data(node)
131
+ elsif node.method?(:alias_method)
132
+ handle_alias_method(node)
133
+ else
134
+ handle_attr_accessor(node)
135
+ end
136
+ end
137
+
138
+ def on_def(node)
139
+ return if node.operator_method? || matches_allowed_pattern?(node.method_name)
140
+
141
+ if forbidden_name?(node.method_name.to_s)
142
+ register_forbidden_name(node)
143
+ else
144
+ check_name(node, node.method_name, node.loc.name)
145
+ end
146
+ end
147
+ alias on_defs on_def
148
+
149
+ def on_alias(node)
150
+ handle_method_name(node.new_identifier, node.new_identifier.value)
151
+ end
152
+
153
+ private
154
+
155
+ def handle_define_method(node)
156
+ return unless node.first_argument&.type?(:str, :sym)
157
+
158
+ handle_method_name(node, node.first_argument.value)
159
+ end
160
+
161
+ def handle_new_struct(node)
162
+ arguments = node.first_argument&.str_type? ? node.arguments[1..] : node.arguments
163
+ arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
164
+ handle_method_name(name, name.value)
165
+ end
166
+ end
167
+
168
+ def handle_define_data(node)
169
+ node.arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
170
+ handle_method_name(name, name.value)
171
+ end
172
+ end
173
+
174
+ def handle_alias_method(node)
175
+ return unless node.arguments.size == 2
176
+ return unless node.first_argument.type?(:str, :sym)
177
+
178
+ handle_method_name(node.first_argument, node.first_argument.value)
179
+ end
180
+
181
+ def handle_attr_accessor(node)
45
182
  return unless (attrs = node.attribute_accessor?)
46
183
 
47
184
  attrs.last.each do |name_item|
48
185
  name = attr_name(name_item)
49
186
  next if !name || matches_allowed_pattern?(name)
50
187
 
51
- check_name(node, name, range_position(node))
188
+ if forbidden_name?(name.to_s)
189
+ register_forbidden_name(node)
190
+ else
191
+ check_name(node, name, range_position(node))
192
+ end
52
193
  end
53
194
  end
54
195
 
55
- def on_def(node)
56
- return if node.operator_method? || matches_allowed_pattern?(node.method_name)
196
+ def handle_method_name(node, name)
197
+ return if !name || matches_allowed_pattern?(name)
57
198
 
58
- check_name(node, node.method_name, node.loc.name)
199
+ if forbidden_name?(name.to_s)
200
+ register_forbidden_name(node)
201
+ elsif !OPERATOR_METHODS.include?(name)
202
+ check_name(node, name, range_position(node))
203
+ end
59
204
  end
60
- alias on_defs on_def
61
205
 
62
- private
206
+ def forbidden_name?(name)
207
+ forbidden_identifier?(name) || forbidden_pattern?(name)
208
+ end
209
+
210
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
211
+ def register_forbidden_name(node)
212
+ if node.any_def_type?
213
+ name_node = node.loc.name
214
+ method_name = node.method_name
215
+ elsif node.literal?
216
+ name_node = node
217
+ method_name = node.value
218
+ elsif (attrs = node.attribute_accessor?)
219
+ name_node = attrs.last.last
220
+ method_name = attr_name(name_node)
221
+ else
222
+ name_node = node.first_argument
223
+ method_name = node.first_argument.value
224
+ end
225
+ message = format(MSG_FORBIDDEN, identifier: method_name)
226
+ add_offense(name_node, message: message)
227
+ end
228
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
63
229
 
64
230
  def attr_name(name_item)
65
231
  sym_name(name_item) || str_name(name_item)
66
232
  end
67
233
 
68
234
  def range_position(node)
69
- selector_end_pos = node.loc.selector.end_pos + 1
70
- expr_end_pos = node.source_range.end_pos
235
+ if node.loc.respond_to?(:selector)
236
+ selector_end_pos = node.loc.selector.end_pos + 1
237
+ expr_end_pos = node.source_range.end_pos
71
238
 
72
- range_between(selector_end_pos, expr_end_pos)
239
+ range_between(selector_end_pos, expr_end_pos)
240
+ else
241
+ node.source_range
242
+ end
73
243
  end
74
244
 
75
245
  def message(style)
@@ -0,0 +1,306 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Naming
6
+ # Checks that predicate methods end with `?` and non-predicate methods do not.
7
+ #
8
+ # The names of predicate methods (methods that return a boolean value) should end
9
+ # in a question mark. Methods that don't return a boolean, shouldn't
10
+ # end in a question mark.
11
+ #
12
+ # The cop assesses a predicate method as one that returns boolean values. Likewise,
13
+ # a method that only returns literal values is assessed as non-predicate. Other predicate
14
+ # method calls are assumed to return boolean values. The cop does not make an assessment
15
+ # if the return type is unknown (non-predicate method calls, variables, etc.).
16
+ #
17
+ # NOTE: Operator methods (`def ==`, etc.) are ignored.
18
+ #
19
+ # By default, the cop runs in `conservative` mode, which allows a method to be named
20
+ # with a question mark as long as at least one return value is boolean. In `aggressive`
21
+ # mode, methods with a question mark will register an offense if any known non-boolean
22
+ # return values are detected.
23
+ #
24
+ # The cop also has `AllowedMethods` configuration in order to prevent the cop from
25
+ # registering an offense from a method name that does not confirm to the naming
26
+ # guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
27
+ # configuration to allow method names by regular expression.
28
+ #
29
+ # Although returning a call to another predicate method is treated as a boolean value,
30
+ # certain method names can be known to not return a boolean, despite ending in a `?`
31
+ # (for example, `Numeric#nonzero?` returns `self` or `nil`). These methods can be
32
+ # configured using `NonBooleanPredicates`.
33
+ #
34
+ # The cop can furthermore be configured to allow all bang methods (method names
35
+ # ending with `!`), with `AllowBangMethods: true` (default false).
36
+ #
37
+ # @example Mode: conservative (default)
38
+ # # bad
39
+ # def foo
40
+ # bar == baz
41
+ # end
42
+ #
43
+ # # good
44
+ # def foo?
45
+ # bar == baz
46
+ # end
47
+ #
48
+ # # bad
49
+ # def foo?
50
+ # 5
51
+ # end
52
+ #
53
+ # # good
54
+ # def foo
55
+ # 5
56
+ # end
57
+ #
58
+ # # bad
59
+ # def foo
60
+ # x == y
61
+ # end
62
+ #
63
+ # # good
64
+ # def foo?
65
+ # x == y
66
+ # end
67
+ #
68
+ # # bad
69
+ # def foo
70
+ # !x
71
+ # end
72
+ #
73
+ # # good
74
+ # def foo?
75
+ # !x
76
+ # end
77
+ #
78
+ # # bad - returns the value of another predicate method
79
+ # def foo
80
+ # bar?
81
+ # end
82
+ #
83
+ # # good
84
+ # def foo?
85
+ # bar?
86
+ # end
87
+ #
88
+ # # good - operator method
89
+ # def ==(other)
90
+ # hash == other.hash
91
+ # end
92
+ #
93
+ # # good - at least one return value is boolean
94
+ # def foo?
95
+ # return unless bar?
96
+ # true
97
+ # end
98
+ #
99
+ # # ok - return type is not known
100
+ # def foo?
101
+ # bar
102
+ # end
103
+ #
104
+ # # ok - return type is not known
105
+ # def foo
106
+ # bar?
107
+ # end
108
+ #
109
+ # @example Mode: aggressive
110
+ # # bad - the method returns nil in some cases
111
+ # def foo?
112
+ # return unless bar?
113
+ # true
114
+ # end
115
+ #
116
+ # @example AllowBangMethods: false (default)
117
+ # # bad
118
+ # def save!
119
+ # true
120
+ # end
121
+ #
122
+ # @example AllowBangMethods: true
123
+ # # good
124
+ # def save!
125
+ # true
126
+ # end
127
+ #
128
+ class PredicateMethod < Base
129
+ include AllowedMethods
130
+ include AllowedPattern
131
+
132
+ MSG_PREDICATE = 'Predicate method names should end with `?`.'
133
+ MSG_NON_PREDICATE = 'Non-predicate method names should not end with `?`.'
134
+
135
+ def on_def(node)
136
+ return if allowed?(node)
137
+
138
+ return_values = return_values(node.body)
139
+ return if acceptable?(return_values)
140
+
141
+ if node.predicate_method? && potential_non_predicate?(return_values)
142
+ add_offense(node.loc.name, message: MSG_NON_PREDICATE)
143
+ elsif !node.predicate_method? && all_return_values_boolean?(return_values)
144
+ add_offense(node.loc.name, message: MSG_PREDICATE)
145
+ end
146
+ end
147
+ alias on_defs on_def
148
+
149
+ private
150
+
151
+ def allowed?(node)
152
+ allowed_method?(node.method_name) ||
153
+ matches_allowed_pattern?(node.method_name) ||
154
+ allowed_bang_method?(node) ||
155
+ node.operator_method? ||
156
+ node.body.nil?
157
+ end
158
+
159
+ def acceptable?(return_values)
160
+ # In `conservative` mode, if the method returns `super`, `zsuper`, or a
161
+ # non-comparison method call, the method name is acceptable.
162
+ return false unless conservative?
163
+
164
+ return_values.any? do |value|
165
+ value.type?(:super, :zsuper) || unknown_method_call?(value)
166
+ end
167
+ end
168
+
169
+ def unknown_method_call?(value)
170
+ return false unless value.call_type?
171
+
172
+ !method_returning_boolean?(value)
173
+ end
174
+
175
+ def return_values(node)
176
+ # Collect all the (implicit and explicit) return values of a node
177
+ return_values = Set.new(node.begin_type? ? [] : [extract_return_value(node)])
178
+
179
+ node.each_descendant(:return) do |return_node|
180
+ return_values << extract_return_value(return_node)
181
+ end
182
+
183
+ last_value = last_value(node)
184
+ return_values << last_value if last_value
185
+
186
+ process_return_values(return_values)
187
+ end
188
+
189
+ def all_return_values_boolean?(return_values)
190
+ values = return_values.reject { |value| value.type?(:super, :zsuper) }
191
+ return false if values.empty?
192
+
193
+ values.all? { |value| boolean_return?(value) }
194
+ end
195
+
196
+ def boolean_return?(value)
197
+ return true if value.boolean_type?
198
+
199
+ method_returning_boolean?(value)
200
+ end
201
+
202
+ def method_returning_boolean?(value)
203
+ return false unless value.call_type?
204
+ return false if wayward_predicate?(value.method_name)
205
+
206
+ value.comparison_method? || value.predicate_method? || value.negation_method?
207
+ end
208
+
209
+ def potential_non_predicate?(return_values)
210
+ # Assumes a method to be non-predicate if all return values are non-boolean literals.
211
+ #
212
+ # In `Mode: conservative`, if any of the return values is a boolean,
213
+ # the method name is acceptable.
214
+ # In `Mode: aggressive`, all return values must be booleans for a predicate
215
+ # method, or else an offense will be registered.
216
+ return false if conservative? && return_values.any? { |value| boolean_return?(value) }
217
+
218
+ return_values.any? do |value|
219
+ value.literal? && !value.boolean_type?
220
+ end
221
+ end
222
+
223
+ def extract_return_value(node)
224
+ return node unless node.return_type?
225
+
226
+ # `return` without a value is a `nil` return.
227
+ return s(:nil) if node.arguments.empty?
228
+
229
+ # When there's a multiple return, it cannot be a predicate
230
+ # so just return an `array` sexp for simplicity.
231
+ return s(:array) unless node.arguments.one?
232
+
233
+ node.first_argument
234
+ end
235
+
236
+ def last_value(node)
237
+ value = node.begin_type? ? node.children.last : node
238
+ value&.return_type? ? extract_return_value(value) : value
239
+ end
240
+
241
+ def process_return_values(return_values)
242
+ return_values.flat_map do |value|
243
+ if value.conditional?
244
+ process_return_values(extract_conditional_branches(value))
245
+ elsif and_or?(value)
246
+ process_return_values(extract_and_or_clauses(value))
247
+ else
248
+ value
249
+ end
250
+ end
251
+ end
252
+
253
+ def and_or?(node)
254
+ node.type?(:and, :or)
255
+ end
256
+
257
+ def extract_and_or_clauses(node)
258
+ # Recursively traverse an `and` or `or` node to collect all clauses within
259
+ return node unless and_or?(node)
260
+
261
+ [extract_and_or_clauses(node.lhs), extract_and_or_clauses(node.rhs)].flatten
262
+ end
263
+
264
+ def extract_conditional_branches(node)
265
+ return node unless node.conditional?
266
+
267
+ if node.type?(:while, :until)
268
+ # If there is no body, act as implicit `nil`.
269
+ node.body ? [last_value(node.body)] : [s(:nil)]
270
+ else
271
+ # Branches with no value act as an implicit `nil`.
272
+ branches = node.branches.map { |branch| branch ? last_value(branch) : s(:nil) }
273
+ # Missing else branches also act as an implicit `nil`.
274
+ branches.push(s(:nil)) unless node.else_branch
275
+ branches
276
+ end
277
+ end
278
+
279
+ def conservative?
280
+ cop_config.fetch('Mode', :conservative).to_sym == :conservative
281
+ end
282
+
283
+ def allowed_bang_method?(node)
284
+ return false unless allow_bang_methods?
285
+
286
+ node.bang_method?
287
+ end
288
+
289
+ def allow_bang_methods?
290
+ cop_config.fetch('AllowBangMethods', false)
291
+ end
292
+
293
+ # If a method ending in `?` is known to not return a boolean value,
294
+ # (for example, `Numeric#nonzero?`) it should be treated as a non-boolean
295
+ # value, despite the method naming.
296
+ def wayward_predicate?(name)
297
+ wayward_predicates.include?(name.to_s)
298
+ end
299
+
300
+ def wayward_predicates
301
+ Array(cop_config.fetch('WaywardPredicates', []))
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end