Valid references remain unresolved across declaration groups, type relations, scopes and imports #536

Open
opened 2026-11-09 08:35:47 +01:00 by buildagent · 0 comments
Member

Scope (consolidated 2026-11-12)

Names that the language binds can remain unresolved when declarations are split, a receiver uses a type relation, or an import needs its full module path. Agents then miss real callers, rename sites and production dependents. Resolver identity must follow the language’s declaration groups, visibility and import rules. Resolution figures are the measuring instrument for this work and must separate actual misses from local, external and misclassified rows.

Priority: High · Risk: wrong answer · Size: L
Merged into this ticket: #538 (Type relations and member dispatch), #541 (Visibility and exact qualifiers), #543 (Import and module routes), #549 (Truthful resolution populations)

Done

  • crates/indexer/src/index/member_lookup.rs groups same-file Rust type/impl and Ruby blocks plus C# partial FQNs, and traverses resolved supertypes; this is narrower than a shared declaration-group identity.
  • C# namespace/type precedence, ambiguity and arity selection are shared by type and receiver lookup through select in crates/indexer/src/index/namespace_types.rs (58801d26/d11a35fb); direct-base repairs are recorded in 9e410c1c.
  • Script importer paths and named constant/static imports shipped in v0.41.49: named_const_fixture, resolved_importer_e2e, embedded_typescript_importers and non_script_targets_use_script_paths in crates/daemon/tests/script_import_correctness.rs cover the .mjs/.ts regression and script routes.
  • PHP global inherited-member lookup has the control a_global_receiver_keeps_its_proven_inherited_member in crates/indexer/tests/global_roots.rs; this does not establish trait/interface completeness.

Remaining

Declaration identity

  • Merge TS type/value declarations and attach module augmentations to the target group; finish C# partial/static and Ruby reopening identity across resolver lookups. Reproduction tables in #536. (#536)
  • Return one primary declaration plus parts through get_symbol/find_references/check_rename, including Rust type/impl; qualified retries and ambiguity hints must use syntax the language can spell and distinct qualified names. (#536)

Type relations and receiver flow

  • Complete Python super/shared-name inheritance; PHP traits, interface receivers and property-typed chains; Rust dyn/impl/generic trait dispatch, UFCS and Self items. Reproduction table in #538. (#538)
  • Expose SUPERTYPE roles and inherit/implement filters in find_references; emit missing Rust impl-header and PHP trait-use relations. (#538)
  • Prove C# extension receiver/import/overload applicability with instance-member precedence; finish Ruby implicit self in blocks and return-value typing, sharing reopening identity above. (#538)
  • Add constructor-to-field flow for storage.request.COOKIES; verify both CurlFactory production dependents with per-language fixtures and the corpus account. (#538)

Scope and exact qualifiers

  • Implement Rust defining-module/subtree privacy; finish C# static-class receivers and Ruby exact qualified constant/mixin matching. Reproductions in #541; qualify beyond the delivered C# namespace subset. (#541)

Full-path imports and dependencies

  • Complete PHP PSR-4 use imports and require/include emission, Python dot-only from imports, and Vue/Svelte script import binding; embedded region scope is shared with #540. (#543)
  • Bind module/namespace alias calls (TS import-star, JS require result, Python pkg.mod.f); retain truthful disclosure for these misses. (#543)
  • Follow TS export-star barrels without js pseudo-symbols, Rust cfg/path/glob reexports and mod edges, and full-suffix Ruby require/autoload routes. (#543)
  • Emit manifest dependency edges; qualify non-script importers, include use-lines in check_rename, and make module_layouts disclosure agree with stored layouts. (#543)

Resolution populations

  • Distinguish receiver heads from type positions and namespace segments; preserve the emitted kind while reporting member-access resolution as an outcome, including function values currently labelled member_access. (#549)
  • Exclude LOCAL refs from name-fallback/resolution denominators; hide local symbols by default in outline/search/top-reference/ambiguity views with an opt-in. Separate internal from external/std populations per kind. (#549)
  • Publish explanatory per-language tables and a composition-versus-package-gap verdict; add the type-position invariant gate and prove denominator-only changes leave binds unchanged. (#549)
  • After the accounting correction, aggregate unresolved shapes in a gap miner with a WONTFIX register. (#549)

Acceptance

  • Group fixtures bind zod type/value declarations and augmentation targets correctly; SqlMapper calls and get_symbol for Sinatra::Base, SqlMapper and Store succeed. Truly different same-name types stay ambiguous; disabling grouping fails the fixtures.
  • All remaining relation/scope/import fixtures in #538, #541 and #543 bind, including both CurlFactory::create production callers, Rust subtree visibility, exact Ruby qualifiers, SFC imports and rename use-sites. Hierarchy roles and filters are queryable.
  • Run the nine-repository bind account, sampling additions, removals, retargets and unchanged binds; add unrelated-type precision decoys. Do not bless an unexplained upward baseline.
  • Publish per-language before/after kind tables and explain every denominator move; accounting-only changes leave binds unchanged. A registry gate rejects type-labelled receiver heads without a type-position role.

Suggested order

  1. Correct denominator populations and record per-language tables; package releases needed for emitted-role changes, unchanged-bind account required.
  2. Unify declaration groups, scope and type-relation lookup; affected producer facts need language package releases and all resolver changes need bind accounts.
  3. Complete full-path imports and reexports; language package releases for missing facts, bind accounts before baseline changes.
  4. Build the gap miner only after the figures are truthful; host work, no bind change intended.

Original report

Summary

Several languages split one logical type into several declarations: TS declaration merging, C# partial, Ruby class/module reopening, a Rust type and its impl blocks, and a TS declare module augmentation. The resolver and the name lookup treat each piece as a rival candidate. #523 reported the get_symbol refusal for Rust; the per-language review shows that the bigger cost is on the resolution side, where whole public APIs get zero refs.

Language Evidence (corpus copy, build 2dcfc615)
TypeScript (ts-zod) zod v4 declares 254 types as export interface X…; export const X: core.$constructor<X>. 24/24 sampled merged symbols have ref_count 0: ZodString, ZodType, ZodObject, $ZodError, … Fixture: interface Foo + const Foo, and useFoo(f: Foo): Foo in the same file stays name_fallback, while the unmerged Bar binds 3/3.
C# (cs-dapper) SqlMapper is 26 partial files: get_symbol refuses with 27 candidates, even qualified (Dapper.SqlMapper). SqlMapper.AddTypeHandler/ResetTypeHandlers/PurgeQueryCache/Identity: 0/70 qualified calls resolved. Type refs: 16/167.
Ruby (ruby-sinatra) get_symbol "Sinatra::Base" refuses (base.rb:971 vs its reopening at test/test_helper.rb:37). Sinatra has more than 20 candidates. A method defined in one reopening is invisible to calls from another reopening in the same file (prove_bare_receivers keys by container index, ruby.rs:667-700).
Rust (rust-ripgrep) 261/395 type names (66%) refuse only because of their own impl.
TS augmentation declare module "zod/v4" { interface ZodType {…} } in v4/classic/tests/prototypes.test.ts:4-9 becomes a standalone interface, and 12 z.ZodType refs in other test files bind to it, while the real ZodType (classic/schemas.ts:78) has 0. That is a wrong bind.
Python module-qualified retries (flask.blueprints.Blueprint) are refused, and the hint suggests the Rust-style Container::X.

Proposal: declaration groups (one mechanism)

  • The language profile declares which declarations merge: TS interface/class/function/const/enum/namespace and module augmentation; C# partial by FQN; Ruby same qualified name and kind; Rust impl whose self type is the type; PHP and Python none.
  • The resolver treats a group as one candidate. It binds to the group, picking the member by position: type position → interface/class, value position → const/function. A group is never "ambiguous with itself".
  • An augmentation contributes members to its target module's group and is never a standalone bind target.
  • get_symbol, find_references and check_rename return the group (primary declaration plus parts: [...]).
  • Ambiguity hints only suggest a retry form the language can spell, and only when the candidates' qualified names actually differ (Dapper.SqlMapper, flask.blueprints.Blueprint, Sinatra::Base).

Acceptance

  • zod v4 ZodString/ZodType get resolved refs.
  • The 12 augmentation binds move to the real ZodType.
  • SqlMapper.* qualified calls resolve.
  • get_symbol succeeds for Sinatra::Base, SqlMapper and Store.
  • A corpus bind diff on all 9 repos, sampled for precision, plus precision_gate decoys for two genuinely different same-name types.
  • Mutation: disable grouping, and the fixtures go red.

Supersedes the resolver half of #523, which stays as the narrow get_symbol case.


Filed from the per-language review on 2026-11-09: 8 agents, one per language plus one for Vue/Svelte/XAML/timeline, run against copies of the pinned corpus repos (tests/corpus/corpus.toml) using installed build 2dcfc615, which differs from master only by XAML/CI commits. The top claim of every issue was spot-checked in the main session. Artifacts live under the review session's scratchpad lang-out/<lang>/ and are available on request.

## Scope (consolidated 2026-11-12) Names that the language binds can remain unresolved when declarations are split, a receiver uses a type relation, or an import needs its full module path. Agents then miss real callers, rename sites and production dependents. Resolver identity must follow the language’s declaration groups, visibility and import rules. Resolution figures are the measuring instrument for this work and must separate actual misses from local, external and misclassified rows. **Priority:** High · **Risk:** wrong answer · **Size:** L **Merged into this ticket:** #538 (Type relations and member dispatch), #541 (Visibility and exact qualifiers), #543 (Import and module routes), #549 (Truthful resolution populations) ## Done - `crates/indexer/src/index/member_lookup.rs` groups same-file Rust type/impl and Ruby blocks plus C# partial FQNs, and traverses resolved supertypes; this is narrower than a shared declaration-group identity. - C# namespace/type precedence, ambiguity and arity selection are shared by type and receiver lookup through `select` in `crates/indexer/src/index/namespace_types.rs` (58801d26/d11a35fb); direct-base repairs are recorded in 9e410c1c. - Script importer paths and named constant/static imports shipped in v0.41.49: `named_const_fixture`, `resolved_importer_e2e`, `embedded_typescript_importers` and `non_script_targets_use_script_paths` in `crates/daemon/tests/script_import_correctness.rs` cover the .mjs/.ts regression and script routes. - PHP global inherited-member lookup has the control `a_global_receiver_keeps_its_proven_inherited_member` in `crates/indexer/tests/global_roots.rs`; this does not establish trait/interface completeness. ## Remaining ### Declaration identity - Merge TS type/value declarations and attach module augmentations to the target group; finish C# partial/static and Ruby reopening identity across resolver lookups. Reproduction tables in #536. (#536) - Return one primary declaration plus parts through get_symbol/find_references/check_rename, including Rust type/impl; qualified retries and ambiguity hints must use syntax the language can spell and distinct qualified names. (#536) ### Type relations and receiver flow - Complete Python super/shared-name inheritance; PHP traits, interface receivers and property-typed chains; Rust dyn/impl/generic trait dispatch, UFCS and Self items. Reproduction table in #538. (#538) - Expose SUPERTYPE roles and inherit/implement filters in find_references; emit missing Rust impl-header and PHP trait-use relations. (#538) - Prove C# extension receiver/import/overload applicability with instance-member precedence; finish Ruby implicit self in blocks and return-value typing, sharing reopening identity above. (#538) - Add constructor-to-field flow for storage.request.COOKIES; verify both CurlFactory production dependents with per-language fixtures and the corpus account. (#538) ### Scope and exact qualifiers - Implement Rust defining-module/subtree privacy; finish C# static-class receivers and Ruby exact qualified constant/mixin matching. Reproductions in #541; qualify beyond the delivered C# namespace subset. (#541) ### Full-path imports and dependencies - Complete PHP PSR-4 use imports and require/include emission, Python dot-only from imports, and Vue/Svelte script import binding; embedded region scope is shared with #540. (#543) - Bind module/namespace alias calls (TS import-star, JS require result, Python pkg.mod.f); retain truthful disclosure for these misses. (#543) - Follow TS export-star barrels without js pseudo-symbols, Rust cfg/path/glob reexports and mod edges, and full-suffix Ruby require/autoload routes. (#543) - Emit manifest dependency edges; qualify non-script importers, include use-lines in check_rename, and make module_layouts disclosure agree with stored layouts. (#543) ### Resolution populations - Distinguish receiver heads from type positions and namespace segments; preserve the emitted kind while reporting member-access resolution as an outcome, including function values currently labelled member_access. (#549) - Exclude LOCAL refs from name-fallback/resolution denominators; hide local symbols by default in outline/search/top-reference/ambiguity views with an opt-in. Separate internal from external/std populations per kind. (#549) - Publish explanatory per-language tables and a composition-versus-package-gap verdict; add the type-position invariant gate and prove denominator-only changes leave binds unchanged. (#549) - After the accounting correction, aggregate unresolved shapes in a gap miner with a WONTFIX register. (#549) ## Acceptance - Group fixtures bind zod type/value declarations and augmentation targets correctly; SqlMapper calls and get_symbol for Sinatra::Base, SqlMapper and Store succeed. Truly different same-name types stay ambiguous; disabling grouping fails the fixtures. - All remaining relation/scope/import fixtures in #538, #541 and #543 bind, including both CurlFactory::create production callers, Rust subtree visibility, exact Ruby qualifiers, SFC imports and rename use-sites. Hierarchy roles and filters are queryable. - Run the nine-repository bind account, sampling additions, removals, retargets and unchanged binds; add unrelated-type precision decoys. Do not bless an unexplained upward baseline. - Publish per-language before/after kind tables and explain every denominator move; accounting-only changes leave binds unchanged. A registry gate rejects type-labelled receiver heads without a type-position role. ## Suggested order 1. Correct denominator populations and record per-language tables; package releases needed for emitted-role changes, unchanged-bind account required. 2. Unify declaration groups, scope and type-relation lookup; affected producer facts need language package releases and all resolver changes need bind accounts. 3. Complete full-path imports and reexports; language package releases for missing facts, bind accounts before baseline changes. 4. Build the gap miner only after the figures are truthful; host work, no bind change intended. ## Original report ## Summary Several languages split **one logical type** into several declarations: TS declaration merging, C# `partial`, Ruby class/module reopening, a Rust type and its `impl` blocks, and a TS `declare module` augmentation. The resolver and the name lookup treat each piece as a **rival candidate**. #523 reported the `get_symbol` refusal for Rust; the per-language review shows that the bigger cost is on the **resolution** side, where whole public APIs get zero refs. | Language | Evidence (corpus copy, build `2dcfc615`) | |---|---| | **TypeScript** (ts-zod) | zod v4 declares 254 types as `export interface X…; export const X: core.$constructor<X>`. **24/24 sampled merged symbols have `ref_count 0`**: `ZodString`, `ZodType`, `ZodObject`, `$ZodError`, … Fixture: `interface Foo` + `const Foo`, and `useFoo(f: Foo): Foo` **in the same file** stays name_fallback, while the unmerged `Bar` binds 3/3. | | **C#** (cs-dapper) | `SqlMapper` is 26 `partial` files: `get_symbol` refuses with 27 candidates, **even qualified** (`Dapper.SqlMapper`). `SqlMapper.AddTypeHandler/ResetTypeHandlers/PurgeQueryCache/Identity`: **0/70** qualified calls resolved. Type refs: 16/167. | | **Ruby** (ruby-sinatra) | `get_symbol "Sinatra::Base"` refuses (`base.rb:971` vs its reopening at `test/test_helper.rb:37`). `Sinatra` has more than 20 candidates. A method defined in one reopening is invisible to calls from another reopening **in the same file** (`prove_bare_receivers` keys by container index, `ruby.rs:667-700`). | | **Rust** (rust-ripgrep) | 261/395 type names (66%) refuse only because of their own `impl`. | | **TS augmentation** | `declare module "zod/v4" { interface ZodType {…} }` in `v4/classic/tests/prototypes.test.ts:4-9` becomes a standalone interface, and **12 `z.ZodType` refs in other test files bind to it**, while the real `ZodType` (`classic/schemas.ts:78`) has 0. That is a wrong bind. | | **Python** | module-qualified retries (`flask.blueprints.Blueprint`) are refused, and the hint suggests the Rust-style `Container::X`. | ## Proposal: declaration groups (one mechanism) - The language profile declares which declarations **merge**: TS interface/class/function/const/enum/namespace and module augmentation; C# `partial` by FQN; Ruby same qualified name and kind; Rust `impl` whose self type is the type; PHP and Python none. - The resolver treats a group as **one candidate**. It binds to the group, picking the member by position: type position → interface/class, value position → const/function. A group is never "ambiguous with itself". - An augmentation contributes members to its target module's group and is never a standalone bind target. - `get_symbol`, `find_references` and `check_rename` return the group (primary declaration plus `parts: [...]`). - Ambiguity hints only suggest a retry form the language can spell, and only when the candidates' qualified names actually differ (`Dapper.SqlMapper`, `flask.blueprints.Blueprint`, `Sinatra::Base`). ## Acceptance - zod v4 `ZodString`/`ZodType` get resolved refs. - The 12 augmentation binds move to the real `ZodType`. - `SqlMapper.*` qualified calls resolve. - `get_symbol` succeeds for `Sinatra::Base`, `SqlMapper` and `Store`. - A corpus bind diff on all 9 repos, sampled for precision, plus `precision_gate` decoys for two genuinely different same-name types. - Mutation: disable grouping, and the fixtures go red. Supersedes the resolver half of #523, which stays as the narrow `get_symbol` case. --- _Filed from the per-language review on 2026-11-09: 8 agents, one per language plus one for Vue/Svelte/XAML/timeline, run against copies of the pinned corpus repos (`tests/corpus/corpus.toml`) using installed build `2dcfc615`, which differs from master only by XAML/CI commits. The top claim of every issue was spot-checked in the main session. Artifacts live under the review session's scratchpad `lang-out/<lang>/` and are available on request._
buildagent changed title from Declaration groups: TS merging, C# partial, Ruby reopening and Rust impl are rival candidates — zod v4's API has 0 resolved refs to Valid references remain unresolved across declaration groups, type relations, scopes and imports 2026-11-12 22:22:53 +01:00
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#536
No description provided.