rubocop 1.36.0 → 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 (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