rubocop 1.53.0 → 1.54.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63ade8f6d7d93161d739cd557220bea89fc0320a2f8923f41cc092015dbcadef
4
- data.tar.gz: bb608b076e84d18f317b1690a6f5ad0b0ecea1232977107ebca57b04d32b6abb
3
+ metadata.gz: b952fc5852376637345f505d8753d44608bc082ea9bb64f9d4e61b5cc9cd6005
4
+ data.tar.gz: e7a695e76983fe1ff49d7951be70eabcb149de9bd20179f564cb61adc04d8b33
5
5
  SHA512:
6
- metadata.gz: '0178518d0c9ab5eb1425395c4047f603f92e9d17ae9ef23d7e52ef9c9cfc51e8dda9abd0b08d39b84014c6f59f238673b5fe73de3c2eb649cd166c39cfc58081'
7
- data.tar.gz: 752ee645c95115d20848ff2f00731597bd6106738bb6bded350a3348b392a471890d6f607e475245f81aae7746c23389ffd302e7884b2cba8476ca66d4d07ec7
6
+ metadata.gz: 1b2f4d9249a3e283b1ed762a73c8d68b31db632de1a2e0005b99769314598fbe9394ea86a56abfff7ead03acd17d4ab343a6540ee712196bc36326959b8f9138
7
+ data.tar.gz: 1d8d0d7ac82f4440385d2b502070f3bfb872d651af0707f78ff2df6f90cd6ee51bd03ccf84c71f4f582d812a661be3af2a637895b1b0137dc37ec7606fcfcb45
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.53', require: false
56
+ gem 'rubocop', '~> 1.54', 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
@@ -30,6 +30,7 @@ AllCops:
30
30
  - '**/*.rbx'
31
31
  - '**/*.ru'
32
32
  - '**/*.ruby'
33
+ - '**/*.schema'
33
34
  - '**/*.spec'
34
35
  - '**/*.thor'
35
36
  - '**/*.watchr'
@@ -55,6 +56,7 @@ AllCops:
55
56
  - '**/Puppetfile'
56
57
  - '**/Rakefile'
57
58
  - '**/rakefile'
59
+ - '**/Schemafile'
58
60
  - '**/Snapfile'
59
61
  - '**/Steepfile'
60
62
  - '**/Thorfile'
@@ -577,6 +579,8 @@ Layout/EmptyLineBetweenDefs:
577
579
  EmptyLineBetweenMethodDefs: true
578
580
  EmptyLineBetweenClassDefs: true
579
581
  EmptyLineBetweenModuleDefs: true
582
+ # `DefLikeMacros` takes the name of any macro that you want to treat like a def.
583
+ DefLikeMacros: []
580
584
  # `AllowAdjacentOneLineDefs` means that single line method definitions don't
581
585
  # need an empty line between them. `true` by default.
582
586
  AllowAdjacentOneLineDefs: true
@@ -135,7 +135,8 @@ module RuboCop
135
135
  return if nodes.all?(&:single_line?) && cop_config['AllowAdjacentOneLineDefs']
136
136
 
137
137
  correction_node = nodes.last
138
- location = correction_node.loc.keyword.join(correction_node.loc.name)
138
+
139
+ location = def_location(correction_node)
139
140
  add_offense(location, message: message(correction_node, count: count)) do |corrector|
140
141
  autocorrect(corrector, *nodes, count)
141
142
  end
@@ -159,10 +160,28 @@ module RuboCop
159
160
 
160
161
  private
161
162
 
163
+ def def_location(correction_node)
164
+ if correction_node.block_type?
165
+ correction_node.source_range.join(correction_node.children.first.source_range)
166
+ else
167
+ correction_node.loc.keyword.join(correction_node.loc.name)
168
+ end
169
+ end
170
+
162
171
  def candidate?(node)
163
172
  return false unless node
164
173
 
165
- method_candidate?(node) || class_candidate?(node) || module_candidate?(node)
174
+ method_candidate?(node) || class_candidate?(node) || module_candidate?(node) ||
175
+ macro_candidate?(node)
176
+ end
177
+
178
+ def empty_line_between_macros
179
+ cop_config.fetch('DefLikeMacros', []).map(&:to_sym)
180
+ end
181
+
182
+ def macro_candidate?(node)
183
+ node.block_type? && node.children.first.macro? &&
184
+ empty_line_between_macros.include?(node.children.first.method_name)
166
185
  end
167
186
 
168
187
  def method_candidate?(node)
@@ -226,7 +245,11 @@ module RuboCop
226
245
  end
227
246
 
228
247
  def def_start(node)
229
- node.loc.keyword.line
248
+ if node.block_type? && node.children.first.send_type?
249
+ node.source_range.line
250
+ else
251
+ node.loc.keyword.line
252
+ end
230
253
  end
231
254
 
232
255
  def def_end(node)
@@ -84,6 +84,8 @@ module RuboCop
84
84
  return unless strings_concatenated_with_backslash?(node)
85
85
 
86
86
  children = node.children
87
+ return if children.empty?
88
+
87
89
  if style == :aligned && !always_indented?(node)
88
90
  check_aligned(children, 1)
89
91
  else
@@ -153,7 +153,9 @@ module RuboCop
153
153
  private
154
154
 
155
155
  def regular_operator?(send_node)
156
- !send_node.unary_operation? && !send_node.dot? && operator_with_regular_syntax?(send_node)
156
+ return false if send_node.unary_operation? || send_node.dot? || send_node.double_colon?
157
+
158
+ operator_with_regular_syntax?(send_node)
157
159
  end
158
160
 
159
161
  def operator_with_regular_syntax?(send_node)
@@ -68,10 +68,7 @@ module RuboCop
68
68
  MSG = 'Remove debugger entry point `%<source>s`.'
69
69
 
70
70
  def on_send(node)
71
- return unless debugger_method?(node)
72
-
73
- # Basically, debugger methods are not used as a method argument without arguments.
74
- return if node.arguments.empty? && node.each_ancestor(:send, :csend).any?
71
+ return if !debugger_method?(node) || assumed_usage_context?(node)
75
72
 
76
73
  add_offense(node)
77
74
  end
@@ -95,6 +92,13 @@ module RuboCop
95
92
  debugger_methods.include?(chained_method_name(send_node))
96
93
  end
97
94
 
95
+ def assumed_usage_context?(node)
96
+ # Basically, debugger methods are not used as a method argument without arguments.
97
+ return false unless node.arguments.empty? && node.each_ancestor(:send, :csend).any?
98
+
99
+ node.each_ancestor.none?(&:lambda_or_proc?)
100
+ end
101
+
98
102
  def chained_method_name(send_node)
99
103
  chained_method_name = send_node.method_name.to_s
100
104
  receiver = send_node.receiver
@@ -84,6 +84,8 @@ module RuboCop
84
84
  end
85
85
 
86
86
  def unsafe_range?(range_start, range_end)
87
+ return false if range_start.length != 1 || range_end.length != 1
88
+
87
89
  range_for(range_start) != range_for(range_end)
88
90
  end
89
91
 
@@ -124,7 +124,7 @@ module RuboCop
124
124
  source == value ||
125
125
  # `Symbol#inspect` uses double quotes, but allow single-quoted
126
126
  # symbols to work as well.
127
- source.tr("'", '"') == value
127
+ source.gsub('"', '\"').tr("'", '"') == value
128
128
  end
129
129
 
130
130
  def requires_quotes?(sym_node)
@@ -113,9 +113,15 @@ module RuboCop
113
113
  def check_var(node)
114
114
  return unless node.variable? || node.const_type?
115
115
 
116
- add_offense(node.loc.name,
117
- message: format(VAR_MSG, var: node.loc.name.source)) do |corrector|
118
- autocorrect_void_var(corrector, node)
116
+ if node.const_type? && node.special_keyword?
117
+ add_offense(node, message: format(VAR_MSG, var: node.source)) do |corrector|
118
+ autocorrect_void_expression(corrector, node)
119
+ end
120
+ else
121
+ add_offense(node.loc.name,
122
+ message: format(VAR_MSG, var: node.loc.name.source)) do |corrector|
123
+ autocorrect_void_expression(corrector, node)
124
+ end
119
125
  end
120
126
  end
121
127
 
@@ -123,7 +129,7 @@ module RuboCop
123
129
  return if !node.literal? || node.xstr_type? || node.range_type?
124
130
 
125
131
  add_offense(node, message: format(LIT_MSG, lit: node.source)) do |corrector|
126
- autocorrect_void_literal(corrector, node)
132
+ autocorrect_void_expression(corrector, node)
127
133
  end
128
134
  end
129
135
 
@@ -131,7 +137,7 @@ module RuboCop
131
137
  return unless node.self_type?
132
138
 
133
139
  add_offense(node, message: SELF_MSG) do |corrector|
134
- autocorrect_void_self(corrector, node)
140
+ autocorrect_void_expression(corrector, node)
135
141
  end
136
142
  end
137
143
 
@@ -181,18 +187,6 @@ module RuboCop
181
187
  end
182
188
  end
183
189
 
184
- def autocorrect_void_var(corrector, node)
185
- corrector.remove(range_with_surrounding_space(range: node.loc.name, side: :left))
186
- end
187
-
188
- def autocorrect_void_literal(corrector, node)
189
- corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
190
- end
191
-
192
- def autocorrect_void_self(corrector, node)
193
- corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
194
- end
195
-
196
190
  def autocorrect_void_expression(corrector, node)
197
191
  corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
198
192
  end
@@ -63,7 +63,7 @@ module RuboCop
63
63
  types
64
64
  end
65
65
 
66
- def code_length(node)
66
+ def code_length(node) # rubocop:disable Metrics/MethodLength
67
67
  if classlike_node?(node)
68
68
  classlike_code_length(node)
69
69
  elsif heredoc_node?(node)
@@ -72,7 +72,14 @@ module RuboCop
72
72
  body = extract_body(node)
73
73
  return 0 unless body
74
74
 
75
- body.source.each_line.count { |line| !irrelevant_line?(line) }
75
+ source =
76
+ if node_with_heredoc?(body)
77
+ source_from_node_with_heredoc(body)
78
+ else
79
+ body.source.lines
80
+ end
81
+
82
+ source.count { |line| !irrelevant_line?(line) }
76
83
  end
77
84
  end
78
85
 
@@ -175,6 +182,27 @@ module RuboCop
175
182
  def another_args?(node)
176
183
  node.call_type? && node.arguments.count > 1
177
184
  end
185
+
186
+ def node_with_heredoc?(node)
187
+ node.each_descendant(:str, :dstr).any? { |descendant| heredoc_node?(descendant) }
188
+ end
189
+
190
+ def source_from_node_with_heredoc(node)
191
+ last_line = -1
192
+ node.each_descendant do |descendant|
193
+ next unless descendant.loc
194
+
195
+ descendant_last_line =
196
+ if heredoc_node?(descendant)
197
+ descendant.loc.heredoc_end.line
198
+ else
199
+ descendant.last_line
200
+ end
201
+
202
+ last_line = [last_line, descendant_last_line].max
203
+ end
204
+ @processed_source[(node.first_line - 1)..(last_line - 1)]
205
+ end
178
206
  end
179
207
  end
180
208
  end
@@ -26,11 +26,15 @@ module RuboCop
26
26
  end
27
27
 
28
28
  def delimiter_string(node)
29
- node.source.match(OPENING_DELIMITER).captures[1]
29
+ return '' unless (match = node.source.match(OPENING_DELIMITER))
30
+
31
+ match.captures[1]
30
32
  end
31
33
 
32
34
  def heredoc_type(node)
33
- node.source.match(OPENING_DELIMITER).captures[0]
35
+ return '' unless (match = node.source.match(OPENING_DELIMITER))
36
+
37
+ match.captures[0]
34
38
  end
35
39
  end
36
40
  end
@@ -47,7 +47,7 @@ module RuboCop
47
47
  def correct_elsif(node)
48
48
  <<~RUBY.chop
49
49
  if #{node.condition.source}
50
- #{node.if_branch.source}
50
+ #{node.if_branch&.source}
51
51
  #{build_else_branch(node.else_branch).chop}
52
52
  end
53
53
  RUBY
@@ -56,7 +56,7 @@ module RuboCop
56
56
  def build_else_branch(second_condition)
57
57
  result = <<~RUBY
58
58
  elsif #{second_condition.condition.source}
59
- #{second_condition.if_branch.source}
59
+ #{second_condition.if_branch&.source}
60
60
  RUBY
61
61
 
62
62
  if second_condition.else_branch
@@ -69,10 +69,10 @@ module RuboCop
69
69
  return unless offending_selector?(node, selector)
70
70
 
71
71
  add_offense(node.send_node.source_range, message: message(node, selector)) do |corrector|
72
- if node.send_node.source == 'lambda'
73
- autocorrect_method_to_literal(corrector, node)
74
- else
72
+ if node.send_node.lambda_literal?
75
73
  LambdaLiteralToMethodCorrector.new(node).call(corrector)
74
+ else
75
+ autocorrect_method_to_literal(corrector, node)
76
76
  end
77
77
  end
78
78
  end
@@ -22,7 +22,7 @@ module RuboCop
22
22
 
23
23
  def on_send(node)
24
24
  return unless node.method?(:require_relative)
25
- return unless node.first_argument.str_content.start_with?(CURRENT_DIRECTORY_PATH)
25
+ return unless node.first_argument.str_content&.start_with?(CURRENT_DIRECTORY_PATH)
26
26
  return unless (index = node.first_argument.source.index(CURRENT_DIRECTORY_PATH))
27
27
 
28
28
  begin_pos = node.first_argument.source_range.begin.begin_pos + index
@@ -40,7 +40,9 @@ module RuboCop
40
40
  byteindex byterindex gsub gsub! partition rpartition scan split start_with? sub sub!
41
41
  ].freeze
42
42
  DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
43
- STR_SPECIAL_CHARS = %w[\n \" \' \\\\ \t \b \f \r].freeze
43
+ STR_SPECIAL_CHARS = %w[
44
+ \a \c \C \e \f \M \n \" \' \\\\ \t \b \f \r \u \v \x \0 \1 \2 \3 \4 \5 \6 \7
45
+ ].freeze
44
46
 
45
47
  def on_send(node)
46
48
  return unless (regexp_node = node.first_argument)
@@ -48,9 +50,7 @@ module RuboCop
48
50
  return if !regexp_node.regopt.children.empty? || regexp_node.content == ' '
49
51
  return unless determinist_regexp?(regexp_node)
50
52
 
51
- new_argument = replacement(regexp_node)
52
- quote = new_argument.include?('"') ? "'" : '"'
53
- prefer = "#{quote}#{new_argument}#{quote}"
53
+ prefer = preferred_argument(regexp_node)
54
54
  message = format(MSG, prefer: prefer, current: regexp_node.source)
55
55
 
56
56
  add_offense(regexp_node, message: message) do |corrector|
@@ -64,6 +64,19 @@ module RuboCop
64
64
  DETERMINISTIC_REGEX.match?(regexp_node.source)
65
65
  end
66
66
 
67
+ def preferred_argument(regexp_node)
68
+ new_argument = replacement(regexp_node)
69
+
70
+ if new_argument.include?('"')
71
+ new_argument.gsub!("'", "\\\\'")
72
+ quote = "'"
73
+ else
74
+ quote = '"'
75
+ end
76
+
77
+ "#{quote}#{new_argument}#{quote}"
78
+ end
79
+
67
80
  def replacement(regexp_node)
68
81
  regexp_content = regexp_node.content
69
82
  stack = []
@@ -118,16 +118,18 @@ module RuboCop
118
118
  node.comparison_method? && !noncommutative_operator?(node)
119
119
  end
120
120
 
121
+ # rubocop:disable Metrics/CyclomaticComplexity
121
122
  def valid_yoda?(node)
122
- lhs = node.receiver
123
- rhs = node.first_argument
123
+ return true unless (rhs = node.first_argument)
124
124
 
125
+ lhs = node.receiver
125
126
  return true if (constant_portion?(lhs) && constant_portion?(rhs)) ||
126
127
  (!constant_portion?(lhs) && !constant_portion?(rhs)) ||
127
128
  interpolation?(lhs)
128
129
 
129
130
  enforce_yoda? ? constant_portion?(lhs) : constant_portion?(rhs)
130
131
  end
132
+ # rubocop:enable Metrics/CyclomaticComplexity
131
133
 
132
134
  def message(node)
133
135
  format(MSG, source: node.source)
@@ -36,6 +36,8 @@ module RuboCop
36
36
  end
37
37
 
38
38
  handle 'initialize' do |request|
39
+ @server.configure(safe_autocorrect: safe_autocorrect?(request))
40
+
39
41
  @server.write(
40
42
  id: request[:id],
41
43
  result: LanguageServer::Protocol::Interface::InitializeResult.new(
@@ -162,6 +164,12 @@ module RuboCop
162
164
 
163
165
  private
164
166
 
167
+ def safe_autocorrect?(request)
168
+ safe_autocorrect = request.dig(:params, :initializationOptions, :safeAutocorrect)
169
+
170
+ safe_autocorrect.nil? || safe_autocorrect == true
171
+ end
172
+
165
173
  def format_file(file_uri)
166
174
  unless (text = @text_cache[file_uri])
167
175
  Logger.log("Format request arrived before text synchronized; skipping: `#{file_uri}'")
@@ -14,9 +14,12 @@ module RuboCop
14
14
  # Runtime for Language Server Protocol of RuboCop.
15
15
  # @api private
16
16
  class Runtime
17
+ attr_writer :safe_autocorrect
18
+
17
19
  def initialize(config_store)
18
20
  @config_store = config_store
19
21
  @logged_paths = []
22
+ @safe_autocorrect = true
20
23
  end
21
24
 
22
25
  # This abuses the `--stdin` option of rubocop and reads the formatted text
@@ -32,7 +35,7 @@ module RuboCop
32
35
  # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/runner.rb#L72
33
36
  def format(path, text)
34
37
  formatting_options = {
35
- stdin: text, force_exclusion: true, autocorrect: true, safe_autocorrect: true
38
+ stdin: text, force_exclusion: true, autocorrect: true, safe_autocorrect: @safe_autocorrect
36
39
  }
37
40
 
38
41
  redirect_stdout { run_rubocop(formatting_options, path) }
@@ -36,8 +36,8 @@ module RuboCop
36
36
  @routes.handle_unsupported_method(request)
37
37
  end
38
38
  rescue StandardError => e
39
- log("Error #{e.class} #{e.message[0..100]}")
40
- log(e.backtrace.inspect)
39
+ Logger.log("Error #{e.class} #{e.message[0..100]}")
40
+ Logger.log(e.backtrace.inspect)
41
41
  end
42
42
  end
43
43
 
@@ -53,6 +53,10 @@ module RuboCop
53
53
  @runtime.offenses(path, text)
54
54
  end
55
55
 
56
+ def configure(safe_autocorrect: true)
57
+ @runtime.safe_autocorrect = safe_autocorrect
58
+ end
59
+
56
60
  def stop(&block)
57
61
  at_exit(&block) if block
58
62
  exit
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.53.0'
6
+ STRING = '1.54.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.53.0
4
+ version: 1.54.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: 2023-06-23 00:00:00.000000000 Z
13
+ date: 2023-07-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -1023,7 +1023,7 @@ metadata:
1023
1023
  homepage_uri: https://rubocop.org/
1024
1024
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
1025
1025
  source_code_uri: https://github.com/rubocop/rubocop/
1026
- documentation_uri: https://docs.rubocop.org/rubocop/1.53/
1026
+ documentation_uri: https://docs.rubocop.org/rubocop/1.54/
1027
1027
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
1028
1028
  rubygems_mfa_required: 'true'
1029
1029
  post_install_message: