fix(code-index): bind scoped glob and inline-mod use calls - #2294
Conversation
A glob use inside a block and a use inside an inline mod left their calls unbound while callers reported complete, and nothing carried the fact that a call site sat under an import the extractor could not model. The Rust extractor (v16) now reads an inline module's use declarations like a block's, with self:: and super:: resolved against the inline module. A call under a block or inline-module glob is rewritten into the glob's one project module and marked with a typed UnmodeledImportShapeV1 on the unresolved reference. The seal keeps a marked call that no edge binds as an unresolved caller, and callers and code_callers report it as partial with an import_unmodeled omission and a support gap naming the shape. The rows-digest and partitioned-codec pins move with the revision string alone.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a43c2fa16a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if glob_candidate && !scope.globs.is_empty() { | ||
| if let [module] = scope.globs.as_slice() { | ||
| reference.reference_name = | ||
| format!("{module}::{}", reference.reference_name); |
There was a problem hiding this comment.
Preserve module-local items ahead of inline-module globs
When an inline module contains both a glob import and a same-module item with the same name, Rust gives the local item precedence, but this branch unconditionally rewrites the call through the single glob. For example, mod inner { use crate::m::*; fn g() {} fn caller() { g(); } } calls inner::g; if crate::m::g also exists, the extractor instead emits a false call edge to that external function. Check the inline module's own item namespace before qualifying a call through its glob.
Useful? React with 👍 / 👎.
| "crate", "self", "super", "Self", "std", "core", "alloc", "Some", "None", "Ok", "Err", "Box", | ||
| "Vec", "String", "Option", "Result", "Default", "drop", "bool", "char", "str", "u8", "u16", |
There was a problem hiding this comment.
Allow project globs to supply prelude-shaped names
A project module can legally export names from this exemption list, and a scoped glob can resolve them ahead of the prelude; for example, mod m { pub fn drop() {} } followed by use crate::m::*; drop(); calls m::drop. Exempting drop leaves the reference bare, after which the cross-file blocklist suppresses it and no unmodeled_import gap is recorded, so callers can report a complete result while omitting the real edge. These names need project-glob resolution rather than a fixed exemption.
Useful? React with 👍 / 👎.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
What was wrong
After #2272, a named
useinside a function body binds. Two shapes from #2270 still dropped their calls whiletracedecay_callersreportedcomplete:useinside a block:fn glob_caller() { use crate::m::*; g(); }.useinside an inline module:mod inner { use crate::m::h; pub fn inline_caller() { h(); } }.Import rows are file-scoped. The extractor only modeled top-level uses and named block uses, so these calls reached the seal as bare names with no cross-file authority. There was also no channel from extraction to caller coverage that said "this call site is under an import I could not model". The only gap feed,
unresolved_call_sources, carried receiver calls and TypeScript imports.What changed
UnresolvedRefandCodeIndexUnresolvedReferenceV1gainunmodeled_import: Option<UnmodeledImportShapeV1>(block_glob|inline_module_glob). The field is skipped whenNone, so existing rows serialize to the same bytes.unresolved_call_limitations) keeps a marked call as an unresolved caller unless aCallsedge binds its exact callee token.CodeGraphInteractiveReader::unresolved_caller_gapsreturnsUnresolvedCallerGapsV1 { exact_target_unavailable, unmodeled_imports }, andhas_unresolved_callersis!gaps.is_empty().tracedecay_callers(application symbol graph) addsPrimitiveSupportGap::import_unmodeled(shape)→ omissionimport_unmodeled. This reuses the fix(code-index): resolve cfg variants and item-list macro bodies #2256 mechanism, so the page ispartialwith support gapimport_unmodeled: <shape>. The typedcode_callerspath addsOmissionReason::ImportUnmodeledbeside the existingUnsupported.modbody'susedeclarations form a scope for that module's functions.self::/super::resolve against the inline module (file_level_use_path), sosuper::m::kfrom one level down becomesself::m::k. Block uses inside inline-module functions get the same treatment.g→crate::m::g), marked with the glob's shape. When it binds, it is a normalNameResolvedcaller and no gap is reported. When the module does not define the name, it stays a disclosed gap. Rust would then fall through to outer scopes, which the index does not model.std::collections::*), macros, path roots, and prelude or primitive heads are left alone.extractor.rust.v15→v16. The rows-digest pin (extract.rs) and the partitioned-codec state and segment pins moved. Their fixtures contain no block, inline-module, or globuse, so only the revision string changed their bytes.OmissionReason::ImportUnmodeledis regenerated into the SDKoperations.ts.contracts:checkis green.Fail before / pass after
Run with only
rust_extractor.rsrestored toorigin/master. The new plumbing stayed, so the tests compile.code-extraction rust::inline_module_uses_qualify_calls_against_the_inline_module: master left[("h", None), ("k", None), ("local", None), ("d", None)]; branch right[("crate::m::h", None), ("self::m::k", None), ("local", None), ("d", None)].code-extraction rust::glob_uses_qualify_and_mark_the_calls_they_could_supply: master left[("g", None), ...]; branch[("crate::m::g", Some(BlockGlob)), ("crate::n::named", None), ("Some", None), ("println", None)]. The test also covers two globs (marked, bare), an external glob (untouched), and an inline-module glob (InlineModuleGlob).code_index_suite import_evidence::rust_calls_bind_through_block_globs_and_inline_module_uses: master left[]; branch["…lib.rs::glob_caller", "…lib.rs::globbed::inline_glob_caller"]form::g, and["…lib.rs::inner::inline_caller"]form::h. A glob miss yieldsunresolved_caller_gaps == {exact_target_unavailable: false, unmodeled_imports: {BlockGlob}}, whilem::gyields no gaps.mcp_suite callers_behavior_test::tracedecay_callers_binds_scoped_uses_and_discloses_an_unmodeled_globdrives the issue's fixture through MCPtracedecay_callers.g→glob_caller,h→inline_caller, bothcomplete.only_in_nunder a glob miss →partial, omission{"domain":"graph","count":1,"reason":"import_unmodeled"}, support gapimport_unmodeled: block_glob. On master,gandhanswercompletewith no callers (the fix(code-index): calls through block glob or inline-mod use are dropped as complete #2270 reproduction).Runtime journey (debug CLI from this branch)
The issue's fixture plus a glob miss. Isolated profile, one daemon under
systemd-run --user --scope -p MemoryMax=6G -p MemorySwapMax=1G,tracedecay tool tracedecay_callersat depth 1:The #2270 report on master showed
completeness complete omissions [] []form::gandm::h.Local verification
tracedecay-code-extraction: lib 39, extract_alloc 7, main 599tracedecay-code-index: lib 259, code_index_suite 172, resident_accounting 2 (after the last rebase over feat(mcp): rank context related symbols before the cut #2292)tracedecay-code-index-runtime: lib 516 (2 ignored), plus 3tracedecay-query: lib 264 (1 ignored), canonical_execution_equivalence 5, retrieval_contract_spine 2, search_quality_suite 71, plus 3tracedecay-contracts420,tracedecay-domain222tracedecay-application: 466 passed, 11 failed. These are the same 11 that fail on master since perf(graph-db): store each code edge as one relation row #2277 (test(application): symbol-graph lib tests red after single-row code edges (#2277) #2279).mcp_suitecallers/callees subset: 16 passed, 4 failed. The 4 are the perf(graph-db): store each code edge as one relation row #2277 read-cost pins (test(application): symbol-graph lib tests red after single-row code edges (#2277) #2279), with identical left/right values on master. After the rebase over feat(mcp): rank context related symbols before the cut #2292,callers_behavior_testpassed 4/4.cargo clippy --all-targets -D warningsis clean on domain, code-extraction, code-index, code-index-runtime, application, contracts, query, graph-query, and tracedecay, both with and withouttest-transport/test-helpers.cargo fmt --all -- --checkis clean.contracts:checkis up to date, and TS SDK 32 tests plustypecheckpass.Fixes #2270