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
@@ -23,10 +23,21 @@ module RuboCop
23
23
  # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k == :bar }
24
24
  # {foo: 1, bar: 2, baz: 3}.select {|k, v| k != :bar }
25
25
  # {foo: 1, bar: 2, baz: 3}.filter {|k, v| k != :bar }
26
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k.eql?(:bar) }
27
+ #
28
+ # # bad
26
29
  # {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[bar].include?(k) }
27
30
  # {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[bar].include?(k) }
28
31
  # {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[bar].include?(k) }
29
32
  #
33
+ # # bad
34
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| !%i[bar].exclude?(k) }
35
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| %i[bar].exclude?(k) }
36
+ #
37
+ # # bad
38
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k.in?(%i[bar]) }
39
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| !k.in?(%i[bar]) }
40
+ #
30
41
  # # good
31
42
  # {foo: 1, bar: 2, baz: 3}.except(:bar)
32
43
  #
@@ -73,9 +84,8 @@ module RuboCop
73
84
  PATTERN
74
85
 
75
86
  def on_send(node)
76
- method_name = node.method_name
77
87
  block = node.parent
78
- return unless bad_method?(method_name, block) && semantically_except_method?(node, block)
88
+ return unless bad_method?(block) && semantically_except_method?(node, block)
79
89
 
80
90
  except_key = except_key(block)
81
91
  return if except_key.nil? || !safe_to_register_offense?(block, except_key)
@@ -92,7 +102,7 @@ module RuboCop
92
102
  private
93
103
 
94
104
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
95
- def bad_method?(method_name, block)
105
+ def bad_method?(block)
96
106
  if active_support_extensions_enabled?
97
107
  bad_method_with_active_support?(block) do |key_arg, send_node|
98
108
  if send_node.method?(:in?) && send_node.receiver&.source != key_arg.source
@@ -104,8 +114,6 @@ module RuboCop
104
114
  end
105
115
  else
106
116
  bad_method_with_poro?(block) do |key_arg, send_node|
107
- return false if method_name == :reject && block.body.method?(:!)
108
-
109
117
  !send_node.method?(:include?) || send_node.first_argument&.source == key_arg.source
110
118
  end
111
119
  end
@@ -129,11 +137,15 @@ module RuboCop
129
137
  end
130
138
 
131
139
  def included?(negated, body)
132
- body.method?('include?') || body.method?('in?') || (negated && body.method?('exclude?'))
140
+ if negated
141
+ body.method?('exclude?')
142
+ else
143
+ body.method?('include?') || body.method?('in?')
144
+ end
133
145
  end
134
146
 
135
147
  def not_included?(negated, body)
136
- body.method?('exclude?') || (negated && (body.method?('include?') || body.method?('in?')))
148
+ included?(!negated, body)
137
149
  end
138
150
 
139
151
  def safe_to_register_offense?(block, except_key)
@@ -68,7 +68,7 @@ module RuboCop
68
68
  # {a: 1, b: 2}
69
69
  # {:c => 3, 'd' => 4}
70
70
  #
71
- # @example EnforcedShorthandSyntax: always (default)
71
+ # @example EnforcedShorthandSyntax: always
72
72
  #
73
73
  # # bad
74
74
  # {foo: foo, bar: bar}
@@ -84,7 +84,7 @@ module RuboCop
84
84
  # # good
85
85
  # {foo: foo, bar: bar}
86
86
  #
87
- # @example EnforcedShorthandSyntax: either
87
+ # @example EnforcedShorthandSyntax: either (default)
88
88
  #
89
89
  # # good
90
90
  # {foo: foo, bar: bar}
@@ -59,7 +59,6 @@ module RuboCop
59
59
  # end
60
60
  #
61
61
  class IfInsideElse < Base
62
- include IgnoredNode
63
62
  include RangeHelp
64
63
  extend AutoCorrector
65
64
 
@@ -71,7 +70,7 @@ module RuboCop
71
70
 
72
71
  else_branch = node.else_branch
73
72
 
74
- return unless else_branch&.if_type? && else_branch&.if?
73
+ return unless else_branch&.if_type? && else_branch.if?
75
74
  return if allow_if_modifier_in_else_branch?(else_branch)
76
75
 
77
76
  add_offense(else_branch.loc.keyword) do |corrector|
@@ -155,8 +155,7 @@ module RuboCop
155
155
  end
156
156
 
157
157
  def require_parentheses?(condition)
158
- condition.and_type? || condition.or_type? ||
159
- (condition.send_type? && condition.comparison_method?)
158
+ condition.operator_keyword? || (condition.send_type? && condition.comparison_method?)
160
159
  end
161
160
  end
162
161
  end
@@ -23,6 +23,7 @@ module RuboCop
23
23
 
24
24
  def on_normal_if_unless(node)
25
25
  return if node.parent&.if_type?
26
+ return if part_of_ignored_node?(node)
26
27
 
27
28
  beginning = node.loc.begin
28
29
  return unless beginning&.is?(';')
@@ -32,13 +33,14 @@ module RuboCop
32
33
  add_offense(node, message: message) do |corrector|
33
34
  autocorrect(corrector, node)
34
35
  end
36
+
37
+ ignore_node(node)
35
38
  end
36
39
 
37
40
  private
38
41
 
39
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
40
42
  def message(node)
41
- template = if node.if_branch&.begin_type?
43
+ template = if require_newline?(node)
42
44
  MSG_NEWLINE
43
45
  elsif node.else_branch&.if_type? || node.else_branch&.begin_type? ||
44
46
  use_block_in_branches?(node)
@@ -49,20 +51,27 @@ module RuboCop
49
51
 
50
52
  format(template, expr: node.condition.source)
51
53
  end
52
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
53
54
 
54
55
  def autocorrect(corrector, node)
55
- if node.branches.compact.any?(&:begin_type?) || use_block_in_branches?(node)
56
+ if require_newline?(node) || use_block_in_branches?(node)
56
57
  corrector.replace(node.loc.begin, "\n")
57
58
  else
58
59
  corrector.replace(node, replacement(node))
59
60
  end
60
61
  end
61
62
 
63
+ def require_newline?(node)
64
+ node.branches.compact.any?(&:begin_type?) || use_return_with_argument?(node)
65
+ end
66
+
62
67
  def use_block_in_branches?(node)
63
68
  node.branches.compact.any? { |branch| branch.block_type? || branch.numblock_type? }
64
69
  end
65
70
 
71
+ def use_return_with_argument?(node)
72
+ node.if_branch&.return_type? && node.if_branch&.arguments&.any?
73
+ end
74
+
66
75
  def replacement(node)
67
76
  return correct_elsif(node) if node.else_branch&.if_type?
68
77
 
@@ -81,16 +90,14 @@ module RuboCop
81
90
  RUBY
82
91
  end
83
92
 
84
- # rubocop:disable Metrics/AbcSize
85
93
  def build_expression(expr)
86
- return expr.source if !expr.call_type? || expr.parenthesized? || expr.arguments.empty?
94
+ return expr.source unless require_argument_parentheses?(expr)
87
95
 
88
96
  method = expr.source_range.begin.join(expr.loc.selector.end)
89
97
  arguments = expr.first_argument.source_range.begin.join(expr.source_range.end)
90
98
 
91
99
  "#{method.source}(#{arguments.source})"
92
100
  end
93
- # rubocop:enable Metrics/AbcSize
94
101
 
95
102
  def build_else_branch(second_condition)
96
103
  result = <<~RUBY
@@ -111,6 +118,12 @@ module RuboCop
111
118
 
112
119
  result
113
120
  end
121
+
122
+ def require_argument_parentheses?(node)
123
+ return false if !node.call_type? || node.arithmetic_operation?
124
+
125
+ !node.parenthesized? && node.arguments.any? && !node.method?(:[]) && !node.method?(:[]=)
126
+ end
114
127
  end
115
128
  end
116
129
  end
@@ -41,7 +41,6 @@ module RuboCop
41
41
  # f != 1
42
42
  # end
43
43
  class InverseMethods < Base
44
- include IgnoredNode
45
44
  include RangeHelp
46
45
  extend AutoCorrector
47
46
 
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # When passing an existing hash as keyword arguments, provide additional arguments
7
+ # directly rather than using `merge`.
8
+ #
9
+ # Providing arguments directly is more performant than using `merge`, and
10
+ # also leads to shorter and simpler code.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # some_method(**opts.merge(foo: true))
15
+ # some_method(**opts.merge(other_opts))
16
+ #
17
+ # # good
18
+ # some_method(**opts, foo: true)
19
+ # some_method(**opts, **other_opts)
20
+ #
21
+ class KeywordArgumentsMerging < Base
22
+ extend AutoCorrector
23
+
24
+ MSG = 'Provide additional arguments directly rather than using `merge`.'
25
+
26
+ # @!method merge_kwargs?(node)
27
+ def_node_matcher :merge_kwargs?, <<~PATTERN
28
+ (send _ _
29
+ ...
30
+ (hash
31
+ (kwsplat
32
+ $(send $_ :merge $...))
33
+ ...))
34
+ PATTERN
35
+
36
+ def on_kwsplat(node)
37
+ return unless (ancestor = node.parent&.parent)
38
+
39
+ merge_kwargs?(ancestor) do |merge_node, hash_node, other_hash_node|
40
+ add_offense(merge_node) do |corrector|
41
+ autocorrect(corrector, node, hash_node, other_hash_node)
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def autocorrect(corrector, kwsplat_node, hash_node, other_hash_node)
49
+ other_hash_node_replacement =
50
+ other_hash_node.map do |node|
51
+ if node.hash_type?
52
+ if node.braces?
53
+ node.source[1...-1]
54
+ else
55
+ node.source
56
+ end
57
+ else
58
+ "**#{node.source}"
59
+ end
60
+ end.join(', ')
61
+
62
+ corrector.replace(kwsplat_node, "**#{hash_node.source}, #{other_hash_node_replacement}")
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -68,7 +68,7 @@ module RuboCop
68
68
 
69
69
  return unless offending_selector?(node, selector)
70
70
 
71
- add_offense(node.send_node.source_range, message: message(node, selector)) do |corrector|
71
+ add_offense(node.send_node, message: message(node, selector)) do |corrector|
72
72
  if node.send_node.lambda_literal?
73
73
  LambdaLiteralToMethodCorrector.new(node).call(corrector)
74
74
  else
@@ -20,7 +20,6 @@ module RuboCop
20
20
  # lambda.(x, y)
21
21
  class LambdaCall < Base
22
22
  include ConfigurableEnforcedStyle
23
- include IgnoredNode
24
23
  extend AutoCorrector
25
24
 
26
25
  MSG = 'Prefer the use of `%<prefer>s` over `%<current>s`.'
@@ -45,6 +44,7 @@ module RuboCop
45
44
  correct_style_detected
46
45
  end
47
46
  end
47
+ alias on_csend on_send
48
48
 
49
49
  private
50
50
 
@@ -55,9 +55,10 @@ module RuboCop
55
55
  def prefer(node)
56
56
  receiver = node.receiver.source
57
57
  arguments = node.arguments.map(&:source).join(', ')
58
+ dot = node.loc.dot.source
58
59
  method = explicit_style? ? "call(#{arguments})" : "(#{arguments})"
59
60
 
60
- "#{receiver}.#{method}"
61
+ "#{receiver}#{dot}#{method}"
61
62
  end
62
63
 
63
64
  def implicit_style?
@@ -13,8 +13,10 @@ module RuboCop
13
13
  # return value of `Enumerable#map` is an `Array`. They are not autocorrected
14
14
  # when a return value could be used because these types differ.
15
15
  #
16
- # NOTE: It only detects when the mapping destination is a local variable
17
- # initialized as an empty array and referred to only by the pushing operation.
16
+ # NOTE: It only detects when the mapping destination is either:
17
+ # * a local variable initialized as an empty array and referred to only by the
18
+ # pushing operation;
19
+ # * or, if it is the single block argument to a `[].tap` block.
18
20
  # This is because, if not, it's challenging to statically guarantee that the
19
21
  # mapping destination variable remains an empty array:
20
22
  #
@@ -42,6 +44,14 @@ module RuboCop
42
44
  # # good
43
45
  # dest = src.map { |e| e * 2 }
44
46
  #
47
+ # # bad
48
+ # [].tap do |dest|
49
+ # src.each { |e| dest << e * 2 }
50
+ # end
51
+ #
52
+ # # good
53
+ # dest = src.map { |e| e * 2 }
54
+ #
45
55
  # # good - contains another operation
46
56
  # dest = []
47
57
  # src.each { |e| dest << e * 2; puts e }
@@ -53,12 +63,17 @@ module RuboCop
53
63
 
54
64
  MSG = 'Use `%<new_method_name>s` instead of `each` to map elements into an array.'
55
65
 
66
+ # @!method suitable_argument_node?(node)
67
+ def_node_matcher :suitable_argument_node?, <<-PATTERN
68
+ !{splat forwarded-restarg forwarded-args (hash (forwarded-kwrestarg)) (block-pass nil?)}
69
+ PATTERN
70
+
56
71
  # @!method each_block_with_push?(node)
57
72
  def_node_matcher :each_block_with_push?, <<-PATTERN
58
73
  [
59
- ^({begin kwbegin} ...)
74
+ ^({begin kwbegin block} ...)
60
75
  ({block numblock} (send !{nil? self} :each) _
61
- (send (lvar _) {:<< :push :append} {send lvar begin}))
76
+ (send (lvar _) {:<< :push :append} #suitable_argument_node?))
62
77
  ]
63
78
  PATTERN
64
79
 
@@ -74,6 +89,16 @@ module RuboCop
74
89
  )
75
90
  PATTERN
76
91
 
92
+ # @!method empty_array_tap(node)
93
+ def_node_matcher :empty_array_tap, <<~PATTERN
94
+ ^^$(
95
+ block
96
+ (send (array) :tap)
97
+ (args (arg _))
98
+ ...
99
+ )
100
+ PATTERN
101
+
77
102
  # @!method lvar_ref?(node, name)
78
103
  def_node_matcher :lvar_ref?, '(lvar %1)'
79
104
 
@@ -89,9 +114,14 @@ module RuboCop
89
114
  return unless each_block_with_push?(node)
90
115
 
91
116
  dest_var = find_dest_var(node)
92
- return unless (asgn = find_closest_assignment(node, dest_var))
93
- return unless empty_array_asgn?(asgn)
94
- return unless dest_used_only_for_mapping?(node, dest_var, asgn)
117
+
118
+ if offending_empty_array_tap?(node, dest_var)
119
+ asgn = dest_var.declaration_node
120
+ else
121
+ return unless (asgn = find_closest_assignment(node, dest_var))
122
+ return unless empty_array_asgn?(asgn)
123
+ return unless dest_used_only_for_mapping?(node, dest_var, asgn)
124
+ end
95
125
 
96
126
  register_offense(node, dest_var, asgn)
97
127
  end
@@ -108,6 +138,15 @@ module RuboCop
108
138
  candidates.find { |v| v.references.any? { |n| n.node.equal?(node) } }
109
139
  end
110
140
 
141
+ def offending_empty_array_tap?(node, dest_var)
142
+ return false unless (tap_block_node = empty_array_tap(dest_var.declaration_node))
143
+
144
+ # A `tap` block only offends if the array push is the only thing in it;
145
+ # otherwise we cannot guarantee that the block variable is still an empty
146
+ # array when pushed to.
147
+ tap_block_node.body == node
148
+ end
149
+
111
150
  def find_closest_assignment(block, dest_var)
112
151
  dest_var.assignments.reverse_each.lazy.map(&:node).find do |node|
113
152
  node.source_range.end_pos < block.source_range.begin_pos
@@ -127,7 +166,13 @@ module RuboCop
127
166
  next if return_value_used?(block)
128
167
 
129
168
  corrector.replace(block.send_node.selector, new_method_name)
130
- remove_assignment(corrector, asgn)
169
+
170
+ if (tap_block_node = empty_array_tap(dest_var.declaration_node))
171
+ remove_tap(corrector, block, tap_block_node)
172
+ else
173
+ remove_assignment(corrector, asgn)
174
+ end
175
+
131
176
  correct_push_node(corrector, block.body)
132
177
  correct_return_value_handling(corrector, block, dest_var)
133
178
  end
@@ -159,6 +204,12 @@ module RuboCop
159
204
  corrector.remove(range)
160
205
  end
161
206
 
207
+ def remove_tap(corrector, node, block_node)
208
+ range = range_between(block_node.source_range.begin_pos, node.source_range.begin_pos)
209
+ corrector.remove(range)
210
+ corrector.remove(range_with_surrounding_space(block_node.loc.end, side: :left))
211
+ end
212
+
162
213
  def correct_push_node(corrector, push_node)
163
214
  range = push_node.source_range
164
215
  arg_range = push_node.first_argument.source_range
@@ -7,6 +7,8 @@ module RuboCop
7
7
  # Style omit_parentheses
8
8
  # rubocop:disable Metrics/ModuleLength, Metrics/CyclomaticComplexity
9
9
  module OmitParentheses
10
+ include RangeHelp
11
+
10
12
  TRAILING_WHITESPACE_REGEX = /\s+\Z/.freeze
11
13
  OMIT_MSG = 'Omit parentheses for method calls with arguments.'
12
14
  private_constant :OMIT_MSG
@@ -30,10 +32,13 @@ module RuboCop
30
32
  end
31
33
 
32
34
  def autocorrect(corrector, node)
35
+ range = args_begin(node)
33
36
  if parentheses_at_the_end_of_multiline_call?(node)
34
- corrector.replace(args_begin(node), ' \\')
37
+ # Whitespace after line continuation (`\ `) is a syntax error
38
+ with_whitespace = range_with_surrounding_space(range, side: :right, newlines: false)
39
+ corrector.replace(with_whitespace, ' \\')
35
40
  else
36
- corrector.replace(args_begin(node), ' ')
41
+ corrector.replace(range, ' ')
37
42
  end
38
43
  corrector.remove(node.loc.end)
39
44
  end
@@ -47,11 +52,11 @@ module RuboCop
47
52
  node.each_ancestor(:def, :defs).any?(&:endless?) && node.arguments.any?
48
53
  end
49
54
 
50
- def require_parentheses_for_hash_value_omission?(node)
55
+ def require_parentheses_for_hash_value_omission?(node) # rubocop:disable Metrics/PerceivedComplexity
51
56
  return false unless (last_argument = node.last_argument)
52
57
  return false if !last_argument.hash_type? || !last_argument.pairs.last&.value_omission?
53
58
 
54
- node.parent&.conditional? || !last_expression?(node)
59
+ node.parent&.conditional? || node.parent&.single_line? || !last_expression?(node)
55
60
  end
56
61
 
57
62
  # Require hash value omission be enclosed in parentheses to prevent the following issue:
@@ -127,7 +132,7 @@ module RuboCop
127
132
  end
128
133
 
129
134
  def call_in_single_line_inheritance?(node)
130
- node.parent&.class_type? && node.parent&.single_line?
135
+ node.parent&.class_type? && node.parent.single_line?
131
136
  end
132
137
 
133
138
  def call_with_ambiguous_arguments?(node) # rubocop:disable Metrics/PerceivedComplexity
@@ -147,7 +152,7 @@ module RuboCop
147
152
  end
148
153
 
149
154
  def call_in_argument_with_block?(node)
150
- parent = node.parent&.block_type? && node.parent&.parent
155
+ parent = node.parent&.block_type? && node.parent.parent
151
156
  return false unless parent
152
157
 
153
158
  parent.call_type? || parent.super_type? || parent.yield_type?
@@ -198,7 +203,7 @@ module RuboCop
198
203
  end
199
204
 
200
205
  def logical_operator?(node)
201
- (node.and_type? || node.or_type?) && node.logical_operator?
206
+ node.operator_keyword? && node.logical_operator?
202
207
  end
203
208
 
204
209
  def hash_literal?(node)
@@ -211,7 +216,7 @@ module RuboCop
211
216
 
212
217
  def unary_literal?(node)
213
218
  (node.numeric_type? && node.sign?) ||
214
- (node.parent&.send_type? && node.parent&.unary_operation?)
219
+ (node.parent&.send_type? && node.parent.unary_operation?)
215
220
  end
216
221
 
217
222
  def assigned_before?(node, target)
@@ -71,9 +71,11 @@ module RuboCop
71
71
  return false if node.receiver
72
72
 
73
73
  any_assignment?(node) do |asgn_node|
74
- next variable_in_mass_assignment?(node.method_name, asgn_node) if asgn_node.masgn_type?
75
-
76
- asgn_node.loc.name.source == node.method_name.to_s
74
+ if asgn_node.masgn_type?
75
+ variable_in_mass_assignment?(node.method_name, asgn_node)
76
+ else
77
+ asgn_node.loc.name.source == node.method_name.to_s
78
+ end
77
79
  end
78
80
  end
79
81
 
@@ -98,20 +100,14 @@ module RuboCop
98
100
  # `obj.method ||= value` parses as (or-asgn (send ...) ...)
99
101
  # which IS an `asgn_node`. Similarly, `obj.method += value` parses
100
102
  # as (op-asgn (send ...) ...), which is also an `asgn_node`.
101
- if asgn_node.shorthand_asgn?
102
- asgn_node, _value = *asgn_node
103
- next if asgn_node.send_type?
104
- end
103
+ next if asgn_node.shorthand_asgn? && asgn_node.lhs.send_type?
105
104
 
106
105
  yield asgn_node
107
106
  end
108
107
  end
109
108
 
110
109
  def variable_in_mass_assignment?(variable_name, node)
111
- mlhs_node, _mrhs_node = *node
112
- var_nodes = *mlhs_node
113
-
114
- var_nodes.any? { |n| n.to_a.first == variable_name }
110
+ node.assignments.any? { |n| n.name == variable_name }
115
111
  end
116
112
 
117
113
  def offense_range(node)
@@ -6,19 +6,49 @@ module RuboCop
6
6
  # Checks for the presence of `method_missing` without also
7
7
  # defining `respond_to_missing?`.
8
8
  #
9
+ # Not defining `respond_to_missing?` will cause metaprogramming
10
+ # methods like `respond_to?` to behave unexpectedly:
11
+ #
12
+ # [source,ruby]
13
+ # ----
14
+ # class StringDelegator
15
+ # def initialize(string)
16
+ # @string = string
17
+ # end
18
+ #
19
+ # def method_missing(name, *args)
20
+ # @string.send(name, *args)
21
+ # end
22
+ # end
23
+ #
24
+ # delegator = StringDelegator.new("foo")
25
+ # # Claims to not respond to `upcase`.
26
+ # delegator.respond_to?(:upcase) # => false
27
+ # # But you can call it.
28
+ # delegator.upcase # => FOO
29
+ # ----
30
+ #
9
31
  # @example
10
32
  # # bad
11
33
  # def method_missing(name, *args)
12
- # # ...
34
+ # if @delegate.respond_to?(name)
35
+ # @delegate.send(name, *args)
36
+ # else
37
+ # super
38
+ # end
13
39
  # end
14
40
  #
15
41
  # # good
16
42
  # def respond_to_missing?(name, include_private)
17
- # # ...
43
+ # @delegate.respond_to?(name) || super
18
44
  # end
19
45
  #
20
46
  # def method_missing(name, *args)
21
- # # ...
47
+ # if @delegate.respond_to?(name)
48
+ # @delegate.send(name, *args)
49
+ # else
50
+ # super
51
+ # end
22
52
  # end
23
53
  #
24
54
  class MissingRespondToMissing < Base
@@ -39,11 +39,11 @@ module RuboCop
39
39
  BRACES_MSG = 'Wrap multiline memoization blocks in `(` and `)`.'
40
40
 
41
41
  def on_or_asgn(node)
42
- _lhs, rhs = *node
42
+ rhs = node.expression
43
43
 
44
44
  return unless bad_rhs?(rhs)
45
45
 
46
- add_offense(node.source_range) do |corrector|
46
+ add_offense(node) do |corrector|
47
47
  if style == :keyword
48
48
  keyword_autocorrect(rhs, corrector)
49
49
  else