rubocop 1.36.0 → 1.37.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +21 -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/first_argument_indentation.rb +1 -0
  9. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  10. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  11. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +28 -3
  12. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  13. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  14. data/lib/rubocop/cop/lint/duplicate_methods.rb +11 -1
  15. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  16. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  17. data/lib/rubocop/cop/lint/empty_conditional_body.rb +19 -7
  18. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  19. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  20. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  21. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  22. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +12 -1
  23. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  24. data/lib/rubocop/cop/lint/redundant_require_statement.rb +29 -9
  25. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  26. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  27. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  28. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +3 -0
  29. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  30. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  31. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  32. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  33. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +6 -3
  34. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  35. data/lib/rubocop/cop/mixin/surrounding_space.rb +6 -5
  36. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  37. data/lib/rubocop/cop/style/access_modifier_declarations.rb +21 -1
  38. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  39. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  40. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  41. data/lib/rubocop/cop/style/collection_compact.rb +6 -1
  42. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  43. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  44. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  45. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  46. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  47. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  48. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  49. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  50. data/lib/rubocop/cop/style/operator_method_call.rb +39 -0
  51. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  52. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  53. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  54. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  55. data/lib/rubocop/cop/style/redundant_string_escape.rb +173 -0
  56. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  57. data/lib/rubocop/cop/style/static_class.rb +32 -1
  58. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  59. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  60. data/lib/rubocop/cop/style/word_array.rb +2 -0
  61. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -2
  62. data/lib/rubocop/options.rb +13 -13
  63. data/lib/rubocop/rspec/shared_contexts.rb +13 -1
  64. data/lib/rubocop/server/cache.rb +5 -1
  65. data/lib/rubocop/server/cli.rb +9 -2
  66. data/lib/rubocop/server/client_command/exec.rb +5 -0
  67. data/lib/rubocop/server/core.rb +2 -1
  68. data/lib/rubocop/server/socket_reader.rb +5 -1
  69. data/lib/rubocop/server.rb +1 -1
  70. data/lib/rubocop/version.rb +8 -3
  71. data/lib/rubocop.rb +3 -0
  72. metadata +10 -5
@@ -209,7 +209,12 @@ module RuboCop
209
209
 
210
210
  add_offense(location, message: message(cop_names)) do |corrector|
211
211
  range = comment_range_with_surrounding_space(location, comment.loc.expression)
212
- corrector.remove(range)
212
+
213
+ if leave_free_comment?(comment, range)
214
+ corrector.replace(range, ' # ')
215
+ else
216
+ corrector.remove(range)
217
+ end
213
218
  end
214
219
  end
215
220
 
@@ -227,6 +232,12 @@ module RuboCop
227
232
  end
228
233
  end
229
234
 
235
+ def leave_free_comment?(comment, range)
236
+ free_comment = comment.text.gsub(range.source.strip, '')
237
+
238
+ !free_comment.empty? && !free_comment.start_with?('#')
239
+ end
240
+
230
241
  def cop_range(comment, cop)
231
242
  cop = remove_department_marker(cop)
232
243
  matching_range(comment.loc.expression, cop) ||
@@ -41,6 +41,7 @@ module RuboCop
41
41
  return unless (receiver = node.receiver)
42
42
  return unless receiver.receiver&.const_type? && receiver.receiver.short_name == :Dir
43
43
  return unless GLOB_METHODS.include?(receiver.method_name)
44
+ return if multiple_argument?(receiver)
44
45
 
45
46
  selector = node.loc.selector
46
47
 
@@ -49,6 +50,12 @@ module RuboCop
49
50
  corrector.remove(node.loc.dot)
50
51
  end
51
52
  end
53
+
54
+ private
55
+
56
+ def multiple_argument?(glob_method)
57
+ glob_method.arguments.count >= 2 || glob_method.first_argument&.splat_type?
58
+ end
52
59
  end
53
60
  end
54
61
  end
@@ -6,13 +6,22 @@ module RuboCop
6
6
  # Checks for unnecessary `require` statement.
7
7
  #
8
8
  # The following features are unnecessary `require` statement because
9
- # they are already loaded.
9
+ # they are already loaded. e.g. Ruby 2.2:
10
10
  #
11
11
  # ruby -ve 'p $LOADED_FEATURES.reject { |feature| %r|/| =~ feature }'
12
12
  # ruby 2.2.8p477 (2017-09-14 revision 59906) [x86_64-darwin13]
13
13
  # ["enumerator.so", "rational.so", "complex.so", "thread.rb"]
14
14
  #
15
- # This cop targets Ruby 2.2 or higher containing these 4 features.
15
+ # Below are the features that each `TargetRubyVersion` targets.
16
+ #
17
+ # * 2.0+ ... `enumerator`
18
+ # * 2.1+ ... `thread`
19
+ # * 2.2+ ... Add `rational` and `complex` above
20
+ # * 2.5+ ... Add `pp` above
21
+ # * 2.7+ ... Add `ruby2_keywords` above
22
+ # * 3.1+ ... Add `fiber` above
23
+ #
24
+ # This cop target those features.
16
25
  #
17
26
  # @example
18
27
  # # bad
@@ -24,21 +33,19 @@ module RuboCop
24
33
  class RedundantRequireStatement < Base
25
34
  include RangeHelp
26
35
  extend AutoCorrector
27
- extend TargetRubyVersion
28
-
29
- minimum_target_ruby_version 2.2
30
36
 
31
37
  MSG = 'Remove unnecessary `require` statement.'
32
38
  RESTRICT_ON_SEND = %i[require].freeze
39
+ RUBY_22_LOADED_FEATURES = %w[rational complex].freeze
33
40
 
