rubocop 1.49.0 → 1.50.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e52db814b1bd4755ea1be2740adc7513a40d731fcc2ff52e323a4d569dab3bb1
4
- data.tar.gz: 06c85b14778fad98f2adb724df26b22b423bb65fdb45fcd159e6ed251a5ab678
3
+ metadata.gz: cc74785ce6643999994c7595e6ab7b6508abc7ab33532032d09fb854808d3ab5
4
+ data.tar.gz: 3313880579d587260cae9f718b148160a597da1a239464c9ae86ce8a773065fc
5
5
  SHA512:
6
- metadata.gz: acc6052f3d94842532268a6dd90f307dde0502e7d126dc39b8dad933187a37523cd5226e5cd0ac14e85eeaa8bf3f5c020eb56a24ebee1735b41011b5279ec98d
7
- data.tar.gz: 25740c7e44cf713b64146bd4749ea28f302c4fc20cc85c16978749bed82014c9bd8096573149d8fc7528eb4fe31647a6f8d808ca4fed739b90d17286baea8ac8
6
+ metadata.gz: 0b4ea0d9447499f45e99d333c05f72edd634980d506717117cb35029ac2f185de08681c68ce40a2d319efe163b23a168e6c9a55f8178dbf25f847ecc7710cb04
7
+ data.tar.gz: e1c57df60ec75958f78eec411b0697496ea34b0a10626b3fa79453a64328162292cb621319557b0aac59fe88d447fa32a5e3fbcf1d4d4efcf9ef9388191a6fee
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.49', require: false
56
+ gem 'rubocop', '~> 1.50', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -1016,8 +1016,6 @@ Layout/LineContinuationLeadingSpace:
1016
1016
  Layout/LineContinuationSpacing:
1017
1017
  Description: 'Checks the spacing in front of backslash in line continuations.'
1018
1018
  Enabled: pending
1019
- AutoCorrect: true
1020
- SafeAutoCorrect: true
1021
1019
  VersionAdded: '1.31'
1022
1020
  EnforcedStyle: space
1023
1021
  SupportedStyles:
@@ -1752,6 +1750,11 @@ Lint/DuplicateMagicComment:
1752
1750
  Enabled: pending
1753
1751
  VersionAdded: '1.37'
1754
1752
 
1753
+ Lint/DuplicateMatchPattern:
1754
+ Description: 'Do not repeat patterns in `in` keywords.'
1755
+ Enabled: pending
1756
+ VersionAdded: '1.50'
1757
+
1755
1758
  Lint/DuplicateMethods:
1756
1759
  Description: 'Check for duplicate method definitions.'
1757
1760
  Enabled: true
@@ -3743,7 +3746,6 @@ Style/FileEmpty:
3743
3746
  Prefer to use `File.empty?('path/to/file')` when checking if a file is empty.
3744
3747
  Enabled: pending
3745
3748
  Safe: false
3746
- SafeAutoCorrect: false
3747
3749
  VersionAdded: '1.48'
3748
3750
 
3749
3751
  Style/FileRead:
@@ -4571,11 +4573,10 @@ Style/NumericPredicate:
4571
4573
  Checks for the use of predicate- or comparison methods for
4572
4574
  numeric comparisons.
4573
4575
  StyleGuide: '#predicate-methods'
4574
- Safe: false
4575
4576
  # This will change to a new method call which isn't guaranteed to be on the
4576
4577
  # object. Switching these methods has to be done with knowledge of the types
4577
4578
  # of the variables which rubocop doesn't have.
4578
- SafeAutoCorrect: false
4579
+ Safe: false
4579
4580
  Enabled: true
4580
4581
  VersionAdded: '0.42'
4581
4582
  VersionChanged: '0.59'
@@ -23,6 +23,14 @@ module RuboCop
23
23
  class << self
24
24
  include FileFinder
25
25
 
26
+ PENDING_BANNER = <<~BANNER
27
+ The following cops were added to RuboCop, but are not configured. Please set Enabled to either `true` or `false` in your `.rubocop.yml` file.
28
+
29
+ Please also note that you can opt-in to new cops by default by adding this to your config:
30
+ AllCops:
31
+ NewCops: enable
32
+ BANNER
33
+
26
34
  attr_accessor :debug, :ignore_parent_exclusion, :disable_pending_cops, :enable_pending_cops,
27
35
  :ignore_unrecognized_cops
28
36
  attr_writer :default_configuration
@@ -165,14 +173,6 @@ module RuboCop
165
173
  ConfigFinder.project_root
166
174
  end
167
175
 
168
- PENDING_BANNER = <<~BANNER
169
- The following cops were added to RuboCop, but are not configured. Please set Enabled to either `true` or `false` in your `.rubocop.yml` file.
170
-
171
- Please also note that you can opt-in to new cops by default by adding this to your config:
172
- AllCops:
173
- NewCops: enable
174
- BANNER
175
-
176
176
  def warn_on_pending_cops(pending_cops)
177
177
  warn Rainbow(PENDING_BANNER).yellow
178
178
 
@@ -159,6 +159,7 @@ module RuboCop
159
159
  previous = index
160
160
  end
161
161
  end
162
+ alias on_sclass on_class
162
163
 
163
164
  private
164
165
 
@@ -137,7 +137,7 @@ module RuboCop
137
137
  end
138
138
 
139
139
  def current_token(comment)
140
- processed_source.find_token { |token| token.pos == comment.source_range }
140
+ processed_source.tokens.find { |token| token.pos == comment.source_range }
141
141
  end
142
142
 
143
143
  def previous_token(node)
@@ -31,7 +31,7 @@ module RuboCop
31
31
  return unless processed_source.raw_source.include?("\n\n\n")
32
32
 
33
33
  lines = Set.new
34
- processed_source.each_token { |token| lines << token.line }
34
+ processed_source.tokens.each { |token| lines << token.line }
35
35
 
36
36
  each_extra_empty_line(lines.sort) do |range|
37
37
  add_offense(range) do |corrector|
@@ -34,7 +34,7 @@ module RuboCop
34
34
  private
35
35
 
36
36
  def first_token
37
- processed_source.find_token { |t| !t.text.start_with?('#') }
37
+ processed_source.tokens.find { |t| !t.text.start_with?('#') }
38
38
  end
39
39
 
40
40
  def space_before(token)
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks that there are no repeated patterns used in `in` keywords.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # case x
12
+ # in 'first'
13
+ # do_something
14
+ # in 'first'
15
+ # do_something_else
16
+ # end
17
+ #
18
+ # # good
19
+ # case x
20
+ # in 'first'
21
+ # do_something
22
+ # in 'second'
23
+ # do_something_else
24
+ # end
25
+ #
26
+ # # bad - repeated alternate patterns with the same conditions don't depend on the order
27
+ # case x
28
+ # in foo | bar
29
+ # first_method
30
+ # in bar | foo
31
+ # second_method
32
+ # end
33
+ #
34
+ # # good
35
+ # case x
36
+ # in foo | bar
37
+ # first_method
38
+ # in bar | baz
39
+ # second_method
40
+ # end
41
+ #
42
+ # # bad - repeated hash patterns with the same conditions don't depend on the order
43
+ # case x
44
+ # in foo: a, bar: b
45
+ # first_method
46
+ # in bar: b, foo: a
47
+ # second_method
48
+ # end
49
+ #
50
+ # # good
51
+ # case x
52
+ # in foo: a, bar: b
53
+ # first_method
54
+ # in bar: b, baz: c
55
+ # second_method
56
+ # end
57
+ #
58
+ # # bad - repeated array patterns with elements in the same order
59
+ # case x
60
+ # in [foo, bar]
61
+ # first_method
62
+ # in [foo, bar]
63
+ # second_method
64
+ # end
65
+ #
66
+ # # good
67
+ # case x
68
+ # in [foo, bar]
69
+ # first_method
70
+ # in [bar, foo]
71
+ # second_method
72
+ # end
73
+ #
74
+ # # bad - repeated the same patterns and guard conditions
75
+ # case x
76
+ # in foo if bar
77
+ # first_method
78
+ # in foo if bar
79
+ # second_method
80
+ # end
81
+ #
82
+ # # good
83
+ # case x
84
+ # in foo if bar
85
+ # first_method
86
+ # in foo if baz
87
+ # second_method
88
+ # end
89
+ #
90
+ class DuplicateMatchPattern < Base
91
+ extend TargetRubyVersion
92
+
93
+ MSG = 'Duplicate `in` pattern detected.'
94
+
95
+ minimum_target_ruby_version 2.7
96
+
97
+ def on_case_match(case_node)
98
+ case_node.in_pattern_branches.each_with_object(Set.new) do |in_pattern_node, previous|
99
+ pattern = in_pattern_node.pattern
100
+ next if previous.add?(pattern_identity(pattern))
101
+
102
+ add_offense(pattern)
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def pattern_identity(pattern)
109
+ pattern_source = if pattern.hash_pattern_type? || pattern.match_alt_type?
110
+ pattern.children.map(&:source).sort
111
+ else
112
+ pattern.source
113
+ end
114
+
115
+ return pattern_source unless (guard = pattern.parent.children[1])
116
+
117
+ pattern_source + guard.source
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for string conversion in string interpolation,
6
+ # Checks for string conversion in string interpolation, `print`, `puts`, and `warn` arguments,
7
7
  # which is redundant.
8
8
  #
9
9
  # @example
@@ -11,18 +11,26 @@ module RuboCop
11
11
  # # bad
12
12
  #
13
13
  # "result is #{something.to_s}"
14
+ # print something.to_s
15
+ # puts something.to_s
16
+ # warn something.to_s
14
17
  #
15
18
  # @example
16
19
  #
17
20
  # # good
18
21
  #
19
22
  # "result is #{something}"
23
+ # print something
24
+ # puts something
25
+ # warn something
26
+ #
20
27
  class RedundantStringCoercion < Base
21
28
  include Interpolation
22
29
  extend AutoCorrector
23
30
 
24
- MSG_DEFAULT = 'Redundant use of `Object#to_s` in interpolation.'
25
- MSG_SELF = 'Use `self` instead of `Object#to_s` in interpolation.'
31
+ MSG_DEFAULT = 'Redundant use of `Object#to_s` in %<context>s.'
32
+ MSG_SELF = 'Use `self` instead of `Object#to_s` in %<context>s.'
33
+ RESTRICT_ON_SEND = %i[print puts warn].freeze
26
34
 
27
35
  # @!method to_s_without_args?(node)
28
36
  def_node_matcher :to_s_without_args?, '(send _ :to_s)'
@@ -32,18 +40,30 @@ module RuboCop
32
40
 
33
41
  return unless to_s_without_args?(final_node)
34
42
 
35
- message = final_node.receiver ? MSG_DEFAULT : MSG_SELF
36
-
37
- add_offense(final_node.loc.selector, message: message) do |corrector|
38
- receiver = final_node.receiver
39
- corrector.replace(
40
- final_node,
41
- if receiver
42
- receiver.source
43
- else
44
- 'self'
45
- end
46
- )
43
+ register_offense(final_node, 'interpolation')
44
+ end
45
+
46
+ def on_send(node)
47
+ return if node.receiver
48
+
49
+ node.each_child_node(:send) do |child|
50
+ next unless child.method?(:to_s)
51
+
52
+ register_offense(child, "`#{node.method_name}`")
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def register_offense(node, context)
59
+ receiver = node.receiver
60
+ template = receiver ? MSG_DEFAULT : MSG_SELF
61
+ message = format(template, context: context)
62
+
63
+ add_offense(node.loc.selector, message: message) do |corrector|
64
+ replacement = receiver ? receiver.source : 'self'
65
+
66
+ corrector.replace(node, replacement)
47
67
  end
48
68
  end
49
69
  end
@@ -42,6 +42,7 @@ module RuboCop
42
42
  def on_class(node)
43
43
  check_code_length(node)
44
44
  end
45
+ alias on_sclass on_class
45
46
 
46
47
  def on_casgn(node)
47
48
  parent = node.parent
@@ -10,7 +10,7 @@ module RuboCop
10
10
  include Util
11
11
 
12
12
  FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
- CLASSLIKE_TYPES = %i[class module].freeze
13
+ CLASSLIKE_TYPES = %i[class module sclass].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
16
16
  def initialize(node, processed_source, count_comments: false, foldable_types: [])
@@ -69,7 +69,7 @@ module RuboCop
69
69
  end
70
70
 
71
71
  def first_line_comment(node)
72
- comment = processed_source.find_comment { |c| same_line?(c, node) }
72
+ comment = processed_source.comments.find { |c| same_line?(c, node) }
73
73
  return unless comment
74
74
 
75
75
  comment_source = comment.source
@@ -57,7 +57,7 @@ module RuboCop
57
57
  CONSTANT_MSG = 'Use only ascii symbols in constants.'
58
58
 
59
59
  def on_new_investigation
60
- processed_source.each_token do |token|
60
+ processed_source.tokens.each do |token|
61
61
  next if !should_check?(token) || token.text.ascii_only?
62
62
 
63
63
  message = token.type == :tIDENTIFIER ? IDENTIFIER_MSG : CONSTANT_MSG
@@ -98,7 +98,7 @@ module RuboCop
98
98
  private
99
99
 
100
100
  def investigate_tokens
101
- processed_source.each_token do |token|
101
+ processed_source.tokens.each do |token|
102
102
  next unless check_token?(token.type)
103
103
 
104
104
  word_locations = scan_for_words(token.text)
@@ -72,7 +72,7 @@ module RuboCop
72
72
  include AllowedPattern
73
73
  extend AutoCorrector
74
74
 
75
- MSG = 'Use `instance_of?(%<class_name>s)` instead of comparing classes.'
75
+ MSG = 'Use `instance_of?%<class_argument>s` instead of comparing classes.'
76
76
 
77
77
  RESTRICT_ON_SEND = %i[== equal? eql?].freeze
78
78
  CLASS_NAME_METHODS = %i[name to_s inspect].freeze
@@ -92,10 +92,12 @@ module RuboCop
92
92
 
93
93
  class_comparison_candidate?(node) do |receiver_node, class_node|
94
94
  range = offense_range(receiver_node, node)
95
- class_name = class_name(class_node, node)
95
+ class_argument = (class_name = class_name(class_node, node)) ? "(#{class_name})" : ''
96
96
 
97
- add_offense(range, message: format(MSG, class_name: class_name)) do |corrector|
98
- corrector.replace(range, "instance_of?(#{class_name})")
97
+ add_offense(range, message: format(MSG, class_argument: class_argument)) do |corrector|
98
+ next unless class_name
99
+
100
+ corrector.replace(range, "instance_of?#{class_argument}")
99
101
  end
100
102
  end
101
103
  end
@@ -104,12 +106,18 @@ module RuboCop
104
106
 
105
107
  def class_name(class_node, node)
106
108
  if class_name_method?(node.children.first.method_name)
107
- return class_node.receiver.source if class_node.receiver
109
+ if (receiver = class_node.receiver) && class_name_method?(class_node.method_name)
110
+ return receiver.source
111
+ end
108
112
 
109
113
  if class_node.str_type?
110
- value = class_node.source.delete('"').delete("'")
111
- value.prepend('::') if class_node.each_ancestor(:class, :module).any?
114
+ value = trim_string_quotes(class_node)
115
+ value.prepend('::') if require_cbase?(class_node)
112
116
  return value
117
+ elsif unable_to_determine_type?(class_node)
118
+ # When a variable or return value of a method is used, it returns nil
119
+ # because the type is not known and cannot be suggested.
120
+ return
113
121
  end
114
122
  end
115
123
 
@@ -120,6 +128,18 @@ module RuboCop
120
128
  CLASS_NAME_METHODS.include?(method_name)
121
129
  end
122
130
 
131
+ def require_cbase?(class_node)
132
+ class_node.each_ancestor(:class, :module).any?
133
+ end
134
+
135
+ def unable_to_determine_type?(class_node)
136
+ class_node.variable? || class_node.call_type?
137
+ end
138
+
139
+ def trim_string_quotes(class_node)
140
+ class_node.source.delete('"').delete("'")
141
+ end
142
+
123
143
  def offense_range(receiver_node, node)
124
144
  range_between(receiver_node.loc.selector.begin_pos, node.source_range.end_pos)
125
145
  end
@@ -82,7 +82,7 @@ module RuboCop
82
82
  def notice_found?(processed_source)
83
83
  notice_found = false
84
84
  notice_regexp = Regexp.new(notice)
85
- processed_source.each_token do |token|
85
+ processed_source.tokens.each do |token|
86
86
  break unless token.comment?
87
87
 
88
88
  notice_found = notice_regexp.match?(token.text)
@@ -6,9 +6,9 @@ module RuboCop
6
6
  # Prefer to use `File.empty?('path/to/file')` when checking if a file is empty.
7
7
  #
8
8
  # @safety
9
- # This cop's autocorrection is unsafe it because `File.size`, `File.read`,
10
- # and `File.binread` raise `ENOENT` exception when there is no file
11
- # corresponding to the path, while `File.empty?` does not raise an exception.
9
+ # This cop is unsafe, because `File.size`, `File.read`, and `File.binread`
10
+ # raise `ENOENT` exception when there is no file corresponding to the path,
11
+ # while `File.empty?` does not raise an exception.
12
12
  #
13
13
  # @example
14
14
  # # bad
@@ -148,7 +148,7 @@ module RuboCop
148
148
  end
149
149
 
150
150
  def frozen_string_literal_comment(processed_source)
151
- processed_source.find_token do |token|
151
+ processed_source.tokens.find do |token|
152
152
  token.text.start_with?(FROZEN_STRING_LITERAL)
153
153
  end
154
154
  end
@@ -28,14 +28,17 @@ module RuboCop
28
28
  return unless node.arguments?
29
29
  return if opening_line(node) == closing_line(node)
30
30
  return if correction_exceeds_max_line_length?(node)
31
+ return unless (begin_of_arguments = node.arguments.loc.begin)
31
32
 
32
- add_offense(node) { |corrector| autocorrect(corrector, node) }
33
+ add_offense(node) do |corrector|
34
+ autocorrect(corrector, node, begin_of_arguments)
35
+ end
33
36
  end
34
37
  alias on_defs on_def
35
38
 
36
39
  private
37
40
 
38
- def autocorrect(corrector, node)
41
+ def autocorrect(corrector, node, begin_of_arguments)
39
42
  arguments = node.arguments
40
43
  joined_arguments = arguments.map(&:source).join(', ')
41
44
  last_line_source_of_arguments = last_line_source_of_arguments(arguments)
@@ -47,7 +50,7 @@ module RuboCop
47
50
  end
48
51
 
49
52
  corrector.remove(arguments_range(node))
50
- corrector.insert_after(arguments.loc.begin, joined_arguments)
53
+ corrector.insert_after(begin_of_arguments, joined_arguments)
51
54
  end
52
55
 
53
56
  def last_line_source_of_arguments(arguments)
@@ -31,22 +31,26 @@ module RuboCop
31
31
  def on_masgn(node)
32
32
  lhs, rhs = *node
33
33
  lhs_elements = *lhs
34
+ rhs = rhs.body if rhs.rescue_type?
34
35
  rhs_elements = Array(rhs).compact # edge case for one constant
35
36
 
36
37
  return if allowed_lhs?(lhs) || allowed_rhs?(rhs) ||
37
38
  allowed_masign?(lhs_elements, rhs_elements)
38
39
 
39
- add_offense(node) { |corrector| autocorrect(corrector, node) }
40
+ range = node.source_range.begin.join(rhs.source_range.end)
41
+
42
+ add_offense(range) do |corrector|
43
+ autocorrect(corrector, node, lhs, rhs)
44
+ end
40
45
  end
41
46
 
42
47
  private
43
48
 
44
- def autocorrect(corrector, node)
45
- left, right = *node
46
- left_elements = *left
47
- right_elements = Array(right).compact
49
+ def autocorrect(corrector, node, lhs, rhs)
50
+ left_elements = *lhs
51
+ right_elements = Array(rhs).compact
48
52
  order = find_valid_order(left_elements, right_elements)
49
- correction = assignment_corrector(node, order)
53
+ correction = assignment_corrector(node, rhs, order)
50
54
 
51
55
  corrector.replace(correction.correction_range, correction.correction)
52
56
  end
@@ -77,14 +81,19 @@ module RuboCop
77
81
  node.block_type? || node.send_type?
78
82
  end
79
83
 
80
- def assignment_corrector(node, order)
81
- _assignment, modifier = *node.parent
84
+ def assignment_corrector(node, rhs, order)
85
+ if node.parent&.rescue_type?
86
+ _assignment, modifier = *node.parent
87
+ else
88
+ _assignment, modifier = *rhs.parent
89
+ end
90
+
82
91
  if modifier_statement?(node.parent)
83
- ModifierCorrector.new(node, config, order)
92
+ ModifierCorrector.new(node, rhs, modifier, config, order)
84
93
  elsif rescue_modifier?(modifier)
85
- RescueCorrector.new(node, config, order)
94
+ RescueCorrector.new(node, rhs, modifier, config, order)
86
95
  else
87
- GenericCorrector.new(node, config, order)
96
+ GenericCorrector.new(node, rhs, modifier, config, order)
88
97
  end
89
98
  end
90
99
 
@@ -181,10 +190,12 @@ module RuboCop
181
190
  class GenericCorrector
182
191
  include Alignment
183
192
 
184
- attr_reader :config, :node
193
+ attr_reader :node, :rhs, :rescue_result, :config
185
194
 
186
- def initialize(node, config, new_elements)
195
+ def initialize(node, rhs, modifier, config, new_elements)
187
196
  @node = node
197
+ @rhs = rhs
198
+ _, _, @rescue_result = *modifier
188
199
  @config = config
189
200
  @new_elements = new_elements
190
201
  end
@@ -228,13 +239,10 @@ module RuboCop
228
239
  # protected by rescue
229
240
  class RescueCorrector < GenericCorrector
230
241
  def correction
231
- _node, rescue_clause = *node.parent
232
- _, _, rescue_result = *rescue_clause
233
-
234
242
  # If the parallel assignment uses a rescue modifier and it is the
235
243
  # only contents of a method, then we want to make use of the
236
244
  # implicit begin
237
- if node.parent.parent&.def_type?
245
+ if rhs.parent.parent.parent&.def_type?
238
246
  super + def_correction(rescue_result)
239
247
  else
240
248
  begin_correction(rescue_result)
@@ -242,7 +250,7 @@ module RuboCop
242
250
  end
243
251
 
244
252
  def correction_range
245
- node.parent.source_range
253
+ rhs.parent.parent.source_range
246
254
  end
247
255
 
248
256
  private
@@ -69,6 +69,7 @@ module RuboCop
69
69
  extend AutoCorrector
70
70
 
71
71
  MSG = 'Redundant line continuation.'
72
+ ALLOWED_STRING_TOKENS = %i[tSTRING tSTRING_CONTENT].freeze
72
73
 
73
74
  def on_new_investigation
74
75
  return unless processed_source.ast
@@ -88,7 +89,8 @@ module RuboCop
88
89
  def require_line_continuation?(range)
89
90
  !ends_with_backslash_without_comment?(range.source_line) ||
90
91
  string_concatenation?(range.source_line) ||
91
- starts_with_plus_or_minus?(processed_source[range.line])
92
+ start_with_arithmetic_operator?(processed_source[range.line]) ||
93
+ inside_string_literal_or_method_with_argument?(range)
92
94
  end
93
95
 
94
96
  def ends_with_backslash_without_comment?(source_line)
@@ -99,18 +101,42 @@ module RuboCop
99
101
  /["']\s*\\\z/.match?(source_line)
100
102
  end
101
103
 
104
+ def inside_string_literal_or_method_with_argument?(range)
105
+ processed_source.tokens.each_cons(2).any? do |token, next_token|
106
+ inside_string_literal?(range, token) || method_with_argument?(token, next_token)
107
+ end
108
+ end
109
+
102
110
  def redundant_line_continuation?(range)
103
111
  return true unless (node = find_node_for_line(range.line))
104
112
  return false if argument_newline?(node)
105
113
 
106
- parse(node.source.gsub(/\\\n/, "\n")).valid_syntax?
114
+ source = node.parent ? node.parent.source : node.source
115
+ parse(source.gsub(/\\\n/, "\n")).valid_syntax?
116
+ end
117
+
118
+ def inside_string_literal?(range, token)
119
+ ALLOWED_STRING_TOKENS.include?(token.type) && token.pos.overlaps?(range)
120
+ end
121
+
122
+ # A method call without parentheses such as the following cannot remove `\`:
123
+ #
124
+ # do_something \
125
+ # argument
126
+ def method_with_argument?(current_token, next_token)
127
+ current_token.type == :tIDENTIFIER && next_token.type == :tIDENTIFIER
107
128
  end
108
129
 
109
130
  def argument_newline?(node)
110
131
  node = node.children.first if node.root? && node.begin_type?
111
- return if !node.send_type? || node.arguments.empty?
112
132
 
113
- node.loc.selector.line != node.first_argument.loc.line
133
+ if argument_is_method?(node)
134
+ argument_newline?(node.first_argument)
135
+ else
136
+ return false unless method_call_with_arguments?(node)
137
+
138
+ node.loc.selector.line != node.first_argument.loc.line
139
+ end
114
140
  end
115
141
 
116
142
  def find_node_for_line(line)
@@ -133,7 +159,18 @@ module RuboCop
133
159
  end
134
160
  end
135
161
 
136
- def starts_with_plus_or_minus?(source_line)
162
+ def argument_is_method?(node)
163
+ return false unless node.send_type?
164
+ return false unless (first_argument = node.first_argument)
165
+
166
+ method_call_with_arguments?(first_argument)
167
+ end
168
+
169
+ def method_call_with_arguments?(node)
170
+ node.call_type? && !node.arguments.empty?
171
+ end
172
+
173
+ def start_with_arithmetic_operator?(source_line)
137
174
  %r{\A\s*[+\-*/%]}.match?(source_line)
138
175
  end
139
176
  end
@@ -34,6 +34,7 @@ module RuboCop
34
34
  )
