rubocop 1.84.1 → 1.84.2

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: 358bce5232f7b6e4e490a3d173d09cb4d3df8fc9e92355b550d0047e10e0f462
4
- data.tar.gz: 8e65d98bd3478db7eee250dd05ffc6fd4f4d17d144207950fc12f5e83f7995f6
3
+ metadata.gz: f04de47751f13e6453b15f6114038e6c9ef89418cde47c8e8ce73abecc1664ee
4
+ data.tar.gz: d1d9cd363db45392d753d24324b1828fe20834bfea01f5efcd2ee246352cbef1
5
5
  SHA512:
6
- metadata.gz: 4b6cf48e7582909807829dd43e7fd379a9e4ec46cd387659a16321f4fd3ee7a321ac16859ce86870d41fae2602abde14197e1bb91094abb8bf9dd809dc6e648e
7
- data.tar.gz: c619779f7ac9d8563aadc5f6675333f22d7c8d2300d6ac1c996260d442210f6b2b809f24e7fa1479b6b908a0ca16bc8f61bc23200f3e2f302e03369bce6dafac
6
+ metadata.gz: f28b74f272f302feb77cdf3053f1116c7b24e3b0c57b139c71fd315d3c38fa8b4f3c122691fd071af1990699f7b7a52d9de5296ac8bb257435ef612ca317ef11
7
+ data.tar.gz: 82cba886044606ae3b12eb9ef3b2b706be63d1503e80a10a9b22df2b329ed318b8b286e1b110d64bce0c9f10d7f39f84ad48735ba8a6b2d2f259dd78f4592550
@@ -25,5 +25,34 @@ module RuboCop
25
25
 
26
26
  File.join(root, 'rubocop_cache')
27
27
  end
28
+
29
+ # Lightweight cache root computation that reads CacheRootDirectory from
30
+ # the toplevel config file and environment variables without going through
31
+ # the full ConfigStore/ConfigLoader. This method can be used, for example,
32
+ # before loading configuration files. Please note that this method doesn't
33
+ # take into account any `inherit_from` dependencies.
34
+ def self.root_dir_from_toplevel_config(cache_root_override = nil)
35
+ root_dir do
36
+ next cache_root_override if cache_root_override
37
+
38
+ config_path = ConfigFinder.find_config_path(Dir.pwd)
39
+ file_contents = File.read(config_path)
40
+
41
+ # Returns early if `CacheRootDirectory` is not used before requiring `erb` or `yaml`.
42
+ next unless file_contents.include?('CacheRootDirectory')
43
+
44
+ require 'erb'
45
+ require 'yaml'
46
+ yaml_code = ERB.new(file_contents).result
47
+ config_yaml = YAML.safe_load(yaml_code, permitted_classes: [Regexp, Symbol], aliases: true)
48
+
49
+ # For compatibility with Ruby 3.0 or lower.
50
+ if Gem::Version.new(Psych::VERSION) < Gem::Version.new('4.0.0')
51
+ config_yaml == false ? nil : config_yaml
52
+ end
53
+
54
+ config_yaml&.dig('AllCops', 'CacheRootDirectory')
55
+ end
56
+ end
28
57
  end
29
58
  end
data/lib/rubocop/cli.rb CHANGED
@@ -166,7 +166,9 @@ module RuboCop
166
166
 
167
167
  @config_store.apply_options!(@options)
168
168
  # Set cache root after apply_options! to ensure force_default_config is applied first.
169
+ early_cache_root = ConfigLoader.cache_root(@options[:cache_root])
169
170
  ConfigLoader.cache_root = ResultCache.cache_root(@config_store, @options[:cache_root])
171
+ warn_if_cache_root_changed(early_cache_root, ConfigLoader.cache_root)
170
172
 
171
173
  handle_exiting_options
172
174
 
@@ -225,5 +227,21 @@ module RuboCop
225
227
  def report_pending_cops
226
228
  PendingCopsReporter.warn_if_needed(@config_store.for_pwd)
227
229
  end
230
+
231
+ def warn_if_cache_root_changed(early, desired)
232
+ return if early == desired
233
+ # Normalize paths to avoid false positives from symlink resolution differences
234
+ if File.exist?(early) && File.exist?(desired) &&
235
+ File.realpath(early) == File.realpath(desired)
236
+ return
237
+ end
238
+
239
+ warn Rainbow(
240
+ "Warning: Remote configuration cache files were stored in `#{early}` " \
241
+ "because a desired cache root (`#{desired}`) was not set at the top level. " \
242
+ 'Consider setting `AllCops: CacheRootDirectory` in your toplevel configuration file, ' \
243
+ 'using the `RUBOCOP_CACHE_ROOT` environment variable, or using CLI options.'
244
+ ).yellow
245
+ end
228
246
  end
229
247
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'erb'
4
4
  require 'yaml'
5
+ require_relative 'cache_config'
5
6
  require_relative 'config_finder'
6
7
 
7
8
  module RuboCop
@@ -23,13 +24,17 @@ module RuboCop
23
24
  include FileFinder
24
25
 
25
26
  attr_accessor :debug, :ignore_parent_exclusion, :disable_pending_cops, :enable_pending_cops,
26
- :ignore_unrecognized_cops, :cache_root
27
- attr_writer :default_configuration
27
+ :ignore_unrecognized_cops
28
+ attr_writer :default_configuration, :cache_root
28
29
  attr_reader :loaded_plugins, :loaded_features
29
30
 
30
31
  alias debug? debug
31
32
  alias ignore_parent_exclusion? ignore_parent_exclusion
32
33
 
34
+ def cache_root(cache_root_override = nil)
35
+ @cache_root ||= CacheConfig.root_dir_from_toplevel_config(cache_root_override)
36
+ end
37
+
33
38
  def clear_options
34
39
  @debug = nil
35
40
  @loaded_plugins = Set.new
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  def on_send(node)
41
41
  return if node.receiver || node.arguments.empty?
42
- return if node.parent&.type?(:send, :any_block)
42
+ return if node.parent&.type?(:send, :any_block, :array)
43
43
 
44
44
  return if next_line_empty_or_enable_directive_comment?(node.last_line)
45
45
 
@@ -187,6 +187,7 @@ module RuboCop
187
187
  def should_correct_entire_chain?(send_node, top_level_send)
188
188
  return false unless style == :special_for_inner_method_call_in_parentheses
189
189
  return false unless inner_call?(top_level_send)
190
+ return false unless display_column(send_node.source_range) < column_delta.abs
190
191
 
191
192
  top_level_send != send_node || begins_its_line?(top_level_send.loc.end)
192
193
  end
@@ -153,6 +153,7 @@ module RuboCop
153
153
 
154
154
  def check_hash_pair_indentation(node, lhs, rhs)
155
155
  @base = find_hash_pair_alignment_base(node) || lhs.source_range
156
+ return if aligned_with_first_line_dot?(node, rhs)
156
157
 
157
158
  calculate_column_delta_offense(rhs, @base.column)
158
159
  end
@@ -165,6 +166,16 @@ module RuboCop
165
166
  first_call.loc.dot.join(first_call.loc.selector)
166
167
  end
167
168
 
169
+ def aligned_with_first_line_dot?(node, rhs)
170
+ return false unless rhs.source.start_with?('.', '&.')
171
+
172
+ first_call = first_call_has_a_dot(node)
173
+ return false if first_call == node.receiver
174
+
175
+ dot = first_call.loc.dot
176
+ dot.line == node.first_line && dot.column == rhs.column
177
+ end
178
+
168
179
  def check_regular_indentation(node, lhs, rhs, given_style)
169
180
  @base = alignment_base(node, rhs, given_style)
170
181
  correct_column = if @base
@@ -269,7 +280,7 @@ module RuboCop
269
280
  return unless rhs.source.start_with?('.', '&.')
270
281
 
271
282
  node = semantic_alignment_node(node)
272
- return unless node&.loc&.selector && node.loc.dot
283
+ return unless node&.loc?(:selector) && node.loc.dot
273
284
 
274
285
  node.loc.dot.join(node.loc.selector)
275
286
  end
@@ -322,9 +333,9 @@ module RuboCop
322
333
  end
323
334
 
324
335
  def method_on_receiver_last_line?(node, base_receiver, type)
325
- base_receiver &&
326
- node.loc.dot.line == base_receiver.last_line &&
327
- base_receiver.type?(type)
336
+ return false unless base_receiver
337
+
338
+ same_line?(node.loc.dot, base_receiver.source_range.end) && base_receiver.type?(type)
328
339
  end
329
340
 
330
341
  def get_dot_right_above(node)
@@ -337,24 +348,33 @@ module RuboCop
337
348
  end
338
349
 
339
350
  def find_multiline_block_chain_node(node)
340
- return find_continuation_receiver(node) if node.block_node
351
+ return find_continuation_node(node) if node.block_node
341
352
 
342
353
  handle_descendant_block(node)
343
354
  end
344
355
 
345
- def find_continuation_receiver(node)
356
+ def find_continuation_node(node)
346
357
  receiver = node.receiver
347
- return unless receiver.call_type? && receiver.loc.dot && receiver.receiver
358
+ return receiver.send_node if single_line_block_receiver?(receiver)
359
+ return unless receiver.call_type? && receiver.loc.dot
360
+ return receiver if receiver.receiver.begin_type? && node.block_node.single_line?
348
361
  return unless receiver.loc.dot.line > receiver.receiver.last_line
349
362
 
350
363
  receiver
351
364
  end
352
365
 
366
+ def single_line_block_receiver?(receiver)
367
+ receiver.single_line? && receiver.any_block_type?
368
+ end
369
+
353
370
  def handle_descendant_block(node)
371
+ receiver = node.receiver
372
+ return receiver.send_node if single_line_block_receiver?(receiver)
373
+
354
374
  block_node = node.each_descendant(:any_block).first
355
375
  return unless block_node&.multiline?
356
376
 
357
- node.receiver.call_type? ? node.receiver : block_node.parent
377
+ receiver.call_type? ? receiver : block_node.parent
358
378
  end
359
379
 
360
380
  def first_call_has_a_dot(node)
@@ -66,6 +66,13 @@ module RuboCop
66
66
  alias_method :last_line, :line
67
67
  alias_method :last_column, :column
68
68
 
69
+ attr_reader :source_buffer
70
+
71
+ def initialize(line, column, source_line, begin_pos, end_pos)
72
+ super
73
+ @source_buffer = Parser::Source::Buffer.new('(pseudo)', source: source_line)
74
+ end
75
+
69
76
  def column_range
70
77
  column...last_column
71
78
  end
@@ -198,12 +198,13 @@ module RuboCop
198
198
  alias on_csend on_send
199
199
 
200
200
  def on_block(node)
201
- return if ignored_node?(node)
201
+ return if part_of_ignored_node?(node)
202
202
  return if proper_block_style?(node)
203
203
 
204
204
  message = message(node)
205
205
  add_offense(node.loc.begin, message: message) do |corrector|
206
206
  autocorrect(corrector, node)
207
+ ignore_node(node)
207
208
  end
208
209
  end
209
210
 
@@ -37,7 +37,6 @@ module RuboCop
37
37
  #
38
38
  class EmptyClassDefinition < Base
39
39
  include ConfigurableEnforcedStyle
40
- include Alignment
41
40
  include RangeHelp
42
41
  extend AutoCorrector
43
42
 
@@ -45,27 +44,24 @@ module RuboCop
45
44
  'Prefer a two-line class definition over `Class.new` for classes with no body.'
46
45
  MSG_CLASS_NEW = 'Prefer `Class.new` over class definition for classes with no body.'
47
46
 
48
- # @!method class_new_assignment?(node)
49
- def_node_matcher :class_new_assignment?, <<~PATTERN
50
- (casgn _ _ (send (const _ :Class) :new ...))
47
+ # @!method class_new_assignment(node)
48
+ def_node_matcher :class_new_assignment, <<~PATTERN
49
+ (casgn _ _ $(send (const _ :Class) :new ...))
51
50
  PATTERN
52
51
 
53
52
  def on_casgn(node)
54
53
  return unless style == :class_definition
55
- return unless node.expression
56
-
57
- class_new_node = find_class_new_node(node.expression)
58
- return if chained_with_any_method?(node.expression, class_new_node)
59
- return if variable_parent_class?(class_new_node)
54
+ return unless (class_new_node = class_new_assignment(node))
55
+ return if (arg = class_new_node.first_argument) && !arg.const_type?
60
56
 
61
57
  add_offense(node, message: MSG_CLASS_DEFINITION) do |corrector|
62
- autocorrect_class_new(corrector, node)
58
+ autocorrect_class_new(corrector, node, class_new_node)
63
59
  end
64
60
  end
65
61
 
66
62
  def on_class(node)
67
63
  return unless style == :class_new
68
- return unless empty_class?(node)
64
+ return if (body = node.body) && !body.children.empty?
69
65
 
70
66
  add_offense(node, message: MSG_CLASS_NEW) do |corrector|
71
67
  autocorrect_class_definition(corrector, node)
@@ -74,69 +70,25 @@ module RuboCop
74
70
 
75
71
  private
76
72
 
77
- def autocorrect_class_new(corrector, node)
73
+ def autocorrect_class_new(corrector, node, class_new_node)
78
74
  indent = ' ' * node.loc.column
79
75
  class_name = node.name
80
- class_new_node = find_class_new_node(node.expression)
81
- parent_class = extract_parent_class(class_new_node)
82
-
83
- replacement = if parent_class
84
- "class #{class_name} < #{parent_class}\n#{indent}end"
85
- else
86
- "class #{class_name}\n#{indent}end"
87
- end
76
+ if (parent_class = class_new_node.first_argument)
77
+ parent_class_name = " < #{parent_class.source}"
78
+ end
88
79
 
89
- corrector.replace(node, replacement)
80
+ corrector.replace(node, "class #{class_name}#{parent_class_name}\n#{indent}end")
90
81
  end
91
82
 
92
83
  def autocorrect_class_definition(corrector, node)
93
- source_line = processed_source.buffer.source_line(node.loc.line)
94
- indent = source_line[/\A */]
84
+ indent = ' ' * node.loc.column
95
85
  class_name = node.identifier.source
96
- parent_class = node.parent_class&.source
86
+ if (parent_class = node.parent_class)
87
+ parent_class_name = "(#{parent_class.source})"
88
+ end
97
89
  range = range_by_whole_lines(node.source_range, include_final_newline: true)
98
90
 
99
- replacement = if parent_class
100
- "#{indent}#{class_name} = Class.new(#{parent_class})\n"
101
- else
102
- "#{indent}#{class_name} = Class.new\n"
103
- end
104
-
105
- corrector.replace(range, replacement)
106
- end
107
-
108
- def extract_parent_class(class_new_node)
109
- first_arg = class_new_node.first_argument
110
- first_arg&.source
111
- end
112
-
113
- def variable_parent_class?(class_new_node)
114
- first_arg = class_new_node.first_argument
115
- return false unless first_arg
116
-
117
- !first_arg.const_type?
118
- end
119
-
120
- def find_class_new_node(node)
121
- return nil unless node.send_type?
122
- return nil unless node.receiver&.const_type?
123
-
124
- return node if node.receiver.const_name.to_sym == :Class && node.method?(:new)
125
-
126
- nil
127
- end
128
-
129
- def chained_with_any_method?(expression_node, class_new_node)
130
- return true unless expression_node == class_new_node
131
-
132
- false
133
- end
134
-
135
- def empty_class?(node)
136
- body = node.body
137
- return true unless body
138
-
139
- body.begin_type? && body.children.empty?
91
+ corrector.replace(range, "#{indent}#{class_name} = Class.new#{parent_class_name}\n")
140
92
  end
141
93
  end
142
94
  end
@@ -118,7 +118,7 @@ module RuboCop
118
118
  return if allowed_unannotated?(detections)
119
119
 
120
120
  detections.each do |detected_sequence, token_range|
121
- check_sequence(detected_sequence, token_range)
121
+ check_sequence(node, detected_sequence, token_range)
122
122
  end
123
123
  end
124
124
 
@@ -143,17 +143,34 @@ module RuboCop
143
143
  matches_allowed_pattern?(send_parent.method_name))
144
144
  end
145
145
 
146
- def check_sequence(detected_sequence, token_range)
146
+ def check_sequence(node, detected_sequence, token_range)
147
147
  if detected_sequence.style == style
148
148
  correct_style_detected
149
149
  elsif correctable_sequence?(detected_sequence.type)
150
150
  style_detected(detected_sequence.style)
151
- add_offense(token_range, message: message(detected_sequence.style)) do |corrector|
151
+ register_offense(node, detected_sequence, token_range)
152
+ end
153
+ end
154
+
155
+ def register_offense(node, detected_sequence, token_range)
156
+ msg = message(detected_sequence.style)
157
+
158
+ if format_string_context?(node)
159
+ add_offense(token_range, message: msg) do |corrector|
152
160
  autocorrect_sequence(corrector, detected_sequence, token_range)
153
161
  end
162
+ else
163
+ add_offense(token_range, message: msg)
154
164
  end
155
165
  end
156
166
 
167
+ def format_string_context?(node)
168
+ format_string_in_typical_context?(node) ||
169
+ node.each_ancestor(:dstr).any? do |dstr_node|
170
+ format_string_in_typical_context?(dstr_node)
171
+ end
172
+ end
173
+
157
174
  def correctable_sequence?(detected_type)
158
175
  detected_type == 's' || style == :annotated || style == :unannotated
159
176
  end
@@ -36,7 +36,7 @@ module RuboCop
36
36
  def on_hash(node)
37
37
  return if node.children.first&.kwsplat_type?
38
38
  return unless (array = containing_array(node))
39
- return unless last_array_item?(array, node) && explicit_array?(array)
39
+ return unless expected_braced_last_array_item?(array, node) && explicit_array?(array)
40
40
 
41
41
  if braces_style?
42
42
  check_braces(node)
@@ -52,10 +52,12 @@ module RuboCop
52
52
  parent if parent&.array_type?
53
53
  end
54
54
 
55
- def last_array_item?(array, node)
56
- return false if array.child_nodes.all?(&:hash_type?)
55
+ def expected_braced_last_array_item?(array, node)
56
+ return false if array.each_value.all? do |node|
57
+ node.hash_type? && (braces_style? ? node.braces? : !node.braces?)
58
+ end
57
59
 
58
- array.children.last.equal?(node)
60
+ !array.values[-2]&.hash_type? && array.values.last.equal?(node)
59
61
  end
60
62
 
61
63
  def explicit_array?(array)
@@ -253,7 +253,7 @@ module RuboCop
253
253
 
254
254
  def sibling_if_shares_line?(child, node)
255
255
  inner = unwrap_begin(child)
256
- inner&.if_type? && shares_line_with?(inner, node)
256
+ inner&.if_type? && !inner.ternary? && shares_line_with?(inner, node)
257
257
  end
258
258
 
259
259
  def unwrap_begin(node)
@@ -264,7 +264,7 @@ module RuboCop
264
264
  end
265
265
 
266
266
  def shares_line_with?(inner, node)
267
- inner.loc.line == node.loc.end.line || inner.loc.end.line == node.loc.line
267
+ same_line?(inner, node.loc.end) || same_line?(inner.loc.end, node)
268
268
  end
269
269
 
270
270
  def find_containing_collection(node)
@@ -103,8 +103,6 @@ module RuboCop
103
103
  MSG_MISSING = 'Use def with parentheses when there are parameters.'
104
104
 
105
105
  def on_def(node)
106
- return if forced_parentheses?(node)
107
-
108
106
  args = node.arguments
109
107
 
110
108
  if require_parentheses?(args)
@@ -113,10 +111,10 @@ module RuboCop
113
111
  else
114
112
  correct_style_detected
115
113
  end
114
+ elsif forced_parentheses?(node)
115
+ correct_style_detected
116
116
  elsif parentheses?(args)
117
117
  unwanted_parentheses(args)
118
- else
119
- correct_style_detected
120
118
  end
121
119
  end
122
120
  alias on_defs on_def
@@ -21,8 +21,6 @@ module RuboCop
21
21
  include RangeHelp
22
22
  extend AutoCorrector
23
23
 
24
- NON_MODIFIER_THEN = /then\s*(#.*)?$/.freeze
25
-
26
24
  MSG = 'Do not use `then` for multi-line `%<keyword>s`.'
27
25
 
28
26
  def on_normal_if_unless(node)
@@ -36,7 +34,7 @@ module RuboCop
36
34
  private
37
35
 
38
36
  def non_modifier_then?(node)
39
- NON_MODIFIER_THEN.match?(node.loc.begin&.source_line)
37
+ node.then? && node.loc.begin.line != node.if_branch&.loc&.line
40
38
  end
41
39
  end
42
40
  end
@@ -203,13 +203,13 @@ module RuboCop
203
203
 
204
204
  return [] if new_text == text
205
205
 
206
- [
206
+ [{
207
207
  newText: new_text,
208
208
  range: {
209
209
  start: { line: 0, character: 0 },
210
210
  end: { line: text.count("\n") + 1, character: 0 }
211
211
  }
212
- ]
212
+ }]
213
213
  end
214
214
 
215
215
  def diagnostic(file_uri, text)
@@ -31,7 +31,7 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
31
31
  ENV['HOME'] = original_home
32
32
  ENV['XDG_CONFIG_HOME'] = original_xdg_config_home
33
33
 
34
- RuboCop::FileFinder.root_level = nil
34
+ RuboCop::ConfigLoader.clear_options # This also resets RuboCop::FileFinder.root_level
35
35
  end
36
36
  end
37
37
  end
@@ -73,33 +73,12 @@ module RuboCop
73
73
  cache_root_dir = if cache_root_path
74
74
  File.join(cache_root_path, 'rubocop_cache')
75
75
  else
76
- cache_root_dir_from_config
76
+ CacheConfig.root_dir_from_toplevel_config
77
77
  end
78
78
 
79
79
  File.expand_path(File.join(cache_root_dir, 'server'))
80
80
  end
81
81
 
82
- def cache_root_dir_from_config
83
- CacheConfig.root_dir do
84
- # `RuboCop::ConfigStore` has heavy dependencies, this is a lightweight implementation
85
- # so that only the necessary `CacheRootDirectory` can be obtained.
86
- config_path = ConfigFinder.find_config_path(Dir.pwd)
87
- file_contents = File.read(config_path)
88
-
89
- # Returns early if `CacheRootDirectory` is not used before requiring `erb` or `yaml`.
90
- next unless file_contents.include?('CacheRootDirectory')
91
-
92
- config_yaml = load_erb_templated_yaml(file_contents)
93
-
94
- # For compatibility with Ruby 3.0 or lower.
95
- if Gem::Version.new(Psych::VERSION) < Gem::Version.new('4.0.0')
96
- config_yaml == false ? nil : config_yaml
97
- end
98
-
99
- config_yaml&.dig('AllCops', 'CacheRootDirectory')
100
- end
101
- end
102
-
103
82
  def port_path
104
83
  dir.join('port')
105
84
  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.84.1'
6
+ STRING = '1.84.2'
7
7
 
8
8
  MSG = '%<version>s (using %<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.84.1
4
+ version: 1.84.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -1097,7 +1097,7 @@ licenses:
1097
1097
  - MIT
1098
1098
  metadata:
1099
1099
  homepage_uri: https://rubocop.org/
1100
- changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.84.1
1100
+ changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.84.2
1101
1101
  source_code_uri: https://github.com/rubocop/rubocop/
1102
1102
  documentation_uri: https://docs.rubocop.org/rubocop/1.84/
1103
1103
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues