rubocop 1.35.1 → 1.37.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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +32 -1
  4. data/lib/rubocop/arguments_env.rb +17 -0
  5. data/lib/rubocop/arguments_file.rb +17 -0
  6. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  7. data/lib/rubocop/cop/generator.rb +1 -2
  8. data/lib/rubocop/cop/layout/block_alignment.rb +14 -12
  9. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  10. data/lib/rubocop/cop/layout/indentation_width.rb +4 -2
  11. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  12. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +25 -9
  13. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +28 -3
  14. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -2
  15. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  16. data/lib/rubocop/cop/lint/duplicate_methods.rb +11 -1
  17. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  18. data/lib/rubocop/cop/lint/duplicate_require.rb +1 -1
  19. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  20. data/lib/rubocop/cop/lint/empty_conditional_body.rb +46 -4
  21. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  22. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  23. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  24. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  25. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +12 -1
  26. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  27. data/lib/rubocop/cop/lint/redundant_require_statement.rb +29 -9
  28. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  29. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  30. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
  31. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +17 -2
  32. data/lib/rubocop/cop/lint/unreachable_loop.rb +2 -2
  33. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  34. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  35. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +1 -1
  36. data/lib/rubocop/cop/mixin/allowed_methods.rb +10 -5
  37. data/lib/rubocop/cop/mixin/allowed_pattern.rb +13 -5
  38. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  39. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  40. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -3
  41. data/lib/rubocop/cop/mixin/hash_transform_method.rb +9 -5
  42. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  43. data/lib/rubocop/cop/mixin/surrounding_space.rb +6 -5
  44. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  45. data/lib/rubocop/cop/style/access_modifier_declarations.rb +97 -1
  46. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  47. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  48. data/lib/rubocop/cop/style/case_equality.rb +40 -10
  49. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  50. data/lib/rubocop/cop/style/collection_compact.rb +6 -1
  51. data/lib/rubocop/cop/style/each_for_simple_loop.rb +40 -5
  52. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  53. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  54. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  55. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  56. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  57. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  58. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  59. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  60. data/lib/rubocop/cop/style/operator_method_call.rb +39 -0
  61. data/lib/rubocop/cop/style/perl_backrefs.rb +22 -1
  62. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  63. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  64. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  65. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  66. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  67. data/lib/rubocop/cop/style/redundant_string_escape.rb +173 -0
  68. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  69. data/lib/rubocop/cop/style/static_class.rb +32 -1
  70. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  71. data/lib/rubocop/cop/style/symbol_proc.rb +6 -5
  72. data/lib/rubocop/cop/style/word_array.rb +2 -0
  73. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -2
  74. data/lib/rubocop/options.rb +13 -13
  75. data/lib/rubocop/rspec/shared_contexts.rb +13 -1
  76. data/lib/rubocop/runner.rb +4 -0
  77. data/lib/rubocop/server/cache.rb +5 -1
  78. data/lib/rubocop/server/cli.rb +9 -2
  79. data/lib/rubocop/server/client_command/exec.rb +5 -0
  80. data/lib/rubocop/server/core.rb +2 -1
  81. data/lib/rubocop/server/socket_reader.rb +5 -1
  82. data/lib/rubocop/server.rb +1 -1
  83. data/lib/rubocop/version.rb +8 -2
  84. data/lib/rubocop.rb +3 -0
  85. metadata +10 -5
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Naming
6
- # This cops recommends the use of inclusive language instead of problematic terms.
6
+ # Recommends the use of inclusive language instead of problematic terms.
7
7
  # The cop can check the following locations for offenses:
8
8
  # - identifiers
9
9
  # - constants
@@ -9,6 +9,11 @@ module RuboCop
9
9
  # Applications of visibility methods to symbols can be controlled
10
10
  # using AllowModifiersOnSymbols config.
11
11
  #
12
+ # @safety
13
+ # Autocorrection is not safe, because the visibility of dynamically
14
+ # defined methods can vary depending on the state determined by
15
+ # the group access modifier.
16
+ #
12
17
  # @example EnforcedStyle: group (default)
13
18
  # # bad
14
19
  # class Foo
@@ -63,7 +68,10 @@ module RuboCop
63
68
  #
64
69
  # end
65
70
  class AccessModifierDeclarations < Base
71
+ extend AutoCorrector
72
+
66
73
  include ConfigurableEnforcedStyle
74
+ include RangeHelp
67
75
 
68
76
  GROUP_STYLE_MESSAGE = [
69
77
  '`%<access_modifier>s` should not be',
@@ -88,7 +96,10 @@ module RuboCop
88
96
  return if allow_modifiers_on_symbols?(node)
89
97
 
90
98
  if offense?(node)
91
- add_offense(node.loc.selector) { opposite_style_detected }
99
+ add_offense(node.loc.selector) do |corrector|
100
+ autocorrect(corrector, node)
101
+ end
102
+ opposite_style_detected
92
103
  else
93
104
  correct_style_detected
94
105
  end
@@ -96,6 +107,23 @@ module RuboCop
96
107
 
97
108
  private
98
109
 
110
+ def autocorrect(corrector, node)
111
+ case style
112
+ when :group
113
+ def_node = find_corresponding_def_node(node)
114
+ return unless def_node
115
+
116
+ remove_node(corrector, def_node)
117
+ remove_node(corrector, node)
118
+ insert_def(corrector, node, def_node.source)
119
+ when :inline
120
+ remove_node(corrector, node)
121
+ select_grouped_def_nodes(node).each do |grouped_def_node|
122
+ insert_inline_modifier(corrector, grouped_def_node, node.method_name)
123
+ end
124
+ end
125
+ end
126
+
99
127
  def allow_modifiers_on_symbols?(node)
100
128
  cop_config['AllowModifiersOnSymbols'] && access_modifier_with_symbol?(node)
101
129
  end
@@ -130,6 +158,74 @@ module RuboCop
130
158
  format(INLINE_STYLE_MESSAGE, access_modifier: access_modifier)
131
159
  end
132
160
  end
161
+
162
+ def find_corresponding_def_node(node)
163
+ if access_modifier_with_symbol?(node)
164
+ method_name = node.arguments.first.value
165
+ node.parent.each_child_node(:def).find do |child|
166
+ child.method?(method_name)
167
+ end
168
+ else
169
+ node.arguments.first
170
+ end
171
+ end
172
+
173
+ def find_argument_less_modifier_node(node)
174
+ node.parent.each_child_node(:send).find do |child|
175
+ child.method?(node.method_name) && child.arguments.empty?
176
+ end
177
+ end
178
+
179
+ def select_grouped_def_nodes(node)
180
+ node.right_siblings.take_while do |sibling|
181
+ !(sibling.send_type? && sibling.bare_access_modifier_declaration?)
182
+ end.select(&:def_type?)
183
+ end
184
+
185
+ def insert_def(corrector, node, source)
186
+ source = [*processed_source.ast_with_comments[node].map(&:text), source].join("\n")
187
+ argument_less_modifier_node = find_argument_less_modifier_node(node)
188
+ if argument_less_modifier_node
189
+ corrector.insert_after(argument_less_modifier_node, "\n\n#{source}")
190
+ else
191
+ corrector.insert_before(
192
+ node.each_ancestor(:block, :class, :module).first.location.end,
193
+ "#{node.method_name}\n\n#{source}\n"
194
+ )
195
+ end
196
+ end
197
+
198
+ def insert_inline_modifier(corrector, node, modifier_name)
199
+ corrector.insert_before(node, "#{modifier_name} ")
200
+ end
201
+
202
+ def remove_node(corrector, node)
203
+ corrector.remove(
204
+ range_by_whole_lines(
205
+ range_with_comments(node),
206
+ include_final_newline: true
207
+ )
208
+ )
209
+ end
210
+
211
+ def range_with_comments(node)
212
+ ranges = [
213
+ node,
214
+ *processed_source.ast_with_comments[node]
215
+ ].map do |element|
216
+ element.location.expression
217
+ end
218
+ ranges.reduce do |result, range|
219
+ add_range(result, range)
220
+ end
221
+ end
222
+
223
+ def add_range(range1, range2)
224
+ range1.with(
225
+ begin_pos: [range1.begin_pos, range2.begin_pos].min,
226
+ end_pos: [range1.end_pos, range2.end_pos].max
227
+ )
228
+ end
133
229
  end
134
230
  end
135
231
  end
@@ -135,12 +135,16 @@ module RuboCop
135
135
  end
136
136
 
137
137
  def separate_accessors(node)
138
- node.arguments.map do |arg|
139
- if arg == node.arguments.first
138
+ node.arguments.flat_map do |arg|
139
+ lines = [
140
+ *processed_source.ast_with_comments[arg].map(&:text),
140
141
  "#{node.method_name} #{arg.source}"
142
+ ]
143
+ if arg == node.arguments.first
144
+ lines
141
145
  else
142
146
  indent = ' ' * node.loc.column
143
- "#{indent}#{node.method_name} #{arg.source}"
147
+ lines.map { |line| "#{indent}#{line}" }
144
148
  end
145
149
  end.join("\n")
146
150
  end
@@ -157,7 +157,7 @@ module RuboCop
157
157
  # process(something)
158
158
  # }
159
159
  #
160
- # @example AllowedPatterns: [/map/]
160
+ # @example AllowedPatterns: ['map']
161
161
  #
162
162
  # # good
163
163
  # things.map { |thing|
@@ -7,6 +7,9 @@ module RuboCop
7
7
  #
8
8
  # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
9
9
  # the case equality operator is a constant.
10
+
11
+ # If `AllowOnSelfClass` option is enabled, the cop will ignore violations when the receiver of
12
+ # the case equality operator is `self.class`. Note intermediate variables are not accepted.
10
13
  #
11
14
  # @example
12
15
  # # bad
@@ -26,6 +29,14 @@ module RuboCop
26
29
  # # good
27
30
  # Array === something
28
31
  #
32
+ # @example AllowOnSelfClass: false (default)
33
+ # # bad
34
+ # self.class === something
35
+ #
36
+ # @example AllowOnSelfClass: true
37
+ # # good
38
+ # self.class === something
39
+ #
29
40
  class CaseEquality < Base
30
41
  extend AutoCorrector
31
42
 
@@ -33,7 +44,10 @@ module RuboCop
33
44
  RESTRICT_ON_SEND = %i[===].freeze
34
45
 
35
46
  # @!method case_equality?(node)
36
- def_node_matcher :case_equality?, '(send $#const? :=== $_)'
47
+ def_node_matcher :case_equality?, '(send $#offending_receiver? :=== $_)'
48
+
49
+ # @!method self_class?(node)
50
+ def_node_matcher :self_class?, '(send (self) :class)'
37
51
 
38
52
  def on_send(node)
39
53
  case_equality?(node) do |lhs, rhs|
@@ -48,12 +62,11 @@ module RuboCop
48
62
 
49
63
  private
50
64
 
51
- def const?(node)
52
- if cop_config.fetch('AllowOnConstant', false)
53
- !node&.const_type?
54
- else
55
- true
56
- end
65
+ def offending_receiver?(node)
66
+ return false if node&.const_type? && cop_config.fetch('AllowOnConstant', false)
67
+ return false if self_class?(node) && cop_config.fetch('AllowOnSelfClass', false)
68
+
69
+ true
57
70
  end
58
71
 
59
72
  def replacement(lhs, rhs)
@@ -66,12 +79,29 @@ module RuboCop
66
79
  #
67
80
  # So here is noop.
68
81
  when :begin
69
- child = lhs.children.first
70
- "#{lhs.source}.include?(#{rhs.source})" if child&.range_type?
82
+ begin_replacement(lhs, rhs)
71
83
  when :const
72
- "#{rhs.source}.is_a?(#{lhs.source})"
84
+ const_replacement(lhs, rhs)
85
+ when :send
86
+ send_replacement(lhs, rhs)
73
87
  end
74
88
  end
89
+
90
+ def begin_replacement(lhs, rhs)
91
+ return unless lhs.children.first&.range_type?
92
+
93
+ "#{lhs.source}.include?(#{rhs.source})"
94
+ end
95
+
96
+ def const_replacement(lhs, rhs)
97
+ "#{rhs.source}.is_a?(#{lhs.source})"
98
+ end
99
+
100
+ def send_replacement(lhs, rhs)
101
+ return unless self_class?(lhs)
102
+
103
+ "#{rhs.source}.is_a?(#{lhs.source})"
104
+ end
75
105
  end
76
106
  end
77
107
  end
@@ -48,7 +48,7 @@ module RuboCop
48
48
  # var.class.eql?(Date)
49
49
  # var.class.name == 'Date'
50
50
  #
51
- # @example AllowedPatterns: [`/eq/`]
51
+ # @example AllowedPatterns: ['eq']
52
52
  # # good
53
53
  # var.instance_of?(Date)
54
54
  # var.class.equal?(Date)
@@ -36,8 +36,8 @@ module RuboCop
36
36
  extend AutoCorrector
37
37
 
38
38
  MSG = 'Use `%<good>s` instead of `%<bad>s`.'
39
-
40
39
  RESTRICT_ON_SEND = %i[reject reject! select select!].freeze
40
+ TO_ENUM_METHODS = %i[to_enum lazy].freeze
41
41
 
42
42
  # @!method reject_method_with_block_pass?(node)
43
43
  def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
@@ -69,6 +69,7 @@ module RuboCop
69
69
 
70
70
  def on_send(node)
71
71
  return unless (range = offense_range(node))
72
+ return if target_ruby_version <= 3.0 && to_enum_method?(node)
72
73
 
73
74
  good = good_method_name(node)
74
75
  message = format(MSG, good: good, bad: range.source)
@@ -94,6 +95,10 @@ module RuboCop
94
95
  end
95
96
  end
96
97
 
98
+ def to_enum_method?(node)
99
+ TO_ENUM_METHODS.include?(node.children.first.method_name)
100
+ end
101
+
97
102
  def good_method_name(node)
98
103
  if node.bang_method?
99
104
  'compact!'
@@ -28,14 +28,14 @@ module RuboCop
28
28
  MSG = 'Use `Integer#times` for a simple loop which iterates a fixed number of times.'
29
29
 
30
30
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
31
- return unless offending_each_range(node)
31
+ return unless offending?(node)
32
32
 
33
33
  send_node = node.send_node
34
34
 
35
35
  range = send_node.receiver.source_range.join(send_node.loc.selector)
36
36
 
37
37
  add_offense(range) do |corrector|
38
- range_type, min, max = offending_each_range(node)
38
+ range_type, min, max = each_range(node)
39
39
 
40
40
  max += 1 if range_type == :irange
41
41
 
@@ -45,9 +45,44 @@ module RuboCop
45
45
 
46
46
  private
47
47
 
48
- # @!method offending_each_range(node)
49
- def_node_matcher :offending_each_range, <<~PATTERN
50
- (block (send (begin (${irange erange} (int $_) (int $_))) :each) (args) ...)
48
+ def offending?(node)
49
+ each_range_with_zero_origin?(node) || each_range_without_block_argument?(node)
50
+ end
51
+
52
+ # @!method each_range(node)
53
+ def_node_matcher :each_range, <<~PATTERN
54
+ (block
55
+ (send
56
+ (begin
57
+ (${irange erange}
58
+ (int $_) (int $_)))
59
+ :each)
60
+ (args ...)
61
+ ...)
62
+ PATTERN
63
+
64
+ # @!method each_range_with_zero_origin?(node)
65
+ def_node_matcher :each_range_with_zero_origin?, <<~PATTERN
66
+ (block
67
+ (send
68
+ (begin
69
+ ({irange erange}
70
+ (int 0) (int _)))
71
+ :each)
72
+ (args ...)
73
+ ...)
74
+ PATTERN
75
+
76
+ # @!method each_range_without_block_argument?(node)
77
+ def_node_matcher :each_range_without_block_argument?, <<~PATTERN
78
+ (block
79
+ (send
80
+ (begin
81
+ ({irange erange}
82
+ (int _) (int _)))
83
+ :each)
84
+ (args)
85
+ ...)
51
86
  PATTERN
52
87
  end
53
88
  end
@@ -52,7 +52,7 @@ module RuboCop
52
52
  MSG_EXPANDED = 'Put the `end` of empty method definitions on the next line.'
53
53
 
54
54
  def on_def(node)
55
- return if node.body || comment_lines?(node)
55
+ return if node.body || processed_source.contains_comment?(node.source_range)
56
56
  return if correct_style?(node)
57
57
 
58
58
  add_offense(node) do |corrector|
@@ -39,7 +39,7 @@ module RuboCop
39
39
  #
40
40
  # @example EnforcedStyle: disallow
41
41
  # # bad
42
- # def my_method; x end
42
+ # def my_method() = x
43
43
  #
44
44
  # # bad
45
45
  # def my_method() = x.foo
@@ -50,6 +50,10 @@ module RuboCop
50
50
  (block $_ (args $...) (yield $...))
51
51
  PATTERN
52
52
 
53
+ def self.autocorrect_incompatible_with
54
+ [Lint::UnusedMethodArgument]
55
+ end
56
+
53
57
  def initialize(config = nil, options = nil)
54
58
  super
55
59
  @def_nodes = Set.new
@@ -77,7 +77,7 @@ module RuboCop
77
77
  # # bad
78
78
  # redirect('foo/%{bar_id}')
79
79
  #
80
- # @example AllowedPatterns: [/redirect/]
80
+ # @example AllowedPatterns: ['redirect']
81
81
  #
82
82
  # # good
83
83
  # redirect('foo/%{bar_id}')
@@ -28,7 +28,7 @@ module RuboCop
28
28
  # * always - forces use of the 3.1 syntax (e.g. {foo:})
29
29
  # * never - forces use of explicit hash literal value
30
30
  # * either - accepts both shorthand and explicit use of hash literal value
31
- # * consistent - like "always", but will avoid mixing styles in a single hash
31
+ # * consistent - like "either", but will avoid mixing styles in a single hash
32
32
  #
33
33
  # @example EnforcedStyle: ruby19 (default)
34
34
  # # bad
@@ -20,8 +20,8 @@ module RuboCop
20
20
  return if require_parentheses_for_hash_value_omission?(node)
21
21
  return if syntax_like_method_call?(node)
22
22
  return if super_call_without_arguments?(node)
23
- return if allowed_camel_case_method_call?(node)
24
23
  return if legitimate_call_with_parentheses?(node)
24
+ return if allowed_camel_case_method_call?(node)
25
25
  return if allowed_string_interpolation_method_call?(node)
26
26
 
27
27
  add_offense(offense_range(node), message: OMIT_MSG) do |corrector|
@@ -97,7 +97,8 @@ module RuboCop
97
97
  call_in_optional_arguments?(node) ||
98
98
  call_in_single_line_inheritance?(node) ||
99
99
  allowed_multiline_call_with_parentheses?(node) ||
100
- allowed_chained_call_with_parentheses?(node)
100
+ allowed_chained_call_with_parentheses?(node) ||
101
+ assignment_in_condition?(node)
101
102
  end
102
103
 
103
104
  def call_in_literals?(node)
@@ -202,6 +203,16 @@ module RuboCop
202
203
  def inside_string_interpolation?(node)
203
204
  node.ancestors.drop_while { |a| !a.begin_type? }.any?(&:dstr_type?)
204
205
  end
206
+
207
+ def assignment_in_condition?(node)
208
+ parent = node.parent
209
+ return false unless parent
210
+
211
+ grandparent = parent.parent
212
+ return false unless grandparent
213
+
214
+ parent.assignment? && (grandparent.conditional? || grandparent.when_type?)
215
+ end
205
216
  end
206
217
  # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
207
218
  end
@@ -49,7 +49,8 @@ module RuboCop
49
49
  def on_if(node)
50
50
  return unless if_else?(node)
51
51
 
52
- condition = node.condition
52
+ condition = unwrap_begin_nodes(node.condition)
53
+
53
54
  return if double_negation?(condition) || !negated_condition?(condition)
54
55
 
55
56
  type = node.ternary? ? 'ternary' : 'if-else'
@@ -71,6 +72,11 @@ module RuboCop
71
72
  !node.elsif? && else_branch && (!else_branch.if_type? || !else_branch.elsif?)
72
73
  end
73
74
 
75
+ def unwrap_begin_nodes(node)
76
+ node = node.children.first while node.begin_type? || node.kwbegin_type?
77
+ node
78
+ end
79
+
74
80
  def negated_condition?(node)
75
81
  node.send_type? &&
76
82
  (node.negation_method? || NEGATED_EQUALITY_METHODS.include?(node.method_name))
@@ -66,7 +66,7 @@ module RuboCop
66
66
  # foo.negative?
67
67
  # bar.baz.positive?
68
68
  #
69
- # @example AllowedPatterns: [/zero/] with EnforcedStyle: predicate
69
+ # @example AllowedPatterns: ['zero'] with EnforcedStyle: predicate
70
70
  # # good
71
71
  # # bad
72
72
  # foo.zero?
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant dot before operator method call.
7
+ # The target operator methods are `|`, `^`, `&`, `<=>`, `==`, `===`, `=~`, `>`, `>=`, `<`,
8
+ # `<=`, `<<`, `>>`, `+`, `-`, `*`, `/`, `%`, `**`, `~`, `!`, `!=`, and `!~`.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # foo.+ bar
14
+ # foo.& bar
15
+ #
16
+ # # good
17
+ # foo + bar
18
+ # foo & bar
19
+ #
20
+ class OperatorMethodCall < Base
21
+ extend AutoCorrector
22
+
23
+ MSG = 'Redundant dot detected.'
24
+ RESTRICT_ON_SEND = %i[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ ! != !~].freeze
25
+
26
+ def on_send(node)
27
+ return unless (dot = node.loc.dot)
28
+
29
+ _lhs, _op, rhs = *node
30
+ return if rhs.children.first
31
+
32
+ add_offense(dot) do |corrector|
33
+ corrector.replace(dot, ' ')
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -83,10 +83,31 @@ module RuboCop
83
83
  end
84
84
  end
85
85
 
86
+ # @private
87
+ # @param [RuboCop::AST::Node] node
88
+ # @return [String, nil]
89
+ def preferred_expression_to_node_with_constant_prefix(node)
90
+ expression = preferred_expression_to(node)
91
+ return unless expression
92
+
93
+ "#{constant_prefix(node)}#{expression}"
94
+ end
95
+
96
+ # @private
97
+ # @param [RuboCop::AST::Node] node
98
+ # @return [String]
99
+ def constant_prefix(node)
100
+ if node.each_ancestor(:class, :module).any?
101
+ '::'
102
+ else
103
+ ''
104
+ end
105
+ end
106
+
86
107
  # @private
87
108
  # @param [RuboCop::AST::Node] node
88
109
  def on_back_ref_or_gvar_or_nth_ref(node)
89
- preferred_expression = preferred_expression_to(node)
110
+ preferred_expression = preferred_expression_to_node_with_constant_prefix(node)
90
111
  return unless preferred_expression
91
112
 
92
113
  add_offense(
@@ -75,6 +75,7 @@ module RuboCop
75
75
 
76
76
  def on_def(node)
77
77
  return unless node.body&.kwbegin_type?
78
+ return if node.endless? && !node.body.children.one?
78
79
 
79
80
  register_offense(node.body)
80
81
  end
@@ -145,11 +145,14 @@ module RuboCop
145
145
 
146
146
  return false unless if_branch && else_branch
147
147
 
148
- if_branch.send_type? && if_branch.arguments.count == 1 &&
149
- else_branch.send_type? && else_branch.arguments.count == 1 &&
148
+ single_argument_method?(if_branch) && single_argument_method?(else_branch) &&
150
149
  same_method?(if_branch, else_branch)
151
150
  end
152
151
 
152
+ def single_argument_method?(node)
153
+ node.send_type? && !node.method?(:[]) && node.arguments.one?
154
+ end
155
+
153
156
  def same_method?(if_branch, else_branch)
154
157
  if_branch.method?(else_branch.method_name) && if_branch.receiver == else_branch.receiver
155
158
  end
@@ -140,7 +140,9 @@ module RuboCop
140
140
  end
141
141
 
142
142
  def allow_comments?(node)
143
- cop_config['AllowComments'] && contains_comments?(node)
143
+ return false unless cop_config['AllowComments']
144
+
145
+ contains_comments?(node) && !comments_contain_disables?(node, name)
144
146
  end
145
147
 
146
148
  def same_args?(super_node, args)
@@ -29,6 +29,9 @@ module RuboCop
29
29
  # @!method rescue?(node)
30
30
  def_node_matcher :rescue?, '{^resbody ^^resbody}'
31
31
 
32
+ # @!method allowed_pin_operator?(node)
33
+ def_node_matcher :allowed_pin_operator?, '^(pin (begin !{lvar ivar cvar gvar}))'
34
+
32
35
  # @!method arg_in_call_with_block?(node)
33
36
  def_node_matcher :arg_in_call_with_block?, '^^(block (send _ _ equal?(%0) ...) ...)'
34
37
 
@@ -44,6 +47,7 @@ module RuboCop
44
47
  empty_parentheses?(node) ||
45
48
  first_arg_begins_with_hash_literal?(node) ||
46
49
  rescue?(node) ||
50
+ allowed_pin_operator?(node) ||
47
51
  allowed_expression?(node)
48
52
  end
49
53
 
@@ -74,7 +74,7 @@ module RuboCop
74
74
 
75
75
  non_redundant =
76
76
  whitespace_in_free_space_mode?(node, class_elem) ||
77
- backslash_b?(class_elem) ||
77
+ backslash_b?(class_elem) || backslash_zero?(class_elem) ||
78
78
  requires_escape_outside_char_class?(class_elem)
79
79
 
80
80
  !non_redundant
@@ -104,6 +104,13 @@ module RuboCop
104
104
  elem == '\b'
105
105
  end
106
106
 
107
+ def backslash_zero?(elem)
108
+ # See https://github.com/rubocop/rubocop/issues/11067 for details - in short "\0" != "0" -
109
+ # the former means an Unicode code point `"\u0000"`, the latter a number character `"0"`.
110
+ # Similarly "\032" means "\u001A". Other numbers starting with "\0" can also be mentioned.
111
+ elem == '\0'
112
+ end
113
+
107
114
  def requires_escape_outside_char_class?(elem)
108
115
  REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS.include?(elem)
109
116
  end