rubocop 1.38.0 → 1.39.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ed47ef39bb205db15249a399d0502f730c2656f712293357651cc0ddd328715
4
- data.tar.gz: 5e6411a2b459e0ac426e0ef0d8b8508b9b1693fea0c5b8cb5d82ed23f71d39b9
3
+ metadata.gz: 881807be0f0f6eea20bd13bd8f08fdb158a5b155c8d3a22fd748683a68c1fef1
4
+ data.tar.gz: 2a8e7d4b02f1ba468e02a4c20f8e3b830ce2a1d8ff0210f86aaeafab597c8c88
5
5
  SHA512:
6
- metadata.gz: 5cfd13ac3c283e77e300f87ee923be282740f2763e26c73d21d27b5ae176629d15d8d580d9d326349acf5b71c74e6335689c2192050a818577b9f2e0a6a4484f
7
- data.tar.gz: 27f36547ccdf6cc1336e1c44c5e164410f65344cba77fda76ad2db463054adaa93c96a406939b3826412280463a1d6a2940708f4080bc3170999bcc81594ba9d
6
+ metadata.gz: 11ea36bec754b07912e06b5481f964d10a6dbf5bfd56f99d8a8842ca46d56ae03a342a283d843336f977760c28a697ee995aa98268254a55b1218fa57a9fb5c7
7
+ data.tar.gz: b5096cb4589d9bafdab0b6ab409ea9e7841dc06b274aeb7f7d5a26e703cb216c2a428d695fb2a087637467f1cdd5ac005398c66ee3e7d28d49cb4b203a87667d
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.38', require: false
56
+ gem 'rubocop', '~> 1.39', 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
@@ -2808,6 +2808,7 @@ Naming/MethodParameterName:
2808
2808
  - by
2809
2809
  - db
2810
2810
  - id
2811
+ - if
2811
2812
  - in
2812
2813
  - io
2813
2814
  - ip
@@ -3448,7 +3449,7 @@ Style/Copyright:
3448
3449
 
3449
3450
  Style/DateTime:
3450
3451
  Description: 'Use Time over DateTime.'
3451
- StyleGuide: '#date--time'
3452
+ StyleGuide: '#date-time'
3452
3453
  Enabled: false
3453
3454
  VersionAdded: '0.51'
3454
3455
  VersionChanged: '0.92'
@@ -3819,8 +3820,9 @@ Style/HashExcept:
3819
3820
  Checks for usages of `Hash#reject`, `Hash#select`, and `Hash#filter` methods
3820
3821
  that can be replaced with `Hash#except` method.
3821
3822
  Enabled: pending
3823
+ Safe: false
3822
3824
  VersionAdded: '1.7'
3823
- VersionChanged: '1.31'
3825
+ VersionChanged: '1.39'
3824
3826
 
3825
3827
  Style/HashLikeCase:
3826
3828
  Description: >-
@@ -42,6 +42,14 @@ module RuboCop
42
42
  # ' long'
43
43
  class LineContinuationLeadingSpace < Base
44
44
  include RangeHelp
45
+ extend AutoCorrector
46
+
47
+ LINE_1_ENDING = /['"]\s*\\\n/.freeze
48
+ LINE_2_BEGINNING = /\A\s*['"]/.freeze
49
+ LEADING_STYLE_OFFENSE = /(?<trailing_spaces>\s+)(?<ending>#{LINE_1_ENDING})/.freeze
50
+ TRAILING_STYLE_OFFENSE = /(?<beginning>#{LINE_2_BEGINNING})(?<leading_spaces>\s+)/.freeze
51
+ private_constant :LINE_1_ENDING, :LINE_2_BEGINNING,
52
+ :LEADING_STYLE_OFFENSE, :TRAILING_STYLE_OFFENSE
45
53
 
46
54
  def on_dstr(node)
47
55
  end_of_first_line = node.loc.expression.begin_pos - node.loc.expression.column
@@ -52,9 +60,9 @@ module RuboCop
52
60
  next unless continuation?(raw_line_one)
53
61
 
54
62
  if enforced_style_leading?
55
- investigate_leading_style(raw_line_one, end_of_first_line)
63
+ investigate_leading_style(raw_line_one, raw_line_two, end_of_first_line)
56
64
  else
57
- investigate_trailing_style(raw_line_two, end_of_first_line)
65
+ investigate_trailing_style(raw_line_one, raw_line_two, end_of_first_line)
58
66
  end
59
67
  end
60
68
  end
@@ -65,24 +73,37 @@ module RuboCop
65
73
  processed_source.raw_source.lines[node.first_line - 1, line_range(node).size]
66
74
  end
67
75
 
68
- def investigate_leading_style(first_line, end_of_first_line)
69
- matches = first_line.match(/(?<trailing_spaces>\s+)(?<ending>['"]\s*\\\n)/)
76
+ def investigate_leading_style(first_line, second_line, end_of_first_line)
77
+ matches = first_line.match(LEADING_STYLE_OFFENSE)
70
78
  return if matches.nil?
71
79
 
72
- add_offense(leading_offense_range(end_of_first_line, matches))
80
+ offense_range = leading_offense_range(end_of_first_line, matches)
81
+ add_offense(offense_range) do |corrector|
82
+ insert_pos = end_of_first_line + second_line[LINE_2_BEGINNING].length
83
+ autocorrect(corrector, offense_range, insert_pos, matches[:trailing_spaces])
84
+ end
73
85
  end
74
86
 
75
- def investigate_trailing_style(second_line, end_of_first_line)
76
- matches = second_line.match(/\A(?<beginning>\s*['"])(?<leading_spaces>\s+)/)
87
+ def investigate_trailing_style(first_line, second_line, end_of_first_line)
88
+ matches = second_line.match(TRAILING_STYLE_OFFENSE)
77
89
  return if matches.nil?
78
90
 
79
- add_offense(trailing_offense_range(end_of_first_line, matches))
91
+ offense_range = trailing_offense_range(end_of_first_line, matches)
92
+ add_offense(offense_range) do |corrector|
93
+ insert_pos = end_of_first_line - first_line[LINE_1_ENDING].length
94
+ autocorrect(corrector, offense_range, insert_pos, matches[:leading_spaces])
95
+ end
80
96
  end
81
97
 
82
98
  def continuation?(line)
83
99
  line.end_with?("\\\n")
84
100
  end
85
101
 
102
+ def autocorrect(corrector, offense_range, insert_pos, spaces)
103
+ corrector.remove(offense_range)
104
+ corrector.replace(range_between(insert_pos, insert_pos), spaces)
105
+ end
106
+
86
107
  def leading_offense_range(end_of_first_line, matches)
87
108
  end_pos = end_of_first_line - matches[:ending].length
88
109
  begin_pos = end_pos - matches[:trailing_spaces].length
@@ -6,6 +6,9 @@ module RuboCop
6
6
  # Checks for unnecessary additional spaces inside array percent literals
7
7
  # (i.e. %i/%w).
8
8
  #
9
+ # Note that blank percent literals (e.g. `%i( )`) are checked by
10
+ # `Layout/SpaceInsidePercentLiteralDelimiters`.
11
+ #
9
12
  # @example
10
13
  #
11
14
  # # bad
@@ -8,14 +8,31 @@ module RuboCop
8
8
  #
9
9
  # @example
10
10
  #
11
+ # # bad
12
+ # %i( foo bar baz )
13
+ #
11
14
  # # good
12
15
  # %i(foo bar baz)
13
16
  #
14
17
  # # bad
15
18
  # %w( foo bar baz )
16
19
  #
20
+ # # good
21
+ # %w(foo bar baz)
22
+ #
17
23
  # # bad
18
24
  # %x( ls -l )
25
+ #
26
+ # # good
27
+ # %x(ls -l)
28
+ #
29
+ # # bad
30
+ # %w( )
31
+ # %w(
32
+ # )
33
+ #
34
+ # # good
35
+ # %w()
19
36
  class SpaceInsidePercentLiteralDelimiters < Base
20
37
  include MatchRange
21
38
  include PercentLiteral
@@ -34,11 +51,21 @@ module RuboCop
34
51
  end
35
52
 
36
53
  def on_percent_literal(node)
54
+ add_offenses_for_blank_spaces(node)
37
55
  add_offenses_for_unnecessary_spaces(node)
38
56
  end
39
57
 
40
58
  private
41
59
 
60
+ def add_offenses_for_blank_spaces(node)
61
+ range = body_range(node)
62
+ return if range.source.empty? || !range.source.strip.empty?
63
+
64
+ add_offense(range) do |corrector|
65
+ corrector.remove(range)
66
+ end
67
+ end
68
+
42
69
  def add_offenses_for_unnecessary_spaces(node)
43
70
  return unless node.single_line?
44
71
 
@@ -54,6 +81,13 @@ module RuboCop
54
81
  each_match_range(contents_range(node), regex, &blk)
55
82
  end
56
83
  end
84
+
85
+ def body_range(node)
86
+ node.location.expression.with(
87
+ begin_pos: node.location.begin.end_pos,
88
+ end_pos: node.location.end.begin_pos
89
+ )
90
+ end
57
91
  end
58
92
  end
59
93
  end
@@ -51,8 +51,8 @@ module RuboCop
51
51
  # alias bar foo
52
52
  class DuplicateMethods < Base
53
53
  MSG = 'Method `%<method>s` is defined at both %<defined>s and %<current>s.'
54
-
55
54
  RESTRICT_ON_SEND = %i[alias_method attr_reader attr_writer attr_accessor attr].freeze
55
+ DEF_TYPES = %i[def defs].freeze
56
56
 
57
57
  def initialize(config = nil, options = nil)
58
58
  super
@@ -127,8 +127,8 @@ module RuboCop
127
127
  found_method(node, "#{enclosing}.#{name}")
128
128
  end
129
129
 
130
- def message_for_dup(node, method_name)
131
- format(MSG, method: method_name, defined: source_location(@definitions[method_name]),
130
+ def message_for_dup(node, method_name, key)
131
+ format(MSG, method: method_name, defined: source_location(@definitions[key]),
132
132
  current: source_location(node))
133
133
  end
134
134
 
@@ -156,18 +156,27 @@ module RuboCop
156
156
  end
157
157
 
158
158
  def found_method(node, method_name)
159
- if @definitions.key?(method_name)
160
- loc = case node.type
161
- when :def, :defs
159
+ key = method_key(node, method_name)
160
+
161
+ if @definitions.key?(key)
162
+ loc = if DEF_TYPES.include?(node.type)
162
163
  node.loc.keyword.join(node.loc.name)
163
164
  else
164
165
  node.loc.expression
165
166
  end
166
- message = message_for_dup(node, method_name)
167
+ message = message_for_dup(node, method_name, key)
167
168
 
168
169
  add_offense(loc, message: message)
169
170
  else
170
- @definitions[method_name] = node
171
+ @definitions[key] = node
172
+ end
173
+ end
174
+
175
+ def method_key(node, method_name)
176
+ if (ancestor_def = node.each_ancestor(*DEF_TYPES).first)
177
+ "#{ancestor_def.method_name}.#{method_name}"
178
+ else
179
+ method_name
171
180
  end
172
181
  end
173
182
 
@@ -116,7 +116,7 @@ module RuboCop
116
116
  private
117
117
 
118
118
  def comment_between_rescue_and_end?(node)
119
- ancestor = node.each_ancestor(:kwbegin, :def, :defs, :block).first
119
+ ancestor = node.each_ancestor(:kwbegin, :def, :defs, :block, :numblock).first
120
120
  return unless ancestor
121
121
 
122
122
  end_line = ancestor.loc.end.line
@@ -20,7 +20,7 @@ module RuboCop
20
20
  #
21
21
  # @example CountRepeatedAttributes: false (default is true)
22
22
  #
23
- # # `model` and `current_user`, refenced 3 times each,
23
+ # # `model` and `current_user`, referenced 3 times each,
24
24
  # # are each counted as only 1 branch each if
25
25
  # # `CountRepeatedAttributes` is set to 'false'
26
26
  #
@@ -126,6 +126,29 @@ module RuboCop
126
126
  pos += size * step while condition && src[pos + offset, size] == needle
127
127
  pos.negative? ? 0 : pos
128
128
  end
129
+
130
+ def range_with_comments_and_lines(node)
131
+ range_by_whole_lines(range_with_comments(node), include_final_newline: true)
132
+ end
133
+
134
+ def range_with_comments(node)
135
+ ranges = [
136
+ node,
137
+ *@processed_source.ast_with_comments[node]
138
+ ].map do |element|
139
+ element.location.expression
140
+ end
141
+ ranges.reduce do |result, range|
142
+ add_range(result, range)
143
+ end
144
+ end
145
+
146
+ def add_range(range1, range2)
147
+ range1.with(
148
+ begin_pos: [range1.begin_pos, range2.begin_pos].min,
149
+ end_pos: [range1.end_pos, range2.end_pos].max
150
+ )
151
+ end
129
152
  end
130
153
  end
131
154
  end
@@ -1,18 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  module RuboCop
4
6
  module Cop
5
7
  # Help methods for determining node visibility.
6
8
  module VisibilityHelp
7
9
  extend NodePattern::Macros
8
10
 
9
- VISIBILITY_SCOPES = %i[private protected public].freeze
11
+ VISIBILITY_SCOPES = ::Set[:private, :protected, :public].freeze
10
12
 
11
13
  private
12
14
 
13
15
  def node_visibility(node)
14
- scope = find_visibility_start(node)
15
- scope&.method_name || :public
16
+ node_visibility_from_visibility_inline(node) ||
17
+ node_visibility_from_visibility_block(node) ||
18
+ :public
19
+ end
20
+
21
+ def node_visibility_from_visibility_inline(node)
22
+ return unless node.def_type?
23
+
24
+ node_visibility_from_visibility_inline_on_def(node) ||
25
+ node_visibility_from_visibility_inline_on_method_name(node)
26
+ end
27
+
28
+ def node_visibility_from_visibility_inline_on_def(node)
29
+ parent = node.parent
30
+ parent.method_name if visibility_inline_on_def?(parent)
31
+ end
32
+
33
+ def node_visibility_from_visibility_inline_on_method_name(node)
34
+ node.right_siblings.reverse.find do |sibling|
35
+ visibility_inline_on_method_name?(sibling, method_name: node.method_name)
36
+ end&.method_name
37
+ end
38
+
39
+ def node_visibility_from_visibility_block(node)
40
+ find_visibility_start(node)&.method_name
16
41
  end
17
42
 
18
43
  def find_visibility_start(node)
@@ -21,7 +46,7 @@ module RuboCop
21
46
 
22
47
  # Navigate to find the last protected method
23
48
  def find_visibility_end(node)
24
- possible_visibilities = VISIBILITY_SCOPES - [node_visibility(node)]
49
+ possible_visibilities = VISIBILITY_SCOPES - ::Set[node_visibility(node)]
25
50
  right = node.right_siblings
26
51
  right.find do |child_node|
27
52
  possible_visibilities.include?(node_visibility(child_node))
@@ -30,7 +55,17 @@ module RuboCop
30
55
 
31
56
  # @!method visibility_block?(node)
32
57
  def_node_matcher :visibility_block?, <<~PATTERN
33
- (send nil? { :private :protected :public })
58
+ (send nil? VISIBILITY_SCOPES)
59
+ PATTERN
60
+
61
+ # @!method visibility_inline_on_def?(node)
62
+ def_node_matcher :visibility_inline_on_def?, <<~PATTERN
63
+ (send nil? VISIBILITY_SCOPES def)
64
+ PATTERN
65
+
66
+ # @!method visibility_inline_on_method_name?(node, method_name:)
67
+ def_node_matcher :visibility_inline_on_method_name?, <<~PATTERN
68
+ (send nil? VISIBILITY_SCOPES (sym %method_name))
34
69
  PATTERN
35
70
  end
36
71
  end
@@ -202,31 +202,7 @@ module RuboCop
202
202
  end
203
203
 
204
204
  def remove_node(corrector, node)
205
- corrector.remove(
206
- range_by_whole_lines(
207
- range_with_comments(node),
208
- include_final_newline: true
209
- )
210
- )
211
- end
212
-
213
- def range_with_comments(node)
214
- ranges = [
215
- node,
216
- *processed_source.ast_with_comments[node]
217
- ].map do |element|
218
- element.location.expression
219
- end
220
- ranges.reduce do |result, range|
221
- add_range(result, range)
222
- end
223
- end
224
-
225
- def add_range(range1, range2)
226
- range1.with(
227
- begin_pos: [range1.begin_pos, range2.begin_pos].min,
228
- end_pos: [range1.end_pos, range2.end_pos].max
229
- )
205
+ corrector.remove(range_with_comments_and_lines(node))
230
206
  end
231
207
  end
232
208
  end
@@ -97,12 +97,14 @@ module RuboCop
97
97
  if node.children.first.method?(:name)
98
98
  return class_node.receiver.source if class_node.receiver
99
99
 
100
- value = class_node.source.delete('"').delete("'")
101
- value.prepend('::') if class_node.each_ancestor(:class, :module).any?
102
- value
103
- else
104
- class_node.source
100
+ if class_node.str_type?
101
+ value = class_node.source.delete('"').delete("'")
102
+ value.prepend('::') if class_node.each_ancestor(:class, :module).any?
103
+ return value
104
+ end
105
105
  end
106
+
107
+ class_node.source
106
108
  end
107
109
 
108
110
  def offense_range(receiver_node, node)
@@ -112,7 +112,7 @@ module RuboCop
112
112
  end
113
113
 
114
114
  def range(begin_pos_node, end_pos_node)
115
- range_between(begin_pos_node.loc.selector.begin_pos, end_pos_node.loc.end.end_pos)
115
+ range_between(begin_pos_node.loc.selector.begin_pos, end_pos_node.loc.expression.end_pos)
116
116
  end
117
117
  end
118
118
  end
@@ -34,6 +34,7 @@ module RuboCop
34
34
  extend AutoCorrector
35
35
 
36
36
  def on_hash(node)
37
+ return if node.children.first&.kwsplat_type?
37
38
  return unless (array = containing_array(node))
38
39
  return unless last_array_item?(array, node) && explicit_array?(array)
39
40
 
@@ -38,28 +38,50 @@ module RuboCop
38
38
  ({block numblock} $(send (send _ ${:keys :values}) :each) ...)
39
39
  PATTERN
40
40
 
41
+ # @!method kv_each_with_block_pass(node)
42
+ def_node_matcher :kv_each_with_block_pass, <<~PATTERN
43
+ (send $(send _ ${:keys :values}) :each (block_pass (sym _)))
44
+ PATTERN
45
+
41
46
  def on_block(node)
42
- register_kv_offense(node)
47
+ kv_each(node) do |target, method|
48
+ register_kv_offense(target, method)
49
+ end
43
50
  end
44
51
 
45
52
  alias on_numblock on_block
46
53
 
54
+ def on_block_pass(node)
55
+ kv_each_with_block_pass(node.parent) do |target, method|
56
+ register_kv_with_block_pass_offense(node, target, method)
57
+ end
58
+ end
59
+
47
60
  private
48
61
 
49
- def register_kv_offense(node)
50
- kv_each(node) do |target, method|
51
- parent_receiver = target.receiver.receiver
52
- return unless parent_receiver
53
- return if allowed_receiver?(parent_receiver)
62
+ def register_kv_offense(target, method)
63
+ return unless (parent_receiver = target.receiver.receiver)
64
+ return if allowed_receiver?(parent_receiver)
65
+
66
+ add_offense(kv_range(target), message: format_message(method)) do |corrector|
67
+ correct_key_value_each(target, corrector)
68
+ end
69
+ end
54
70
 
55
- msg = format(message, prefer: "each_#{method[0..-2]}", current: "#{method}.each")
71
+ def register_kv_with_block_pass_offense(node, target, method)
72
+ return unless (parent_receiver = node.parent.receiver.receiver)
73
+ return if allowed_receiver?(parent_receiver)
56
74
 
57
- add_offense(kv_range(target), message: msg) do |corrector|
58
- correct_key_value_each(target, corrector)
59
- end
75
+ range = target.loc.selector.with(end_pos: node.parent.loc.selector.end_pos)
76
+ add_offense(range, message: format_message(method)) do |corrector|
77
+ corrector.replace(range, "each_#{method[0..-2]}")
60
78
  end
61
79
  end
62
80
 
81
+ def format_message(method_name)
82
+ format(MSG, prefer: "each_#{method_name[0..-2]}", current: "#{method_name}.each")
83
+ end
84
+
63
85
  def check_argument(variable)
64
86
  return unless variable.block_argument?
65
87
 
@@ -13,6 +13,10 @@ module RuboCop
13
13
  # when used `==`.
14
14
  # And do not check `Hash#delete_if` and `Hash#keep_if` to change receiver object.
15
15
  #
16
+ # @safety
17
+ # This cop is unsafe because it cannot be guaranteed that the receiver
18
+ # is a `Hash` or responds to the replacement method.
19
+ #
16
20
  # @example
17
21
  #
18
22
  # # bad
@@ -25,6 +25,9 @@ module RuboCop
25
25
  class ObjectThen < Base
26
26
  include ConfigurableEnforcedStyle
27
27
  extend AutoCorrector
28
+ extend TargetRubyVersion
29
+
30
+ minimum_target_ruby_version 2.6
28
31
 
29
32
  MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
30
33
 
@@ -31,9 +31,22 @@ module RuboCop
31
31
  return if rhs.nil? || rhs.children.first
32
32
 
33
33
  add_offense(dot) do |corrector|
34
+ wrap_in_parentheses_if_chained(corrector, node)
34
35
  corrector.replace(dot, ' ')
35
36
  end
36
37
  end
38
+
39
+ private
40
+
41
+ def wrap_in_parentheses_if_chained(corrector, node)
42
+ return unless node.parent&.call_type?
43
+
44
+ operator = node.loc.selector
45
+
46
+ ParenthesesCorrector.correct(corrector, node)
47
+ corrector.insert_after(operator, ' ')
48
+ corrector.wrap(node, '(', ')')
49
+ end
37
50
  end
38
51
  end
39
52
  end
@@ -93,7 +93,7 @@ module RuboCop
93
93
  end
94
94
 
95
95
  # The conversion process doubles escaped slashes, so they have to be reverted
96
- correction.gsub('\\\\', '\\')
96
+ correction.gsub('\\\\', '\\').gsub('\"', '"')
97
97
  end
98
98
 
99
99
  def style
@@ -7,7 +7,7 @@ module RuboCop
7
7
  #
8
8
  # @safety
9
9
  # This cop is unsafe, as it can produce false positives if the receiver
10
- # is not an `Enumerable`.
10
+ # is not an `Enumerator`.
11
11
  #
12
12
  # @example
13
13
  #
@@ -61,19 +61,24 @@ module RuboCop
61
61
 
62
62
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
63
63
  def redundant_each_method(node)
64
- if node.method?(:each) && !node.parent.block_type?
64
+ return if node.last_argument&.block_pass_type?
65
+
66
+ if node.method?(:each) && !node.parent&.block_type?
65
67
  ancestor_node = node.each_ancestor(:send).detect do |ancestor|
66
- RESTRICT_ON_SEND.include?(ancestor.method_name) || ancestor.method?(:reverse_each)
68
+ ancestor.receiver == node &&
69
+ (RESTRICT_ON_SEND.include?(ancestor.method_name) || ancestor.method?(:reverse_each))
67
70
  end
71
+
72
+ return ancestor_node if ancestor_node
68
73
  end
69
74
 
70
- ancestor_node || node.each_descendant(:send).detect do |descendant|
71
- next if descendant.parent.block_type?
75
+ return unless (prev_method = node.children.first)
76
+ return if !prev_method.send_type? ||
77
+ prev_method.parent.block_type? || prev_method.last_argument&.block_pass_type?
72
78
 
73
- detected = descendant.method_name.to_s.start_with?('each_') unless node.method?(:each)
79
+ detected = prev_method.method_name.to_s.start_with?('each_') unless node.method?(:each)
74
80
 
75
- detected || descendant.method?(:reverse_each)
76
- end
81
+ prev_method if detected || prev_method.method?(:reverse_each)
77
82
  end
78
83
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
79
84
 
@@ -44,7 +44,7 @@ module RuboCop
44
44
 
45
45
  def on_regexp(node)
46
46
  each_escape(node) do |char, index, within_character_class|
47
- next if allowed_escape?(node, char, within_character_class)
47
+ next if allowed_escape?(node, char, index, within_character_class)
48
48
 
49
49
  location = escape_range_at_index(node, index)
50
50
 
@@ -56,7 +56,7 @@ module RuboCop
56
56
 
57
57
  private
58
58
 
59
- def allowed_escape?(node, char, within_character_class)
59
+ def allowed_escape?(node, char, index, within_character_class)
60
60
  # Strictly speaking a few single-letter metachars are currently
61
61
  # unnecessary to "escape", e.g. i, E, F, but enumerating them is
62
62
  # rather difficult, and their behavior could change over time with
@@ -65,12 +65,21 @@ module RuboCop
65
65
  return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
66
66
 
67
67
  if within_character_class
68
- ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char)
68
+ ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char) &&
69
+ !char_class_begins_or_ends_with_escaped_hyphen?(node, index)
69
70
  else
70
71
  ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES.include?(char)
71
72
  end
72
73
  end
73
74
 
75
+ def char_class_begins_or_ends_with_escaped_hyphen?(node, index)
76
+ # The hyphen character is allowed to be escaped within a character class
77
+ # but it's not necessry to escape hyphen if it's the first or last character
78
+ # within the character class. This method checks if that's the case.
79
+ # e.g. "[0-9\\-]" or "[\\-0-9]" would return true
80
+ node.source[index] == '[' || node.source[index + 3] == ']'
81
+ end
82
+
74
83
  def delimiter?(node, char)
75
84
  delimiters = [node.loc.begin.source[-1], node.loc.end.source[0]]
76
85
 
@@ -122,7 +122,7 @@ module RuboCop
122
122
  string.inspect
123
123
  else
124
124
  # In a single-quoted strings, double quotes don't need to be escaped
125
- "'#{string.gsub('\"', '"').gsub('\\') { '\\\\' }}'"
125
+ "'#{string.gsub('\\') { '\\\\' }.gsub('\"', '"')}'"
126
126
  end
127
127
  end
128
128
 
@@ -183,11 +183,10 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
183
183
  # rubocop:enable Metrics/CyclomaticComplexity,Metrics/MethodLength
184
184
 
185
185
  def to_table(header, content)
186
- # Specify `[separator=¦]` to prevent the regexp `|` is not used as a table separator.
187
- # See: https://docs.asciidoctor.org/asciidoc/latest/tables/data-format/#escape-the-cell-separator
188
- table = ['[separator=¦]', '|===', "| #{header.join(' | ')}\n\n"].join("\n")
186
+ table = ['|===', "| #{header.join(' | ')}\n\n"].join("\n")
189
187
  marked_contents = content.map do |plain_content|
190
- plain_content.map { |c| #{c}" }.join("\n")
188
+ # Escape `|` with backslash to prevent the regexp `|` is not used as a table separator.
189
+ plain_content.map { |c| "| #{c.gsub(/\|/, '\|')}" }.join("\n")
191
190
  end
192
191
  table << marked_contents.join("\n\n")
193
192
  table << "\n|===\n"
@@ -17,6 +17,8 @@ module RuboCop
17
17
  # The core of server process. It starts TCP server and perform socket communication.
18
18
  # @api private
19
19
  class Core
20
+ JSON_FORMATS = %w[json j].freeze
21
+
20
22
  def self.token
21
23
  @token ||= SecureRandom.hex(4)
22
24
  end
@@ -57,6 +59,10 @@ module RuboCop
57
59
  def start_server(host, port)
58
60
  @server = TCPServer.open(host, port)
59
61
 
62
+ # JSON format does not expected output message when IDE integration with server mode.
63
+ # See: https://github.com/rubocop/rubocop/issues/11164
64
+ return if use_json_format?
65
+
60
66
  output_stream = ARGV.include?('--stderr') ? $stderr : $stdout
61
67
  output_stream.puts "RuboCop server starting on #{@server.addr[3]}:#{@server.addr[1]}."
62
68
  end
@@ -76,6 +82,15 @@ module RuboCop
76
82
  ensure
77
83
  socket.close
78
84
  end
85
+
86
+ def use_json_format?
87
+ return true if ARGV.include?('--format=json') || ARGV.include?('--format=j')
88
+ return false unless (index = ARGV.index('--format'))
89
+
90
+ format = ARGV[index + 1]
91
+
92
+ JSON_FORMATS.include?(format)
93
+ end
79
94
  end
80
95
  end
81
96
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.38.0'
6
+ STRING = '1.39.0'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
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.38.0
4
+ version: 1.39.0
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: 2022-11-01 00:00:00.000000000 Z
13
+ date: 2022-11-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -985,7 +985,7 @@ metadata:
985
985
  homepage_uri: https://rubocop.org/
986
986
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
987
987
  source_code_uri: https://github.com/rubocop/rubocop/
988
- documentation_uri: https://docs.rubocop.org/rubocop/1.38/
988
+ documentation_uri: https://docs.rubocop.org/rubocop/1.39/
989
989
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
990
990
  rubygems_mfa_required: 'true'
991
991
  post_install_message: