Skip to content

feat(rust): infer let types from Self-returning associated constructors#242

Merged
zzet merged 1 commit into
mainfrom
feat/rust-let-constructor-inference
Jul 4, 2026
Merged

feat(rust): infer let types from Self-returning associated constructors#242
zzet merged 1 commit into
mainfrom
feat/rust-let-constructor-inference

Conversation

@zzet

@zzet zzet commented Jul 4, 2026

Copy link
Copy Markdown
Owner

What

Extends the Rust extractor's local type environment: a let x = Type::new(..) already seeded x's type so a later x.method() selector call resolves against Type. This generalizes that seeding to the other conventional value constructors idiomatic Rust reserves for returning the owning type — from, default, with_capacity, with_config, empty — so let x = Matcher::from(cfg); x.find() binds find to Matcher::find.

Precision is preserved by two guards: (1) the inferred name must be a bare UpperCamelCase type, so a lowercase module function, Self, or a single-letter generic parameter is never seeded; (2) the resolver still refuses to bind unless that type genuinely owns the called method, so a misfit inference falls through to unresolved rather than to a wrong owner. Wrapper-returning constructors are deliberately excluded — from_str/open yield Result<Self> and builder a distinct struct, so their binding's real type is the wrapper, not the qualifier (admitting them was a latent wrong-owner risk this PR also removes).

Measured impact — honest

On ripgrep (native resolution, rust-analyzer off), this produces zero change in resolved-vs-unresolved edges: ripgrep's residual ambiguous method calls are dominated by common names (push/len/map/is_empty) on standard-library receiver types (Vec/String/Option/&[u8]) whose owner types aren't graph nodes, so no amount of native qualifier/receiver inference can bind them — that is inherently language-server territory. This change is a correctness/completeness improvement to the extractor (it infers more local types, and fixes the wrapper-constructor precision hole) that helps codebases with richer internal polymorphism than ripgrep; it is not expected to move recall on std-heavy repos.

Tests

  • Extractor tests: Config::default() / Buf::with_capacity() / Matcher::from() seed the local type; thing::from (lowercase) and T::from (generic) do not.
  • Resolver test: end-to-end bind of a constructor-bound local's method to the correct owner, refusing a same-named method on another type.
  • go build, go test -race ./internal/parser/... ./internal/resolver/..., golangci-lint all green.

A `let x = Type::new(..)` already seeded the local type environment so a
later selector call on x resolves against Type; extend that to the other
conventional value constructors (from/default/with_capacity/with_config/
empty) idiomatic Rust reserves for returning the owning type itself. The
inferred name must be a bare UpperCamelCase type, so a lowercase module
function, `Self`, or a single-letter generic parameter is never seeded,
and the resolver still refuses to bind unless that type genuinely owns
the called method, so a misfit inference falls through to unresolved
rather than to a wrong owner. Wrapper-returning constructors are
deliberately excluded — from_str and open yield Result<Self> and builder
a distinct struct, so the binding's real type is that wrapper, not the
qualifier.
@zzet zzet merged commit 020a301 into main Jul 4, 2026
9 checks passed
@zzet zzet deleted the feat/rust-let-constructor-inference branch July 6, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant