rubocop 1.48.1 → 1.52.1

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 (164) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +59 -12
  4. data/lib/rubocop/cli/command/execute_runner.rb +7 -2
  5. data/lib/rubocop/cli.rb +6 -6
  6. data/lib/rubocop/config.rb +5 -1
  7. data/lib/rubocop/config_loader.rb +8 -8
  8. data/lib/rubocop/config_obsoletion.rb +2 -2
  9. data/lib/rubocop/cop/autocorrect_logic.rb +28 -12
  10. data/lib/rubocop/cop/base.rb +5 -1
  11. data/lib/rubocop/cop/cop.rb +2 -2
  12. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
  13. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  14. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  15. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -1
  16. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  17. data/lib/rubocop/cop/internal_affairs/cop_description.rb +33 -9
  18. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +2 -2
  19. data/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb +1 -1
  20. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  21. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +29 -2
  22. data/lib/rubocop/cop/layout/class_structure.rb +1 -0
  23. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +0 -1
  24. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  25. data/lib/rubocop/cop/layout/empty_lines.rb +1 -1
  26. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +2 -0
  27. data/lib/rubocop/cop/layout/end_alignment.rb +5 -1
  28. data/lib/rubocop/cop/layout/extra_spacing.rb +6 -1
  29. data/lib/rubocop/cop/layout/first_argument_indentation.rb +6 -1
  30. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +25 -34
  31. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +7 -19
  32. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +42 -52
  33. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +38 -55
  34. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +4 -4
  35. data/lib/rubocop/cop/layout/initial_indentation.rb +1 -1
  36. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +8 -27
  37. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +7 -26
  38. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +4 -21
  39. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +6 -30
  40. data/lib/rubocop/cop/layout/redundant_line_break.rb +6 -7
  41. data/lib/rubocop/cop/layout/space_before_first_arg.rb +1 -1
  42. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +2 -0
  43. data/lib/rubocop/cop/layout/space_inside_parens.rb +2 -2
  44. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +13 -1
  45. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +3 -3
  46. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +122 -0
  47. data/lib/rubocop/cop/lint/empty_interpolation.rb +1 -1
  48. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -4
  49. data/lib/rubocop/cop/lint/identity_comparison.rb +0 -1
  50. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -3
  51. data/lib/rubocop/cop/lint/inherit_exception.rb +9 -0
  52. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  53. data/lib/rubocop/cop/lint/missing_super.rb +3 -0
  54. data/lib/rubocop/cop/lint/nested_method_definition.rb +2 -2
  55. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +2 -2
  56. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +0 -1
  57. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -2
  58. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +1 -1
  59. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +35 -15
  60. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -2
  61. data/lib/rubocop/cop/lint/shadowed_exception.rb +5 -11
  62. data/lib/rubocop/cop/lint/to_enum_arguments.rb +7 -1
  63. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +23 -9
  64. data/lib/rubocop/cop/lint/unreachable_loop.rb +3 -3
  65. data/lib/rubocop/cop/lint/useless_assignment.rb +59 -1
  66. data/lib/rubocop/cop/lint/useless_method_definition.rb +10 -2
  67. data/lib/rubocop/cop/lint/void.rb +69 -9
  68. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  69. data/lib/rubocop/cop/metrics/class_length.rb +1 -0
  70. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -2
  71. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  72. data/lib/rubocop/cop/mixin/allowed_receivers.rb +34 -0
  73. data/lib/rubocop/cop/mixin/comments_help.rb +7 -3
  74. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  75. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  76. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  77. data/lib/rubocop/cop/naming/ascii_identifiers.rb +1 -1
  78. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  79. data/lib/rubocop/cop/naming/inclusive_language.rb +23 -4
  80. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +22 -7
  81. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +11 -3
  82. data/lib/rubocop/cop/naming/variable_name.rb +6 -1
  83. data/lib/rubocop/cop/style/accessor_grouping.rb +5 -1
  84. data/lib/rubocop/cop/style/attr.rb +11 -1
  85. data/lib/rubocop/cop/style/begin_block.rb +1 -2
  86. data/lib/rubocop/cop/style/class_and_module_children.rb +2 -2
  87. data/lib/rubocop/cop/style/class_equality_comparison.rb +51 -40
  88. data/lib/rubocop/cop/style/collection_compact.rb +19 -6
  89. data/lib/rubocop/cop/style/colon_method_call.rb +2 -2
  90. data/lib/rubocop/cop/style/combinable_loops.rb +26 -6
  91. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  92. data/lib/rubocop/cop/style/copyright.rb +6 -3
  93. data/lib/rubocop/cop/style/data_inheritance.rb +75 -0
  94. data/lib/rubocop/cop/style/dir.rb +1 -1
  95. data/lib/rubocop/cop/style/dir_empty.rb +8 -14
  96. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +2 -2
  97. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  98. data/lib/rubocop/cop/style/documentation.rb +1 -1
  99. data/lib/rubocop/cop/style/double_negation.rb +2 -2
  100. data/lib/rubocop/cop/style/eval_with_location.rb +5 -5
  101. data/lib/rubocop/cop/style/exact_regexp_match.rb +68 -0
  102. data/lib/rubocop/cop/style/file_empty.rb +3 -3
  103. data/lib/rubocop/cop/style/file_read.rb +2 -2
  104. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  105. data/lib/rubocop/cop/style/guard_clause.rb +2 -0
  106. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -22
  107. data/lib/rubocop/cop/style/hash_except.rb +23 -12
  108. data/lib/rubocop/cop/style/hash_syntax.rb +4 -1
  109. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  110. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  111. data/lib/rubocop/cop/style/if_inside_else.rb +6 -0
  112. data/lib/rubocop/cop/style/if_unless_modifier.rb +41 -12
  113. data/lib/rubocop/cop/style/invertible_unless_condition.rb +9 -5
  114. data/lib/rubocop/cop/style/map_to_hash.rb +4 -1
  115. data/lib/rubocop/cop/style/map_to_set.rb +4 -1
  116. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -9
  117. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +43 -36
  118. data/lib/rubocop/cop/style/multiline_method_signature.rb +6 -3
  119. data/lib/rubocop/cop/style/multiple_comparison.rb +14 -0
  120. data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
  121. data/lib/rubocop/cop/style/parallel_assignment.rb +26 -18
  122. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -3
  123. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  124. data/lib/rubocop/cop/style/redundant_array_constructor.rb +77 -0
  125. data/lib/rubocop/cop/style/redundant_fetch_block.rb +6 -4
  126. data/lib/rubocop/cop/style/redundant_filter_chain.rb +101 -0
  127. data/lib/rubocop/cop/style/redundant_line_continuation.rb +183 -0
  128. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  129. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  130. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +2 -2
  131. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +46 -0
  132. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  133. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -3
  134. data/lib/rubocop/cop/style/regexp_literal.rb +11 -2
  135. data/lib/rubocop/cop/style/require_order.rb +11 -5
  136. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -3
  137. data/lib/rubocop/cop/style/select_by_regexp.rb +15 -5
  138. data/lib/rubocop/cop/style/semicolon.rb +12 -1
  139. data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
  140. data/lib/rubocop/cop/style/sole_nested_conditional.rb +5 -3
  141. data/lib/rubocop/cop/style/special_global_vars.rb +3 -4
  142. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  143. data/lib/rubocop/cop/style/trailing_body_on_class.rb +1 -0
  144. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  145. data/lib/rubocop/cop/style/unless_logical_operators.rb +1 -0
  146. data/lib/rubocop/cop/team.rb +1 -1
  147. data/lib/rubocop/cop/variable_force/assignment.rb +33 -1
  148. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  149. data/lib/rubocop/cop/variable_force.rb +1 -0
  150. data/lib/rubocop/cops_documentation_generator.rb +10 -3
  151. data/lib/rubocop/ext/regexp_node.rb +1 -1
  152. data/lib/rubocop/ext/regexp_parser.rb +1 -1
  153. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
  154. data/lib/rubocop/options.rb +4 -1
  155. data/lib/rubocop/result_cache.rb +2 -2
  156. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  157. data/lib/rubocop/server/cache.rb +1 -1
  158. data/lib/rubocop/server/client_command/exec.rb +2 -1
  159. data/lib/rubocop/server/helper.rb +1 -1
  160. data/lib/rubocop/server/server_command/exec.rb +1 -1
  161. data/lib/rubocop/target_ruby.rb +3 -2
  162. data/lib/rubocop/version.rb +10 -6
  163. data/lib/rubocop.rb +8 -0
  164. metadata +20 -12
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for exact regexp match inside Regexp literals.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # string =~ /\Astring\z/
12
+ # string === /\Astring\z/
13
+ # string.match(/\Astring\z/)
14
+ # string.match?(/\Astring\z/)
15
+ #
16
+ # # good
17
+ # string == 'string'
18
+ #
19
+ # # bad
20
+ # string !~ /\Astring\z/
21
+ #
22
+ # # good
23
+ # string != 'string'
24
+ #
25
+ class ExactRegexpMatch < Base
26
+ extend AutoCorrector
27
+
28
+ MSG = 'Use `%<prefer>s`.'
29
+ RESTRICT_ON_SEND = %i[=~ === !~ match match?].freeze
30
+
31
+ # @!method exact_regexp_match(node)
32
+ def_node_matcher :exact_regexp_match, <<~PATTERN
33
+ (send
34
+ _ {:=~ :=== :!~ :match :match?}
35
+ (regexp
36
+ (str $_)
37
+ (regopt)))
38
+ PATTERN
39
+
40
+ def on_send(node)
41
+ return unless (regexp = exact_regexp_match(node))
42
+
43
+ parsed_regexp = Regexp::Parser.parse(regexp)
44
+ return unless exact_match_pattern?(parsed_regexp)
45
+
46
+ prefer = "#{node.receiver.source} #{new_method(node)} '#{parsed_regexp[1].text}'"
47
+
48
+ add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
49
+ corrector.replace(node, prefer)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def exact_match_pattern?(parsed_regexp)
56
+ tokens = parsed_regexp.map(&:token)
57
+ return false unless tokens[0] == :bos && tokens[1] == :literal && tokens[2] == :eos
58
+
59
+ !parsed_regexp[1].quantifier
60
+ end
61
+
62
+ def new_method(node)
63
+ node.method?(:!~) ? '!=' : '=='
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -6,9 +6,9 @@ module RuboCop
6
6
  # Prefer to use `File.empty?('path/to/file')` when checking if a file is empty.
7
7
  #
8
8
  # @safety
9
- # This cop's autocorrection is unsafe it because `File.size`, `File.read`,
10
- # and `File.binread` raise `ENOENT` exception when there is no file
11
- # corresponding to the path, while `File.empty?` does not raise an exception.
9
+ # This cop is unsafe, because `File.size`, `File.read`, and `File.binread`
10
+ # raise `ENOENT` exception when there is no file corresponding to the path,
11
+ # while `File.empty?` does not raise an exception.
12
12
  #
13
13
  # @example
14
14
  # # bad
@@ -63,7 +63,7 @@ module RuboCop
63
63
 
64
64
  # @!method block_read?(node)
65
65
  def_node_matcher :block_read?, <<~PATTERN
66
- (block _ (args (arg $_)) (send (lvar $_) :read))
66
+ (block _ (args (arg _name)) (send (lvar _name) :read))
67
67
  PATTERN
68
68
 
69
69
  def on_send(node)
@@ -100,7 +100,7 @@ module RuboCop
100
100
  def file_open_read?(node)
101
101
  return true if send_read?(node)
102
102
 
103
- block_read?(node) { |block_arg, read_lvar| block_arg == read_lvar }
103
+ block_read?(node)
104
104
  end
105
105
 
106
106
  def read_method(mode)
@@ -148,7 +148,7 @@ module RuboCop
148
148
  end
149
149
 
150
150
  def frozen_string_literal_comment(processed_source)
151
- processed_source.find_token do |token|
151
+ processed_source.tokens.find do |token|
152
152
  token.text.start_with?(FROZEN_STRING_LITERAL)
153
153
  end
154
154
  end
@@ -187,6 +187,8 @@ module RuboCop
187
187
  if_branch = node.if_branch
188
188
  else_branch = node.else_branch
189
189
 
190
+ corrector.replace(node.loc.begin, "\n") if node.loc.begin&.is?('then')
191
+
190
192
  if if_branch&.send_type? && heredoc?(if_branch.last_argument)
191
193
  autocorrect_heredoc_argument(corrector, node, if_branch, else_branch, guard)
192
194
  elsif else_branch&.send_type? && heredoc?(else_branch.last_argument)
@@ -28,6 +28,7 @@ module RuboCop
28
28
  # execute(sql).keys.each { |v| p v }
29
29
  # execute(sql).values.each { |v| p v }
30
30
  class HashEachMethods < Base
31
+ include AllowedReceivers
31
32
  include Lint::UnusedArgument
32
33
  extend AutoCorrector
33
34
 
@@ -116,28 +117,6 @@ module RuboCop
116
117
  def kv_range(outer_node)
117
118
  outer_node.receiver.loc.selector.join(outer_node.loc.selector)
118
119
  end
119
-
120
- def allowed_receiver?(receiver)
121
- receiver_name = receiver_name(receiver)
122
-
123
- allowed_receivers.include?(receiver_name)
124
- end
125
-
126
- def receiver_name(receiver)
127
- if receiver.send_type?
128
- if receiver.receiver
129
- "#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
130
- else
131
- receiver.method_name.to_s
132
- end
133
- else
134
- receiver.source
135
- end
136
- end
137
-
138
- def allowed_receivers
139
- cop_config.fetch('AllowedReceivers', [])
140
- end
141
120
  end
142
121
  end
143
122
  end
@@ -45,13 +45,13 @@ module RuboCop
45
45
  (block
46
46
  (send _ _)
47
47
  (args
48
- (arg _)
48
+ $(arg _)
49
49
  (arg _))
50
50
  {
51
- (send
51
+ $(send
52
52
  _ {:== :!= :eql? :include?} _)
53
53
  (send
54
- (send
54
+ $(send
55
55
  _ {:== :!= :eql? :include?} _) :!)
56
56
  })
57
57
  PATTERN
@@ -61,13 +61,13 @@ module RuboCop
61
61
  (block
62
62
  (send _ _)
63
63
  (args
64
- (arg _)
64
+ $(arg _)
65
65
  (arg _))
66
66
  {
67
- (send
67
+ $(send
68
68
  _ {:== :!= :eql? :in? :include? :exclude?} _)
69
69
  (send
70
- (send
70
+ $(send
71
71
  _ {:== :!= :eql? :in? :include? :exclude?} _) :!)
72
72
  })
73
73
  PATTERN
@@ -89,13 +89,24 @@ module RuboCop
89
89
 
90
90
  private
91
91
 
92
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
92
93
  def bad_method?(block)
93
94
  if active_support_extensions_enabled?
94
- bad_method_with_active_support?(block)
95
+ bad_method_with_active_support?(block) do |key_arg, send_node|
96
+ if send_node.method?(:in?) && send_node.receiver&.source != key_arg.source
97
+ return false
98
+ end
99
+ return true if !send_node.method?(:include?) && !send_node.method?(:exclude?)
100
+
101
+ send_node.first_argument&.source == key_arg.source
102
+ end
95
103
  else
96
- bad_method_with_poro?(block)
104
+ bad_method_with_poro?(block) do |key_arg, send_node|
105
+ !send_node.method?(:include?) || send_node.first_argument&.source == key_arg.source
106
+ end
97
107
  end
98
108
  end
109
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
99
110
 
100
111
  def semantically_except_method?(send, block)
101
112
  body = block.body
@@ -122,8 +133,8 @@ module RuboCop
122
133
  end
123
134
 
124
135
  def safe_to_register_offense?(block, except_key)
125
- extracted = extract_body_if_nagated(block.body)
126
- if extracted.method?('in?') || extracted.method?('include?') || \
136
+ extracted = extract_body_if_negated(block.body)
137
+ if extracted.method?('in?') || extracted.method?('include?') ||
127
138
  extracted.method?('exclude?')
128
139
  return true
129
140
  end
@@ -132,7 +143,7 @@ module RuboCop
132
143
  except_key.sym_type? || except_key.str_type?
133
144
  end
134
145
 
135
- def extract_body_if_nagated(body)
146
+ def extract_body_if_negated(body)
136
147
  return body unless body.method?('!')
137
148
 
138
149
  body.receiver
@@ -161,7 +172,7 @@ module RuboCop
161
172
 
162
173
  def except_key(node)
163
174
  key_argument = node.argument_list.first.source
164
- body = extract_body_if_nagated(node.body)
175
+ body = extract_body_if_negated(node.body)
165
176
  lhs, _method_name, rhs = *body
166
177
  return if [lhs, rhs].map(&:source).none?(key_argument)
167
178
 
@@ -88,6 +88,9 @@ module RuboCop
88
88
  # {foo: foo, bar: bar}
89
89
  #
90
90
  # # good
91
+ # {foo: foo, bar:}
92
+ #
93
+ # # good
91
94
  # {foo:, bar:}
92
95
  #
93
96
  # @example EnforcedShorthandSyntax: consistent
@@ -207,7 +210,7 @@ module RuboCop
207
210
  return true if /\A[_a-z]\w*[?!]?\z/i.match?(sym_name)
208
211
 
209
212
  # For more complicated hash keys, let the parser validate the syntax.
210
- ProcessedSource.new("{ #{sym_name}: :foo }", target_ruby_version).valid_syntax?
213
+ parse("{ #{sym_name}: :foo }").valid_syntax?
211
214
  end
212
215
 
213
216
  def check(pairs, delim, msg)
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of `_.each_with_object({}) {...}`,
7
- # `_.map {...}.to_h`, and `Hash[_.map {...}]` that are actually just
6
+ # Looks for uses of `\_.each_with_object({}) {...}`,
7
+ # `\_.map {...}.to_h`, and `Hash[\_.map {...}]` that are actually just
8
8
  # transforming the keys of a hash, and tries to use a simpler & faster
9
9
  # call to `transform_keys` instead.
10
10
  # It should only be enabled on Ruby version 2.5 or newer.
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of `_.each_with_object({}) {...}`,
7
- # `_.map {...}.to_h`, and `Hash[_.map {...}]` that are actually just
6
+ # Looks for uses of `\_.each_with_object({}) {...}`,
7
+ # `\_.map {...}.to_h`, and `Hash[\_.map {...}]` that are actually just
8
8
  # transforming the values of a hash, and tries to use a simpler & faster
9
9
  # call to `transform_values` instead.
10
10
  #
@@ -59,11 +59,13 @@ module RuboCop
59
59
  # end
60
60
  #
61
61
  class IfInsideElse < Base
62
+ include IgnoredNode
62
63
  include RangeHelp
63
64
  extend AutoCorrector
64
65
 
65
66
  MSG = 'Convert `if` nested inside `else` to `elsif`.'
66
67
 
68
+ # rubocop:disable Metrics/CyclomaticComplexity
67
69
  def on_if(node)
68
70
  return if node.ternary? || node.unless?
69
71
 
@@ -73,9 +75,13 @@ module RuboCop
73
75
  return if allow_if_modifier_in_else_branch?(else_branch)
74
76
 
75
77
  add_offense(else_branch.loc.keyword) do |corrector|
78
+ next if part_of_ignored_node?(node)
79
+
76
80
  autocorrect(corrector, else_branch)
81
+ ignore_node(node)
77
82
  end
78
83
  end
84
+ # rubocop:enable Metrics/CyclomaticComplexity
79
85
 
80
86
  private
81
87
 
@@ -11,6 +11,18 @@ module RuboCop
11
11
  # cop. The tab size is configured in the `IndentationWidth` of the
12
12
  # `Layout/IndentationStyle` cop.
13
13
  #
14
+ # One-line pattern matching is always allowed. To ensure that there are few cases
15
+ # where the match variable is not used, and to prevent oversights. The variable `x`
16
+ # becomes undefined and raises `NameError` when the following example is changed to
17
+ # the modifier form:
18
+ #
19
+ # [source,ruby]
20
+ # ----
21
+ # if [42] in [x]
22
+ # x # `x` is undefined when using modifier form.
23
+ # end
24
+ # ----
25
+ #
14
26
  # NOTE: It is allowed when `defined?` argument has an undefined value,
15
27
  # because using the modifier form causes the following incompatibility:
16
28
  #
@@ -66,27 +78,26 @@ module RuboCop
66
78
  end
67
79
 
68
80
  def on_if(node)
69
- return if defined_nodes(node).any? { |n| defined_argument_is_undefined?(node, n) }
70
-
71
- msg = if single_line_as_modifier?(node) && !named_capture_in_condition?(node)
72
- MSG_USE_MODIFIER
73
- elsif too_long_due_to_modifier?(node)
74
- MSG_USE_NORMAL
75
- end
76
- return unless msg
81
+ condition = node.condition
82
+ return if defined_nodes(condition).any? { |n| defined_argument_is_undefined?(node, n) } ||
83
+ pattern_matching_nodes(condition).any?
84
+ return unless (msg = message(node))
77
85
 
78
86
  add_offense(node.loc.keyword, message: format(msg, keyword: node.keyword)) do |corrector|
87
+ next if part_of_ignored_node?(node)
88
+
79
89
  autocorrect(corrector, node)
90
+ ignore_node(node)
80
91
  end
81
92
  end
82
93
 
83
94
  private
84
95
 
85
- def defined_nodes(node)
86
- if node.condition.defined_type?
87
- [node.condition]
96
+ def defined_nodes(condition)
97
+ if condition.defined_type?
98
+ [condition]
88
99
  else
89
- node.condition.each_descendant.select(&:defined_type?)
100
+ condition.each_descendant.select(&:defined_type?)
90
101
  end
91
102
  end
92
103
 
@@ -100,6 +111,24 @@ module RuboCop
100
111
  end
101
112
  end
102
113
 
114
+ def pattern_matching_nodes(condition)
115
+ if condition.match_pattern_type? || condition.match_pattern_p_type?
116
+ [condition]
117
+ else
118
+ condition.each_descendant.select do |node|
119
+ node.match_pattern_type? || node.match_pattern_p_type?
120
+ end
121
+ end
122
+ end
123
+
124
+ def message(node)
125
+ if single_line_as_modifier?(node) && !named_capture_in_condition?(node)
126
+ MSG_USE_MODIFIER
127
+ elsif too_long_due_to_modifier?(node)
128
+ MSG_USE_NORMAL
129
+ end
130
+ end
131
+
103
132
  def autocorrect(corrector, node)
104
133
  replacement = if node.modifier_form?
105
134
  replacement_for_modifier_form(corrector, node)
@@ -10,12 +10,16 @@ module RuboCop
10
10
  # Methods that can be inverted should be defined in `InverseMethods`. Note that
11
11
  # the relationship of inverse methods needs to be defined in both directions.
12
12
  # For example,
13
- # InverseMethods:
14
- # :!=: :==
15
- # :even?: :odd?
16
- # :odd?: :even?
17
13
  #
18
- # will suggest both `even?` and `odd?` to be inverted, but only `!=` (and not `==`).
14
+ # [source,yaml]
15
+ # ----
16
+ # InverseMethods:
17
+ # :!=: :==
18
+ # :even?: :odd?
19
+ # :odd?: :even?
20
+ # ----
21
+ #
22
+ # will suggest both `even?` and `odd?` to be inverted, but only `!=` (and not `==`).
19
23
  #
20
24
  # @safety
21
25
  # This cop is unsafe because it cannot be guaranteed that the method
@@ -39,7 +39,10 @@ module RuboCop
39
39
 
40
40
  # @!method map_to_h?(node)
41
41
  def_node_matcher :map_to_h?, <<~PATTERN
42
- $(send (block $(send _ {:map :collect}) ...) :to_h)
42
+ {
43
+ $(send ({block numblock} $(send _ {:map :collect}) ...) :to_h)
44
+ $(send $(send _ {:map :collect} (block_pass sym)) :to_h)
45
+ }
43
46
  PATTERN
44
47
 
45
48
  def on_send(node)
@@ -32,7 +32,10 @@ module RuboCop
32
32
 
33
33
  # @!method map_to_set?(node)
34
34
  def_node_matcher :map_to_set?, <<~PATTERN
35
- $(send (block $(send _ {:map :collect}) ...) :to_set)
35
+ {
36
+ $(send ({block numblock} $(send _ {:map :collect}) ...) :to_set)
37
+ $(send $(send _ {:map :collect} (block_pass sym)) :to_set)
38
+ }
36
39
  PATTERN
37
40
 
38
41
  def on_send(node)
@@ -50,17 +50,13 @@ module RuboCop
50
50
  return false unless (last_argument = node.last_argument)
51
51
  return false if !last_argument.hash_type? || !last_argument.pairs.last&.value_omission?
52
52
 
53
- modifier_form?(node) || exist_next_line_expression?(node)
54
- end
55
-
56
- def modifier_form?(node)
57
- node.parent.respond_to?(:modifier_form?) && node.parent.modifier_form?
53
+ node.parent&.conditional? || !last_expression?(node)
58
54
  end
59
55
 
60
56
  # Require hash value omission be enclosed in parentheses to prevent the following issue:
61
57
  # https://bugs.ruby-lang.org/issues/18396.
62
- def exist_next_line_expression?(node)
63
- node.parent&.assignment? ? node.parent.right_sibling : node.right_sibling
58
+ def last_expression?(node)
59
+ !(node.parent&.assignment? ? node.parent.right_sibling : node.right_sibling)
64
60
  end
65
61
 
66
62
  def syntax_like_method_call?(node)
@@ -139,8 +135,7 @@ module RuboCop
139
135
  end
140
136
 
141
137
  def call_with_braced_block?(node)
142
- (node.send_type? || node.super_type?) &&
143
- ((node.parent&.block_type? || node.parent&.numblock_type?) && node.parent&.braces?)
138
+ (node.send_type? || node.super_type?) && node.block_node&.braces?
144
139
  end
145
140
 
146
141
  def call_as_argument_or_chain?(node)
@@ -7,21 +7,19 @@ module RuboCop
7
7
  # method calls containing parameters.
8
8
  #
9
9
  # In the default style (require_parentheses), macro methods are allowed.
10
- # Additional methods can be added to the `AllowedMethods`
11
- # or `AllowedPatterns` list. These options are
12
- # valid only in the default style. Macros can be included by
13
- # either setting `IgnoreMacros` to false or adding specific macros to
14
- # the `IncludedMacros` list.
10
+ # Additional methods can be added to the `AllowedMethods` or
11
+ # `AllowedPatterns` list. These options are valid only in the default
12
+ # style. Macros can be included by either setting `IgnoreMacros` to false
13
+ # or adding specific macros to the `IncludedMacros` list.
15
14
  #
16
- # Precedence of options is all follows:
15
+ # Precedence of options is as follows:
17
16
  #
18
17
  # 1. `AllowedMethods`
19
18
  # 2. `AllowedPatterns`
20
19
  # 3. `IncludedMacros`
21
20
  #
22
- # eg. If a method is listed in both
23
- # `IncludedMacros` and `AllowedMethods`, then the latter takes
24
- # precedence (that is, the method is allowed).
21
+ # If a method is listed in both `IncludedMacros` and `AllowedMethods`,
22
+ # then the latter takes precedence (that is, the method is allowed).
25
23
  #
26
24
  # In the alternative style (omit_parentheses), there are three additional
27
25
  # options.
@@ -40,14 +38,29 @@ module RuboCop
40
38
  # to `true` allows the presence of parentheses in such a method call
41
39
  # even with arguments.
42
40
  #
43
- # NOTE: Parentheses are still allowed in cases where omitting them
44
- # results in ambiguous or syntactically incorrect code. For example,
45
- # parentheses are required around a method with arguments when inside an
46
- # endless method definition introduced in Ruby 3.0. Parentheses are also
47
- # allowed when forwarding arguments with the triple-dot syntax introduced
48
- # in Ruby 2.7 as omitting them starts an endless range.
49
- # And Ruby 3.1's hash omission syntax has a case that requires parentheses
50
- # because of the following issue: https://bugs.ruby-lang.org/issues/18396.
41
+ # NOTE: The style of `omit_parentheses` allows parentheses in cases where
42
+ # omitting them results in ambiguous or syntactically incorrect code.
43
+ #
44
+ # Non-exhaustive list of examples:
45
+ #
46
+ # - Parentheses are required allowed in method calls with arguments inside
47
+ # literals, logical operators, setting default values in position and
48
+ # keyword arguments, chaining and more.
49
+ # - Parentheses are allowed in method calls with arguments inside
50
+ # operators to avoid ambiguity.
51
+ # triple-dot syntax introduced in Ruby 2.7 as omitting them starts an
52
+ # endless range.
53
+ # - Parentheses are allowed when forwarding arguments with the
54
+ # triple-dot syntax introduced in Ruby 2.7 as omitting them starts an
55
+ # endless range.
56
+ # - Parentheses are required in calls with arguments when inside an
57
+ # endless method definition introduced in Ruby 3.0.
58
+ # - Ruby 3.1's hash omission syntax allows parentheses if the method call
59
+ # is in conditionals and requires parentheses if the call
60
+ # is not the value-returning expression. See
61
+ # https://bugs.ruby-lang.org/issues/18396.
62
+ # - Parentheses are required in anonymous arguments, keyword arguments
63
+ # and block passing in Ruby 3.2.
51
64
  #
52
65
  # @example EnforcedStyle: require_parentheses (default)
53
66
  #
@@ -80,34 +93,28 @@ module RuboCop
80
93
  # array.delete e
81
94
  #
82
95
  # # bad
83
- # foo.enforce(strict: true)
96
+ # action.enforce(strict: true)
84
97
  #
85
98
  # # good
86
- # foo.enforce strict: true
99
+ # action.enforce strict: true
87
100
  #
88
101
  # # good
89
- # # Allows parens for calls that won't produce valid Ruby or be ambiguous.
90
- # model.validate strict(true)
102
+ # # Parentheses are allowed for code that can be ambiguous without
103
+ # # them.
104
+ # action.enforce(condition) || other_condition
91
105
  #
92
106
  # # good
93
- # # Allows parens for calls that won't produce valid Ruby or be ambiguous.
107
+ # # Parentheses are allowed for calls that won't produce valid Ruby
108
+ # # without them.
94
109
  # yield path, File.basename(path)
95
110
  #
96
111
  # # good
97
- # # Operators methods calls with parens
98
- # array&.[](index)
99
- #
100
- # # good
101
- # # Operators methods without parens, if you prefer
102
- # array.[] index
103
- #
104
- # # good
105
- # # Operators methods calls with parens
106
- # array&.[](index)
107
- #
108
- # # good
109
- # # Operators methods without parens, if you prefer
110
- # array.[] index
112
+ # # Omitting the parentheses in Ruby 3.1 hash omission syntax can lead
113
+ # # to ambiguous code. We allow them in conditionals and non-last
114
+ # # expressions. See https://bugs.ruby-lang.org/issues/18396
115
+ # if meets(criteria:, action:)
116
+ # safe_action(action) || dangerous_action(action)
117
+ # end
111
118
  #
112
119
  # @example IgnoreMacros: true (default)
113
120
  #
@@ -28,14 +28,17 @@ module RuboCop
28
28
  return unless node.arguments?
29
29
  return if opening_line(node) == closing_line(node)
30
30
  return if correction_exceeds_max_line_length?(node)
31
+ return unless (begin_of_arguments = node.arguments.loc.begin)
31
32
 
32
- add_offense(node) { |corrector| autocorrect(corrector, node) }
33
+ add_offense(node) do |corrector|
34
+ autocorrect(corrector, node, begin_of_arguments)
35
+ end
33
36
  end
34
37
  alias on_defs on_def
35
38
 
36
39
  private
37
40
 
38
- def autocorrect(corrector, node)
41
+ def autocorrect(corrector, node, begin_of_arguments)
39
42
  arguments = node.arguments
40
43
  joined_arguments = arguments.map(&:source).join(', ')
41
44
  last_line_source_of_arguments = last_line_source_of_arguments(arguments)
@@ -47,7 +50,7 @@ module RuboCop
47
50
  end
48
51
 
49
52
  corrector.remove(arguments_range(node))
50
- corrector.insert_after(arguments.loc.begin, joined_arguments)
53
+ corrector.insert_after(begin_of_arguments, joined_arguments)
51
54
  end
52
55
 
53
56
  def last_line_source_of_arguments(arguments)
@@ -40,6 +40,15 @@ module RuboCop
40
40
  #
41
41
  # # good
42
42
  # foo if [b.lightweight, b.heavyweight].include?(a)
43
+ #
44
+ # @example ComparisonsThreshold: 2 (default)
45
+ # # bad
46
+ # foo if a == 'a' || a == 'b'
47
+ #
48
+ # @example ComparisonsThreshold: 3
49
+ # # good
50
+ # foo if a == 'a' || a == 'b'
51
+ #
43
52
  class MultipleComparison < Base
44
53
  extend AutoCorrector
45
54
 
@@ -58,6 +67,7 @@ module RuboCop
58
67
  return unless node == root_of_or_node
59
68
  return unless nested_variable_comparison?(root_of_or_node)
60
69
  return if @allowed_method_comparison
70
+ return if @compared_elements.size < comparisons_threshold
61
71
 
62
72
  add_offense(node) do |corrector|
63
73
  elements = @compared_elements.join(', ')
@@ -151,6 +161,10 @@ module RuboCop
151
161
  def allow_method_comparison?
152
162
  cop_config.fetch('AllowMethodComparison', true)
153
163
  end
164
+
165
+ def comparisons_threshold
166
+ cop_config.fetch('ComparisonsThreshold', 2)
167
+ end
154
168
  end
155
169
  end
156
170
  end
@@ -121,7 +121,7 @@ module RuboCop
121
121
 
122
122
  def allowed_patterns
123
123
  # Convert the patterns to be anchored
124
- super.map { |regexp| Regexp.new(/\A#{regexp}\z/) }
124
+ super.map { |regexp| /\A#{regexp}\z/ }
125
125
  end
126
126
  end
127
127
  end