solargraph 0.60.2 → 0.60.3

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: 6827dc4bfa0ff6effa9c70ae5dc13bae988d29450b12bbfaca306bd4067f02ee
4
- data.tar.gz: a0301099ab04a1e2701971b819a51248abab41d49f03c83f724b1b1ec7d6b519
3
+ metadata.gz: 0a62c1ebc82583e5c0ee905b75e3f065a1c6185c8268e7b529115fe445afb1a6
4
+ data.tar.gz: 8b4cc8722f4eb6b3aecb58aaec22d2db54c49a3b2f6477bc7019331de86fbfc7
5
5
  SHA512:
6
- metadata.gz: 6973d2d9b0230a817e109553e92b9234830feb98429f46c0e1ad882f1f0a63574a9f38fc77d15303f8dac5f57e99815df0e501a798d37060d0753f60dc4d8e7b
7
- data.tar.gz: 42a08cb0653d48efffe32ab4c8dd01a7bee94bba8c8e55e8fc5fca953b382935415da7260e862808ae07e9bf61f504e95fdaf5e3aa165ae0fd2e2a919c59db87
6
+ metadata.gz: 8882c0dffd4a6ac9c4d25c53fdb1789840951fa00860120a25237353c9aa4b2c3454bf04cd94921e56f52871a97978f3ea5b4ed38fba34841d8395eb1223a650
7
+ data.tar.gz: 5ec51b9ab0cb3d3f445e3282c0c132641a7c9bf52643598a4476ddc5eb23d5527442f0bd434f772b9fcc94a437a956b38607f13d9dc74752df69fae75017963b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 0.60.3 - August 5, 2026
2
+ - Pin rdoc to ~> 7.0 (#1216)
3
+ - (tag: v0.60.2) Release 0.60.2 (rdoc fix)
4
+ - Fix stale Hash::_Key expectations from upstream RBS core drift (#1224)
5
+ - Bump overcommit to ~> 0.71.0 (#1221)
6
+ - Resolve remote constants (#1234)
7
+ - Fix exponential blowup in Pin::Method#combine_same_type_arity_signatures (#1238)
8
+ - Respect #- yard comment-block separator (#1218)
9
+ - Typecheck attribute return tags against their backing ivar's type (#1219)
10
+ - Fix recursive-mutex deadlock in Library#sync_catalog (#1220)
11
+ - Fix Gemspecs#in_this_bundle? crash when no Gemfile is discoverable (#1225)
12
+ - Remove duplicate dead methods in RbsMap::Conversions (#1253)
13
+
1
14
  ## 0.60.2 - June 27, 2026
2
15
  - Extraneous debug output (#1214)
3
16
  - Pin rdoc to ~> 7.0 (#1216)
@@ -155,7 +155,7 @@ module Solargraph
155
155
  mixin = resolve(ref.name, ref.reference_gates)
156
156
  next unless mixin
157
157
 
158
- resolved = simple_resolve(name, mixin, internal)
158
+ resolved = resolve(name, mixin)
159
159
  return [resolved, gates[(idx + 1)..]] if resolved
160
160
  end
161
161
  end
@@ -403,11 +403,10 @@ module Solargraph
403
403
  # @param candidates [Array<Pin::BaseVariable>]
404
404
  # @param name [String]
405
405
  # @param closure [Pin::Closure]
406
- # @param location [Location]
406
+ # @param location [Location, nil]
407
407
  #
408
408
  # @return [Pin::BaseVariable, nil]
409
409
  def var_at_location candidates, name, closure, location
410
- # @todo Location can be nil if clips have trouble finding node recipients
411
410
  return unless location
412
411
 
413
412
  with_correct_name = candidates.select { |pin| pin.name == name }
@@ -605,8 +605,12 @@ module Solargraph
605
605
  queued_gemspec_cache.push(spec)
606
606
  return if pending - queued_gemspec_cache.length < 1
607
607
 
608
- catalog
609
- sync_catalog
608
+ # Try the next cacheable gemspec. This method is always called
609
+ # from inside sync_catalog's mutex (either directly or via this
610
+ # same recursive call), so recursing through sync_catalog here
611
+ # would try to re-lock a mutex this thread already holds and
612
+ # deadlock (https://github.com/castwide/solargraph/issues/1111).
613
+ cache_next_gemspec
610
614
  else
611
615
  logger.info "Caching #{spec.name} #{spec.version}"
612
616
  Thread.new do
@@ -509,11 +509,6 @@ module Solargraph
509
509
  #
510
510
  # @return [Array<Pin::Signature>]
511
511
  def combine_same_type_arity_signatures same_type_arity_signatures
512
- # @todo Stubbing this method while we debug an infinite loop bug in Ruby 3.x.
513
- # The body below is intentionally preserved for when the stub is removed.
514
- return same_type_arity_signatures
515
-
516
- # rubocop:disable Lint/UnreachableCode
517
512
  # This is an O(n^2) operation, so bail out if n is not small
518
513
  return same_type_arity_signatures if same_type_arity_signatures.length > 10
519
514
 
@@ -521,7 +516,9 @@ module Solargraph
521
516
  # @param new_signature [Pin::Signature]
522
517
  same_type_arity_signatures.reduce([]) do |old_signatures, new_signature|
523
518
  next old_signatures + [new_signature] if old_signatures.empty?
524
- old_signatures.flat_map do |old_signature|
519
+
520
+ merged = false
521
+ combined = old_signatures.map do |old_signature|
525
522
  potential_new_signature = old_signature.combine_with(new_signature)
526
523
 
527
524
  if potential_new_signature.type_arity == old_signature.type_arity
@@ -534,13 +531,14 @@ module Solargraph
534
531
  # based on types, not just arity, allowing for type
535
532
  # information describing how methods behave based on
536
533
  # their input types)
537
- old_signatures - [old_signature] + [potential_new_signature]
534
+ merged = true
535
+ potential_new_signature
538
536
  else
539
- old_signatures + [new_signature]
537
+ old_signature
540
538
  end
541
539
  end
540
+ merged ? combined : old_signatures + [new_signature]
542
541
  end
543
- # rubocop:enable Lint/UnreachableCode
544
542
  end
545
543
 
546
544
  # @param name [String]
@@ -159,27 +159,6 @@ module Solargraph
159
159
  RBS_TO_CLASS.fetch(ns, ns)
160
160
  end
161
161
 
162
- # @param type_name [RBS::TypeName]
163
- # @param type_args [Enumerable<RBS::Types::Bases::Base>]
164
- # @return [ComplexType::UniqueType]
165
- def build_type type_name, type_args = []
166
- # we use .absolute? below to tell the type object what to
167
- # expect
168
- rbs_name = type_name.relative!.to_s
169
- base = RBS_TO_CLASS.fetch(rbs_name, rbs_name)
170
-
171
- params = type_args.map { |a| RbsTranslator.to_complex_type(a) }
172
- # @todo Tuples are in flux
173
- # tuples have their own class and are handled in other_type_to_type
174
- if base == 'Hash' && params.length == 2
175
- ComplexType::UniqueType.new(base, [params.first], [params.last], rooted: type_name.absolute?,
176
- parameters_type: :hash)
177
- else
178
- ComplexType::UniqueType.new(base, [], params.reject(&:undefined?), rooted: type_name.absolute?,
179
- parameters_type: :list)
180
- end
181
- end
182
-
183
162
  # @param decl [RBS::AST::Declarations::Module::Self]
184
163
  # @param closure [Pin::Namespace]
185
164
  # @return [void]
@@ -561,7 +540,8 @@ module Solargraph
561
540
  def method_def_to_sigs decl, pin
562
541
  # rubocop:disable Style/SafeNavigationChainLength
563
542
  implicit_nil = decl.overloads.first&.annotations&.map(&:string)&.include?('implicitly-returns-nil') || false
564
- # rubocop:enable Style/SafeNavigationChainLength # @param overload [RBS::AST::Members::MethodDefinition::Overload]
543
+ # rubocop:enable Style/SafeNavigationChainLength
544
+ # @param overload [RBS::AST::Members::MethodDefinition::Overload]
565
545
  decl.overloads.map do |overload|
566
546
  type_location = location_decl_to_pin_location(overload.method_type.location)
567
547
  generics = overload.method_type.type_params.map(&:name).map(&:to_s)
@@ -587,84 +567,6 @@ module Solargraph
587
567
  Location.new(location.name.to_s, range)
588
568
  end
589
569
 
590
- # @param type [RBS::MethodType, RBS::Types::Block]
591
- # @param pin [Pin::Method]
592
- # @param implicit_nil [Boolean]
593
- # @return [Array(Array<Pin::Parameter>, ComplexType)]
594
- def parts_of_function type, pin, implicit_nil
595
- type_location = pin.type_location
596
- if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction)
597
- return [
598
- [Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs,
599
- type_location: type_location)],
600
- method_type_to_type(type, implicit_nil)
601
- ]
602
- end
603
-
604
- parameters = []
605
- arg_num = -1
606
- type.type.required_positionals.each do |param|
607
- # @sg-ignore Unresolved call to name
608
- name = param.name ? param.name.to_s : "arg_#{arg_num += 1}"
609
- parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin,
610
- # @sg-ignore RBS generic type understanding issue
611
- return_type: other_type_to_type(param.type),
612
- source: :rbs, type_location: type_location)
613
- end
614
- type.type.optional_positionals.each do |param|
615
- # @sg-ignore Unresolved call to name
616
- name = param.name ? param.name.to_s : "arg_#{arg_num += 1}"
617
- parameters.push Solargraph::Pin::Parameter.new(decl: :optarg, name: name, closure: pin,
618
- # @sg-ignore RBS generic type understanding issue
619
- return_type: other_type_to_type(param.type),
620
- type_location: type_location,
621
- source: :rbs)
622
- end
623
- if type.type.rest_positionals
624
- name = type.type.rest_positionals.name ? type.type.rest_positionals.name.to_s : "arg_#{arg_num += 1}"
625
- inner_rest_positional_type = other_type_to_type(type.type.rest_positionals.type)
626
- rest_positional_type = ComplexType::UniqueType.new('Array',
627
- [],
628
- [inner_rest_positional_type],
629
- rooted: true, parameters_type: :list)
630
- parameters.push Solargraph::Pin::Parameter.new(decl: :restarg, name: name, closure: pin,
631
- source: :rbs, type_location: type_location,
632
- return_type: rest_positional_type)
633
- end
634
- type.type.trailing_positionals.each do |param|
635
- # @sg-ignore Unresolved call to name
636
- name = param.name ? param.name.to_s : "arg_#{arg_num += 1}"
637
- parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, source: :rbs,
638
- type_location: type_location)
639
- end
640
- type.type.required_keywords.each do |orig, param|
641
- # @sg-ignore Unresolved call to to_s
642
- name = orig ? orig.to_s : "arg_#{arg_num += 1}"
643
- parameters.push Solargraph::Pin::Parameter.new(decl: :kwarg, name: name, closure: pin,
644
- # @sg-ignore RBS generic type understanding issue
645
- return_type: other_type_to_type(param.type),
646
- source: :rbs, type_location: type_location)
647
- end
648
- type.type.optional_keywords.each do |orig, param|
649
- # @sg-ignore Unresolved call to to_s
650
- name = orig ? orig.to_s : "arg_#{arg_num += 1}"
651
- parameters.push Solargraph::Pin::Parameter.new(decl: :kwoptarg, name: name, closure: pin,
652
- # @sg-ignore RBS generic type understanding issue
653
- return_type: other_type_to_type(param.type),
654
- type_location: type_location,
655
- source: :rbs)
656
- end
657
- if type.type.rest_keywords
658
- name = type.type.rest_keywords.name ? type.type.rest_keywords.name.to_s : "arg_#{arg_num += 1}"
659
- parameters.push Solargraph::Pin::Parameter.new(decl: :kwrestarg,
660
- name: type.type.rest_keywords.name.to_s, closure: pin,
661
- source: :rbs, type_location: type_location)
662
- end
663
-
664
- return_type = method_type_to_type(type, implicit_nil)
665
- [parameters, return_type]
666
- end
667
-
668
570
  # @param type [RBS::MethodType,RBS::Types::Block]
669
571
  # @param pin [Pin::Method]
670
572
  # @param implicit_nil [Boolean]
@@ -14,7 +14,7 @@ module Solargraph
14
14
  # @return [String]
15
15
  attr_reader :word
16
16
 
17
- # @return [Location]
17
+ # @return [Location, nil]
18
18
  attr_reader :location
19
19
 
20
20
  # @return [::Array<Chain>]
@@ -13,10 +13,6 @@ module Solargraph
13
13
  @location = location
14
14
  end
15
15
 
16
- # @sg-ignore Declared return type
17
- # ::Array<::Solargraph::Pin::Base> does not match inferred
18
- # type ::Array<::Solargraph::Pin::BaseVariable, ::NilClass>
19
- # for Solargraph::Source::Chain::InstanceVariable#resolve
20
16
  def resolve api_map, name_pin, locals
21
17
  ivars = api_map.get_instance_variable_pins(name_pin.context.namespace, name_pin.context.scope).select do |p|
22
18
  p.name == word
@@ -27,8 +23,7 @@ module Solargraph
27
23
 
28
24
  private
29
25
 
30
- # @todo: Missed nil violation
31
- # @return [Location]
26
+ # @return [Location, nil]
32
27
  attr_reader :location
33
28
  end
34
29
  end
@@ -209,7 +209,7 @@ module Solargraph
209
209
  stringified_comments[rng.start.line] ||= begin
210
210
  # @sg-ignore Need to add nil check here
211
211
  buff = associated_comments[rng.start.line]
212
- buff ? stringify_comment_array(buff) : nil
212
+ stringify_comment_array(buff)
213
213
  end
214
214
  end
215
215
 
@@ -249,20 +249,20 @@ module Solargraph
249
249
 
250
250
  # Get a hash of comments grouped by the line numbers of the associated code.
251
251
  #
252
- # @return [Hash{Integer => String, nil}]
252
+ # @return [Hash{Integer => Array<String>}]
253
253
  def associated_comments
254
254
  @associated_comments ||= begin
255
255
  # @type [Hash{Integer => String}]
256
256
  result = {}
257
- buffer = String.new('')
257
+ buffer = []
258
258
  # @type [Integer, nil]
259
259
  last = nil
260
260
  comments.each_pair do |num, snip|
261
261
  if !last || num == last + 1
262
- buffer.concat "#{snip.text}\n"
262
+ buffer.push "#{snip.text}\n"
263
263
  else
264
264
  result[first_not_empty_from(last + 1)] = buffer.clone
265
- buffer.replace "#{snip.text}\n"
265
+ buffer.replace ["#{snip.text}\n"]
266
266
  end
267
267
  last = num
268
268
  end
@@ -305,27 +305,31 @@ module Solargraph
305
305
 
306
306
  # Get a string representation of an array of comments.
307
307
  #
308
- # @param comments [String]
308
+ # @param comments [Array<String>, nil]
309
309
  # @return [String]
310
310
  def stringify_comment_array comments
311
- ctxt = String.new('')
311
+ ctxt = []
312
312
  started = false
313
313
  skip = nil
314
- comments.lines.each do |l|
314
+ comments&.each do |l|
315
+ if l =~ /^#-\R/
316
+ ctxt.clear
317
+ next
318
+ end
315
319
  # Trim the comment and minimum leading whitespace
316
320
  p = l.force_encoding('UTF-8').encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '')
317
321
  if p.strip.empty?
318
322
  next unless started
319
- ctxt.concat p
323
+ ctxt.push p
320
324
  else
321
325
  here = p.index(/[^ \t]/)
322
326
  # @sg-ignore flow sensitive typing should be able to handle redefinition
323
327
  skip = here if skip.nil? || here < skip
324
- ctxt.concat p[skip..]
328
+ ctxt.push p[skip..]
325
329
  end
326
330
  started = true
327
331
  end
328
- ctxt
332
+ ctxt.join('')
329
333
  end
330
334
 
331
335
  # A hash of line numbers and their associated comments.
@@ -145,8 +145,8 @@ module Solargraph
145
145
  )
146
146
  end
147
147
  # @sg-ignore Need to add nil check here
148
- com_pos = Position.new(line + 1 - comments.lines.length, 0)
149
- process_comment(src_pos, com_pos, comments)
148
+ com_pos = Position.new(line + 1 - comments.length, 0)
149
+ process_comment(src_pos, com_pos, comments.join(''))
150
150
  end
151
151
  rescue StandardError => e
152
152
  raise e.class, "Error processing comment directives in #{@filename}: #{e.message}"
@@ -162,10 +162,19 @@ module Solargraph
162
162
  result.push Problem.new(pin.location, "Untyped method #{pin.path} could not be inferred")
163
163
  end
164
164
  elsif rules.validate_tags?
165
- unless pin.node.nil? || declared.void? || virtual_pin?(pin) || abstract?(pin)
165
+ # Attribute pins never have a node (attr_reader/attr_writer/attr_accessor are
166
+ # synthesized, not parsed method bodies), but their inferred type is still
167
+ # meaningful: #probe walks the backing ivar's assignments via infer_from_iv.
168
+ # Without this carve-out, a manually-written @return tag on an attribute is
169
+ # never cross-checked against how the ivar is actually assigned (e.g. a nilable
170
+ # default in the constructor), so a bad tag silently passes at every level.
171
+ unless (pin.node.nil? && !pin.attribute?) || declared.void? || virtual_pin?(pin) || abstract?(pin)
166
172
  inferred = pin.probe(api_map).self_to_type(pin.full_context)
167
173
  if inferred.undefined?
168
- unless rules.ignore_all_undefined? || external?(pin)
174
+ # An attribute with no locally-discoverable ivar assignment (set via a
175
+ # mixin, metaprogramming, etc.) isn't a tag/inference mismatch - just
176
+ # unproven. Only methods with an actual body get flagged for this.
177
+ unless rules.ignore_all_undefined? || external?(pin) || pin.attribute?
169
178
  result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin)
170
179
  end
171
180
  else
@@ -848,7 +857,7 @@ module Solargraph
848
857
  def all_sg_ignore_lines
849
858
  source.associated_comments.select do |_line, text|
850
859
  # @sg-ignore Need to add nil check here
851
- text.include?('@sg-ignore')
860
+ text.any? { |t| t.include?('@sg-ignore') }
852
861
  end.keys.to_set
853
862
  end
854
863
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.60.2')
4
+ VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.60.3')
5
5
  end
@@ -220,6 +220,12 @@ module Solargraph
220
220
  # @sg-ignore need boolish support for ? methods
221
221
  def in_this_bundle?
222
222
  Bundler.definition&.lockfile&.to_s&.start_with?(directory)
223
+ rescue Bundler::GemfileNotFound
224
+ # Solargraph itself isn't running under a discoverable Gemfile
225
+ # (e.g. installed and invoked as a standalone gem), so it can't
226
+ # be "this bundle" - fall back to treating the workspace as an
227
+ # external bundle.
228
+ false
223
229
  end
224
230
 
225
231
  # @return [Array<Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification>]
data/solargraph.gemspec CHANGED
@@ -65,7 +65,7 @@ Gem::Specification.new do |s|
65
65
  #
66
66
  # even more specific on RuboCop itself, which is written into _todo
67
67
  # file.
68
- s.add_development_dependency 'overcommit', '~> 0.68.0'
68
+ s.add_development_dependency 'overcommit', '~> 0.71.0'
69
69
  s.add_development_dependency 'rubocop', '~> 1.80.0.0'
70
70
  s.add_development_dependency 'rubocop-rake', '~> 0.7.1'
71
71
  s.add_development_dependency 'rubocop-rspec', '~> 3.6.0'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.60.2
4
+ version: 0.60.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
@@ -419,14 +419,14 @@ dependencies:
419
419
  requirements:
420
420
  - - "~>"
421
421
  - !ruby/object:Gem::Version
422
- version: 0.68.0
422
+ version: 0.71.0
423
423
  type: :development
424
424
  prerelease: false
425
425
  version_requirements: !ruby/object:Gem::Requirement
426
426
  requirements:
427
427
  - - "~>"
428
428
  - !ruby/object:Gem::Version
429
- version: 0.68.0
429
+ version: 0.71.0
430
430
  - !ruby/object:Gem::Dependency
431
431
  name: rubocop
432
432
  requirement: !ruby/object:Gem::Requirement