Rust: V::Value with a generic parameter V binds to a function-local struct V in another crate #579

Closed
opened 2026-11-12 16:45:26 +01:00 by buildagent · 1 comment
Member

Observed

Release v0.41.50, this repository indexed as its own project.

A test file declared a method with a type parameter named V:

// crates/indexer/tests/configuration_reference.rs
fn deserialize_any<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Self::Error> {

crates/mcp-server/src/server.rs has a struct local to one function body:

fn de_symbol_id_or_string<'de, D>(d: D) -> Result<String, D::Error> ... {
    struct V;
    impl serde::de::Visitor<'_> for V {
        type Value = String;

find_references on that type Value (server.rs:5440) returned both V::Value sites of the test file as resolution: "resolved", qualifier: "V", resolved_by: "tier1q_pass1". They are wrong binds twice over:

  • the qualifier V at the use site is a type parameter of the enclosing method, not a type;
  • the target's container is declared inside a function body in another crate, so no path outside that body can name it.

crates/indexer/tests/manifest_layering.rs::no_resolved_edge_crosses_an_undeclared_crate_boundary caught it in the v0.41.51 full gate (crates/indexer -> crates/mcp-server (2 edge(s))), only because the two files are in crates with no declared dependency. The same bind inside one crate would pass that gate unseen.

Effect

find_references, change_impact, review_diff report a caller that does not exist, marked as resolved.

What was done in v0.41.51

The test's type parameter was renamed (V → Vis) so the tree is graded clean. Renaming it back reproduces the bind. The resolver is unchanged.

Suggested mechanism (not yet measured)

Two independent rules, both generic:

  1. A symbol whose container is declared inside a function or method body is reachable only from references inside that body's span. The qualified tier (and any other cross-file rule) should not offer it as a candidate elsewhere.
  2. A qualifier that names a type parameter of an enclosing item is not a type name; the qualified tier should not look it up as one. This needs the Rust package to report type parameters (it does not today).

Rule 1 is host-side and needs no package change. Either rule only removes binds, but it moves corpus counts, so it needs a bind account (which binds disappear, split by resolved_by) before any baseline is touched.

Tool gap noticed while attributing

No tool lists the resolved references going OUT of a file or symbol for kinds other than calls. find_callees covers call/method_call only, so the two type references could be found only by guessing candidate target names with search_symbols and running find_references on each.

## Observed Release v0.41.50, this repository indexed as its own project. A test file declared a method with a type parameter named `V`: ```rust // crates/indexer/tests/configuration_reference.rs fn deserialize_any<V: Visitor<'de>>(self, _: V) -> Result<V::Value, Self::Error> { ``` `crates/mcp-server/src/server.rs` has a struct local to one function body: ```rust fn de_symbol_id_or_string<'de, D>(d: D) -> Result<String, D::Error> ... { struct V; impl serde::de::Visitor<'_> for V { type Value = String; ``` `find_references` on that `type Value` (`server.rs:5440`) returned both `V::Value` sites of the test file as `resolution: "resolved"`, `qualifier: "V"`, `resolved_by: "tier1q_pass1"`. They are wrong binds twice over: - the qualifier `V` at the use site is a type parameter of the enclosing method, not a type; - the target's container is declared inside a function body in another crate, so no path outside that body can name it. `crates/indexer/tests/manifest_layering.rs::no_resolved_edge_crosses_an_undeclared_crate_boundary` caught it in the v0.41.51 full gate (`crates/indexer -> crates/mcp-server (2 edge(s))`), only because the two files are in crates with no declared dependency. The same bind inside one crate would pass that gate unseen. ## Effect `find_references`, `change_impact`, `review_diff` report a caller that does not exist, marked as resolved. ## What was done in v0.41.51 The test's type parameter was renamed (`V` → `Vis`) so the tree is graded clean. Renaming it back reproduces the bind. The resolver is unchanged. ## Suggested mechanism (not yet measured) Two independent rules, both generic: 1. A symbol whose container is declared inside a function or method body is reachable only from references inside that body's span. The qualified tier (and any other cross-file rule) should not offer it as a candidate elsewhere. 2. A qualifier that names a type parameter of an enclosing item is not a type name; the qualified tier should not look it up as one. This needs the Rust package to report type parameters (it does not today). Rule 1 is host-side and needs no package change. Either rule only removes binds, but it moves corpus counts, so it needs a bind account (which binds disappear, split by `resolved_by`) before any baseline is touched. ## Tool gap noticed while attributing No tool lists the resolved references going OUT of a file or symbol for kinds other than calls. `find_callees` covers `call`/`method_call` only, so the two `type` references could be found only by guessing candidate target names with `search_symbols` and running `find_references` on each.
Author
Member

Merged into #531.
The fixture parameter was renamed V → Vis in crates/indexer/tests/configuration_reference.rs; the resolver is unchanged.
Remaining: Namespace and lexical identity. Outgoing non-call navigation is also in #521, Reference navigation.

Merged into #531. The fixture parameter was renamed V → Vis in crates/indexer/tests/configuration_reference.rs; the resolver is unchanged. Remaining: Namespace and lexical identity. Outgoing non-call navigation is also in #521, Reference navigation.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
h-dv/code-index#579
No description provided.