rubocop 1.66.1 → 1.72.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 (434) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +162 -14
  5. data/config/internal_affairs.yml +11 -0
  6. data/lib/rubocop/cached_data.rb +12 -4
  7. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
  8. data/lib/rubocop/cli/command/execute_runner.rb +4 -4
  9. data/lib/rubocop/cli/command/lsp.rb +2 -2
  10. data/lib/rubocop/cli/command/show_cops.rb +24 -2
  11. data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
  12. data/lib/rubocop/cli/command/version.rb +2 -2
  13. data/lib/rubocop/comment_config.rb +2 -2
  14. data/lib/rubocop/config.rb +17 -4
  15. data/lib/rubocop/config_loader.rb +48 -8
  16. data/lib/rubocop/config_loader_resolver.rb +36 -11
  17. data/lib/rubocop/config_validator.rb +20 -9
  18. data/lib/rubocop/cop/autocorrect_logic.rb +36 -19
  19. data/lib/rubocop/cop/base.rb +13 -3
  20. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
  21. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  22. data/lib/rubocop/cop/bundler/gem_filename.rb +0 -1
  23. data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
  24. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +0 -1
  25. data/lib/rubocop/cop/cop.rb +8 -0
  26. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
  27. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +1 -1
  28. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  29. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
  30. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -2
  31. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +0 -2
  32. data/lib/rubocop/cop/generator.rb +6 -0
  33. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  34. data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
  35. data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -2
  36. data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
  37. data/lib/rubocop/cop/internal_affairs/location_expression.rb +2 -1
  38. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -4
  39. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +3 -2
  40. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  41. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +63 -0
  42. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +131 -0
  43. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +229 -0
  44. data/lib/rubocop/cop/internal_affairs/node_type_multiple_predicates.rb +126 -0
  45. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
  46. data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +1 -1
  47. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +90 -0
  48. data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +48 -0
  49. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  50. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  51. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +11 -2
  52. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
  53. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +0 -2
  54. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
  55. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  56. data/lib/rubocop/cop/internal_affairs.rb +6 -0
  57. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +6 -2
  58. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -9
  59. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  60. data/lib/rubocop/cop/layout/begin_end_alignment.rb +0 -1
  61. data/lib/rubocop/cop/layout/block_alignment.rb +3 -2
  62. data/lib/rubocop/cop/layout/class_structure.rb +9 -9
  63. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  64. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  65. data/lib/rubocop/cop/layout/else_alignment.rb +2 -2
  66. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -3
  67. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +7 -11
  68. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +3 -3
  69. data/lib/rubocop/cop/layout/empty_lines_around_begin_body.rb +5 -6
  70. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +4 -5
  71. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +23 -1
  72. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  73. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  74. data/lib/rubocop/cop/layout/first_argument_indentation.rb +3 -8
  75. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
  76. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -7
  77. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +1 -1
  78. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  79. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +2 -2
  80. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -4
  81. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -1
  82. data/lib/rubocop/cop/layout/indentation_width.rb +11 -12
  83. data/lib/rubocop/cop/layout/leading_comment_space.rb +71 -1
  84. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +11 -2
  85. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  86. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -2
  87. data/lib/rubocop/cop/layout/line_length.rb +119 -4
  88. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +1 -1
  89. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +25 -0
  90. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +2 -1
  91. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +4 -4
  92. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
  93. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -3
  94. data/lib/rubocop/cop/layout/parameter_alignment.rb +3 -4
  95. data/lib/rubocop/cop/layout/redundant_line_break.rb +10 -41
  96. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -3
  97. data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -1
  98. data/lib/rubocop/cop/layout/space_after_colon.rb +2 -2
  99. data/lib/rubocop/cop/layout/space_after_comma.rb +1 -1
  100. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
  101. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  102. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -1
  103. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  104. data/lib/rubocop/cop/layout/space_around_operators.rb +19 -20
  105. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
  106. data/lib/rubocop/cop/layout/space_before_comma.rb +1 -1
  107. data/lib/rubocop/cop/layout/space_before_semicolon.rb +1 -1
  108. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +6 -0
  109. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
  110. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +4 -0
  111. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +0 -1
  112. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
  113. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  114. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  115. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +119 -0
  116. data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -3
  117. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  118. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +10 -12
  119. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  120. data/lib/rubocop/cop/lint/circular_argument_reference.rb +6 -0
  121. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +3 -3
  122. data/lib/rubocop/cop/lint/constant_reassignment.rb +148 -0
  123. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
  124. data/lib/rubocop/cop/lint/debugger.rb +1 -1
  125. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +2 -1
  126. data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
  127. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +1 -1
  128. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +1 -1
  129. data/lib/rubocop/cop/lint/duplicate_set_element.rb +87 -0
  130. data/lib/rubocop/cop/lint/empty_ensure.rb +1 -1
  131. data/lib/rubocop/cop/lint/empty_expression.rb +0 -2
  132. data/lib/rubocop/cop/lint/empty_file.rb +0 -2
  133. data/lib/rubocop/cop/lint/ensure_return.rb +1 -4
  134. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  135. data/lib/rubocop/cop/lint/float_comparison.rb +20 -9
  136. data/lib/rubocop/cop/lint/float_out_of_range.rb +2 -4
  137. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +2 -2
  138. data/lib/rubocop/cop/lint/hash_new_with_keyword_arguments_as_default.rb +55 -0
  139. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +11 -5
  140. data/lib/rubocop/cop/lint/interpolation_check.rb +9 -0
  141. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +8 -14
  142. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -0
  143. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +1 -1
  144. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +49 -8
  145. data/lib/rubocop/cop/lint/missing_super.rb +2 -2
  146. data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -6
  147. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -1
  148. data/lib/rubocop/cop/lint/nested_method_definition.rb +9 -5
  149. data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -1
  150. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +2 -2
  151. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +12 -3
  152. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -3
  153. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  154. data/lib/rubocop/cop/lint/number_conversion.rb +0 -1
  155. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -2
  156. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +93 -0
  157. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -2
  158. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +3 -2
  159. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +6 -11
  160. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  161. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  162. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +13 -8
  163. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -7
  164. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +2 -2
  165. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +231 -0
  166. data/lib/rubocop/cop/lint/refinement_import_methods.rb +1 -1
  167. data/lib/rubocop/cop/lint/regexp_as_condition.rb +0 -1
  168. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
  169. data/lib/rubocop/cop/lint/rescue_type.rb +3 -7
  170. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -1
  171. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +109 -41
  172. data/lib/rubocop/cop/lint/self_assignment.rb +8 -10
  173. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  174. data/lib/rubocop/cop/lint/shared_mutable_default.rb +65 -0
  175. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  176. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
  177. data/lib/rubocop/cop/lint/symbol_conversion.rb +2 -2
  178. data/lib/rubocop/cop/lint/syntax.rb +4 -1
  179. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
  180. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +1 -1
  181. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -1
  182. data/lib/rubocop/cop/lint/unreachable_code.rb +51 -2
  183. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  184. data/lib/rubocop/cop/lint/unused_method_argument.rb +18 -2
  185. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  186. data/lib/rubocop/cop/lint/useless_access_modifier.rb +4 -4
  187. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
  188. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +80 -0
  189. data/lib/rubocop/cop/lint/useless_defined.rb +55 -0
  190. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +4 -0
  191. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  192. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +2 -1
  193. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
  194. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -2
  195. data/lib/rubocop/cop/lint/useless_setter_call.rb +14 -25
  196. data/lib/rubocop/cop/lint/void.rb +8 -11
  197. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  198. data/lib/rubocop/cop/metrics/class_length.rb +9 -9
  199. data/lib/rubocop/cop/metrics/collection_literal_length.rb +7 -0
  200. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +5 -2
  201. data/lib/rubocop/cop/metrics/method_length.rb +8 -1
  202. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  203. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  204. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  205. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -3
  206. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  207. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  208. data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
  209. data/lib/rubocop/cop/mixin/check_assignment.rb +4 -12
  210. data/lib/rubocop/cop/mixin/check_line_breakable.rb +20 -10
  211. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +49 -0
  212. data/lib/rubocop/cop/mixin/comments_help.rb +8 -3
  213. data/lib/rubocop/cop/mixin/dig_help.rb +27 -0
  214. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
  215. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -2
  216. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +22 -22
  217. data/lib/rubocop/cop/mixin/hash_subset.rb +188 -0
  218. data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
  219. data/lib/rubocop/cop/mixin/line_length_help.rb +5 -4
  220. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  221. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +5 -9
  222. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  223. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  224. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +68 -30
  225. data/lib/rubocop/cop/mixin/range_help.rb +3 -4
  226. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  227. data/lib/rubocop/cop/mixin/statement_modifier.rb +11 -5
  228. data/lib/rubocop/cop/mixin/string_help.rb +2 -2
  229. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  230. data/lib/rubocop/cop/mixin/target_ruby_version.rb +17 -1
  231. data/lib/rubocop/cop/mixin/trailing_comma.rb +3 -3
  232. data/lib/rubocop/cop/naming/accessor_method_name.rb +6 -6
  233. data/lib/rubocop/cop/naming/block_forwarding.rb +20 -16
  234. data/lib/rubocop/cop/naming/constant_name.rb +6 -7
  235. data/lib/rubocop/cop/naming/file_name.rb +0 -2
  236. data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
  237. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +11 -12
  238. data/lib/rubocop/cop/naming/predicate_name.rb +45 -1
  239. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +6 -14
  240. data/lib/rubocop/cop/naming/variable_name.rb +3 -4
  241. data/lib/rubocop/cop/naming/variable_number.rb +2 -3
  242. data/lib/rubocop/cop/offense.rb +4 -5
  243. data/lib/rubocop/cop/security/compound_hash.rb +2 -0
  244. data/lib/rubocop/cop/security/yaml_load.rb +3 -2
  245. data/lib/rubocop/cop/style/access_modifier_declarations.rb +96 -28
  246. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  247. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
  248. data/lib/rubocop/cop/style/and_or.rb +1 -1
  249. data/lib/rubocop/cop/style/arguments_forwarding.rb +78 -22
  250. data/lib/rubocop/cop/style/array_first_last.rb +18 -2
  251. data/lib/rubocop/cop/style/array_intersect.rb +5 -4
  252. data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
  253. data/lib/rubocop/cop/style/block_delimiters.rb +49 -19
  254. data/lib/rubocop/cop/style/case_like_if.rb +8 -11
  255. data/lib/rubocop/cop/style/class_and_module_children.rb +6 -3
  256. data/lib/rubocop/cop/style/collection_compact.rb +10 -10
  257. data/lib/rubocop/cop/style/collection_methods.rb +1 -1
  258. data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
  259. data/lib/rubocop/cop/style/combinable_loops.rb +9 -2
  260. data/lib/rubocop/cop/style/commented_keyword.rb +17 -1
  261. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
  262. data/lib/rubocop/cop/style/conditional_assignment.rb +26 -26
  263. data/lib/rubocop/cop/style/constant_visibility.rb +3 -12
  264. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  265. data/lib/rubocop/cop/style/dig_chain.rb +89 -0
  266. data/lib/rubocop/cop/style/documentation.rb +1 -1
  267. data/lib/rubocop/cop/style/double_negation.rb +3 -3
  268. data/lib/rubocop/cop/style/each_for_simple_loop.rb +4 -7
  269. data/lib/rubocop/cop/style/each_with_object.rb +2 -3
  270. data/lib/rubocop/cop/style/empty_else.rb +4 -2
  271. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  272. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  273. data/lib/rubocop/cop/style/endless_method.rb +1 -14
  274. data/lib/rubocop/cop/style/eval_with_location.rb +2 -2
  275. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -3
  276. data/lib/rubocop/cop/style/explicit_block_argument.rb +15 -2
  277. data/lib/rubocop/cop/style/exponential_notation.rb +1 -1
  278. data/lib/rubocop/cop/style/fetch_env_var.rb +2 -1
  279. data/lib/rubocop/cop/style/file_null.rb +89 -0
  280. data/lib/rubocop/cop/style/file_touch.rb +75 -0
  281. data/lib/rubocop/cop/style/float_division.rb +8 -4
  282. data/lib/rubocop/cop/style/for.rb +0 -1
  283. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  284. data/lib/rubocop/cop/style/global_vars.rb +1 -3
  285. data/lib/rubocop/cop/style/guard_clause.rb +16 -3
  286. data/lib/rubocop/cop/style/hash_conversion.rb +1 -2
  287. data/lib/rubocop/cop/style/hash_each_methods.rb +9 -6
  288. data/lib/rubocop/cop/style/hash_except.rb +35 -147
  289. data/lib/rubocop/cop/style/hash_slice.rb +80 -0
  290. data/lib/rubocop/cop/style/hash_syntax.rb +8 -5
  291. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  292. data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
  293. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
  294. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -3
  295. data/lib/rubocop/cop/style/if_with_semicolon.rb +26 -11
  296. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  297. data/lib/rubocop/cop/style/inverse_methods.rb +6 -7
  298. data/lib/rubocop/cop/style/it_assignment.rb +36 -0
  299. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
  300. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  301. data/lib/rubocop/cop/style/lambda.rb +1 -1
  302. data/lib/rubocop/cop/style/lambda_call.rb +3 -2
  303. data/lib/rubocop/cop/style/map_into_array.rb +60 -9
  304. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  305. data/lib/rubocop/cop/style/map_to_set.rb +3 -2
  306. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +32 -20
  307. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -0
  308. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +8 -11
  309. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +2 -4
  310. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  311. data/lib/rubocop/cop/style/missing_else.rb +2 -0
  312. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +33 -3
  313. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  314. data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
  315. data/lib/rubocop/cop/style/multiple_comparison.rb +52 -51
  316. data/lib/rubocop/cop/style/mutable_constant.rb +7 -8
  317. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -5
  318. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  319. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +2 -2
  320. data/lib/rubocop/cop/style/nested_ternary_operator.rb +5 -4
  321. data/lib/rubocop/cop/style/not.rb +1 -1
  322. data/lib/rubocop/cop/style/object_then.rb +14 -15
  323. data/lib/rubocop/cop/style/one_line_conditional.rb +29 -4
  324. data/lib/rubocop/cop/style/open_struct_use.rb +5 -5
  325. data/lib/rubocop/cop/style/operator_method_call.rb +25 -7
  326. data/lib/rubocop/cop/style/or_assignment.rb +3 -6
  327. data/lib/rubocop/cop/style/parallel_assignment.rb +9 -18
  328. data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
  329. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  330. data/lib/rubocop/cop/style/proc.rb +1 -2
  331. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  332. data/lib/rubocop/cop/style/raise_args.rb +7 -5
  333. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  334. data/lib/rubocop/cop/style/redundant_argument.rb +3 -1
  335. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -1
  336. data/lib/rubocop/cop/style/redundant_begin.rb +5 -1
  337. data/lib/rubocop/cop/style/redundant_condition.rb +39 -24
  338. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +2 -1
  339. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -10
  340. data/lib/rubocop/cop/style/redundant_each.rb +1 -1
  341. data/lib/rubocop/cop/style/redundant_exception.rb +2 -2
  342. data/lib/rubocop/cop/style/redundant_format.rb +238 -0
  343. data/lib/rubocop/cop/style/redundant_freeze.rb +2 -2
  344. data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
  345. data/lib/rubocop/cop/style/redundant_line_continuation.rb +56 -17
  346. data/lib/rubocop/cop/style/redundant_parentheses.rb +37 -25
  347. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
  348. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
  349. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  350. data/lib/rubocop/cop/style/redundant_return.rb +2 -2
  351. data/lib/rubocop/cop/style/redundant_self.rb +8 -15
  352. data/lib/rubocop/cop/style/redundant_self_assignment.rb +20 -32
  353. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +4 -4
  354. data/lib/rubocop/cop/style/redundant_sort.rb +3 -3
  355. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -2
  356. data/lib/rubocop/cop/style/require_order.rb +1 -1
  357. data/lib/rubocop/cop/style/rescue_modifier.rb +15 -4
  358. data/lib/rubocop/cop/style/return_nil.rb +1 -1
  359. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  360. data/lib/rubocop/cop/style/safe_navigation.rb +105 -51
  361. data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
  362. data/lib/rubocop/cop/style/select_by_regexp.rb +10 -7
  363. data/lib/rubocop/cop/style/self_assignment.rb +11 -17
  364. data/lib/rubocop/cop/style/semicolon.rb +2 -2
  365. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
  366. data/lib/rubocop/cop/style/signal_exception.rb +2 -3
  367. data/lib/rubocop/cop/style/single_argument_dig.rb +9 -5
  368. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  369. data/lib/rubocop/cop/style/single_line_do_end_block.rb +12 -3
  370. data/lib/rubocop/cop/style/single_line_methods.rb +3 -4
  371. data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
  372. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -5
  373. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  374. data/lib/rubocop/cop/style/string_concatenation.rb +14 -13
  375. data/lib/rubocop/cop/style/string_literals.rb +1 -1
  376. data/lib/rubocop/cop/style/string_methods.rb +1 -1
  377. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  378. data/lib/rubocop/cop/style/super_arguments.rb +65 -17
  379. data/lib/rubocop/cop/style/swap_values.rb +4 -15
  380. data/lib/rubocop/cop/style/ternary_parentheses.rb +26 -5
  381. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
  382. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -1
  383. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +4 -4
  384. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  385. data/lib/rubocop/cop/style/variable_interpolation.rb +1 -2
  386. data/lib/rubocop/cop/style/while_until_modifier.rb +0 -1
  387. data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
  388. data/lib/rubocop/cop/style/yoda_expression.rb +2 -1
  389. data/lib/rubocop/cop/team.rb +8 -1
  390. data/lib/rubocop/cop/util.rb +12 -5
  391. data/lib/rubocop/cop/utils/format_string.rb +7 -5
  392. data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
  393. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  394. data/lib/rubocop/cop/variable_force/variable.rb +18 -2
  395. data/lib/rubocop/cop/variable_force/variable_table.rb +5 -5
  396. data/lib/rubocop/cop/variable_force.rb +4 -10
  397. data/lib/rubocop/cops_documentation_generator.rb +100 -51
  398. data/lib/rubocop/directive_comment.rb +44 -10
  399. data/lib/rubocop/file_finder.rb +9 -4
  400. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  401. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  402. data/lib/rubocop/lsp/diagnostic.rb +189 -0
  403. data/lib/rubocop/lsp/logger.rb +2 -2
  404. data/lib/rubocop/lsp/routes.rb +7 -23
  405. data/lib/rubocop/lsp/runtime.rb +18 -48
  406. data/lib/rubocop/lsp/server.rb +0 -3
  407. data/lib/rubocop/lsp/stdin_runner.rb +83 -0
  408. data/lib/rubocop/magic_comment.rb +3 -3
  409. data/lib/rubocop/options.rb +28 -12
  410. data/lib/rubocop/path_util.rb +15 -8
  411. data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
  412. data/lib/rubocop/plugin/load_error.rb +26 -0
  413. data/lib/rubocop/plugin/loader.rb +100 -0
  414. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  415. data/lib/rubocop/plugin.rb +39 -0
  416. data/lib/rubocop/rake_task.rb +4 -1
  417. data/lib/rubocop/result_cache.rb +13 -13
  418. data/lib/rubocop/rspec/cop_helper.rb +9 -0
  419. data/lib/rubocop/rspec/expect_offense.rb +7 -2
  420. data/lib/rubocop/rspec/shared_contexts.rb +4 -1
  421. data/lib/rubocop/rspec/support.rb +1 -2
  422. data/lib/rubocop/runner.rb +22 -12
  423. data/lib/rubocop/server/cache.rb +39 -1
  424. data/lib/rubocop/server/cli.rb +2 -2
  425. data/lib/rubocop/server/core.rb +1 -0
  426. data/lib/rubocop/target_finder.rb +1 -0
  427. data/lib/rubocop/target_ruby.rb +28 -13
  428. data/lib/rubocop/version.rb +42 -6
  429. data/lib/rubocop/yaml_duplication_checker.rb +20 -27
  430. data/lib/rubocop.rb +29 -0
  431. data/lib/ruby_lsp/rubocop/addon.rb +75 -0
  432. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +47 -0
  433. metadata +75 -19
  434. data/lib/rubocop/rspec/host_environment_simulation_helper.rb +0 -28
@@ -66,14 +66,12 @@ module RuboCop
66
66
  # Assignment of self.x
67
67
 
68
68
  def on_or_asgn(node)
69
- lhs, _rhs = *node
70
- allow_self(lhs)
69
+ allow_self(node.lhs)
71
70
  end
72
71
  alias on_and_asgn on_or_asgn
73
72
 
74
73
  def on_op_asgn(node)
75
- lhs, _op, _rhs = *node
76
- allow_self(lhs)
74
+ allow_self(node.lhs)
77
75
  end
78
76
 
79
77
  # Using self.x to distinguish from local variable x
@@ -92,13 +90,11 @@ module RuboCop
92
90
  end
93
91
 
94
92
  def on_masgn(node)
95
- lhs, rhs = *node
96
- add_masgn_lhs_variables(rhs, lhs)
93
+ add_masgn_lhs_variables(node.rhs, node.lhs)
97
94
  end
98
95
 
99
96
  def on_lvasgn(node)
100
- lhs, rhs = *node
101
- add_lhs_to_local_variables_scopes(rhs, lhs)
97
+ add_lhs_to_local_variables_scopes(node.rhs, node.lhs)
102
98
  end
103
99
 
104
100
  def on_in_pattern(node)
@@ -127,12 +123,10 @@ module RuboCop
127
123
  # Allow conditional nodes to use `self` in the condition if that variable
128
124
  # name is used in an `lvasgn` or `masgn` within the `if`.
129
125
  node.child_nodes.each do |child_node|
130
- lhs, _rhs = *child_node
131
-
132
126
  if child_node.lvasgn_type?
133
- add_lhs_to_local_variables_scopes(node.condition, lhs)
127
+ add_lhs_to_local_variables_scopes(node.condition, child_node.lhs)
134
128
  elsif child_node.masgn_type?
135
- add_masgn_lhs_variables(node.condition, lhs)
129
+ add_masgn_lhs_variables(node.condition, child_node.lhs)
136
130
  end
137
131
  end
138
132
  end
@@ -181,9 +175,8 @@ module RuboCop
181
175
  def on_argument(node)
182
176
  if node.mlhs_type?
183
177
  on_args(node)
184
- else
185
- name, = *node
186
- @local_variables_scopes[node] << name
178
+ elsif node.respond_to?(:name)
179
+ @local_variables_scopes[node] << node.name
187
180
  end
188
181
  end
189
182
 
@@ -21,12 +21,8 @@ module RuboCop
21
21
  # args += foo
22
22
  # hash.merge!(other)
23
23
  #
24
- # # bad
25
- # self.foo = foo.concat(ary)
26
- #
27
24
  # # good
28
25
  # foo.concat(ary)
29
- # self.foo += ary
30
26
  #
31
27
  class RedundantSelfAssignment < Base
32
28
  include RangeHelp
@@ -49,19 +45,31 @@ module RuboCop
49
45
  gvasgn: :gvar
50
46
  }.freeze
51
47
 
48
+ # @!method redundant_self_assignment?
49
+ def_node_matcher :redundant_self_assignment?, <<~PATTERN
50
+ (call
51
+ %1 _
52
+ (call
53
+ (call
54
+ %1 %2) #method_returning_self?
55
+ ...))
56
+ PATTERN
57
+
58
+ # rubocop:disable Metrics/AbcSize
52
59
  def on_lvasgn(node)
53
- lhs, rhs = *node
54
- receiver, method_name, = *rhs
55
- return unless receiver && method_returning_self?(method_name)
60
+ return unless (rhs = node.rhs)
61
+ return unless rhs.call_type? && method_returning_self?(rhs.method_name)
62
+ return unless (receiver = rhs.receiver)
56
63
 
57
64
  receiver_type = ASSIGNMENT_TYPE_TO_RECEIVER_TYPE[node.type]
58
- return unless receiver.type == receiver_type && receiver.children.first == lhs
65
+ return unless receiver.type == receiver_type && receiver.children.first == node.lhs
59
66
 
60
- message = format(MSG, method_name: method_name)
67
+ message = format(MSG, method_name: rhs.method_name)
61
68
  add_offense(node.loc.operator, message: message) do |corrector|
62
69
  corrector.replace(node, rhs.source)
63
70
  end
64
71
  end
72
+ # rubocop:enable Metrics/AbcSize
65
73
  alias on_ivasgn on_lvasgn
66
74
  alias on_cvasgn on_lvasgn
67
75
  alias on_gvasgn on_lvasgn
@@ -75,6 +83,7 @@ module RuboCop
75
83
  corrector.remove(correction_range(node))
76
84
  end
77
85
  end
86
+ alias on_csend on_send
78
87
 
79
88
  private
80
89
 
@@ -82,31 +91,10 @@ module RuboCop
82
91
  METHODS_RETURNING_SELF.include?(method_name)
83
92
  end
84
93
 
85
- # @!method redundant_self_assignment?(node, method_name)
86
- def_node_matcher :redundant_self_assignment?, <<~PATTERN
87
- (send
88
- (self) _
89
- (send
90
- (send
91
- {(self) nil?} %1) #method_returning_self?
92
- ...))
93
- PATTERN
94
-
95
- # @!method redundant_nonself_assignment?(node, receiver, method_name)
96
- def_node_matcher :redundant_nonself_assignment?, <<~PATTERN
97
- (send
98
- %1 _
99
- (send
100
- (send
101
- %1 %2) #method_returning_self?
102
- ...))
103
- PATTERN
104
-
105
94
  def redundant_assignment?(node)
106
- receiver_name = node.method_name.to_s[0...-1].to_sym
95
+ receiver_name = node.method_name.to_s.delete_suffix('=').to_sym
107
96
 
108
- redundant_self_assignment?(node, receiver_name) ||
109
- redundant_nonself_assignment?(node, node.receiver, receiver_name)
97
+ redundant_self_assignment?(node, node.receiver, receiver_name)
110
98
  end
111
99
 
112
100
  def correction_range(node)
@@ -23,7 +23,6 @@ module RuboCop
23
23
  # foo = bar unless condition
24
24
  #
25
25
  class RedundantSelfAssignmentBranch < Base
26
- include RangeHelp
27
26
  extend AutoCorrector
28
27
 
29
28
  MSG = 'Remove the self-assignment branch.'
@@ -34,16 +33,17 @@ module RuboCop
34
33
  PATTERN
35
34
 
36
35
  def on_lvasgn(node)
37
- variable, expression = *node
36
+ expression = node.expression
37
+
38
38
  return unless use_if_and_else_branch?(expression)
39
39
 
40
40
  if_branch = expression.if_branch
41
41
  else_branch = expression.else_branch
42
42
  return if inconvertible_to_modifier?(if_branch, else_branch)
43
43
 
44
- if self_assign?(variable, if_branch)
44
+ if self_assign?(node.name, if_branch)
45
45
  register_offense(expression, if_branch, else_branch, 'unless')
46
- elsif self_assign?(variable, else_branch)
46
+ elsif self_assign?(node.name, else_branch)
47
47
  register_offense(expression, else_branch, if_branch, 'if')
48
48
  end
49
49
  end
@@ -93,9 +93,9 @@ module RuboCop
93
93
  (call $(call _ $:sort_by _) ${:last :first})
94
94
  (send $(send _ $:sort_by _) ${:[] :at :slice} {(int 0) (int -1)})
95
95
 
96
- (call ({block numblock} $(call _ ${:sort_by :sort}) ...) ${:last :first})
96
+ (call (any_block $(call _ ${:sort_by :sort}) ...) ${:last :first})
97
97
  (call
98
- ({block numblock} $(call _ ${:sort_by :sort}) ...)
98
+ (any_block $(call _ ${:sort_by :sort}) ...)
99
99
  ${:[] :at :slice} {(int 0) (int -1)}
100
100
  )
101
101
  }
@@ -201,7 +201,7 @@ module RuboCop
201
201
  def with_logical_operator?(node)
202
202
  return false unless (parent = node.parent)
203
203
 
204
- parent.or_type? || parent.and_type?
204
+ parent.operator_keyword?
205
205
  end
206
206
  end
207
207
  end
@@ -41,7 +41,7 @@ module RuboCop
41
41
  MSG = 'Redundant escape of %<char>s inside string literal.'
42
42
 
43
43
  def on_str(node)
44
- return if node.parent&.regexp_type? || node.parent&.xstr_type? || node.character_literal?
44
+ return if node.parent&.type?(:regexp, :xstr) || node.character_literal?
45
45
 
46
46
  str_contents_range = str_contents_range(node)
47
47
 
@@ -147,7 +147,7 @@ module RuboCop
147
147
  end
148
148
 
149
149
  def heredoc?(node)
150
- (node.str_type? || node.dstr_type?) && node.heredoc?
150
+ node.type?(:str, :dstr) && node.heredoc?
151
151
  end
152
152
 
153
153
  def delimiter?(node, char)
@@ -103,7 +103,7 @@ module RuboCop
103
103
  next unless sibling.is_a?(AST::Node)
104
104
 
105
105
  sibling = sibling_node(sibling)
106
- break unless sibling&.send_type? && sibling&.method?(node.method_name)
106
+ break unless sibling&.send_type? && sibling.method?(node.method_name)
107
107
  break unless sibling.arguments? && !sibling.receiver
108
108
  break unless in_same_section?(sibling, node)
109
109
  break unless node.first_argument.str_type? && sibling.first_argument.str_type?
@@ -68,17 +68,16 @@ module RuboCop
68
68
  end
69
69
 
70
70
  def correct_rescue_block(corrector, node, parenthesized)
71
- operation, rescue_modifier, = *node
72
- *_, rescue_args = *rescue_modifier
71
+ operation = node.body
73
72
 
74
73
  node_indentation, node_offset = indentation_and_offset(node, parenthesized)
75
74
 
76
75
  corrector.remove(range_between(operation.source_range.end_pos, node.source_range.end_pos))
77
76
  corrector.insert_before(operation, "begin\n#{node_indentation}")
78
- corrector.insert_after(operation, <<~RESCUE_CLAUSE.chop)
77
+ corrector.insert_after(heredoc_end(operation) || operation, <<~RESCUE_CLAUSE.chop)
79
78
 
80
79
  #{node_offset}rescue
81
- #{node_indentation}#{rescue_args.source}
80
+ #{node_indentation}#{node.resbody_branches.first.body.source}
82
81
  #{node_offset}end
83
82
  RESCUE_CLAUSE
84
83
  end
@@ -92,6 +91,18 @@ module RuboCop
92
91
  end
93
92
  [node_indentation, node_offset]
94
93
  end
94
+
95
+ def heredoc_end(node)
96
+ return unless node.call_type?
97
+
98
+ heredoc = node.arguments.reverse.find do |argument|
99
+ argument.respond_to?(:heredoc?) && argument.heredoc?
100
+ end
101
+
102
+ return unless heredoc
103
+
104
+ heredoc.loc.heredoc_end
105
+ end
95
106
  end
96
107
  end
97
108
  end
@@ -83,7 +83,7 @@ module RuboCop
83
83
  end
84
84
 
85
85
  def scoped_node?(node)
86
- node.def_type? || node.defs_type? || node.lambda?
86
+ node.type?(:def, :defs) || node.lambda?
87
87
  end
88
88
 
89
89
  # @!method chained_send?(node)
@@ -3,7 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks if `return` or `return nil` is used in predicate method definitions.
6
+ # Checks for predicate method definitions that return `nil`.
7
+ # A predicate method should only return a boolean value.
7
8
  #
8
9
  # @safety
9
10
  # Autocorrection is marked as unsafe because the change of the return value
@@ -31,6 +32,24 @@ module RuboCop
31
32
  # do_something?
32
33
  # end
33
34
  #
35
+ # # bad
36
+ # def foo?
37
+ # if condition
38
+ # nil
39
+ # else
40
+ # true
41
+ # end
42
+ # end
43
+ #
44
+ # # good
45
+ # def foo?
46
+ # if condition
47
+ # false
48
+ # else
49
+ # true
50
+ # end
51
+ # end
52
+ #
34
53
  # @example AllowedMethods: ['foo?']
35
54
  # # good
36
55
  # def foo?
@@ -64,24 +83,25 @@ module RuboCop
64
83
  return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
65
84
  return unless (body = node.body)
66
85
 
67
- body.each_descendant(:return) do |return_node|
68
- register_offense(return_node, 'return false') if return_nil?(return_node)
69
- end
70
-
71
- return unless (nil_node = nil_node_at_the_end_of_method_body(body))
86
+ body.each_descendant(:return) { |return_node| handle_return(return_node) }
72
87
 
73
- register_offense(nil_node, 'false')
88
+ handle_implicit_return_values(body)
74
89
  end
75
90
  alias on_defs on_def
76
91
 
77
92
  private
78
93
 
79
- def nil_node_at_the_end_of_method_body(body)
80
- return body if body.nil_type?
81
- return unless body.begin_type?
82
- return unless (last_child = body.children.last)
94
+ def last_node_of_type(node, type)
95
+ return unless node
96
+ return node if node_type?(node, type)
97
+ return unless node.begin_type?
98
+ return unless (last_child = node.children.last)
99
+
100
+ last_child if last_child.is_a?(AST::Node) && node_type?(last_child, type)
101
+ end
83
102
 
84
- last_child if last_child.is_a?(AST::Node) && last_child.nil_type?
103
+ def node_type?(node, type)
104
+ node.type == type.to_sym
85
105
  end
86
106
 
87
107
  def register_offense(offense_node, replacement)
@@ -89,6 +109,28 @@ module RuboCop
89
109
  corrector.replace(offense_node, replacement)
90
110
  end
91
111
  end
112
+
113
+ def handle_implicit_return_values(node)
114
+ handle_if(last_node_of_type(node, :if))
115
+ handle_nil(last_node_of_type(node, :nil))
116
+ end
117
+
118
+ def handle_return(return_node)
119
+ register_offense(return_node, 'return false') if return_nil?(return_node)
120
+ end
121
+
122
+ def handle_nil(nil_node)
123
+ return unless nil_node
124
+
125
+ register_offense(nil_node, 'false')
126
+ end
127
+
128
+ def handle_if(if_node)
129
+ return unless if_node
130
+
131
+ handle_implicit_return_values(if_node.if_branch)
132
+ handle_implicit_return_values(if_node.else_branch)
133
+ end
92
134
  end
93
135
  end
94
136
  end
@@ -21,6 +21,11 @@ module RuboCop
21
21
  # We have limited the cop to not register an offense for method chains
22
22
  # that exceed this option's value.
23
23
  #
24
+ # NOTE: This cop will recognize offenses but not autocorrect code when the
25
+ # right hand side (RHS) of the `&&` statement is an `||` statement
26
+ # (eg. `foo && (foo.bar? || foo.baz?)`). It can be corrected
27
+ # manually by removing the `foo &&` and adding `&.` to each `foo` on the RHS.
28
+ #
24
29
  # @safety
25
30
  # Autocorrection is unsafe because if a value is `false`, the resulting
26
31
  # code will have different behavior or raise an error.
@@ -81,7 +86,7 @@ module RuboCop
81
86
  # foo.baz = bar if foo
82
87
  # foo.baz + bar if foo
83
88
  # foo.bar > 2 if foo
84
- class SafeNavigation < Base
89
+ class SafeNavigation < Base # rubocop:disable Metrics/ClassLength
85
90
  include NilMethods
86
91
  include RangeHelp
87
92
  extend AutoCorrector
@@ -121,50 +126,122 @@ module RuboCop
121
126
  }
122
127
  PATTERN
123
128
 
129
+ # @!method and_with_rhs_or?(node)
130
+ def_node_matcher :and_with_rhs_or?, '(and _ {or (begin or)})'
131
+
124
132
  # @!method not_nil_check?(node)
125
133
  def_node_matcher :not_nil_check?, '(send (send $_ :nil?) :!)'
126
134
 
135
+ # @!method and_inside_begin?(node)
136
+ def_node_matcher :and_inside_begin?, '`(begin and ...)'
137
+
138
+ # @!method strip_begin(node)
139
+ def_node_matcher :strip_begin, '{ (begin $!begin) $!(begin) }'
140
+
127
141
  def on_if(node)
128
142
  return if allowed_if_condition?(node)
129
143
 
130
- check_node(node)
144
+ checked_variable, receiver, method_chain, _method = extract_parts_from_if(node)
145
+ return unless offending_node?(node, checked_variable, method_chain, receiver)
146
+
147
+ body = extract_if_body(node)
148
+ method_call = receiver.parent
149
+
150
+ removal_ranges = [begin_range(node, body), end_range(node, body)]
151
+
152
+ report_offense(node, method_chain, method_call, *removal_ranges) do |corrector|
153
+ corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
154
+ end
131
155
  end
132
156
 
133
- def on_and(node)
134
- check_node(node)
157
+ def on_and(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
158
+ collect_and_clauses(node).each do |(lhs, lhs_operator_range), (rhs, _rhs_operator_range)|
159
+ lhs_not_nil_check = not_nil_check?(lhs)
160
+ lhs_receiver = lhs_not_nil_check || lhs
161
+ rhs_receiver = find_matching_receiver_invocation(strip_begin(rhs), lhs_receiver)
162
+
163
+ next if !cop_config['ConvertCodeThatCanStartToReturnNil'] && lhs_not_nil_check
164
+ next unless offending_node?(node, lhs_receiver, rhs, rhs_receiver)
165
+
166
+ # Since we are evaluating every clause in potentially a complex chain of `and` nodes,
167
+ # we need to ensure that there isn't an object check happening
168
+ lhs_method_chain = find_method_chain(lhs_receiver)
169
+ next unless lhs_method_chain == lhs_receiver || lhs_not_nil_check
170
+
171
+ report_offense(
172
+ node,
173
+ rhs, rhs_receiver,
174
+ range_with_surrounding_space(range: lhs.source_range, side: :right),
175
+ range_with_surrounding_space(range: lhs_operator_range, side: :right),
176
+ offense_range: range_between(lhs.source_range.begin_pos, rhs.source_range.end_pos)
177
+ )
178
+ end
179
+ end
180
+
181
+ def report_offense(node, rhs, rhs_receiver, *removal_ranges, offense_range: node)
182
+ add_offense(offense_range) do |corrector|
183
+ # If the RHS is an `or` we cannot safely autocorrect because in order to remove
184
+ # the non-nil check we need to add safe-navs to all clauses where the receiver is used
185
+ next if and_with_rhs_or?(node)
186
+
187
+ removal_ranges.each { |range| corrector.remove(range) }
188
+ yield corrector if block_given?
189
+
190
+ handle_comments(corrector, node, rhs)
191
+
192
+ add_safe_nav_to_all_methods_in_chain(corrector, rhs_receiver, rhs)
193
+ end
135
194
  end
136
195
 
137
196
  private
138
197
 
139
- def check_node(node)
140
- checked_variable, receiver, method_chain, method = extract_parts(node)
141
- return if receiver != checked_variable || receiver.nil?
142
- return if use_var_only_in_unless_modifier?(node, checked_variable)
143
- return if chain_length(method_chain, method) > max_chain_length
144
- return if unsafe_method_used?(method_chain, method)
145
- return if method_chain.method?(:empty?)
198
+ def find_method_chain(node)
199
+ return node unless node&.parent&.call_type?
146
200
 
147
- add_offense(node) { |corrector| autocorrect(corrector, node) }
201
+ find_method_chain(node.parent)
148
202
  end
149
203
 
150
- def use_var_only_in_unless_modifier?(node, variable)
151
- node.if_type? && node.unless? && !method_called?(variable)
204
+ def collect_and_clauses(node)
205
+ # Collect the lhs, operator and rhs of all `and` nodes
206
+ # `and` nodes can be nested and can contain `begin` nodes
207
+ # This gives us a source-ordered list of clauses that is then used to look
208
+ # for matching receivers as well as operator locations for offense and corrections
209
+ node.each_descendant(:and)
210
+ .inject(and_parts(node)) { |nodes, and_node| concat_nodes(nodes, and_node) }
211
+ .sort_by { |a| a.is_a?(RuboCop::AST::Node) ? a.source_range.begin_pos : a.begin_pos }
212
+ .each_slice(2)
213
+ .each_cons(2)
152
214
  end
153
215
 
154
- def autocorrect(corrector, node)
155
- body = extract_body(node)
156
- method_call = method_call(node)
216
+ def concat_nodes(nodes, and_node)
217
+ return nodes if and_node.each_ancestor(:block).any?
157
218
 
158
- corrector.remove(begin_range(node, body))
159
- corrector.remove(end_range(node, body))
160
- corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
161
- handle_comments(corrector, node, method_call)
219
+ nodes.concat(and_parts(and_node))
220
+ end
162
221
 
163
- add_safe_nav_to_all_methods_in_chain(corrector, method_call, body)
222
+ def and_parts(node)
223
+ parts = [node.loc.operator]
224
+ parts << node.rhs unless and_inside_begin?(node.rhs)
225
+ parts << node.lhs unless node.lhs.and_type? || and_inside_begin?(node.lhs)
226
+ parts
164
227
  end
165
228
 
166
- def extract_body(node)
167
- if node.if_type? && node.ternary?
229
+ def offending_node?(node, lhs_receiver, rhs, rhs_receiver) # rubocop:disable Metrics/CyclomaticComplexity
230
+ return false if lhs_receiver != rhs_receiver || rhs_receiver.nil?
231
+ return false if use_var_only_in_unless_modifier?(node, lhs_receiver)
232
+ return false if chain_length(rhs, rhs_receiver) > max_chain_length
233
+ return false if unsafe_method_used?(rhs, rhs_receiver.parent)
234
+ return false if rhs.send_type? && rhs.method?(:empty?)
235
+
236
+ true
237
+ end
238
+
239
+ def use_var_only_in_unless_modifier?(node, variable)
240
+ node.if_type? && node.unless? && !method_called?(variable)
241
+ end
242
+
243
+ def extract_if_body(node)
244
+ if node.ternary?
168
245
  node.branches.find { |branch| !branch.nil_type? }
169
246
  else
170
247
  node.node_parts[1]
@@ -201,20 +278,6 @@ module RuboCop
201
278
  node.else? || node.elsif?
202
279
  end
203
280
 
204
- def method_call(node)
205
- _checked_variable, matching_receiver, = extract_parts(node)
206
- matching_receiver.parent
207
- end
208
-
209
- def extract_parts(node)
210
- case node.type
211
- when :if
212
- extract_parts_from_if(node)
213
- when :and
214
- extract_parts_from_and(node)
215
- end
216
- end
217
-
218
281
  def extract_parts_from_if(node)
219
282
  variable, receiver =
220
283
  if node.ternary?
@@ -230,16 +293,6 @@ module RuboCop
230
293
  [checked_variable, matching_receiver, receiver, method]
231
294
  end
232
295
 
233
- def extract_parts_from_and(node)
234
- checked_variable, rhs = *node
235
- if cop_config['ConvertCodeThatCanStartToReturnNil']
236
- checked_variable = not_nil_check?(checked_variable) || checked_variable
237
- end
238
-
239
- checked_variable, matching_receiver, method = extract_common_parts(rhs, checked_variable)
240
- [checked_variable, matching_receiver, rhs, method]
241
- end
242
-
243
296
  def extract_common_parts(method_chain, checked_variable)
244
297
  matching_receiver = find_matching_receiver_invocation(method_chain, checked_variable)
245
298
 
@@ -249,7 +302,7 @@ module RuboCop
249
302
  end
250
303
 
251
304
  def find_matching_receiver_invocation(method_chain, checked_variable)
252
- return nil unless method_chain
305
+ return nil unless method_chain.respond_to?(:receiver)
253
306
 
254
307
  receiver = method_chain.receiver
255
308
 
@@ -259,7 +312,7 @@ module RuboCop
259
312
  end
260
313
 
261
314
  def chain_length(method_chain, method)
262
- method.each_ancestor(:send).inject(1) do |total, ancestor|
315
+ method.each_ancestor(:call).inject(0) do |total, ancestor|
263
316
  break total + 1 if ancestor == method_chain
264
317
 
265
318
  total + 1
@@ -270,7 +323,7 @@ module RuboCop
270
323
  return true if unsafe_method?(method)
271
324
 
272
325
  method.each_ancestor(:send).any? do |ancestor|
273
- break true unless config.for_cop('Lint/SafeNavigationChain')['Enabled']
326
+ break true unless config.cop_enabled?('Lint/SafeNavigationChain')
274
327
 
275
328
  break true if unsafe_method?(ancestor)
276
329
  break true if nil_methods.include?(ancestor.method_name)
@@ -310,6 +363,7 @@ module RuboCop
310
363
  start_method.each_ancestor do |ancestor|
311
364
  break unless %i[send block].include?(ancestor.type)
312
365
  next unless ancestor.send_type?
366
+ next if ancestor.safe_navigation?
313
367
 
314
368
  corrector.insert_before(ancestor.loc.dot, '&')
315
369
 
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Enforces safe navigation chains length to not exceed the configured maximum.
7
+ # The longer the chain is, the harder it becomes to track what on it could be
8
+ # returning `nil`.
9
+ #
10
+ # There is a potential interplay with `Style/SafeNavigation` - if both are enabled
11
+ # and their settings are "incompatible", one of the cops will complain about what
12
+ # the other proposes.
13
+ #
14
+ # E.g. if `Style/SafeNavigation` is configured with `MaxChainLength: 2` (default)
15
+ # and this cop is configured with `Max: 1`, then for `foo.bar.baz if foo` the former
16
+ # will suggest `foo&.bar&.baz`, which is an offense for the latter.
17
+ #
18
+ # @example Max: 2 (default)
19
+ # # bad
20
+ # user&.address&.zip&.upcase
21
+ #
22
+ # # good
23
+ # user&.address&.zip
24
+ # user.address.zip if user
25
+ #
26
+ class SafeNavigationChainLength < Base
27
+ MSG = 'Avoid safe navigation chains longer than %<max>d calls.'
28
+
29
+ def on_csend(node)
30
+ safe_navigation_chains = safe_navigation_chains(node)
31
+ return if safe_navigation_chains.size < max
32
+
33
+ add_offense(safe_navigation_chains.last, message: format(MSG, max: max))
34
+ end
35
+
36
+ private
37
+
38
+ def safe_navigation_chains(node)
39
+ node.each_ancestor.with_object([]) do |parent, chains|
40
+ break chains unless parent.csend_type?
41
+
42
+ chains << parent
43
+ end
44
+ end
45
+
46
+ def max
47
+ cop_config['Max'] || 2
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end