Skip to content

fix(code-index): resolve calls through block-scoped use - #2272

Merged
ScriptedAlchemy merged 1 commit into
masterfrom
fleet/fnbody-use
Sep 26, 2026
Merged

ScriptedAlchemy merged 1 commit into
masterfrom
fleet/fnbody-use

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Fixes #2253

Cause

Rust import rows are file-scoped. The extractor records bindings only for a use whose parent is the source file. A use declared inside a function body or a nested block therefore produced no binding, so a call through it, such as context::with_current(..) after use crate::runtime::{context, task};, never bound. When the name also had a module-scope import, the call bound to that import instead, which the block use shadows. Nothing marked the site as unresolved, so callers stayed complete.

Change

In tracedecay-code-extraction, the Rust extractor collects, for each block in a function, the names its use declarations bind. These are the same extract_use_bindings rows a module-scope use produces, canonicalized the same way. Each call reference from that function whose site lies inside the block, and whose head segment the innermost such block binds, is rewritten to the declared path. The existing qualified resolver then binds crate::runtime::context::with_current exactly like a module-scope import, through re-exports.

  • Inner blocks shadow outer ones, and a block use shadows a module import.
  • A path that may stay inside this file keeps its bare name, so same-file binding is unchanged. That covers self::… and a crate root's crate::… unless they go through a root mod name; file module, and super::… from inside an inline module. The cross-file resolver never binds into the referencing file.
  • Rust extractor revision v14 becomes v15, so retained generations re-extract.
  • The pinned digests move with the revision only. None of the pinned fixtures contains a block-scoped use: extract.rs RUST_SOURCE, and the partitioned codec fixture src/alpha.rs, beta.rs, unresolved.rs. Segment sizes are unchanged except one deflate byte (1262 to 1263).

Still unmodeled: a glob use inside a block, and a use inside an inline mod. They are filed as #2270 with a reproduction. A typed omission for them needs a per-site gap channel from extraction to caller coverage that does not exist today.

Fail before / pass after

tracedecay-code-index code_index_suite::import_evidence::rust_calls_bind_through_a_use_declared_in_the_calling_block. The literal fixture includes pub fn f() { use crate::m::g; g(); } beside a module-scope use crate::n::g;, plus the tokio shapes (use crate::runtime::{context, task}; at the top of a function, and use crate::runtime::context; inside a #[cfg(feature = "rt")] { .. } block). On master:

assertion `left == right` failed
  left: []
 right: ["crates/app/src/lib.rs::f"]

On the branch it passes. It asserts:

  • m::g ← [f]
  • n::g ← [module_scope], which is the shadowing check
  • with_current ← [spawn_inner, timer]
  • task::schedule ← [spawn_inner]
  • inner::x ← [same_file], for a block use self::inner::x

Journey (debug CLI from this branch, fresh isolated profile, one daemon under systemd-run --user --scope -p MemoryMax=6G -p MemorySwapMax=1G)

tokio tokio-1.53.1 (75fef53), tracedecay tool tracedecay_callers on tokio/src/runtime/context/current.rs::with_current, depth 1.

Before (pre-change binary, same profile shape):

completeness complete omissions [] total 5 next False

After:

completeness complete omissions [] total 7 next False
   tokio/src/runtime/handle.rs::Handle::try_current tokio/src/runtime/handle.rs:155
   tokio/src/runtime/scheduler/mod.rs::Handle::current tokio/src/runtime/scheduler/mod.rs:92
   tokio/src/runtime/scheduler/current_thread/mod.rs::CurrentThread::shutdown tokio/src/runtime/scheduler/current_thread/mod.rs:257
   tokio/src/task/spawn.rs::spawn_inner tokio/src/task/spawn.rs:188
   tokio/src/task/coop/mod.rs::inc_budget_forced_yield_count tokio/src/task/coop/mod.rs:384
   tokio/src/task/local.rs::spawn_local_inner tokio/src/task/local.rs:410
   tokio/src/runtime/time_alt/timer.rs::with_current_temp_local_context tokio/src/runtime/time_alt/timer.rs:60

rg 'context::with_current\(' tokio/src finds the same 7 call sites.

Suites

Counts after the rebase onto beefa817c9:

  • tracedecay-code-extraction: lib 39, extract_alloc 7, main 597
  • tracedecay-code-index: lib 258, code_index_suite 170, resident_accounting 1 (+2, +3 in the other targets)
  • tracedecay-code-index-runtime lib: 516 (2 ignored), run with --test-threads=4. Two unthrottled runs under host load average 50–65 had deadline-only failures (Elapsed, "seat never arrived"). The reconcile module alone passed 148/148.
  • mcp_suite (test-transport, callers|callees): 19
  • cargo clippy -p tracedecay-code-extraction -p tracedecay-code-index -p tracedecay-code-index-runtime --all-targets -- -D warnings: clean. No tracedecay crate sources changed, so test-transport does not change what is linted here.
  • cargo fmt --all -- --check: clean. No contract or schema shape changed.

@changeset-bot

changeset-bot Bot commented Sep 26, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 1274b53

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ScriptedAlchemy
ScriptedAlchemy merged commit 56839b9 into master Sep 26, 2026
1 check passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-26T22:04:24.839101Z 1274b53 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1274b537c5

ℹ️ 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".

Comment on lines +2457 to +2459
let path = format!("{}::{imported}", import.module_specifier);
let leaves_file = Self::block_use_path_leaves_file(state, function, &path);
paths.insert(local, leaves_file.then_some(path));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retain the target of same-file aliased imports

When a block import aliases an item defined in the same file, such as use self::inner::x as y, then_some(path) stores None and the call remains named y. The same-file resolver therefore cannot select inner::x; worse, if another same-file function is actually named y, it emits a false call edge to that function even though the block import shadows it. Preserve enough path information to resolve aliases rather than treating every same-file import as safely bindable by its local name.

Useful? React with 👍 / 👎.

if !file_relative {
return true;
}
let Some(module) = segments.next() else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Traverse nested modules before classifying imports as same-file

For a valid path such as crate::outer::disk::g where outer is an inline module but disk is declared as mod disk;, this examines only the first segment (outer) and concludes that the target remains in the current file. The call is consequently left bare and cannot resolve to outer/disk.rs::g. Classification needs to walk the complete module path, since a later segment can cross a file boundary.

Useful? React with 👍 / 👎.

{
let path = format!("{}::{imported}", import.module_specifier);
let leaves_file = Self::block_use_path_leaves_file(state, function, &path);
paths.insert(local, leaves_file.then_some(path));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not overwrite cfg-selected block imports

Valid platform-selected imports can bind the same local name in one block, for example #[cfg(unix)] use crate::unix::g; followed by #[cfg(windows)] use crate::windows::g;. This unconditional map insertion discards the first binding without inspecting either cfg, so every call is attributed to whichever declaration appears last, even when that declaration is inactive for the indexed platform. Retain all conditional alternatives or abstain instead of producing a deterministic false caller edge.

Useful? React with 👍 / 👎.

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.

fix(code-index): calls through function-local use are dropped as complete

1 participant