rubocop 1.50.2 → 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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +38 -5
  4. data/lib/rubocop/config.rb +4 -0
  5. data/lib/rubocop/config_obsoletion.rb +2 -2
  6. data/lib/rubocop/cop/base.rb +5 -1
  7. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
  8. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  9. data/lib/rubocop/cop/internal_affairs/cop_description.rb +32 -8
  10. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  11. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +0 -1
  12. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  13. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +2 -0
  14. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +13 -1
  15. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -4
  16. data/lib/rubocop/cop/lint/identity_comparison.rb +0 -1
  17. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -3
  18. data/lib/rubocop/cop/lint/inherit_exception.rb +9 -0
  19. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  20. data/lib/rubocop/cop/lint/missing_super.rb +3 -0
  21. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +2 -2
  22. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +0 -1
  23. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -2
  24. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +1 -1
  25. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -2
  26. data/lib/rubocop/cop/lint/shadowed_exception.rb +5 -11
  27. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +23 -9
  28. data/lib/rubocop/cop/lint/useless_assignment.rb +59 -1
  29. data/lib/rubocop/cop/lint/void.rb +62 -6
  30. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -2
  31. data/lib/rubocop/cop/mixin/allowed_receivers.rb +34 -0
  32. data/lib/rubocop/cop/mixin/comments_help.rb +6 -2
  33. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  34. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  35. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +22 -7
  36. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +11 -3
  37. data/lib/rubocop/cop/naming/variable_name.rb +6 -1
  38. data/lib/rubocop/cop/style/accessor_grouping.rb +5 -1
  39. data/lib/rubocop/cop/style/attr.rb +11 -1
  40. data/lib/rubocop/cop/style/begin_block.rb +1 -2
  41. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  42. data/lib/rubocop/cop/style/class_equality_comparison.rb +17 -39
  43. data/lib/rubocop/cop/style/collection_compact.rb +16 -6
  44. data/lib/rubocop/cop/style/colon_method_call.rb +2 -2
  45. data/lib/rubocop/cop/style/combinable_loops.rb +26 -6
  46. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  47. data/lib/rubocop/cop/style/copyright.rb +5 -2
  48. data/lib/rubocop/cop/style/dir.rb +1 -1
  49. data/lib/rubocop/cop/style/dir_empty.rb +8 -14
  50. data/lib/rubocop/cop/style/documentation.rb +1 -1
  51. data/lib/rubocop/cop/style/eval_with_location.rb +5 -5
  52. data/lib/rubocop/cop/style/exact_regexp_match.rb +68 -0
  53. data/lib/rubocop/cop/style/file_read.rb +2 -2
  54. data/lib/rubocop/cop/style/guard_clause.rb +2 -0
  55. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -22
  56. data/lib/rubocop/cop/style/hash_except.rb +19 -8
  57. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  58. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  59. data/lib/rubocop/cop/style/if_inside_else.rb +6 -0
  60. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -0
  61. data/lib/rubocop/cop/style/invertible_unless_condition.rb +9 -5
  62. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -2
  63. data/lib/rubocop/cop/style/multiple_comparison.rb +14 -0
  64. data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
  65. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  66. data/lib/rubocop/cop/style/redundant_array_constructor.rb +77 -0
  67. data/lib/rubocop/cop/style/redundant_filter_chain.rb +101 -0
  68. data/lib/rubocop/cop/style/redundant_line_continuation.rb +5 -1
  69. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +46 -0
  70. data/lib/rubocop/cop/style/regexp_literal.rb +11 -2
  71. data/lib/rubocop/cop/style/require_order.rb +11 -5
  72. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -3
  73. data/lib/rubocop/cop/style/select_by_regexp.rb +15 -5
  74. data/lib/rubocop/cop/style/semicolon.rb +12 -1
  75. data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
  76. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  77. data/lib/rubocop/cop/style/special_global_vars.rb +3 -4
  78. data/lib/rubocop/cop/team.rb +1 -1
  79. data/lib/rubocop/cop/variable_force/assignment.rb +33 -1
  80. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  81. data/lib/rubocop/cop/variable_force.rb +1 -0
  82. data/lib/rubocop/result_cache.rb +1 -1
  83. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  84. data/lib/rubocop/server/client_command/exec.rb +2 -1
  85. data/lib/rubocop/target_ruby.rb +3 -2
  86. data/lib/rubocop/version.rb +10 -6
  87. data/lib/rubocop.rb +5 -0
  88. metadata +15 -10
@@ -57,38 +57,58 @@ module RuboCop
57
57
  # end
58
58
  #
59
59
  class CombinableLoops < Base
60
+ extend AutoCorrector
61
+
62
+ include RangeHelp
63
+
60
64
  MSG = 'Combine this loop with the previous loop.'
61
65
 
62
66
  def on_block(node)
63
67
  return unless node.parent&.begin_type?
64
68
  return unless collection_looping_method?(node)
69
+ return unless same_collection_looping_block?(node, node.left_sibling)
65
70
 
66
- add_offense(node) if same_collection_looping?(node, node.left_sibling)
71
+ add_offense(node) do |corrector|
72
+ combine_with_left_sibling(corrector, node)
73
+ end
67
74
  end
68
75
 
69
76
  alias on_numblock on_block
70
77
 
71
78
  def on_for(node)
72
79
  return unless node.parent&.begin_type?
80
+ return unless same_collection_looping_for?(node, node.left_sibling)
73
81
 
74
- sibling = node.left_sibling
75
- add_offense(node) if sibling&.for_type? && node.collection == sibling.collection
82
+ add_offense(node) do |corrector|
83
+ combine_with_left_sibling(corrector, node)
84
+ end
76
85
  end
77
86
 
78
87
  private
79
88
 
80
89
  def collection_looping_method?(node)
81
- # TODO: Remove `Symbol#to_s` after supporting only Ruby >= 2.7.
82
- method_name = node.method_name.to_s
90
+ method_name = node.method_name
83
91
  method_name.start_with?('each') || method_name.end_with?('_each')
84
92
  end
85
93
 
86
- def same_collection_looping?(node, sibling)
94
+ def same_collection_looping_block?(node, sibling)
87
95
  (sibling&.block_type? || sibling&.numblock_type?) &&
88
96
  sibling.send_node.method?(node.method_name) &&
89
97
  sibling.receiver == node.receiver &&
90
98
  sibling.send_node.arguments == node.send_node.arguments
91
99
  end
100
+
101
+ def same_collection_looping_for?(node, sibling)
102
+ sibling&.for_type? && node.collection == sibling.collection
103
+ end
104
+
105
+ def combine_with_left_sibling(corrector, node)
106
+ corrector.replace(
107
+ node.left_sibling.body,
108
+ "#{node.left_sibling.body.source}\n#{node.body.source}"
109
+ )
110
+ corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
111
+ end
92
112
  end
93
113
  end
94
114
  end
@@ -350,7 +350,7 @@ module RuboCop
350
350
  end
351
351
 
352
352
  def ternary_condition?(node)
353
- [node, node.children.first].any? { |n| n.if_type? && n.ternary? }
353
+ [node, node.children.first].compact.any? { |n| n.if_type? && n.ternary? }
354
354
  end
355
355
 
356
356
  def lhs_all_match?(branches)
@@ -386,7 +386,7 @@ module RuboCop
386
386
  def allowed_statements?(branches)
387
387
  return false unless branches.all?
388
388
 
389
- statements = branches.map { |branch| tail(branch) }.compact
389
+ statements = branches.filter_map { |branch| tail(branch) }
390
390
 
391
391
  lhs_all_match?(statements) && statements.none?(&:masgn_type?) &&
392
392
  assignment_types_match?(*statements)
@@ -8,8 +8,11 @@ module RuboCop
8
8
  # The default regexp for an acceptable copyright notice can be found in
9
9
  # config/default.yml. The default can be changed as follows:
10
10
  #
11
- # Style/Copyright:
12
- # Notice: '^Copyright (\(c\) )?2\d{3} Acme Inc'
11
+ # [source,yaml]
12
+ # ----
13
+ # Style/Copyright:
14
+ # Notice: '^Copyright (\(c\) )?2\d{3} Acme Inc'
15
+ # ----
13
16
  #
14
17
  # This regex string is treated as an unanchored regex. For each file
15
18
  # that RuboCop scans, a comment that matches this regex must be found or
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for places where the `#__dir__` method can replace more
6
+ # Checks for places where the `#\_\_dir\_\_` method can replace more
7
7
  # complex constructs to retrieve a canonicalized absolute path to the
8
8
  # current file.
9
9
  #
@@ -19,18 +19,16 @@ module RuboCop
19
19
  extend AutoCorrector
20
20
  extend TargetRubyVersion
21
21
 
22
- MSG = 'Use `Dir.empty?(%<arg>s)` instead.'
23
- RESTRICT_ON_SEND = %i[== > empty? none?].freeze
22
+ MSG = 'Use `%<replacement>s` instead.'
23
+ RESTRICT_ON_SEND = %i[== != > empty? none?].freeze
24
24
 
25
25
  minimum_target_ruby_version 2.4
26
26
 
27
27
  # @!method offensive?(node)
28
28
  def_node_matcher :offensive?, <<~PATTERN
29
29
  {
30
- (send (send (send $(const {nil? cbase} :Dir) :entries $_) :size) {:== :>} (int 2))
31
- (send (send (send $(const {nil? cbase} :Dir) :children $_) :size) {:== :>} (int 0))
32
- (send (send (send (send $(const {nil? cbase} :Dir) :entries $_) :size) :!) {:== :>} (int 2))
33
- (send (send (send (send $(const {nil? cbase} :Dir) :children $_) :size) :!) {:== :>} (int 0))
30
+ (send (send (send $(const {nil? cbase} :Dir) :entries $_) :size) {:== :!= :>} (int 2))
31
+ (send (send (send $(const {nil? cbase} :Dir) :children $_) :size) {:== :!= :>} (int 0))
34
32
  (send (send $(const {nil? cbase} :Dir) :children $_) :empty?)
35
33
  (send (send $(const {nil? cbase} :Dir) :each_child $_) :none?)
36
34
  }
@@ -38,10 +36,9 @@ module RuboCop
38
36
 
39
37
  def on_send(node)
40
38
  offensive?(node) do |const_node, arg_node|
41
- add_offense(node, message: format(MSG, arg: arg_node.source)) do |corrector|
42
- bang(node)
43
- corrector.replace(node,
44
- "#{bang(node)}#{const_node.source}.empty?(#{arg_node.source})")
39
+ replacement = "#{bang(node)}#{const_node.source}.empty?(#{arg_node.source})"
40
+ add_offense(node, message: format(MSG, replacement: replacement)) do |corrector|
41
+ corrector.replace(node, replacement)
45
42
  end
46
43
  end
47
44
  end
@@ -49,10 +46,7 @@ module RuboCop
49
46
  private
50
47
 
51
48
  def bang(node)
52
- if (node.method?(:==) && node.child_nodes.first.method?(:!)) ||
53
- (node.method?(:>) && !node.child_nodes.first.method?(:!))
54
- '!'
55
- end
49
+ '!' if %i[!= >].include? node.method_name
56
50
  end
57
51
  end
58
52
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # declarations.
11
11
  #
12
12
  # The documentation requirement is annulled if the class or module has
13
- # a "#:nodoc:" comment next to it. Likewise, "#:nodoc: all" does the
13
+ # a `#:nodoc:` comment next to it. Likewise, `#:nodoc: all` does the
14
14
  # same for all its children.
15
15
  #
16
16
  # @example
@@ -4,12 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Ensures that eval methods (`eval`, `instance_eval`, `class_eval`
7
- # and `module_eval`) are given filename and line number values (`__FILE__`
8
- # and `__LINE__`). This data is used to ensure that any errors raised
7
+ # and `module_eval`) are given filename and line number values (`\_\_FILE\_\_`
8
+ # and `\_\_LINE\_\_`). This data is used to ensure that any errors raised
9
9
  # within the evaluated code will be given the correct identification
10
10
  # in a backtrace.
11
11
  #
12
- # The cop also checks that the line number given relative to `__LINE__` is
12
+ # The cop also checks that the line number given relative to `\_\_LINE\_\_` is
13
13
  # correct.
14
14
  #
15
15
  # This cop will autocorrect incorrect or missing filename and line number
@@ -57,7 +57,7 @@ module RuboCop
57
57
  extend AutoCorrector
58
58
 
59
59
  MSG = 'Pass `__FILE__` and `__LINE__` to `%<method_name>s`.'
60
- MSG_EVAL = 'Pass a binding, `__FILE__` and `__LINE__` to `eval`.'
60
+ MSG_EVAL = 'Pass a binding, `__FILE__`, and `__LINE__` to `eval`.'
61
61
  MSG_INCORRECT_FILE = 'Incorrect file for `%<method_name>s`; ' \
62
62
  'use `%<expected>s` instead of `%<actual>s`.'
63
63
  MSG_INCORRECT_LINE = 'Incorrect line number for `%<method_name>s`; ' \
@@ -210,7 +210,7 @@ module RuboCop
210
210
  def add_offense_for_missing_line(node, code)
211
211
  register_offense(node) do |corrector|
212
212
  line_str = missing_line(node, code)
213
- corrector.insert_after(node.source_range.end, ", #{line_str}")
213
+ corrector.insert_after(node.last_argument.source_range.end, ", #{line_str}")
214
214
  end
215
215
  end
216
216
 
@@ -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
@@ -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)
@@ -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
@@ -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
 
@@ -84,7 +84,10 @@ module RuboCop
84
84
  return unless (msg = message(node))
85
85
 
86
86
  add_offense(node.loc.keyword, message: format(msg, keyword: node.keyword)) do |corrector|
87
+ next if part_of_ignored_node?(node)
88
+
87
89
  autocorrect(corrector, node)
90
+ ignore_node(node)
88
91
  end
89
92
  end
90
93
 
@@ -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
@@ -135,8 +135,7 @@ module RuboCop
135
135
  end
136
136
 
137
137
  def call_with_braced_block?(node)
138
- (node.send_type? || node.super_type?) &&
139
- ((node.parent&.block_type? || node.parent&.numblock_type?) && node.parent&.braces?)
138
+ (node.send_type? || node.super_type?) && node.block_node&.braces?
140
139
  end
141
140
 
142
141
  def call_as_argument_or_chain?(node)
@@ -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
@@ -93,7 +93,7 @@ module RuboCop
93
93
  def contains_delimiter?(node, delimiters)
94
94
  delimiters_regexp = Regexp.union(delimiters)
95
95
 
96
- node.children.map { |n| string_source(n) }.compact.any?(delimiters_regexp)
96
+ node.children.filter_map { |n| string_source(n) }.any?(delimiters_regexp)
97
97
  end
98
98
 
99
99
  def string_source(node)
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for the instantiation of array using redundant `Array` constructor.
7
+ # Autocorrect replaces to array literal which is the simplest and fastest.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # Array.new([])
13
+ # Array[]
14
+ # Array([])
15
+ # Array.new(['foo', 'foo', 'foo'])
16
+ # Array['foo', 'foo', 'foo']
17
+ # Array(['foo', 'foo', 'foo'])
18
+ #
19
+ # # good
20
+ # []
21
+ # ['foo', 'foo', 'foo']
22
+ # Array.new(3, 'foo')
23
+ # Array.new(3) { 'foo' }
24
+ #
25
+ class RedundantArrayConstructor < Base
26
+ extend AutoCorrector
27
+
28
+ MSG = 'Remove the redundant `Array` constructor.'
29
+
30
+ RESTRICT_ON_SEND = %i[new [] Array].freeze
31
+
32
+ # @!method redundant_array_constructor(node)
33
+ def_node_matcher :redundant_array_constructor, <<~PATTERN
34
+ {
35
+ (send
36
+ (const {nil? cbase} :Array) :new
37
+ $(array ...))
38
+ (send
39
+ (const {nil? cbase} :Array) :[]
40
+ $...)
41
+ (send
42
+ nil? :Array
43
+ $(array ...))
44
+ }
45
+ PATTERN
46
+
47
+ def on_send(node)
48
+ return unless (array_literal = redundant_array_constructor(node))
49
+
50
+ receiver = node.receiver
51
+ selector = node.loc.selector
52
+
53
+ if node.method?(:new)
54
+ range = receiver.source_range.join(selector)
55
+ replacement = array_literal
56
+ elsif node.method?(:Array)
57
+ range = selector
58
+ replacement = array_literal
59
+ else
60
+ range = receiver
61
+ replacement = selector.begin.join(node.source_range.end)
62
+ end
63
+
64
+ register_offense(range, node, replacement)
65
+ end
66
+
67
+ private
68
+
69
+ def register_offense(range, node, replacement)
70
+ add_offense(range) do |corrector|
71
+ corrector.replace(node, replacement.source)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end