rubocop 1.53.0 → 1.54.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: 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: