rubocop 1.19.0 → 1.23.0

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 (236) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +129 -21
  4. data/lib/rubocop/config.rb +5 -0
  5. data/lib/rubocop/config_loader.rb +5 -3
  6. data/lib/rubocop/config_validator.rb +9 -1
  7. data/lib/rubocop/cop/base.rb +3 -3
  8. data/lib/rubocop/cop/bundler/gem_comment.rb +3 -3
  9. data/lib/rubocop/cop/bundler/gem_filename.rb +103 -0
  10. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +45 -21
  11. data/lib/rubocop/cop/bundler/ordered_gems.rb +3 -12
  12. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  13. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  14. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +11 -10
  15. data/lib/rubocop/cop/documentation.rb +1 -1
  16. data/lib/rubocop/cop/gemspec/date_assignment.rb +2 -10
  17. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -10
  18. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +3 -12
  19. data/lib/rubocop/cop/gemspec/require_mfa.rb +146 -0
  20. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +31 -24
  21. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
  22. data/lib/rubocop/cop/generator.rb +14 -8
  23. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +60 -0
  24. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  26. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  27. data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
  28. data/lib/rubocop/cop/layout/block_alignment.rb +3 -3
  29. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  30. data/lib/rubocop/cop/layout/dot_position.rb +34 -5
  31. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  32. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
  33. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
  34. data/lib/rubocop/cop/layout/end_alignment.rb +2 -3
  35. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  36. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
  37. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  38. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  39. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  40. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  41. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  42. data/lib/rubocop/cop/layout/line_length.rb +9 -7
  43. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  44. data/lib/rubocop/cop/layout/multiline_block_layout.rb +3 -3
  45. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  46. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  47. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -0
  48. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +6 -5
  49. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  50. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  51. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  52. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  53. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  54. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  55. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
  56. data/lib/rubocop/cop/layout/space_inside_parens.rb +74 -28
  57. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  58. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +111 -0
  59. data/lib/rubocop/cop/lint/ambiguous_range.rb +11 -11
  60. data/lib/rubocop/cop/lint/assignment_in_condition.rb +7 -5
  61. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +18 -5
  62. data/lib/rubocop/cop/lint/boolean_symbol.rb +5 -0
  63. data/lib/rubocop/cop/lint/debugger.rb +2 -4
  64. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  65. data/lib/rubocop/cop/lint/deprecated_constants.rb +3 -2
  66. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +24 -1
  67. data/lib/rubocop/cop/lint/else_layout.rb +10 -6
  68. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  69. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  70. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  71. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +12 -3
  72. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  73. data/lib/rubocop/cop/lint/interpolation_check.rb +5 -0
  74. data/lib/rubocop/cop/lint/loop.rb +4 -3
  75. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -1
  76. data/lib/rubocop/cop/lint/number_conversion.rb +16 -2
  77. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  78. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +4 -2
  79. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +17 -0
  80. data/lib/rubocop/cop/lint/percent_string_array.rb +10 -0
  81. data/lib/rubocop/cop/lint/raise_exception.rb +4 -0
  82. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +5 -4
  83. data/lib/rubocop/cop/lint/require_relative_self_path.rb +50 -0
  84. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  85. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  86. data/lib/rubocop/cop/lint/triple_quotes.rb +1 -1
  87. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +8 -3
  88. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  89. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -2
  90. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
  91. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  92. data/lib/rubocop/cop/lint/useless_times.rb +4 -3
  93. data/lib/rubocop/cop/metrics/abc_size.rb +6 -0
  94. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  95. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  96. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  97. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  98. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  99. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  100. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  101. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -2
  102. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +23 -1
  103. data/lib/rubocop/cop/mixin/gemspec_help.rb +30 -0
  104. data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
  105. data/lib/rubocop/cop/mixin/heredoc.rb +1 -3
  106. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  107. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  108. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  109. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +9 -1
  110. data/lib/rubocop/cop/mixin/percent_array.rb +11 -3
  111. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  112. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  113. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  114. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  115. data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
  116. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  117. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  118. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  119. data/lib/rubocop/cop/naming/file_name.rb +37 -4
  120. data/lib/rubocop/cop/naming/inclusive_language.rb +9 -9
  121. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +5 -4
  122. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +7 -0
  123. data/lib/rubocop/cop/security/io_methods.rb +49 -0
  124. data/lib/rubocop/cop/security/json_load.rb +8 -7
  125. data/lib/rubocop/cop/security/open.rb +4 -0
  126. data/lib/rubocop/cop/security/yaml_load.rb +4 -0
  127. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  128. data/lib/rubocop/cop/style/and_or.rb +5 -0
  129. data/lib/rubocop/cop/style/arguments_forwarding.rb +13 -2
  130. data/lib/rubocop/cop/style/array_coercion.rb +21 -3
  131. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  132. data/lib/rubocop/cop/style/block_delimiters.rb +23 -6
  133. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  134. data/lib/rubocop/cop/style/case_like_if.rb +5 -0
  135. data/lib/rubocop/cop/style/class_and_module_children.rb +9 -0
  136. data/lib/rubocop/cop/style/collection_compact.rb +7 -5
  137. data/lib/rubocop/cop/style/collection_methods.rb +8 -6
  138. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  139. data/lib/rubocop/cop/style/comment_annotation.rb +25 -39
  140. data/lib/rubocop/cop/style/commented_keyword.rb +9 -4
  141. data/lib/rubocop/cop/style/date_time.rb +5 -0
  142. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  143. data/lib/rubocop/cop/style/documentation.rb +23 -8
  144. data/lib/rubocop/cop/style/double_negation.rb +27 -6
  145. data/lib/rubocop/cop/style/empty_method.rb +2 -2
  146. data/lib/rubocop/cop/style/encoding.rb +26 -15
  147. data/lib/rubocop/cop/style/explicit_block_argument.rb +21 -11
  148. data/lib/rubocop/cop/style/float_division.rb +10 -2
  149. data/lib/rubocop/cop/style/format_string_token.rb +2 -1
  150. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +7 -2
  151. data/lib/rubocop/cop/style/global_std_stream.rb +4 -0
  152. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +11 -0
  153. data/lib/rubocop/cop/style/hash_each_methods.rb +5 -0
  154. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  155. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -6
  156. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -6
  157. data/lib/rubocop/cop/style/identical_conditional_branches.rb +18 -16
  158. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +18 -4
  159. data/lib/rubocop/cop/style/infinite_loop.rb +4 -3
  160. data/lib/rubocop/cop/style/inverse_methods.rb +9 -2
  161. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  162. data/lib/rubocop/cop/style/line_end_concatenation.rb +14 -1
  163. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  164. data/lib/rubocop/cop/style/module_function.rb +8 -9
  165. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  166. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
  167. data/lib/rubocop/cop/style/mutable_constant.rb +73 -6
  168. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  169. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  170. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  171. data/lib/rubocop/cop/style/not.rb +2 -2
  172. data/lib/rubocop/cop/style/numbered_parameters.rb +46 -0
  173. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +50 -0
  174. data/lib/rubocop/cop/style/numeric_literals.rb +7 -8
  175. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  176. data/lib/rubocop/cop/style/open_struct_use.rb +69 -0
  177. data/lib/rubocop/cop/style/optional_arguments.rb +4 -0
  178. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +14 -4
  179. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  180. data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -2
  181. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  182. data/lib/rubocop/cop/style/preferred_hash_methods.rb +9 -4
  183. data/lib/rubocop/cop/style/quoted_symbols.rb +21 -7
  184. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  185. data/lib/rubocop/cop/style/redundant_argument.rb +19 -9
  186. data/lib/rubocop/cop/style/redundant_begin.rb +25 -0
  187. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  188. data/lib/rubocop/cop/style/redundant_fetch_block.rb +4 -0
  189. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +12 -3
  190. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -4
  191. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  192. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  193. data/lib/rubocop/cop/style/redundant_self.rb +10 -0
  194. data/lib/rubocop/cop/style/redundant_self_assignment.rb +4 -3
  195. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +23 -28
  196. data/lib/rubocop/cop/style/redundant_sort.rb +51 -18
  197. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  198. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  199. data/lib/rubocop/cop/style/safe_navigation.rb +13 -2
  200. data/lib/rubocop/cop/style/select_by_regexp.rb +139 -0
  201. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -0
  202. data/lib/rubocop/cop/style/slicing_with_range.rb +13 -0
  203. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -0
  204. data/lib/rubocop/cop/style/special_global_vars.rb +4 -0
  205. data/lib/rubocop/cop/style/static_class.rb +5 -5
  206. data/lib/rubocop/cop/style/string_chars.rb +4 -2
  207. data/lib/rubocop/cop/style/string_concatenation.rb +5 -1
  208. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -0
  209. data/lib/rubocop/cop/style/struct_inheritance.rb +4 -0
  210. data/lib/rubocop/cop/style/swap_values.rb +4 -2
  211. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  212. data/lib/rubocop/cop/style/symbol_proc.rb +26 -0
  213. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +19 -0
  214. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  215. data/lib/rubocop/cop/style/word_array.rb +3 -3
  216. data/lib/rubocop/cop/style/yoda_condition.rb +24 -7
  217. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -0
  218. data/lib/rubocop/cop/util.rb +15 -4
  219. data/lib/rubocop/cops_documentation_generator.rb +17 -5
  220. data/lib/rubocop/formatter/html_formatter.rb +5 -2
  221. data/lib/rubocop/formatter/json_formatter.rb +4 -1
  222. data/lib/rubocop/magic_comment.rb +44 -15
  223. data/lib/rubocop/options.rb +126 -112
  224. data/lib/rubocop/rake_task.rb +1 -1
  225. data/lib/rubocop/remote_config.rb +1 -1
  226. data/lib/rubocop/result_cache.rb +3 -3
  227. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  228. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  229. data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
  230. data/lib/rubocop/rspec/support.rb +1 -0
  231. data/lib/rubocop/runner.rb +2 -3
  232. data/lib/rubocop/target_finder.rb +1 -1
  233. data/lib/rubocop/version.rb +1 -1
  234. data/lib/rubocop/yaml_duplication_checker.rb +1 -1
  235. data/lib/rubocop.rb +14 -2
  236. metadata +20 -5
@@ -120,11 +120,10 @@ module RuboCop
120
120
  end
121
121
 
122
122
  def require_parentheses?(node)
123
- node.basic_conditional? &&
124
- node.modifier_form? ||
123
+ (node.basic_conditional? && node.modifier_form?) ||
125
124
  node.range_type? ||
126
125
  node.rescue_type? ||
127
- node.respond_to?(:semantic_operator?) && node.semantic_operator?
126
+ (node.respond_to?(:semantic_operator?) && node.semantic_operator?)
128
127
  end
129
128
 
130
129
  def without_argument_parentheses_method?(node)
@@ -9,6 +9,10 @@ module RuboCop
9
9
  # In such cases `fetch(key, value)` method is faster
10
10
  # than `fetch(key) { value }`.
11
11
  #
12
+ # @safety
13
+ # This cop is unsafe because it cannot be guaranteed that the receiver
14
+ # does not have a different implementation of `fetch`.
15
+ #
12
16
  # @example SafeForConstants: false (default)
13
17
  # # bad
14
18
  # hash.fetch(:key) { 5 }
@@ -25,6 +25,7 @@ module RuboCop
25
25
  # require_relative '../foo.so'
26
26
  #
27
27
  class RedundantFileExtensionInRequire < Base
28
+ include RangeHelp
28
29
  extend AutoCorrector
29
30
 
30
31
  MSG = 'Redundant `.rb` file extension detected.'
@@ -39,13 +40,21 @@ module RuboCop
39
40
  require_call?(node) do |name_node|
40
41
  return unless name_node.value.end_with?('.rb')
41
42
 
42
- add_offense(name_node) do |corrector|
43
- correction = name_node.value.delete_suffix('.rb')
43
+ extension_range = extension_range(name_node)
44
44
 
45
- corrector.replace(name_node, "'#{correction}'")
45
+ add_offense(extension_range) do |corrector|
46
+ corrector.remove(extension_range)
46
47
  end
47
48
  end
48
49
  end
50
+
51
+ private
52
+
53
+ def extension_range(name_node)
54
+ end_of_path_string = name_node.source_range.end_pos
55
+
56
+ range_between(end_of_path_string - 4, end_of_path_string - 1)
57
+ end
49
58
  end
50
59
  end
51
60
  end
@@ -7,6 +7,9 @@ module RuboCop
7
7
  #
8
8
  # NOTE: Regexp and Range literals are frozen objects since Ruby 3.0.
9
9
  #
10
+ # NOTE: From Ruby 3.0, this cop allows explicit freezing of interpolated
11
+ # string literals when `# frozen-string-literal: true` is used.
12
+ #
10
13
  # @example
11
14
  # # bad
12
15
  # CONST = 1.freeze
@@ -37,9 +40,7 @@ module RuboCop
37
40
  node = strip_parenthesis(node)
38
41
 
39
42
  return true if node.immutable_literal?
40
-
41
- return true if FROZEN_STRING_LITERAL_TYPES.include?(node.type) &&
42
- frozen_string_literals_enabled?
43
+ return true if frozen_string_literal?(node)
43
44
 
44
45
  target_ruby_version >= 3.0 && (node.regexp_type? || node.range_type?)
45
46
  end
@@ -58,7 +59,6 @@ module RuboCop
58
59
  (begin (send {float int} {:+ :- :* :** :/ :% :<<} _))
59
60
  (begin (send !{(str _) array} {:+ :- :* :** :/ :%} {float int}))
60
61
  (begin (send _ {:== :=== :!= :<= :>= :< :>} _))
61
- (send (const {nil? cbase} :ENV) :[] _)
62
62
  (send _ {:count :length :size} ...)
63
63
  (block (send _ {:count :length :size} ...) ...)
64
64
  }
@@ -56,7 +56,7 @@ module RuboCop
56
56
  first_child = node.children.first
57
57
 
58
58
  variable_interpolation?(first_child) ||
59
- first_child.send_type? && !first_child.operator_method?
59
+ (first_child.send_type? && !first_child.operator_method?)
60
60
  end
61
61
 
62
62
  def interpolation?(node)
@@ -65,9 +65,8 @@ module RuboCop
65
65
  end
66
66
 
67
67
  def allowed_percent_q?(node)
68
- node.source.start_with?(PERCENT_Q) && acceptable_q?(node) ||
69
- node.source.start_with?(PERCENT_CAPITAL_Q) &&
70
- acceptable_capital_q?(node)
68
+ (node.source.start_with?(PERCENT_Q) && acceptable_q?(node)) ||
69
+ (node.source.start_with?(PERCENT_CAPITAL_Q) && acceptable_capital_q?(node))
71
70
  end
72
71
 
73
72
  def message(node)
@@ -100,6 +100,10 @@ module RuboCop
100
100
  add_lhs_to_local_variables_scopes(rhs, lhs)
101
101
  end
102
102
 
103
+ def on_in_pattern(node)
104
+ add_match_var_scopes(node)
105
+ end
106
+
103
107
  def on_send(node)
104
108
  return unless node.self_receiver? && regular_method_call?(node)
105
109
  return if node.parent&.mlhs_type?
@@ -185,6 +189,12 @@ module RuboCop
185
189
  add_lhs_to_local_variables_scopes(rhs, child.to_a.first)
186
190
  end
187
191
  end
192
+
193
+ def add_match_var_scopes(in_pattern_node)
194
+ in_pattern_node.each_descendant(:match_var) do |match_var_node|
195
+ @local_variables_scopes[in_pattern_node] << match_var_node.children.first
196
+ end
197
+ end
188
198
  end
189
199
  end
190
200
  end
@@ -6,9 +6,10 @@ module RuboCop
6
6
  # This cop checks for places where redundant assignments are made for in place
7
7
  # modification methods.
8
8
  #
9
- # This cop is marked as unsafe, because it can produce false positives for
10
- # user defined methods having one of the expected names, but not modifying
11
- # its receiver in place.
9
+ # @safety
10
+ # This cop is unsafe, because it can produce false positives for
11
+ # user defined methods having one of the expected names, but not modifying
12
+ # its receiver in place.
12
13
  #
13
14
  # @example
14
15
  # # bad
@@ -5,6 +5,9 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for places where conditional branch makes redundant self-assignment.
7
7
  #
8
+ # It only detects local variable because it may replace state of instance variable,
9
+ # class variable, and global variable that have state across methods with `nil`.
10
+ #
8
11
  # @example
9
12
  #
10
13
  # # bad
@@ -32,11 +35,11 @@ module RuboCop
32
35
 
33
36
  def on_lvasgn(node)
34
37
  variable, expression = *node
35
- return unless expression&.if_type?
36
- return unless expression.ternary? || expression.else?
38
+ return unless use_if_and_else_branch?(expression)
37
39
 
38
40
  if_branch = expression.if_branch
39
41
  else_branch = expression.else_branch
42
+ return if inconvertible_to_modifier?(if_branch, else_branch)
40
43
 
41
44
  if self_assign?(variable, if_branch)
42
45
  register_offense(expression, if_branch, else_branch, 'unless')
@@ -45,41 +48,33 @@ module RuboCop
45
48
  end
46
49
  end
47
50
 
48
- alias on_ivasgn on_lvasgn
49
- alias on_cvasgn on_lvasgn
50
- alias on_gvasgn on_lvasgn
51
-
52
51
  private
53
52
 
54
- def self_assign?(variable, branch)
55
- variable.to_s == branch&.source
53
+ def use_if_and_else_branch?(expression)
54
+ return false unless expression&.if_type?
55
+
56
+ !expression.ternary? || !expression.else?
56
57
  end
57
58
 
58
- def register_offense(if_node, offense_branch, opposite_branch, keyword)
59
- add_offense(offense_branch) do |corrector|
60
- if if_node.ternary?
61
- replacement = "#{opposite_branch.source} #{keyword} #{if_node.condition.source}"
62
- corrector.replace(if_node, replacement)
63
- else
64
- if_node_loc = if_node.loc
59
+ def inconvertible_to_modifier?(if_branch, else_branch)
60
+ multiple_statements?(if_branch) || multiple_statements?(else_branch) ||
61
+ (else_branch.respond_to?(:elsif?) && else_branch.elsif?)
62
+ end
65
63
 
66
- range = range_by_whole_lines(offense_branch.source_range, include_final_newline: true)
67
- corrector.remove(range)
68
- range = range_by_whole_lines(if_node_loc.else, include_final_newline: true)
69
- corrector.remove(range)
64
+ def multiple_statements?(branch)
65
+ branch && branch.children.compact.count > 1
66
+ end
70
67
 
71
- autocorrect_if_condition(corrector, if_node, if_node_loc, keyword)
72
- end
73
- end
68
+ def self_assign?(variable, branch)
69
+ variable.to_s == branch&.source
74
70
  end
75
71
 
76
- def autocorrect_if_condition(corrector, if_node, if_node_loc, keyword)
77
- else_branch = if_node.else_branch
72
+ def register_offense(if_node, offense_branch, opposite_branch, keyword)
73
+ add_offense(offense_branch) do |corrector|
74
+ assignment_value = opposite_branch ? opposite_branch.source : 'nil'
75
+ replacement = "#{assignment_value} #{keyword} #{if_node.condition.source}"
78
76
 
79
- if else_branch.respond_to?(:elsif?) && else_branch.elsif?
80
- corrector.replace(if_node.condition, else_branch.condition.source)
81
- else
82
- corrector.replace(if_node_loc.keyword, keyword)
77
+ corrector.replace(if_node, replacement)
83
78
  end
84
79
  end
85
80
  end
@@ -12,6 +12,33 @@ module RuboCop
12
12
  # `Enumerable#max_by` can replace `Enumerable#sort_by` calls
13
13
  # after which only the first or last element is used.
14
14
  #
15
+ # @safety
16
+ # This cop is unsafe, because `sort...last` and `max` may not return the
17
+ # same element in all cases.
18
+ #
19
+ # In an enumerable where there are multiple elements where `a <=> b == 0`,
20
+ # or where the transformation done by the `sort_by` block has the
21
+ # same result, `sort.last` and `max` (or `sort_by.last` and `max_by`)
22
+ # will return different elements. `sort.last` will return the last
23
+ # element but `max` will return the first element.
24
+ #
25
+ # For example:
26
+ #
27
+ # [source,ruby]
28
+ # ----
29
+ # class MyString < String; end
30
+ # strings = [MyString.new('test'), 'test']
31
+ # strings.sort.last.class #=> String
32
+ # strings.max.class #=> MyString
33
+ # ----
34
+ #
35
+ # [source,ruby]
36
+ # ----
37
+ # words = %w(dog horse mouse)
38
+ # words.sort_by { |word| word.length }.last #=> 'mouse'
39
+ # words.max_by { |word| word.length } #=> 'horse'
40
+ # ----
41
+ #
15
42
  # @example
16
43
  # # bad
17
44
  # [2, 1, 3].sort.first
@@ -75,33 +102,39 @@ module RuboCop
75
102
  MATCHER
76
103
 
77
104
  def on_send(node)
78
- if (sort_node, sorter, accessor = redundant_sort?(node.parent))
79
- ancestor = node.parent
80
- elsif (sort_node, sorter, accessor = redundant_sort?(node.parent&.parent))
81
- ancestor = node.parent.parent
82
- else
83
- return
84
- end
105
+ ancestor, sort_node, sorter, accessor =
106
+ find_redundant_sort(node.parent, node.parent&.parent)
107
+ return unless ancestor
85
108
 
86
- message = message(ancestor, sorter, accessor)
109
+ register_offense(ancestor, sort_node, sorter, accessor)
110
+ end
111
+
112
+ private
87
113
 
88
- add_offense(offense_range(sort_node, ancestor), message: message) do |corrector|
89
- autocorrect(corrector, ancestor, sort_node, sorter, accessor)
114
+ def find_redundant_sort(*nodes)
115
+ nodes.each do |node|
116
+ if (sort_node, sorter, accessor = redundant_sort?(node))
117
+ return [node, sort_node, sorter, accessor]
118
+ end
90
119
  end
120
+
121
+ nil
91
122
  end
92
123
 
93
- private
124
+ def register_offense(node, sort_node, sorter, accessor)
125
+ message = message(node, sorter, accessor)
94
126
 
95
- def autocorrect(corrector, node, sort_node, sorter, accessor)
96
- # Remove accessor, e.g. `first` or `[-1]`.
97
- corrector.remove(range_between(accessor_start(node), node.loc.expression.end_pos))
127
+ add_offense(offense_range(sort_node, node), message: message) do |corrector|
128
+ # Remove accessor, e.g. `first` or `[-1]`.
129
+ corrector.remove(range_between(accessor_start(node), node.loc.expression.end_pos))
98
130
 
99
- # Replace "sort" or "sort_by" with the appropriate min/max method.
100
- corrector.replace(sort_node.loc.selector, suggestion(sorter, accessor, arg_value(node)))
131
+ # Replace "sort" or "sort_by" with the appropriate min/max method.
132
+ corrector.replace(sort_node.loc.selector, suggestion(sorter, accessor, arg_value(node)))
133
+ end
101
134
  end
102
135
 
103
- def offense_range(sort_node, ancestor)
104
- range_between(sort_node.loc.selector.begin_pos, ancestor.loc.expression.end_pos)
136
+ def offense_range(sort_node, node)
137
+ range_between(sort_node.loc.selector.begin_pos, node.loc.expression.end_pos)
105
138
  end
106
139
 
107
140
  def message(node, sorter, accessor)
@@ -107,7 +107,7 @@ module RuboCop
107
107
  private
108
108
 
109
109
  def allowed_slash_literal?(node)
110
- style == :slashes && !contains_disallowed_slash?(node) || allowed_mixed_slash?(node)
110
+ (style == :slashes && !contains_disallowed_slash?(node)) || allowed_mixed_slash?(node)
111
111
  end
112
112
 
113
113
  def allowed_mixed_slash?(node)
@@ -115,13 +115,13 @@ module RuboCop
115
115
  end
116
116
 
117
117
  def allowed_percent_r_literal?(node)
118
- style == :slashes && contains_disallowed_slash?(node) ||
118
+ (style == :slashes && contains_disallowed_slash?(node)) ||
119
119
  style == :percent_r ||
120
120
  allowed_mixed_percent_r?(node) || allowed_omit_parentheses_with_percent_r_literal?(node)
121
121
  end
122
122
 
123
123
  def allowed_mixed_percent_r?(node)
124
- style == :mixed && node.multiline? || contains_disallowed_slash?(node)
124
+ (style == :mixed && node.multiline?) || contains_disallowed_slash?(node)
125
125
  end
126
126
 
127
127
  def contains_disallowed_slash?(node)
@@ -74,7 +74,8 @@ module RuboCop
74
74
  end
75
75
 
76
76
  def correct_style?(node)
77
- style == :return && !return_nil_node?(node) || style == :return_nil && !return_node?(node)
77
+ (style == :return && !return_nil_node?(node)) ||
78
+ (style == :return_nil && !return_node?(node))
78
79
  end
79
80
 
80
81
  def scoped_node?(node)
@@ -10,14 +10,25 @@ module RuboCop
10
10
  # need to be changed to use safe navigation. We have limited the cop to
11
11
  # not register an offense for method chains that exceed 2 methods.
12
12
  #
13
- # Configuration option: ConvertCodeThatCanStartToReturnNil
14
- # The default for this is `false`. When configured to `true`, this will
13
+ # The default for `ConvertCodeThatCanStartToReturnNil` is `false`.
14
+ # When configured to `true`, this will
15
15
  # check for code in the format `!foo.nil? && foo.bar`. As it is written,
16
16
  # the return of this code is limited to `false` and whatever the return
17
17
  # of the method is. If this is converted to safe navigation,
18
18
  # `foo&.bar` can start returning `nil` as well as what the method
19
19
  # returns.
20
20
  #
21
+ # @safety
22
+ # Autocorrection is unsafe because if a value is `false`, the resulting
23
+ # code will have different behaviour or raise an error.
24
+ #
25
+ # [source,ruby]
26
+ # ----
27
+ # x = false
28
+ # x && x.foo # return false
29
+ # x&.foo # raises NoMethodError
30
+ # ----
31
+ #
21
32
  # @example
22
33
  # # bad
23
34
  # foo.bar if foo
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop looks for places where an subset of an Enumerable (array,
7
+ # range, set, etc.; see note below) is calculated based on a `Regexp`
8
+ # match, and suggests `grep` or `grep_v` instead.
9
+ #
10
+ # NOTE: Hashes do not behave as you may expect with `grep`, which
11
+ # means that `hash.grep` is not equivalent to `hash.select`. Although
12
+ # RuboCop is limited by static analysis, this cop attempts to avoid
13
+ # registering an offense when the receiver is a hash (hash literal,
14
+ # `Hash.new`, `Hash#[]`, or `to_h`/`to_hash`).
15
+ #
16
+ # NOTE: `grep` and `grep_v` were optimized when used without a block
17
+ # in Ruby 3.0, but may be slower in previous versions.
18
+ # See https://bugs.ruby-lang.org/issues/17030
19
+ #
20
+ # @safety
21
+ # Autocorrection is marked as unsafe because `MatchData` will
22
+ # not be created by `grep`, but may have previously been relied
23
+ # upon after the `match?` or `=~` call.
24
+ #
25
+ # Additionally, the cop cannot guarantee that the receiver of
26
+ # `select` or `reject` is actually an array by static analysis,
27
+ # so the correction may not be actually equivalent.
28
+ #
29
+ # @example
30
+ # # bad (select or find_all)
31
+ # array.select { |x| x.match? /regexp/ }
32
+ # array.select { |x| /regexp/.match?(x) }
33
+ # array.select { |x| x =~ /regexp/ }
34
+ # array.select { |x| /regexp/ =~ x }
35
+ #
36
+ # # bad (reject)
37
+ # array.reject { |x| x.match? /regexp/ }
38
+ # array.reject { |x| /regexp/.match?(x) }
39
+ # array.reject { |x| x =~ /regexp/ }
40
+ # array.reject { |x| /regexp/ =~ x }
41
+ #
42
+ # # good
43
+ # array.grep(regexp)
44
+ # array.grep_v(regexp)
45
+ class SelectByRegexp < Base
46
+ extend AutoCorrector
47
+ include RangeHelp
48
+
49
+ MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a regexp match.'
50
+ RESTRICT_ON_SEND = %i[select find_all reject].freeze
51
+ REPLACEMENTS = { select: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
52
+ REGEXP_METHODS = %i[match? =~].to_set.freeze
53
+
54
+ # @!method regexp_match?(node)
55
+ def_node_matcher :regexp_match?, <<~PATTERN
56
+ {
57
+ (block send (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
58
+ (numblock send $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
59
+ }
60
+ PATTERN
61
+
62
+ # Returns true if a node appears to return a hash
63
+ # @!method creates_hash?(node)
64
+ def_node_matcher :creates_hash?, <<~PATTERN
65
+ {
66
+ (send (const _ :Hash) {:new :[]} ...)
67
+ (block (send (const _ :Hash) :new ...) ...)
68
+ (send _ { :to_h :to_hash } ...)
69
+ }
70
+ PATTERN
71
+
72
+ # @!method calls_lvar?(node, name)
73
+ def_node_matcher :calls_lvar?, <<~PATTERN
74
+ {
75
+ (send (lvar %1) ...)
76
+ (send ... (lvar %1))
77
+ (match-with-lvasgn regexp (lvar %1))
78
+ }
79
+ PATTERN
80
+
81
+ def on_send(node)
82
+ return unless (block_node = node.block_node)
83
+ return if block_node.body.begin_type?
84
+ return if receiver_allowed?(block_node.receiver)
85
+ return unless (regexp_method_send_node = extract_send_node(block_node))
86
+ return if match_predicate_without_receiver?(regexp_method_send_node)
87
+
88
+ regexp = find_regexp(regexp_method_send_node, block_node)
89
+ register_offense(node, block_node, regexp)
90
+ end
91
+
92
+ private
93
+
94
+ def receiver_allowed?(node)
95
+ return false unless node
96
+
97
+ node.hash_type? || creates_hash?(node)
98
+ end
99
+
100
+ def register_offense(node, block_node, regexp)
101
+ replacement = REPLACEMENTS[node.method_name.to_sym]
102
+ message = format(MSG, replacement: replacement, original_method: node.method_name)
103
+
104
+ add_offense(block_node, message: message) do |corrector|
105
+ # Only correct if it can be determined what the regexp is
106
+ if regexp
107
+ range = range_between(node.loc.selector.begin_pos, block_node.loc.end.end_pos)
108
+ corrector.replace(range, "#{replacement}(#{regexp.source})")
109
+ end
110
+ end
111
+ end
112
+
113
+ def extract_send_node(block_node)
114
+ return unless (block_arg_name, regexp_method_send_node = regexp_match?(block_node))
115
+
116
+ block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
117
+ return unless calls_lvar?(regexp_method_send_node, block_arg_name)
118
+
119
+ regexp_method_send_node
120
+ end
121
+
122
+ def find_regexp(node, block)
123
+ return node.child_nodes.first if node.match_with_lvasgn_type?
124
+
125
+ if node.receiver.lvar_type? &&
126
+ (block.numblock_type? || node.receiver.source == block.arguments.first.source)
127
+ node.first_argument
128
+ elsif node.first_argument.lvar_type?
129
+ node.receiver
130
+ end
131
+ end
132
+
133
+ def match_predicate_without_receiver?(node)
134
+ node.send_type? && node.method?(:match?) && node.receiver.nil?
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -6,6 +6,11 @@ module RuboCop
6
6
  # Sometimes using dig method ends up with just a single
7
7
  # argument. In such cases, dig should be replaced with [].
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because it cannot be guaranteed that the receiver
11
+ # is an `Enumerable` or does not have a nonstandard implementation
12
+ # of `dig`.
13
+ #
9
14
  # @example
10
15
  # # bad
11
16
  # { key: 'value' }.dig(:key)
@@ -6,6 +6,19 @@ module RuboCop
6
6
  # This cop checks that arrays are sliced with endless ranges instead of
7
7
  # `ary[start..-1]` on Ruby 2.6+.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because `x..-1` and `x..` are only guaranteed to
11
+ # be equivalent for `Array#[]`, and the cop cannot determine what class
12
+ # the receiver is.
13
+ #
14
+ # For example:
15
+ # [source,ruby]
16
+ # ----
17
+ # sum = proc { |ary| ary.sum }
18
+ # sum[-3..-1] # => -6
19
+ # sum[-3..] # Hangs forever
20
+ # ----
21
+ #
9
22
  # @example
10
23
  # # bad
11
24
  # items[1..-1]
@@ -38,6 +38,10 @@ module RuboCop
38
38
 
39
39
  MSG = 'Consider merging nested conditions into outer `%<conditional_type>s` conditions.'
40
40
 
41
+ def self.autocorrect_incompatible_with
42
+ [Style::NegatedIf, Style::NegatedUnless]
43
+ end
44
+
41
45
  def on_if(node)
42
46
  return if node.ternary? || node.else? || node.elsif?
43
47
 
@@ -9,6 +9,10 @@ module RuboCop
9
9
  # will add a require statement to the top of the file if
10
10
  # enabled by RequireEnglish config.
11
11
  #
12
+ # @safety
13
+ # Autocorrection is marked as unsafe because if `RequireEnglish` is not
14
+ # true, replacing perl-style variables with english variables will break.
15
+ #
12
16
  # @example EnforcedStyle: use_english_names (default)
13
17
  # # good
14
18
  # require 'English' # or this could be in another file.
@@ -7,9 +7,10 @@ module RuboCop
7
7
  # replaced with a module. Classes should be used only when it makes sense to create
8
8
  # instances out of them.
9
9
  #
10
- # This cop is marked as unsafe, because it is possible that this class is a parent
11
- # for some other subclass, monkey-patched with instance methods or
12
- # a dummy instance is instantiated from it somewhere.
10
+ # @safety
11
+ # This cop is unsafe, because it is possible that this class is a parent
12
+ # for some other subclass, monkey-patched with instance methods or
13
+ # a dummy instance is instantiated from it somewhere.
13
14
  #
14
15
  # @example
15
16
  # # bad
@@ -60,8 +61,7 @@ module RuboCop
60
61
  return false if nodes.empty?
61
62
 
62
63
  nodes.all? do |node|
63
- node_visibility(node) == :public &&
64
- node.defs_type? ||
64
+ (node_visibility(node) == :public && node.defs_type?) ||
65
65
  sclass_convertible_to_module?(node) ||
66
66
  node.equals_asgn? ||
67
67
  extend_call?(node)
@@ -5,8 +5,10 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for uses of `String#split` with empty string or regexp literal argument.
7
7
  #
8
- # This cop is marked as unsafe. But probably it's quite unlikely that some other class would
9
- # define a `split` method that takes exactly the same arguments.
8
+ # @safety
9
+ # This cop is unsafe because it cannot be guaranteed that the receiver
10
+ # is actually a string. If another class has a `split` method with
11
+ # different behaviour, it would be registered as a false positive.
10
12
  #
11
13
  # @example
12
14
  # # bad
@@ -23,6 +23,10 @@ module RuboCop
23
23
  # This is useful when the receiver is some expression that returns string like `Pathname`
24
24
  # instead of a string literal.
25
25
  #
26
+ # @safety
27
+ # This cop is unsafe in `aggressive` mode, as it cannot be guaranteed that
28
+ # the receiver is actually a string, which can result in a false positive.
29
+ #
26
30
  # @example Mode: aggressive (default)
27
31
  # # bad
28
32
  # email_with_name = user.name + ' <' + user.email + '>'
@@ -93,7 +97,7 @@ module RuboCop
93
97
 
94
98
  def offensive_for_mode?(receiver_node)
95
99
  mode = cop_config['Mode'].to_sym
96
- mode == :aggressive || mode == :conservative && receiver_node.str_type?
100
+ mode == :aggressive || (mode == :conservative && receiver_node.str_type?)
97
101
  end
98
102
 
99
103
  def line_end_concatenation?(node)
@@ -6,6 +6,10 @@ module RuboCop
6
6
  # This cop checks for the use of strings as keys in hashes. The use of
7
7
  # symbols is preferred instead.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because while symbols are preferred for hash keys,
11
+ # there are instances when string keys are required.
12
+ #
9
13
  # @example
10
14
  # # bad
11
15
  # { 'one' => 1, 'two' => 2, 'three' => 3 }