34
- # @!method unnecessary_require_statement?(node)
35
- def_node_matcher :unnecessary_require_statement?, <<~PATTERN
41
+ # @!method redundant_require_statement?(node)
42
+ def_node_matcher :redundant_require_statement?, <<~PATTERN
36
43
  (send nil? :require
37
- (str {"enumerator" "rational" "complex" "thread"}))
44
+ (str #redundant_feature?))
38
45
  PATTERN
39
46
 
40
47
  def on_send(node)
41
- return unless unnecessary_require_statement?(node)
48
+ return unless redundant_require_statement?(node)
42
49
 
43
50
  add_offense(node) do |corrector|
44
51
  range = range_with_surrounding_space(node.loc.expression, side: :right)
@@ -46,6 +53,19 @@ module RuboCop
46
53
  corrector.remove(range)
47
54
  end
48
55
  end
56
+
57
+ private
58
+
59
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
60
+ def redundant_feature?(feature_name)
61
+ feature_name == 'enumerator' ||
62
+ (target_ruby_version >= 2.1 && feature_name == 'thread') ||
63
+ (target_ruby_version >= 2.2 && RUBY_22_LOADED_FEATURES.include?(feature_name)) ||
64
+ (target_ruby_version >= 2.5 && feature_name == 'pp') ||
65
+ (target_ruby_version >= 2.7 && feature_name == 'ruby2_keywords') ||
66
+ (target_ruby_version >= 3.1 && feature_name == 'fiber')
67
+ end
68
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
49
69
  end
50
70
  end
51
71
  end
@@ -46,7 +46,7 @@ module RuboCop
46
46
  private
47
47
 
48
48
  def check_ternary(ternary, node)
49
- return unless ternary.condition.operator_keyword?
49
+ return if node.method?(:[]) || !ternary.condition.operator_keyword?
50
50
 
51
51
  range = range_between(node.source_range.begin_pos, ternary.condition.source_range.end_pos)
52
52
 
@@ -31,6 +31,7 @@ module RuboCop
31
31
  minimum_target_ruby_version 2.3
32
32
 
33
33
  MSG = 'Do not chain ordinary method call after safe navigation operator.'
34
+ PLUS_MINUS_METHODS = %i[+@ -@].freeze
34
35
 
35
36
  # @!method bad_method?(node)
36
37
  def_node_matcher :bad_method?, <<~PATTERN
@@ -42,7 +43,7 @@ module RuboCop
42
43
 
43
44
  def on_send(node)
44
45
  bad_method?(node) do |safe_nav, method|
45
- return if nil_methods.include?(method)
46
+ return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
46
47
 
47
48
  method_chain = method_chain(node)
48
49
  location =
@@ -71,7 +72,7 @@ module RuboCop
71
72
  else
72
73
  offense_range.source.dup
73
74
  end
74
- source.prepend('.') unless send_node.dot?
75
+ source.prepend('.') unless source.start_with?('.')
75
76
  source.prepend('&')
76
77
  end
77
78
 
@@ -155,16 +155,6 @@ module RuboCop
155
155
  end
156
156
  end
157
157
 
158
- # @param [RuboCop::AST::Node] rescue_group is a node of array_type
159
- def rescued_exceptions(rescue_group)
160
- klasses = *rescue_group
161
- klasses.map do |klass|
162
- next unless klass.const_type?
163
-
164
- klass.source
165
- end.compact
166
- end
167
-
168
158
  def find_shadowing_rescue(rescues)
169
159
  rescued_groups = rescued_groups_for(rescues)
170
160
  rescued_groups.zip(rescues).each do |group, res|
@@ -12,9 +12,12 @@ module RuboCop
12
12
  # because `Ractor` should not access outer variables.
13
13
  # eg. following style is encouraged:
14
14
  #
15
+ # [source,ruby]
16
+ # ----
15
17
  # worker_id, pipe = env
16
18
  # Ractor.new(worker_id, pipe) do |worker_id, pipe|
17
19
  # end
20
+ # ----
18
21
  #
19
22
  # @example
20
23
  #
@@ -79,7 +79,7 @@ module RuboCop
79
79
  # # bad
80
80
  # 2.times { raise ArgumentError }
81
81
  #
82
- # @example AllowedPatterns: [/(exactly|at_least|at_most)\(\d+\)\.times/] (default)
82
+ # @example AllowedPatterns: ['(exactly|at_least|at_most)\(\d+\)\.times'] (default)
83
83
  #
84
84
  # # good
85
85
  # exactly(2).times { raise StandardError }
@@ -68,6 +68,10 @@ module RuboCop
68
68
  (send nil? :fail ...)}
69
69
  PATTERN
70
70
 
71
+ def self.autocorrect_incompatible_with
72
+ [Style::ExplicitBlockArgument]
73
+ end
74
+
71
75
  def self.joining_forces
72
76
  VariableForce
73
77
  end
@@ -22,6 +22,18 @@ module RuboCop
22
22
  processed_source.each_comment_in_lines(start_line...end_line)
23
23
  end
24
24
 
25
+ def comments_contain_disables?(node, cop_name)
26
+ disabled_ranges = processed_source.disabled_line_ranges[cop_name]
27
+
28
+ return unless disabled_ranges
29
+
30
+ node_range = node.source_range.line...find_end_line(node)
31
+
32
+ disabled_ranges.any? do |disable_range|
33
+ disable_range.cover?(node_range) || node_range.cover?(disable_range)
34
+ end
35
+ end
36
+
25
37
  private
26
38
 
27
39
  def end_position_for(node)
@@ -69,6 +69,10 @@ module RuboCop
69
69
  end
70
70
  end
71
71
 
72
+ def leading_magic_comments
73
+ leading_comment_lines.map { |line| MagicComment.parse(line) }
74
+ end
75
+
72
76
  def leading_comment_lines
73
77
  first_non_comment_token = processed_source.tokens.find { |token| !token.comment? }
74
78
 
@@ -96,11 +96,14 @@ module RuboCop
96
96
  end
97
97
 
98
98
  def without_parentheses_call_expr_follows?(ancestor)
99
+ return false unless ancestor.respond_to?(:parenthesized?) && !ancestor.parenthesized?
100
+
99
101
  right_sibling = ancestor.right_sibling
100
- right_sibling ||= ancestor.each_ancestor.find(&:assignment?)&.right_sibling
101
- return false unless right_sibling
102
+ right_sibling ||= ancestor.each_ancestor.find do |node|
103
+ node.assignment? || node.send_type?
104
+ end&.right_sibling
102
105
 
103
- ancestor.respond_to?(:parenthesized?) && !ancestor.parenthesized? && !!right_sibling
106
+ !!right_sibling
104
107
  end
105
108
 
106
109
  def breakdown_value_types_of_hash(hash_node)
@@ -11,7 +11,9 @@ module RuboCop
11
11
  private
12
12
 
13
13
  def rescue_modifier?(node)
14
- node&.resbody_type? && @modifier_locations.include?(node.loc.keyword)
14
+ return false unless node.respond_to?(:resbody_type?)
15
+
16
+ node.resbody_type? && @modifier_locations.include?(node.loc.keyword)
15
17
  end
16
18
 
17
19
  # @deprecated Use ResbodyNode#exceptions instead
@@ -13,7 +13,7 @@ module RuboCop
13
13
 
14
14
  private
15
15
 
16
- def side_space_range(range:, side:)
16
+ def side_space_range(range:, side:, include_newlines: false)
17
17
  buffer = processed_source.buffer
18
18
  src = buffer.source
19
19
 
@@ -21,11 +21,11 @@ module RuboCop
21
21
  end_pos = range.end_pos
22
22
  if side == :left
23
23
  end_pos = begin_pos
24
- begin_pos = reposition(src, begin_pos, -1)
24
+ begin_pos = reposition(src, begin_pos, -1, include_newlines: include_newlines)
25
25
  end
26
26
  if side == :right
27
27
  begin_pos = end_pos
28
- end_pos = reposition(src, end_pos, 1)
28
+ end_pos = reposition(src, end_pos, 1, include_newlines: include_newlines)
29
29
  end
30
30
  Parser::Source::Range.new(buffer, begin_pos, end_pos)
31
31
  end
@@ -75,9 +75,10 @@ module RuboCop
75
75
  end
76
76
  end
77
77
 
78
- def reposition(src, pos, step)
78
+ def reposition(src, pos, step, include_newlines: false)
79
79
  offset = step == -1 ? -1 : 0
80
- pos += step while SINGLE_SPACE_REGEXP.match?(src[pos + offset])
80
+ pos += step while SINGLE_SPACE_REGEXP.match?(src[pos + offset]) ||
81
+ (include_newlines && src[pos + offset] == "\n")
81
82
  pos.negative? ? 0 : pos
82
83
  end
83
84
 
@@ -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
@@ -183,6 +183,7 @@ module RuboCop
183
183
  end
184
184
 
185
185
  def insert_def(corrector, node, source)
186
+ source = [*processed_source.ast_with_comments[node].map(&:text), source].join("\n")
186
187
  argument_less_modifier_node = find_argument_less_modifier_node(node)
187
188
  if argument_less_modifier_node
188
189
  corrector.insert_after(argument_less_modifier_node, "\n\n#{source}")
@@ -201,11 +202,30 @@ module RuboCop
201
202
  def remove_node(corrector, node)
202
203
  corrector.remove(
203
204
  range_by_whole_lines(
204
- node.location.expression,
205
+ range_with_comments(node),
205
206
  include_final_newline: true
206
207
  )
207
208
  )
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
209
229
  end
210
230
  end
211
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|
@@ -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!'
@@ -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
@@ -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)
@@ -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