Conversation
…ntyped UniqueType#resolve_generics erased a class's own generic type param to ComplexType::UNDEFINED whenever the querying context supplied no concrete value and no default existed, rather than leaving it as an unbound placeholder. This fires whenever a method is reached only through a superclass/mixin reference (ApiMap#inner_get_methods_from_reference), so a concretely-parametrized reference like NoMethodError<String>#receiver still resolved to untyped even though NoMethodError<String> supplies T, because resolve_generics is called twice in that path (once while resolving the superclass reference itself, once while resolving the returned method pins) and the fallback nuked the intermediate value before the second call could use it. bundle exec solargraph pin 'NoMethodError<String>#receiver' --rbs now shows `(::String | nil)` instead of `untyped`. A bare, unrooted query (`NoMethodError#receiver`, no type args) still shows `untyped` via the CLI `pin` command -- that comes from ApiMap#erase_generics, a separate and deliberate policy (see spec/source_map/clip_spec.rb's "erases unresolvable class generics" example that intentionally erases an unbound generic for real type-inference call sites, and is unaffected by this change. EOF )
apiology
commented
Aug 24, 2026
Demonstrates the case fixed in the previous commit: a class with two @Generic params, referenced with no type arguments, must leave the non-first generic as generic<V> instead of erasing it to undefined.
The existing spec exercises resolve_generics directly; add one closer to the PR description repro of NoMethodError<String>#receiver, going through real workspace source and ApiMap so the fix is also demonstrated at the level a user would actually hit it.
apiology
marked this pull request as ready for review
August 26, 2026 19:51
apiology
added a commit
to apiology/solargraph
that referenced
this pull request
Aug 27, 2026
unresolved instead of erasing to untyped
…ass-substitution-clean
apiology
marked this pull request as draft
August 31, 2026 19:20
Shrink the resolve_generics else-branch comment from 8 lines to 2, and cut the clip_spec.rb example comment from 4 lines to 1, per Vince's pending review directives on PR 1329.
apiology
marked this pull request as ready for review
September 6, 2026 16:22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Claude:
A class reference with a concrete generic argument doesn't propagate that argument through a method inherited from its superclass:
NoMethodError<T> < NameError<T>, andNameError#receiveris declared to returnT. GivenNoMethodError<String>,#receivershould resolve toString(orString | nil) — instead it erases tountyped.Root cause:
UniqueType#resolve_genericserased a class's own generic type parameter toComplexType::UNDEFINEDwhenever the querying context supplied no concrete value and no default existed, rather than leaving it as an unbound placeholder.resolve_genericsis called twice on this path (ApiMap#inner_get_methods_from_reference) — once resolving the superclass reference itself, once resolving the returned method pins — and the fallback nuked the intermediate value before the second call could use it.After the fix:
A bare, unrooted query (
NoMethodError#receiver, no type args) still showsuntypedvia the CLIpincommand — that comes fromApiMap#erase_generics, a separate, deliberate policy (seespec/source_map/clip_spec.rb's "erases unresolvable class generics" example), and is unaffected by this change.Full spec suite: 1624 examples, 0 failures. Rubocop: 2 pre-existing offenses, both outside this diff (
Lint/UnreachableCodeat lines 151 and 379, unrelated to the change at line ~466).This PR was written by Claude Code on behalf of @apiology.