35
35
  end
36
36
  end
37
+ alias on_sclass on_class
37
38
  end
38
39
  end
39
40
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
4
+
3
5
  # Class for generating documentation of all cops departments
4
6
  # @api private
5
7
  class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
@@ -14,6 +16,8 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
14
16
  @departments = departments.map(&:to_sym).sort!
15
17
  @cops = RuboCop::Cop::Registry.global
16
18
  @config = RuboCop::ConfigLoader.default_configuration
19
+ @docs_path = "#{Dir.pwd}/docs/modules/ROOT/pages/"
20
+ FileUtils.mkdir_p(@docs_path)
17
21
  end
18
22
 
19
23
  def call
@@ -27,7 +31,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
27
31
 
28
32
  private
29
33
 
30
- attr_reader :departments, :cops, :config
34
+ attr_reader :departments, :cops, :config, :docs_path
31
35
 
32
36
  def cops_of_department(department)
33
37
  cops.with_department(department).sort!
@@ -252,7 +256,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
252
256
  content = +"= #{department}\n"
253
257
  selected_cops.each { |cop| content << print_cop_with_doc(cop) }
254
258
  content << footer_for_department(department)
255
- file_name = "#{Dir.pwd}/docs/modules/ROOT/pages/#{department_to_basename(department)}.adoc"
259
+ file_name = "#{docs_path}/#{department_to_basename(department)}.adoc"
256
260
  File.open(file_name, 'w') do |file|
257
261
  puts "* generated #{file_name}"
258
262
  file.write("#{content.strip}\n")
@@ -298,7 +302,10 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
298
302
  end
299
303
 
300
304
  def print_table_of_contents
301
- path = "#{Dir.pwd}/docs/modules/ROOT/pages/cops.adoc"
305
+ path = "#{docs_path}/cops.adoc"
306
+
307
+ File.write(path, table_contents) and return unless File.exist?(path)
308
+
302
309
  original = File.read(path)
303
310
  content = +"// START_COP_LIST\n\n"
304
311
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.49.0'
6
+ STRING = '1.50.1'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -299,6 +299,7 @@ require_relative 'rubocop/cop/lint/duplicate_case_condition'
299
299
  require_relative 'rubocop/cop/lint/duplicate_elsif_condition'
300
300
  require_relative 'rubocop/cop/lint/duplicate_hash_key'
301
301
  require_relative 'rubocop/cop/lint/duplicate_magic_comment'
302
+ require_relative 'rubocop/cop/lint/duplicate_match_pattern'
302
303
  require_relative 'rubocop/cop/lint/duplicate_methods'
303
304
  require_relative 'rubocop/cop/lint/duplicate_regexp_character_class_element'
304
305
  require_relative 'rubocop/cop/lint/duplicate_require'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.49.0
4
+ version: 1.50.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2023-04-03 00:00:00.000000000 Z
13
+ date: 2023-04-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -420,6 +420,7 @@ files:
420
420
  - lib/rubocop/cop/lint/duplicate_elsif_condition.rb
421
421
  - lib/rubocop/cop/lint/duplicate_hash_key.rb
422
422
  - lib/rubocop/cop/lint/duplicate_magic_comment.rb
423
+ - lib/rubocop/cop/lint/duplicate_match_pattern.rb
423
424
  - lib/rubocop/cop/lint/duplicate_methods.rb
424
425
  - lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb
425
426
  - lib/rubocop/cop/lint/duplicate_require.rb
@@ -990,7 +991,7 @@ metadata:
990
991
  homepage_uri: https://rubocop.org/
991
992
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
992
993
  source_code_uri: https://github.com/rubocop/rubocop/
993
- documentation_uri: https://docs.rubocop.org/rubocop/1.49/
994
+ documentation_uri: https://docs.rubocop.org/rubocop/1.50/
994
995
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
995
996
  rubygems_mfa_required: 'true'
996
997
  post_install_message: