diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index 6ad366d2b..f06226214 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -231,7 +231,7 @@ def deserialize_yard_pin_cache gemspec end # @param gemspec [Gem::Specification] - # @return [void] + # @return [Array, nil] def deserialize_combined_pin_cache gemspec unless combined_pins_in_memory[[gemspec.name, gemspec.version]].nil? return combined_pins_in_memory[[gemspec.name, gemspec.version]] @@ -270,7 +270,13 @@ def deserialize_combined_pin_cache gemspec combined_pins_in_memory[[gemspec.name, gemspec.version]] else logger.debug { "Pins not yet cached for #{gemspec.name}:#{gemspec.version}" } - nil + # Not stored in combined_pins_in_memory: that index is process-wide + # and keyed only by name and version, so a provisional set there would + # outlive the build that supersedes it. The gemspec stays on the + # uncached lists the two deserialize calls above pushed it onto. + fallback = rbs_map.fallback_pins + logger.debug { "Using fallback RBS pins for #{gemspec.name}:#{gemspec.version}" } if fallback + fallback end end diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index c86dc6b74..54802d3d4 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -153,6 +153,17 @@ def resolved? @resolved end + # A standalone substitute for the combined pins of this gem, for a + # caller that needs something before a combined cache entry exists. + # A base RbsMap has none to offer: a combined entry merges these pins + # with separately-cached YARD pins, and dropping the YARD half is not + # safe in general. + # + # @return [Array, nil] + def fallback_pins + nil + end + # @return [RBS::Repository] def repository @repository ||= RBS::Repository.new(no_stdlib: false).tap do |repo| diff --git a/lib/solargraph/rbs_map/stdlib_map.rb b/lib/solargraph/rbs_map/stdlib_map.rb index e6ebcf90f..b1ca8b633 100644 --- a/lib/solargraph/rbs_map/stdlib_map.rb +++ b/lib/solargraph/rbs_map/stdlib_map.rb @@ -61,6 +61,14 @@ def resolve_dependencies? true end + # Stdlib pins need no merge with YARD pins, so they stand alone + # as a substitute for a combined cache entry. + # + # @return [Array, nil] + def fallback_pins + pins if resolved? + end + # @param library [String] # @return [StdlibMap] def self.load library diff --git a/spec/doc_map_spec.rb b/spec/doc_map_spec.rb index 2dbe28fb7..af6d153e4 100644 --- a/spec/doc_map_spec.rb +++ b/spec/doc_map_spec.rb @@ -121,6 +121,33 @@ end end + context 'without a combined pin cache entry for a gem' do + let(:pre_cache) { false } + + # A version nothing will ever have cached on disk, so the read path + # takes the uncached branch however much of the suite ran first. + def uncached_gemspec name + Gem::Specification.new(name, '999.0.0') + end + + it 'serves RBS stdlib pins as a fallback' do + pins = doc_map.send(:deserialize_combined_pin_cache, uncached_gemspec('logger')) + expect(pins.map(&:path)).to include('Logger#info') + end + + it 'does not record the fallback as if it were a real build' do + gemspec = uncached_gemspec('logger') + doc_map.send(:deserialize_combined_pin_cache, gemspec) + expect(doc_map.combined_pins_in_memory).not_to have_key([gemspec.name, gemspec.version]) + expect(doc_map.uncached_gemspecs).to include(gemspec) + end + + it 'has no fallback for a gem outside the RBS stdlib' do + pins = doc_map.send(:deserialize_combined_pin_cache, uncached_gemspec('backport')) + expect(pins).to be_nil + end + end + context 'with convention' do let(:pre_cache) { false } diff --git a/spec/rbs_map/stdlib_map_spec.rb b/spec/rbs_map/stdlib_map_spec.rb index 4364fcfef..fccef9bae 100644 --- a/spec/rbs_map/stdlib_map_spec.rb +++ b/spec/rbs_map/stdlib_map_spec.rb @@ -62,6 +62,18 @@ end end + it 'offers its pins as a fallback when resolved' do + map = described_class.load('yaml') + expect(map.fallback_pins).not_to be_empty + expect(map.fallback_pins).to eq(map.pins) + end + + it 'offers no fallback pins when unresolved' do + map = described_class.new('not_a_stdlib_library') + expect(map).not_to be_resolved + expect(map.fallback_pins).to be_nil + end + it 'pins are marked as coming from RBS parsing' do map = Solargraph::RbsMap::StdlibMap.load('yaml') store = Solargraph::ApiMap::Store.new(map.pins) diff --git a/spec/rbs_map_spec.rb b/spec/rbs_map_spec.rb index 09e7a1a80..b958ca60d 100644 --- a/spec/rbs_map_spec.rb +++ b/spec/rbs_map_spec.rb @@ -14,6 +14,13 @@ expect(rbs_map).not_to be_resolved end + it 'offers no fallback pins for a gem outside the RBS stdlib' do + spec = Gem::Specification.find_by_name('rbs') + rbs_map = described_class.from_gemspec(spec, nil, nil) + expect(rbs_map).to be_resolved + expect(rbs_map.fallback_pins).to be_nil + end + it 'fails if it does not find data from name' do rbs_map = described_class.new('lskdflaksdfjl') expect(rbs_map.pins).to be_empty