rubocop 1.66.1 → 1.69.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (246) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +96 -12
  4. data/config/internal_affairs.yml +11 -0
  5. data/lib/rubocop/cached_data.rb +12 -4
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
  7. data/lib/rubocop/cli/command/execute_runner.rb +1 -1
  8. data/lib/rubocop/cli/command/lsp.rb +2 -2
  9. data/lib/rubocop/cli/command/version.rb +2 -2
  10. data/lib/rubocop/config_loader_resolver.rb +3 -3
  11. data/lib/rubocop/config_validator.rb +2 -1
  12. data/lib/rubocop/cop/autocorrect_logic.rb +22 -2
  13. data/lib/rubocop/cop/base.rb +7 -3
  14. data/lib/rubocop/cop/bundler/gem_filename.rb +0 -1
  15. data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
  16. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +0 -1
  17. data/lib/rubocop/cop/cop.rb +8 -0
  18. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
  19. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +1 -1
  20. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  21. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
  22. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -2
  23. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +0 -2
  24. data/lib/rubocop/cop/generator.rb +6 -0
  25. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  26. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -4
  27. data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +1 -1
  28. data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +46 -0
  29. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  30. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
  31. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +0 -2
  32. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  33. data/lib/rubocop/cop/internal_affairs.rb +17 -0
  34. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
  35. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -2
  36. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  37. data/lib/rubocop/cop/layout/begin_end_alignment.rb +0 -1
  38. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  39. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  40. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +2 -2
  41. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +2 -3
  42. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +3 -4
  43. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +3 -1
  44. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  45. data/lib/rubocop/cop/layout/indentation_width.rb +11 -12
  46. data/lib/rubocop/cop/layout/leading_comment_space.rb +71 -1
  47. data/lib/rubocop/cop/layout/line_length.rb +118 -4
  48. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  49. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
  50. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -3
  51. data/lib/rubocop/cop/layout/parameter_alignment.rb +3 -4
  52. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -35
  53. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -2
  54. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  55. data/lib/rubocop/cop/layout/space_around_operators.rb +16 -17
  56. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
  57. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +6 -0
  58. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
  59. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +4 -0
  60. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +0 -1
  61. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  62. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  63. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +10 -12
  64. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  65. data/lib/rubocop/cop/lint/circular_argument_reference.rb +6 -0
  66. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +2 -1
  67. data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
  68. data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
  69. data/lib/rubocop/cop/lint/empty_ensure.rb +1 -1
  70. data/lib/rubocop/cop/lint/empty_file.rb +0 -2
  71. data/lib/rubocop/cop/lint/ensure_return.rb +1 -4
  72. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  73. data/lib/rubocop/cop/lint/float_comparison.rb +15 -7
  74. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -3
  75. data/lib/rubocop/cop/lint/hash_new_with_keyword_arguments_as_default.rb +55 -0
  76. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
  77. data/lib/rubocop/cop/lint/interpolation_check.rb +9 -0
  78. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +8 -14
  79. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -0
  80. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +1 -1
  81. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
  82. data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -5
  83. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  84. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +2 -2
  85. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +8 -1
  86. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -3
  87. data/lib/rubocop/cop/lint/number_conversion.rb +0 -1
  88. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -2
  89. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +106 -0
  90. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -2
  91. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  92. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
  93. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  94. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +13 -8
  95. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -7
  96. data/lib/rubocop/cop/lint/refinement_import_methods.rb +1 -1
  97. data/lib/rubocop/cop/lint/regexp_as_condition.rb +0 -1
  98. data/lib/rubocop/cop/lint/rescue_type.rb +3 -7
  99. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +9 -0
  100. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +109 -41
  101. data/lib/rubocop/cop/lint/self_assignment.rb +8 -10
  102. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  103. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  104. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +91 -0
  105. data/lib/rubocop/cop/lint/unreachable_code.rb +51 -2
  106. data/lib/rubocop/cop/lint/unused_method_argument.rb +18 -2
  107. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  108. data/lib/rubocop/cop/lint/useless_defined.rb +55 -0
  109. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +4 -0
  110. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
  111. data/lib/rubocop/cop/lint/useless_setter_call.rb +14 -25
  112. data/lib/rubocop/cop/lint/void.rb +3 -2
  113. data/lib/rubocop/cop/metrics/class_length.rb +7 -7
  114. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +4 -1
  115. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  116. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -3
  117. data/lib/rubocop/cop/mixin/check_assignment.rb +4 -12
  118. data/lib/rubocop/cop/mixin/check_line_breakable.rb +10 -0
  119. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +49 -0
  120. data/lib/rubocop/cop/mixin/dig_help.rb +27 -0
  121. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
  122. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +3 -1
  123. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +5 -9
  124. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  125. data/lib/rubocop/cop/mixin/range_help.rb +0 -1
  126. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
  127. data/lib/rubocop/cop/mixin/target_ruby_version.rb +17 -1
  128. data/lib/rubocop/cop/naming/accessor_method_name.rb +6 -6
  129. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  130. data/lib/rubocop/cop/naming/constant_name.rb +6 -7
  131. data/lib/rubocop/cop/naming/file_name.rb +0 -2
  132. data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
  133. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +11 -12
  134. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  135. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +3 -11
  136. data/lib/rubocop/cop/naming/variable_name.rb +3 -4
  137. data/lib/rubocop/cop/naming/variable_number.rb +2 -3
  138. data/lib/rubocop/cop/offense.rb +4 -5
  139. data/lib/rubocop/cop/security/compound_hash.rb +1 -0
  140. data/lib/rubocop/cop/security/yaml_load.rb +3 -2
  141. data/lib/rubocop/cop/style/access_modifier_declarations.rb +64 -25
  142. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  143. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
  144. data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
  145. data/lib/rubocop/cop/style/array_intersect.rb +5 -4
  146. data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
  147. data/lib/rubocop/cop/style/block_delimiters.rb +41 -5
  148. data/lib/rubocop/cop/style/case_like_if.rb +8 -11
  149. data/lib/rubocop/cop/style/collection_compact.rb +10 -10
  150. data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
  151. data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
  152. data/lib/rubocop/cop/style/commented_keyword.rb +17 -1
  153. data/lib/rubocop/cop/style/conditional_assignment.rb +20 -22
  154. data/lib/rubocop/cop/style/constant_visibility.rb +3 -12
  155. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  156. data/lib/rubocop/cop/style/dig_chain.rb +89 -0
  157. data/lib/rubocop/cop/style/endless_method.rb +1 -14
  158. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  159. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -0
  160. data/lib/rubocop/cop/style/file_null.rb +73 -0
  161. data/lib/rubocop/cop/style/file_touch.rb +75 -0
  162. data/lib/rubocop/cop/style/for.rb +0 -1
  163. data/lib/rubocop/cop/style/global_vars.rb +1 -3
  164. data/lib/rubocop/cop/style/guard_clause.rb +16 -3
  165. data/lib/rubocop/cop/style/hash_conversion.rb +1 -2
  166. data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
  167. data/lib/rubocop/cop/style/hash_except.rb +19 -7
  168. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  169. data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
  170. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -2
  171. data/lib/rubocop/cop/style/if_with_semicolon.rb +20 -7
  172. data/lib/rubocop/cop/style/inverse_methods.rb +0 -1
  173. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
  174. data/lib/rubocop/cop/style/lambda.rb +1 -1
  175. data/lib/rubocop/cop/style/lambda_call.rb +3 -2
  176. data/lib/rubocop/cop/style/map_into_array.rb +59 -8
  177. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -8
  178. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +7 -11
  179. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +33 -3
  180. data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
  181. data/lib/rubocop/cop/style/multiple_comparison.rb +28 -39
  182. data/lib/rubocop/cop/style/mutable_constant.rb +4 -5
  183. data/lib/rubocop/cop/style/negated_if_else_condition.rb +6 -4
  184. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  185. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  186. data/lib/rubocop/cop/style/nested_ternary_operator.rb +5 -4
  187. data/lib/rubocop/cop/style/not.rb +1 -1
  188. data/lib/rubocop/cop/style/object_then.rb +1 -0
  189. data/lib/rubocop/cop/style/one_line_conditional.rb +29 -4
  190. data/lib/rubocop/cop/style/operator_method_call.rb +25 -7
  191. data/lib/rubocop/cop/style/or_assignment.rb +3 -6
  192. data/lib/rubocop/cop/style/parallel_assignment.rb +8 -13
  193. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  194. data/lib/rubocop/cop/style/redundant_argument.rb +3 -1
  195. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -1
  196. data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
  197. data/lib/rubocop/cop/style/redundant_condition.rb +37 -22
  198. data/lib/rubocop/cop/style/redundant_line_continuation.rb +24 -5
  199. data/lib/rubocop/cop/style/redundant_parentheses.rb +11 -13
  200. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -0
  201. data/lib/rubocop/cop/style/redundant_return.rb +2 -2
  202. data/lib/rubocop/cop/style/redundant_self.rb +8 -15
  203. data/lib/rubocop/cop/style/redundant_self_assignment.rb +7 -5
  204. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +4 -4
  205. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  206. data/lib/rubocop/cop/style/require_order.rb +1 -1
  207. data/lib/rubocop/cop/style/rescue_modifier.rb +15 -4
  208. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  209. data/lib/rubocop/cop/style/safe_navigation.rb +104 -50
  210. data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
  211. data/lib/rubocop/cop/style/select_by_regexp.rb +10 -7
  212. data/lib/rubocop/cop/style/self_assignment.rb +11 -17
  213. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  214. data/lib/rubocop/cop/style/signal_exception.rb +2 -3
  215. data/lib/rubocop/cop/style/single_argument_dig.rb +9 -5
  216. data/lib/rubocop/cop/style/single_line_do_end_block.rb +13 -3
  217. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -3
  218. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  219. data/lib/rubocop/cop/style/string_concatenation.rb +13 -12
  220. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  221. data/lib/rubocop/cop/style/swap_values.rb +4 -15
  222. data/lib/rubocop/cop/style/ternary_parentheses.rb +26 -5
  223. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +4 -4
  224. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  225. data/lib/rubocop/cop/style/variable_interpolation.rb +1 -2
  226. data/lib/rubocop/cop/team.rb +8 -1
  227. data/lib/rubocop/cop/util.rb +1 -1
  228. data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
  229. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  230. data/lib/rubocop/cop/variable_force/variable.rb +5 -1
  231. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  232. data/lib/rubocop/cop/variable_force.rb +4 -10
  233. data/lib/rubocop/cops_documentation_generator.rb +90 -41
  234. data/lib/rubocop/file_finder.rb +9 -4
  235. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  236. data/lib/rubocop/lsp/runtime.rb +2 -0
  237. data/lib/rubocop/lsp/server.rb +0 -1
  238. data/lib/rubocop/rspec/expect_offense.rb +1 -0
  239. data/lib/rubocop/runner.rb +17 -6
  240. data/lib/rubocop/server/cache.rb +6 -1
  241. data/lib/rubocop/server/core.rb +1 -0
  242. data/lib/rubocop/target_ruby.rb +13 -13
  243. data/lib/rubocop/version.rb +28 -7
  244. data/lib/rubocop/yaml_duplication_checker.rb +20 -27
  245. data/lib/rubocop.rb +18 -0
  246. metadata +32 -14
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Certain numeric operations have a constant result, usually 0 or 1.
7
+ # Subtracting a number from itself or multiplying it by 0 will always return 0.
8
+ # Additionally, a variable modulo 0 or itself will always return 0.
9
+ # Dividing a number by itself or raising it to the power of 0 will always return 1.
10
+ # As such, they can be replaced with that result.
11
+ # These are probably leftover from debugging, or are mistakes.
12
+ # Other numeric operations that are similarly leftover from debugging or mistakes
13
+ # are handled by Lint/UselessNumericOperation.
14
+ #
15
+ # @example
16
+ #
17
+ # # bad
18
+ # x - x
19
+ # x * 0
20
+ # x % 1
21
+ # x % x
22
+ #
23
+ # # good
24
+ # 0
25
+ #
26
+ # # bad
27
+ # x -= x
28
+ # x *= 0
29
+ # x %= 1
30
+ # x %= x
31
+ #
32
+ # # good
33
+ # x = 0
34
+ #
35
+ # # bad
36
+ # x / x
37
+ # x ** 0
38
+ #
39
+ # # good
40
+ # 1
41
+ #
42
+ # # bad
43
+ # x /= x
44
+ # x **= 0
45
+ #
46
+ # # good
47
+ # x = 1
48
+ #
49
+ class NumericOperationWithConstantResult < Base
50
+ extend AutoCorrector
51
+ MSG = 'Numeric operation with a constant result detected.'
52
+ RESTRICT_ON_SEND = %i[- * / % **].freeze
53
+
54
+ # @!method operation_with_constant_result?(node)
55
+ def_node_matcher :operation_with_constant_result?,
56
+ '(send (send nil? $_) $_ ({int | send nil?} $_))'
57
+
58
+ # @!method abbreviated_assignment_with_constant_result?(node)
59
+ def_node_matcher :abbreviated_assignment_with_constant_result?,
60
+ '(op-asgn (lvasgn $_) $_ ({int | lvar} $_))'
61
+
62
+ def on_send(node)
63
+ return unless operation_with_constant_result?(node)
64
+
65
+ variable, operation, number = operation_with_constant_result?(node)
66
+ result = constant_result?(variable, operation, number)
67
+ return unless result
68
+
69
+ add_offense(node) do |corrector|
70
+ corrector.replace(node, result.to_s)
71
+ end
72
+ end
73
+
74
+ def on_op_asgn(node)
75
+ return unless abbreviated_assignment_with_constant_result?(node)
76
+
77
+ variable, operation, number = abbreviated_assignment_with_constant_result?(node)
78
+ result = constant_result?(variable, operation, number)
79
+ return unless result
80
+
81
+ add_offense(node) do |corrector|
82
+ corrector.replace(node, "#{variable} = #{result}")
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ # rubocop :disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
89
+ def constant_result?(variable, operation, number)
90
+ if number.to_s == '0'
91
+ return 0 if operation == :*
92
+ return 1 if operation == :**
93
+ elsif number.to_s == '1'
94
+ return 0 if operation == :%
95
+ elsif number == variable
96
+ return 0 if %i[- %].include?(operation)
97
+ return 1 if operation == :/
98
+ end
99
+ # If we weren't able to find any matches, return false so we can bail out.
100
+ false
101
+ end
102
+ # rubocop :enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
103
+ end
104
+ end
105
+ end
106
+ end
@@ -27,8 +27,7 @@ module RuboCop
27
27
  MSG = 'Avoid using or-assignment with constants.'
28
28
 
29
29
  def on_or_asgn(node)
30
- lhs, _rhs = *node
31
- return unless lhs&.casgn_type?
30
+ return unless node.lhs&.casgn_type?
32
31
 
33
32
  add_offense(node.loc.operator) do |corrector|
34
33
  next if node.each_ancestor(:def, :defs).any?
@@ -75,7 +75,7 @@ module RuboCop
75
75
  end
76
76
 
77
77
  def on_nth_ref(node)
78
- backref, = *node
78
+ backref = node.children.first
79
79
  return if @valid_ref.nil? || backref <= @valid_ref
80
80
 
81
81
  message = format(
@@ -37,9 +37,7 @@ module RuboCop
37
37
  private
38
38
 
39
39
  def valid_context?(node)
40
- unless node.arguments.one? && first_argument_starts_with_left_parenthesis?(node)
41
- return true
42
- end
40
+ return true unless node.arguments.one? && node.first_argument.parenthesized_call?
43
41
  return true if first_argument_block_type?(node.first_argument)
44
42
 
45
43
  node.operator_method? || node.setter_method? || chained_calls?(node) ||
@@ -51,11 +49,12 @@ module RuboCop
51
49
  end
52
50
 
53
51
  def valid_first_argument?(first_arg)
54
- first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg)
52
+ first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg) ||
53
+ compound_range?(first_arg)
55
54
  end
56
55
 
57
- def first_argument_starts_with_left_parenthesis?(node)
58
- node.first_argument.source.start_with?('(')
56
+ def compound_range?(first_arg)
57
+ first_arg.range_type? && first_arg.parenthesized_call?
59
58
  end
60
59
 
61
60
  def chained_calls?(node)
@@ -111,7 +111,7 @@ module RuboCop
111
111
  range_between(start + begin_pos - 1, start + end_pos)
112
112
  end
113
113
 
114
- # If the list of cops is comma-separated, but without a empty space after the comma,
114
+ # If the list of cops is comma-separated, but without an empty space after the comma,
115
115
  # we should **not** remove the prepending empty space, thus begin_pos += 1
116
116
  def range_with_comma_after(comment, start, begin_pos, end_pos)
117
117
  begin_pos += 1 if comment.source[end_pos + 1] != ' '
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks for redundant safe navigation calls.
7
7
  # Use cases where a constant, named in camel case for classes and modules is `nil` are rare,
8
8
  # and an offense is not detected when the receiver is a constant. The detection also applies
9
- # to literal receivers, except for `nil`.
9
+ # to `self`, and to literal receivers, except for `nil`.
10
10
  #
11
11
  # For all receivers, the `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`,
12
12
  # and `equal?` methods are checked by default.
@@ -29,6 +29,9 @@ module RuboCop
29
29
  # # bad
30
30
  # CamelCaseConst&.do_something
31
31
  #
32
+ # # good
33
+ # CamelCaseConst.do_something
34
+ #
32
35
  # # bad
33
36
  # do_something if attrs&.respond_to?(:[])
34
37
  #
@@ -41,9 +44,6 @@ module RuboCop
41
44
  # end
42
45
  #
43
46
  # # good
44
- # CamelCaseConst.do_something
45
- #
46
- # # good
47
47
  # while node.is_a?(BeginNode)
48
48
  # node = node.parent
49
49
  # end
@@ -67,6 +67,12 @@ module RuboCop
67
67
  # foo.to_f
68
68
  # foo.to_s
69
69
  #
70
+ # # bad
71
+ # self&.foo
72
+ #
73
+ # # good
74
+ # self.foo
75
+ #
70
76
  # @example AllowedMethods: [nil_safe_method]
71
77
  # # bad
72
78
  # do_something if attrs&.nil_safe_method(:[])
@@ -131,9 +137,9 @@ module RuboCop
131
137
  private
132
138
 
133
139
  def assume_receiver_instance_exists?(receiver)
134
- return true if receiver.const_type? && !receiver.source.match?(SNAKE_CASE)
140
+ return true if receiver.const_type? && !receiver.short_name.match?(SNAKE_CASE)
135
141
 
136
- receiver.literal? && !receiver.nil_type?
142
+ receiver.self_type? || (receiver.literal? && !receiver.nil_type?)
137
143
  end
138
144
 
139
145
  def check?(node)
@@ -141,8 +147,7 @@ module RuboCop
141
147
  return false unless parent
142
148
 
143
149
  condition?(parent, node) ||
144
- parent.and_type? ||
145
- parent.or_type? ||
150
+ parent.operator_keyword? ||
146
151
  (parent.send_type? && parent.negation_method?)
147
152
  end
148
153
 
@@ -135,10 +135,10 @@ module RuboCop
135
135
  grandparent.array_type? && grandparent.children.size > 1
136
136
  end
137
137
 
138
+ # rubocop:disable Metrics/AbcSize
138
139
  def replacement_range_and_content(node)
139
- variable, = *node
140
- loc = node.loc
141
- expression = loc.expression
140
+ variable = node.children.first
141
+ expression = node.source_range
142
142
 
143
143
  if array_new?(variable)
144
144
  expression = node.parent.source_range if node.parent.array_type?
@@ -148,16 +148,17 @@ module RuboCop
148
148
  elsif redundant_brackets?(node)
149
149
  [expression, remove_brackets(variable)]
150
150
  else
151
- [loc.operator, '']
151
+ [node.loc.operator, '']
152
152
  end
153
153
  end
154
+ # rubocop:enable Metrics/AbcSize
154
155
 
155
156
  def array_splat?(node)
156
157
  node.children.first.array_type?
157
158
  end
158
159
 
159
160
  def method_argument?(node)
160
- node.parent.send_type?
161
+ node.parent.call_type?
161
162
  end
162
163
 
163
164
  def part_of_an_array?(node)
@@ -171,7 +172,7 @@ module RuboCop
171
172
  parent = node.parent
172
173
  grandparent = node.parent.parent
173
174
 
174
- parent.when_type? || parent.send_type? || part_of_an_array?(node) ||
175
+ parent.when_type? || method_argument?(node) || part_of_an_array?(node) ||
175
176
  grandparent&.resbody_type?
176
177
  end
177
178
 
@@ -196,7 +197,7 @@ module RuboCop
196
197
  def use_percent_literal_array_argument?(node)
197
198
  argument = node.children.first
198
199
 
199
- node.parent.send_type? &&
200
+ method_argument?(node) &&
200
201
  (argument.percent_literal?(:string) || argument.percent_literal?(:symbol))
201
202
  end
202
203
 
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks if `include` or `prepend` is called in `refine` block.
7
7
  # These methods are deprecated and should be replaced with `Refinement#import_methods`.
8
8
  #
9
- # It emulates deprecation warnings in Ruby 3.1.
9
+ # It emulates deprecation warnings in Ruby 3.1. Functionality has been removed in Ruby 3.2.
10
10
  #
11
11
  # @safety
12
12
  # This cop's autocorrection is unsafe because `include M` will affect the included class
@@ -17,7 +17,6 @@ module RuboCop
17
17
  # do_something
18
18
  # end
19
19
  class RegexpAsCondition < Base
20
- include IgnoredNode
21
20
  extend AutoCorrector
22
21
 
23
22
  MSG = 'Do not use regexp literal as a condition. ' \
@@ -42,15 +42,11 @@ module RuboCop
42
42
  INVALID_TYPES = %i[array dstr float hash nil int str sym].freeze
43
43
 
44
44
  def on_resbody(node)
45
- rescued, _, _body = *node
46
- return if rescued.nil?
47
-
48
- exceptions = *rescued
49
- invalid_exceptions = invalid_exceptions(exceptions)
45
+ invalid_exceptions = invalid_exceptions(node.exceptions)
50
46
  return if invalid_exceptions.empty?
51
47
 
52
48
  add_offense(
53
- node.loc.keyword.join(rescued.source_range),
49
+ node.loc.keyword.join(node.children.first.source_range),
54
50
  message: format(MSG, invalid_exceptions: invalid_exceptions.map(&:source).join(', '))
55
51
  ) do |corrector|
56
52
  autocorrect(corrector, node)
@@ -58,7 +54,7 @@ module RuboCop
58
54
  end
59
55
 
60
56
  def autocorrect(corrector, node)
61
- rescued, _, _body = *node
57
+ rescued = node.children.first
62
58
  range = node.loc.keyword.end.join(rescued.source_range.end)
63
59
 
64
60
  corrector.replace(range, correction(*rescued))
@@ -38,6 +38,8 @@ module RuboCop
38
38
  PATTERN
39
39
 
40
40
  def on_send(node)
41
+ return unless require_safe_navigation?(node)
42
+
41
43
  bad_method?(node) do |safe_nav, method|
42
44
  return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
43
45
 
@@ -52,6 +54,13 @@ module RuboCop
52
54
 
53
55
  private
54
56
 
57
+ def require_safe_navigation?(node)
58
+ parent = node.parent
59
+ return true unless parent&.and_type?
60
+
61
+ parent.rhs != node || parent.lhs.receiver != parent.rhs.receiver
62
+ end
63
+
55
64
  # @param [Parser::Source::Range] offense_range
56
65
  # @param [RuboCop::AST::SendNode] send_node
57
66
  # @return [String]
@@ -3,88 +3,156 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Check to make sure that if safe navigation is used for a method
7
- # call in an `&&` or `||` condition that safe navigation is used for all
8
- # method calls on that same object.
6
+ # Check to make sure that if safe navigation is used in an `&&` or `||` condition,
7
+ # consistent and appropriate safe navigation, without excess or deficiency,
8
+ # is used for all method calls on the same object.
9
9
  #
10
10
  # @example
11
11
  # # bad
12
- # foo&.bar && foo.baz
12
+ # foo&.bar && foo&.baz
13
13
  #
14
- # # bad
15
- # foo.bar || foo&.baz
14
+ # # good
15
+ # foo&.bar && foo.baz
16
16
  #
17
17
  # # bad
18
- # foo&.bar && (foobar.baz || foo.baz)
18
+ # foo.bar && foo&.baz
19
19
  #
20
20
  # # good
21
21
  # foo.bar && foo.baz
22
22
  #
23
+ # # bad
24
+ # foo&.bar || foo.baz
25
+ #
23
26
  # # good
24
27
  # foo&.bar || foo&.baz
25
28
  #
29
+ # # bad
30
+ # foo.bar || foo&.baz
31
+ #
26
32
  # # good
33
+ # foo.bar || foo.baz
34
+ #
35
+ # # bad
27
36
  # foo&.bar && (foobar.baz || foo&.baz)
28
37
  #
38
+ # # good
39
+ # foo&.bar && (foobar.baz || foo.baz)
40
+ #
29
41
  class SafeNavigationConsistency < Base
30
- include IgnoredNode
31
42
  include NilMethods
32
43
  extend AutoCorrector
33
44
 
34
- MSG = 'Ensure that safe navigation is used consistently inside of `&&` and `||`.'
45
+ USE_DOT_MSG = 'Use `.` instead of unnecessary `&.`.'
46
+ USE_SAFE_NAVIGATION_MSG = 'Use `&.` for consistency with safe navigation.'
35
47
 
36
- def on_csend(node)
37
- return unless node.parent&.operator_keyword?
48
+ def on_and(node)
49
+ all_operands = collect_operands(node, [])
50
+ operand_groups = all_operands.group_by { |operand| receiver_name_as_key(operand, +'') }
38
51
 
39
- check(node)
40
- end
52
+ operand_groups.each_value do |grouped_operands|
53
+ next unless (dot_op, begin_of_rest_operands = find_consistent_parts(grouped_operands))
41
54
 
42
- def check(node)
43
- ancestor = top_conditional_ancestor(node)
44
- conditions = ancestor.conditions
45
- safe_nav_receiver = node.receiver
55
+ rest_operands = grouped_operands[begin_of_rest_operands..]
56
+ rest_operands.each do |operand|
57
+ next if already_appropriate_call?(operand, dot_op)
46
58
 
47
- method_calls = conditions.select(&:send_type?)
48
- unsafe_method_calls = unsafe_method_calls(method_calls, safe_nav_receiver)
59
+ register_offense(operand, dot_op)
60
+ end
61
+ end
62
+ end
63
+ alias on_or on_and
49
64
 
50
- unsafe_method_calls.each do |unsafe_method_call|
51
- location = location(node, unsafe_method_call)
65
+ private
52
66
 
53
- add_offense(location) { |corrector| autocorrect(corrector, unsafe_method_call) }
67
+ def collect_operands(node, operand_nodes)
68
+ operand_nodes(node.lhs, operand_nodes)
69
+ operand_nodes(node.rhs, operand_nodes)
54
70
 
55
- ignore_node(unsafe_method_call)
71
+ operand_nodes
72
+ end
73
+
74
+ def receiver_name_as_key(method, fully_receivers)
75
+ if method.parent.call_type?
76
+ receiver(method.parent, fully_receivers)
77
+ else
78
+ fully_receivers << method.receiver&.source.to_s
56
79
  end
57
80
  end
58
81
 
59
- private
82
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
+ def find_consistent_parts(grouped_operands)
84
+ csend_in_and, csend_in_or, send_in_and, send_in_or = most_left_indices(grouped_operands)
60
85
 
61
- def autocorrect(corrector, node)
62
- return unless node.dot?
86
+ return if csend_in_and && csend_in_or && csend_in_and < csend_in_or
63
87
 
64
- corrector.insert_before(node.loc.dot, '&')
88
+ if csend_in_and
89
+ ['.', (send_in_and ? [send_in_and, csend_in_and].min : csend_in_and) + 1]
90
+ elsif send_in_or && csend_in_or
91
+ send_in_or < csend_in_or ? ['.', send_in_or + 1] : ['&.', csend_in_or + 1]
92
+ elsif send_in_and && csend_in_or && send_in_and < csend_in_or
93
+ ['.', csend_in_or]
94
+ end
65
95
  end
96
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
66
97
 
67
- def location(node, unsafe_method_call)
68
- node.source_range.join(unsafe_method_call.source_range)
98
+ def already_appropriate_call?(operand, dot_op)
99
+ return true if operand.safe_navigation? && dot_op == '&.'
100
+
101
+ (operand.dot? || operand.operator_method?) && dot_op == '.'
69
102
  end
70
103
 
71
- def top_conditional_ancestor(node)
72
- parent = node.parent
73
- unless parent &&
74
- (parent.operator_keyword? ||
75
- (parent.begin_type? && parent.parent && parent.parent.operator_keyword?))
76
- return node
104
+ def register_offense(operand, dot_operator)
105
+ offense_range = operand.operator_method? ? operand : operand.loc.dot
106
+ message = dot_operator == '.' ? USE_DOT_MSG : USE_SAFE_NAVIGATION_MSG
107
+
108
+ add_offense(offense_range, message: message) do |corrector|
109
+ next if operand.operator_method?
110
+
111
+ corrector.replace(operand.loc.dot, dot_operator)
77
112
  end
113
+ end
78
114
 
79
- top_conditional_ancestor(parent)
115
+ def operand_nodes(operand, operand_nodes)
116
+ if operand.operator_keyword?
117
+ collect_operands(operand, operand_nodes)
118
+ elsif operand.call_type?
119
+ operand_nodes << operand
120
+ end
80
121
  end
81
122
 
82
- def unsafe_method_calls(method_calls, safe_nav_receiver)
83
- method_calls.select do |method_call|
84
- safe_nav_receiver == method_call.receiver &&
85
- !nil_methods.include?(method_call.method_name) &&
86
- !ignored_node?(method_call)
123
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
124
+ def most_left_indices(grouped_operands)
125
+ indices = { csend_in_and: nil, csend_in_or: nil, send_in_and: nil, send_in_or: nil }
126
+
127
+ grouped_operands.each_with_index do |operand, index|
128
+ indices[:csend_in_and] ||= index if operand_in_and?(operand) && operand.csend_type?
129
+ indices[:csend_in_or] ||= index if operand_in_or?(operand) && operand.csend_type?
130
+ indices[:send_in_and] ||= index if operand_in_and?(operand) && !nilable?(operand)
131
+ indices[:send_in_or] ||= index if operand_in_or?(operand) && !nilable?(operand)
87
132
  end
133
+
134
+ indices.values
135
+ end
136
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
137
+
138
+ def operand_in_and?(node)
139
+ return true if node.parent.and_type?
140
+
141
+ parent = node.parent.parent while node.parent.begin_type?
142
+
143
+ parent&.and_type?
144
+ end
145
+
146
+ def operand_in_or?(node)
147
+ return true if node.parent.or_type?
148
+
149
+ parent = node.parent.parent while node.parent.begin_type?
150
+
151
+ parent&.or_type?
152
+ end
153
+
154
+ def nilable?(node)
155
+ node.csend_type? || nil_methods.include?(node.method_name)
88
156
  end
89
157
  end
90
158
  end
@@ -43,23 +43,21 @@ module RuboCop
43
43
  alias on_csend on_send
44
44
 
45
45
  def on_lvasgn(node)
46
- lhs, rhs = *node
47
- return unless rhs
46
+ return unless node.rhs
48
47
 
49
48
  rhs_type = ASSIGNMENT_TYPE_TO_RHS_TYPE[node.type]
50
49
 
51
- add_offense(node) if rhs.type == rhs_type && rhs.source == lhs.to_s
50
+ add_offense(node) if node.rhs.type == rhs_type && node.rhs.source == node.lhs.to_s
52
51
  end
53
52
  alias on_ivasgn on_lvasgn
54
53
  alias on_cvasgn on_lvasgn
55
54
  alias on_gvasgn on_lvasgn
56
55
 
57
56
  def on_casgn(node)
58
- lhs_scope, lhs_name, rhs = *node
59
- return unless rhs&.const_type?
57
+ return unless node.rhs&.const_type?
60
58
 
61
- rhs_scope, rhs_name = *rhs
62
- add_offense(node) if lhs_scope == rhs_scope && lhs_name == rhs_name
59
+ add_offense(node) if node.namespace == node.rhs.namespace &&
60
+ node.short_name == node.rhs.short_name
63
61
  end
64
62
 
65
63
  def on_masgn(node)
@@ -67,15 +65,15 @@ module RuboCop
67
65
  end
68
66
 
69
67
  def on_or_asgn(node)
70
- lhs, rhs = *node
71
- add_offense(node) if rhs_matches_lhs?(rhs, lhs)
68
+ add_offense(node) if rhs_matches_lhs?(node.rhs, node.lhs)
72
69
  end
73
70
  alias on_and_asgn on_or_asgn
74
71
 
75
72
  private
76
73
 
77
74
  def multiple_self_assignment?(node)
78
- lhs, rhs = *node
75
+ lhs = node.lhs
76
+ rhs = node.rhs
79
77
  return false unless rhs.array_type?
80
78
  return false unless lhs.children.size == rhs.children.size
81
79
 
@@ -67,7 +67,7 @@ module RuboCop
67
67
  def on_rescue(node)
68
68
  return if rescue_modifier?(node)
69
69
 
70
- _body, *rescues, _else = *node
70
+ rescues = node.resbody_branches
71
71
  rescued_groups = rescued_groups_for(rescues)
72
72
 
73
73
  rescue_group_rescues_multiple_levels = rescued_groups.any? do |group|
@@ -141,7 +141,7 @@ module RuboCop
141
141
  end
142
142
 
143
143
  def in_percent_literal_array?(node)
144
- node.parent&.array_type? && node.parent&.percent_literal?
144
+ node.parent&.array_type? && node.parent.percent_literal?
145
145
  end
146
146
 
147
147
  def correct_hash_key(node)