Skip to content

fix(rust): resolve bare calls through exact use bindings - #2308

Closed
danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:codex/rust-exact-use-bindings
Closed

danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:codex/rust-exact-use-bindings

Conversation

@danusha2345

@danusha2345 danusha2345 commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A bare Rust call never looked at the use that brought its name in. With

// src/util.rs
pub struct Buf;
impl Buf { pub fn take(&self) {} }
pub fn take() {}

// src/a.rs
pub fn take() {}
pub fn consume() {}

// src/caller.rs
use crate::util::take;
pub fn imported() { take(); }

// src/alias.rs
use crate::util::take as consume;
pub fn aliased() { consume(); }

imported() was linked to a::take, and aliased() to the unrelated a::consume, because name matching only sees the local name. The same happened with nested groups (use crate::{util::{take as selected}, a::take as other}), self:: / super:: imports and use inside a function body.

A call written as a module path had a related problem (this is #2259): crate::util::take(3), super::util::take(3) or util::take(3) mapped the module prefix to the right file, then took the first node named take in it. Methods were an accepted kind, so impl Buf { fn take } above the free pub fn take caught every such call. Paths that end in the caller's own file (self::take(), crate::this_module::f() from inside it) and super::Item from a submodule file were not resolved by the module walk at all.

Fix

  • A new per-file view of named use bindings (rust-use-bindings.ts), parsed once per file per resolution pass and dropped with the other import-resolver memos. It knows which use is nearest to a source position (block and module scopes, nested groups, as, self, raw identifiers), and hides comments, strings and char literals. Globs bind no name and are skipped.
  • resolveRustImportedCall runs before name matching for a bare Rust call. When a use binds the name, the call goes to the item that path names: the module's free function, a crate-root item, an enum variant through its own enum, or a tuple-struct constructor. A parameter, a let already in effect, or a nested fn of the same name still shadows the import.
  • A module path names the module's own item: never a method, never an associated type or const of an impl or trait body, never a fn-local item. A module-level item wins; with none, an item of an inline module reached through the module's re-export (#[cfg(…)] mod imp { … } plus pub use imp::*;, a common platform pattern). Two module-level items of one name are #[cfg] variants, and the first one stands for both.
  • The module walk now also follows super:: to the parent module's file, ends in the caller's own file for self::, and goes through an inline module (mod support { pub mod panic; }) to a file-backed submodule, including in Cargo's standalone tests/, examples/ and benches/ files.
  • No EXTRACTION_VERSION bump: main already raised it to 28 for this release.

This PR includes #2259 (its source change and its whole test file) and supersedes it.

Rebased onto main after #2354 and #2359. Neither overlaps: #2354 adds a references edge from a written Enum::Variant path to its enum, while this PR resolves bare calls through the use that binds the name (including use crate::models::E::Good as Build; Build(1)), and #2359 only touches Axum/Actix route linking.

Deliberately left as before

A binding this resolver cannot follow keeps today's resolution, so nothing that resolves now goes unresolved:

  • another crate of the workspace named by its package (use my_core::partition::read_table;),
  • a pub use re-export in the module the path names,
  • a relative use inside an inline module (mod tests { use super::parse; }),
  • two imports of one name in one scope.

The exceptions are an alias whose path cannot be followed (use external::take as consume;) and an enum without that variant: name matching on the local name could only find an unrelated item, so those calls stay unresolved. Glob imports (use super::*) and #[path] modules are left alone; #1892 covers that case and should compose with this.

Tests

  • __tests__/rust-use-bindings.test.ts (17 cases): the binding parser by itself. Groups, aliases, self, raw identifiers, nearest-scope lookup, inline modules, comments and string kinds, let shadowing, UTF-16 columns and CRLF, and inline-module items told apart from impl/trait/macro bodies. It also covers impl Trait + use<'a> (precise capturing), which must not read as an import.
  • __tests__/rust-use-call.test.ts (15 cases) and __tests__/rust-module-path-call.test.ts (5 cases, fix(rust): a module path names the module's free function, not a method #2259's 4 plus an associated-type decoy). Each one indexes a real temporary Cargo project into SQLite: imported vs same-named decoys, aliases, groups, self/super, block-local imports, shadowing, enum variants, crate-root and same-file imports, a workspace crate by package name, mod tests { use super::…; }, #[cfg] variants, an inline module's re-export, Unicode columns, and refresh after edit + sync. 17 of the 20 call-level cases fail on current main.
  • Every rust-* suite (including main's rust-enum-variant-reference and rust-route-handler-line), resolution.test.ts and extraction.test.ts (13 files, 1023 tests) pass with the native kernel and with CODEGRAPH_KERNEL=0.

Real repositories (Rust edges, main → this branch)

The tokio and GNSS numbers were measured before the rebase.

  • tokio 1.53.1 (555 files): 14 new calls, all correct. That is 10 super::unowned(…) reaching the test helper through its inline module's re-export, two crate::sync::watch::channel(…) from inside watch.rs, and two support::mpsc_stream::… from an integration test. 4 SendError(value) constructions in mpsc moved from broadcast::SendError to the mpsc::error::SendError their use names. There are also 42 new use import edges, all checked, and 0 lost edges.
  • A GNSS simulator crate (161 files): 13 crate::gnsstime::utc_to_gps_time(…)-style calls moved from the same-named GnssTimeConverter methods to the module's free functions, plus 7 import edges. All correct.
  • Two other application projects (33 and 71 files), measured again after the rebase onto current main: every call edge is identical (817 and 3633), with 8 new use super::… import edges in the larger one, all correct.
  • Node counts are unchanged everywhere. Indexing time on tokio is within noise. Peak RSS is about 10% higher (≈1.0 GB vs ≈0.9 GB), because each resolver worker keeps the per-file binding view for the pass. The view is about 2× the Rust source it covers.

🤖 Generated with Claude Code

@danusha2345
danusha2345 force-pushed the codex/rust-exact-use-bindings branch from fa3f3ac to 826c811 Compare October 3, 2026 09:09
@danusha2345
danusha2345 force-pushed the codex/rust-exact-use-bindings branch 4 times, most recently from 4bbca0c to c99a26b Compare October 10, 2026 11:29
@danusha2345
danusha2345 force-pushed the codex/rust-exact-use-bindings branch from c99a26b to b23125d Compare October 15, 2026 17:38
@danusha2345
danusha2345 force-pushed the codex/rust-exact-use-bindings branch from b23125d to 3365c14 Compare October 19, 2026 12:18
@danusha2345
danusha2345 force-pushed the codex/rust-exact-use-bindings branch from 3365c14 to c928581 Compare October 20, 2026 10:07
@danusha2345
danusha2345 force-pushed the codex/rust-exact-use-bindings branch from c928581 to c7688f4 Compare October 21, 2026 14:12
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 21, 2026
…ymchenry#1921, colbymchenry#1935, colbymchenry#1955, colbymchenry#1957, colbymchenry#1965, colbymchenry#2575, colbymchenry#2595, colbymchenry#2597, colbymchenry#2603–colbymchenry#2608, colbymchenry#2611 landed upstream)

Conflicts: files owned by the foreign PRs now in main (colbymchenry#1921's Python import
resolver and tests, colbymchenry#1935's class-strategy method) take main's version; our
colbymchenry#1954/colbymchenry#1942/colbymchenry#1945/colbymchenry#1939/colbymchenry#1715/colbymchenry#1951/colbymchenry#2308 hunks sit beside main's new Rust and
Python blocks as in the rebased PR branches; java.rs keeps the integration's
`constant_declaration` fields under main's initializer walk; three CHANGELOG
lines the merged PRs had already contributed are deduplicated, and the method
blocks the merge duplicated in queries.ts and extraction/index.ts (the
resolution-edge queries and the sync resurrection helpers, present once on
main) are kept once.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 21, 2026
…olbymchenry#2308 follow-up for colbymchenry#2711's test)

The same change went into the rebased colbymchenry#2308 branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@danusha2345
danusha2345 force-pushed the codex/rust-exact-use-bindings branch 3 times, most recently from 1d2a8e8 to 5ca190d Compare October 22, 2026 10:43
A bare call bound by a `use` (`use crate::util::take; take(3)`, also
through `as` aliases, nested groups, `self`/`super`/`crate` paths and
block-local imports) now resolves to the item that path names before
name matching runs. A parameter, `let` or nested fn of that name still
shadows the import. A binding the resolver cannot follow (another
workspace crate, a `pub use` re-export, a relative path inside an
inline module) keeps the previous resolution.

A module path (`crate::util::take(3)`) names the module's own item:
never a method, an associated item of an impl or trait body, or a
fn-local item. A module-level item wins over an inline module's item
reached through a re-export. Includes and supersedes colbymchenry#2259.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@danusha2345
danusha2345 force-pushed the codex/rust-exact-use-bindings branch from 5ca190d to 9b6f7ef Compare October 23, 2026 09:02
colbymchenry pushed a commit that referenced this pull request Oct 28, 2026
A Rust path into a module, `crate::util::take(1)`, `super::util::take(1)`,
`util::take(1)` or a `use crate::util::take;`, went to the first node of
that name in the module's file, methods included, so an `impl Buf { fn
take }` above `pub fn take` caught every such call. A function written
inside another function, or in a `mod tests { ... }` above the module's
own item, could take it too, a path into the module's own file linked
nothing, and a path through a module a `use` takes from the standard
library (`fs::read` under `use std::fs;`) went to the project's own `fs`
module.

The path's item is now the module's own: never a method, an impl's or a
trait's associated item, or an item of a function body; one written at
the file's top level before one in a module the file declares inline,
which the path reaches through the module's re-export (`#[cfg(unix)] mod
imp { ... }` + `pub use imp::*;`). A path whose head a `use` binds from
the standard library is left to it (main's isBoundToOutOfRepoImport rule
for bare names), and a path into the module's own file resolves.

Validation:
- rust-module-path-call.test.ts: 8/8; on main 7 fail (method Buf::take,
  Deep::get, Only::grab, no import edge, the inline-module namesake, the
  re-export from its own file, std `fs::read` -> src/fs/mod.rs).
- A/B on main 7cceba5, 6 Rust repos: 162 changed sites, all inspected and
  right. gleam: 133 calls like `fs::write(...)` / `crate::fs::mkdir(...)` move
  from `impl ... for ProjectIO`'s methods to compiler-cli's free functions,
  4 wrong `type_::list(...)` -> `Type::list` edges are gone, 15 `use`s link;
  tokio: `crate::io::uring::open::open(...)` moves from `Op::open` to the free
  `open`, +4; dioxus +7, 1 more; ripgrep +1; clap and serde unchanged.
  express and flask byte-identical.
- Cost (medians of 3 interleaved runs): tokio CPU 36.6 -> 36.0 s, gleam
  34.3 -> 35.2 s, both within the runs' spread; peak RSS unchanged. In a
  CPU profile of tokio the changed functions take under 0.1 s. The
  standard-library check reads the file's `use`s, so it runs only when the
  path would otherwise link a project item (asked for every path, it cost
  0.3 s on tokio).

This is the separable part of PR #2308. Its bare-call bindings (`as`
aliases, block-local `use`, shadowing by locals, enum variants through a
`use`) and the second `use` parser it adds are left out: main's `use` walk
(rustUsesOf, rustWalkModules, rustModuleItems) and scope reader
(rust-scopes.ts) cover the same ground, and folding those cases into them
is proposed separately.

Original PR #2308 by @danusha2345.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@colbymchenry

Copy link
Copy Markdown
Owner

Thank you for this careful work, @danusha2345! The module-path part has landed in #2955 with you as the commit author: calls like crate::util::take() and use crate::util::take; now reach the module's own function instead of a same-named method above it, which fixes 133 calls on gleam alone. We left out the bare-call bindings and the new use parser for now: since you opened this, main has grown its own use walk and scope reader (#2702, #2714, #2715), and two parsers of the same thing would drift. We'll fold aliases, block-local use, shadowing and variants into those in a follow-up, with your tests and credit in the changelog, and close this PR when that lands.

danusha2345 pushed a commit to danusha2345/codegraph that referenced this pull request Oct 28, 2026
…mchenry#2972; 10 landed PRs yield to main; EXTRACTION_VERSION 56)

Main landed ten more of our PRs as our own commits with the maintainer's additions
(colbymchenry#2923 ← colbymchenry#1936, colbymchenry#2924 ← colbymchenry#1951, colbymchenry#2925 ← colbymchenry#1939, colbymchenry#2930 ← colbymchenry#2921, colbymchenry#2940 ← colbymchenry#1736,
colbymchenry#2942 ← colbymchenry#1947, colbymchenry#2950 ← colbymchenry#1952, colbymchenry#2961 ← colbymchenry#1954, colbymchenry#2965 ← colbymchenry#1942, colbymchenry#2972 ← colbymchenry#1933) and the
module-path half of colbymchenry#2308 (colbymchenry#2955). Where the integration carried our version, main's
wins: the enclosing-scope member pick and its tests, the Go and Java receiver typers,
the delegating-call self-loop rule, the duplicated-module copy confidence, the C #if
collapse, the module-path resolution of Rust paths, the trail/path hardening in
utils.ts. Kept beside main: colbymchenry#1875's Verilog hooks and status fields, colbymchenry#2839's
implementations command, colbymchenry#1923, colbymchenry#2922, colbymchenry#2258, colbymchenry#1761, colbymchenry#1583, the bare-call half of
colbymchenry#2308 (rust-use-bindings.ts). CHANGELOG is main's plus the open PRs' bullets.

EXTRACTION_VERSION 56: the C #if group handling (colbymchenry#2940) and the kernel change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
colbymchenry added a commit that referenced this pull request Oct 29, 2026
…he crate's modules

A bare Rust name took the file's `use`s all at once: a `use` inside one
function (`fn first() { use crate::util::take; take() }`) bound `take` for
every other function too, and for the file's own `fn take`; a renamed one
(`use crate::util::take as consume; consume()`) linked nothing, since no
item carries the alias; a parameter or closure of the function's name
(`fn run(take: fn()) { take() }`, `let take = || {};`) linked to the
imported function; and `use crate::models::E::Good; Good(1)` took any
`E::Good`. Paths through modules (`resolveRustPathReference`) walked the
directories instead of the modules: `super::helper()` and `crate::config()`
with no module between linked nothing, `super` inside an inline
`mod tests { }` went to the parent directory's module, and an item a module
passes on (`pub use`, a glob) or another workspace crate's was not found.

The fix keeps one `use` reader, rustUsesOf:
- each `use` keeps the block it is written in (a function's body or a
  block; an inline `mod` and a macro's braces, `cfg_if!`'s nested ones
  included, are the module's), and std `use`s are kept by position;
- a reference reads the binding of its innermost scope (rustVisibleUse);
  isRustNameInScope uses it, so a block's `use` shadows the file's own item
  and a variant `use` names its enum's variant (rustUsePathItems' enum tail);
- matchRustUseBoundName, ahead of the pre-filter, resolves a rename to the
  item its path names, and a call to a name a local binds (rust-scopes'
  bindings, nearer than any `use`) to nothing, unless the local is a `let`
  holding a function by its path (serde's `let test = test::<i8>;`);
- resolveRustPathReference is rustWalkModules + rustModuleItems from the
  module the path is written in (rustPathTarget), with today's crate-root
  fallback; resolveRustModuleFile, collectRustUseBindings and
  rustItemPlacement are gone, and isBoundToOutOfRepoImport reads
  rustUsesOf's std `use`s;
- `use`s are read with strings blanked (blankRustCode), so a string's
  "use crate::x;" binds nothing;
- the crate graph reads a package's name by its section (axum-core writes
  `categories = [..]` first) and a `[lib]`/`[[bin]]` path of its own
  (ripgrep's `crates/core/main.rs`), which `crate::` then starts from; a
  file no manifest covers starts it from the nearest lib.rs or main.rs
  above it, as the directory walk did.

Validation:
- rust-use-call.test.ts (PR 2308's 15 cases), rust-use-scopes.test.ts,
  rust-crate-roots.test.ts and 5 new rust-module-path-call cases: on main's
  source 18 of 32 fail; all pass. Every rust-* suite passes.
- A/B on main 01e0ac3, 7 repos, the same sites on the kernel and on wasm:
  tokio +2,434 sites (1,354 calls, 1,053 imports), 159 removed (152 locals
  called or passed as values, 6 `Result`s std's, 1 import through a path
  attribute module); ripgrep +248 / -149; axum +710 / -60; clap +730 / -112;
  serde +155 / -207 (192 calls to a closure named `test`); dioxus
  +2,950 / -272; gleam +1,573 / -359. Groups and samples inspected, every
  changed site's crate checked against the manifests' dependencies;
  express and flask byte-identical.
- Cost (kernel, medians of 3 interleaved): tokio CPU 34.5 -> 33.0 s, peak
  RSS 991 -> 1,007 MB; gleam 32.5 -> 30.6 s, 1,022 -> 1,012 MB; dioxus
  45.4 -> 45.4 s, 1,101 -> 1,107 MB.
- Original PR #2308 by @danusha2345; its module-path part landed as PR #2955.

Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry added a commit that referenced this pull request Oct 29, 2026
…he crate's modules

A bare Rust name took the file's `use`s all at once: a `use` inside one
function (`fn first() { use crate::util::take; take() }`) bound `take` for
every other function too, and for the file's own `fn take`; a renamed one
(`use crate::util::take as consume; consume()`) linked nothing, since no
item carries the alias; a parameter or closure of the function's name
(`fn run(take: fn()) { take() }`, `let take = || {};`) linked to the
imported function; and `use crate::models::E::Good; Good(1)` took any
`E::Good`. Paths through modules (`resolveRustPathReference`) walked the
directories instead of the modules: `super::helper()` and `crate::config()`
with no module between linked nothing, `super` inside an inline
`mod tests { }` went to the parent directory's module, and an item a module
passes on (`pub use`, a glob) or another workspace crate's was not found.

The fix keeps one `use` reader, rustUsesOf:
- each `use` keeps the block it is written in (a function's body or a
  block; an inline `mod` and a macro's braces, `cfg_if!`'s nested ones
  included, are the module's), and std `use`s are kept by position;
- a reference reads the binding of its innermost scope (rustVisibleUse);
  isRustNameInScope uses it, so a block's `use` shadows the file's own item
  and a variant `use` names its enum's variant (rustUsePathItems' enum tail);
- matchRustUseBoundName, ahead of the pre-filter, resolves a rename to the
  item its path names, and a call to a name a local binds (rust-scopes'
  bindings, nearer than any `use`) to nothing, unless the local is a `let`
  holding a function by its path (serde's `let test = test::<i8>;`);
- resolveRustPathReference is rustWalkModules + rustModuleItems from the
  module the path is written in (rustPathTarget), with today's crate-root
  fallback; resolveRustModuleFile, collectRustUseBindings and
  rustItemPlacement are gone, and isBoundToOutOfRepoImport reads
  rustUsesOf's std `use`s;
- `use`s are read with strings blanked (blankRustCode), so a string's
  "use crate::x;" binds nothing;
- the crate graph reads a package's name by its section (axum-core writes
  `categories = [..]` first) and a `[lib]`/`[[bin]]` path of its own
  (ripgrep's `crates/core/main.rs`), which `crate::` then starts from; a
  file no manifest covers starts it from the nearest lib.rs or main.rs
  above it, as the directory walk did.

Validation:
- rust-use-call.test.ts (PR 2308's 15 cases), rust-use-scopes.test.ts,
  rust-crate-roots.test.ts and 5 new rust-module-path-call cases: on main's
  source 18 of 32 fail; all pass. Every rust-* suite passes.
- A/B on main 01e0ac3, 7 repos, the same sites on the kernel and on wasm:
  tokio +2,434 sites (1,354 calls, 1,053 imports), 159 removed (152 locals
  called or passed as values, 6 `Result`s std's, 1 import through a path
  attribute module); ripgrep +248 / -149; axum +710 / -60; clap +730 / -112;
  serde +155 / -207 (192 calls to a closure named `test`); dioxus
  +2,950 / -272; gleam +1,573 / -359. Groups and samples inspected, every
  changed site's crate checked against the manifests' dependencies;
  express and flask byte-identical.
- Cost (kernel, medians of 3 interleaved): tokio CPU 34.5 -> 33.0 s, peak
  RSS 991 -> 1,007 MB; gleam 32.5 -> 30.6 s, 1,022 -> 1,012 MB; dioxus
  45.4 -> 45.4 s, 1,101 -> 1,107 MB.
- Original PR #2308 by @danusha2345; its module-path part landed as PR #2955.

Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry added a commit that referenced this pull request Oct 29, 2026
…he crate's modules

A bare Rust name took the file's `use`s all at once: a `use` inside one
function (`fn first() { use crate::util::take; take() }`) bound `take` for
every other function too, and for the file's own `fn take`; a renamed one
(`use crate::util::take as consume; consume()`) linked nothing, since no
item carries the alias; a parameter or closure of the function's name
(`fn run(take: fn()) { take() }`, `let take = || {};`) linked to the
imported function; and `use crate::models::E::Good; Good(1)` took any
`E::Good`. Paths through modules (`resolveRustPathReference`) walked the
directories instead of the modules: `super::helper()` and `crate::config()`
with no module between linked nothing, `super` inside an inline
`mod tests { }` went to the parent directory's module, and an item a module
passes on (`pub use`, a glob) or another workspace crate's was not found.

The fix keeps one `use` reader, rustUsesOf:
- each `use` keeps the block it is written in (a function's body or a
  block; an inline `mod` and a macro's braces, `cfg_if!`'s nested ones
  included, are the module's), and std `use`s are kept by position;
- a reference reads the binding of its innermost scope (rustVisibleUse);
  isRustNameInScope uses it, so a block's `use` shadows the file's own item
  and a variant `use` names its enum's variant (rustUsePathItems' enum tail);
- matchRustUseBoundName, ahead of the pre-filter, resolves a rename to the
  item its path names, and a call to a name a local binds (rust-scopes'
  bindings, nearer than any `use`) to nothing, unless the local is a `let`
  holding a function by its path (serde's `let test = test::<i8>;`);
- resolveRustPathReference is rustWalkModules + rustModuleItems from the
  module the path is written in (rustPathTarget), with today's crate-root
  fallback; resolveRustModuleFile, collectRustUseBindings and
  rustItemPlacement are gone, and isBoundToOutOfRepoImport reads
  rustUsesOf's std `use`s;
- `use`s are read with strings blanked (blankRustCode), so a string's
  "use crate::x;" binds nothing;
- the crate graph reads a package's name by its section (axum-core writes
  `categories = [..]` first) and a `[lib]`/`[[bin]]` path of its own
  (ripgrep's `crates/core/main.rs`), which `crate::` then starts from; a
  file no manifest covers starts it from the nearest lib.rs or main.rs
  above it, as the directory walk did.

Validation:
- rust-use-call.test.ts (PR 2308's 15 cases), rust-use-scopes.test.ts,
  rust-crate-roots.test.ts and 5 new rust-module-path-call cases: on main's
  source 18 of 32 fail; all pass. Every rust-* suite passes.
- A/B on main 01e0ac3, 7 repos, the same sites on the kernel and on wasm:
  tokio +2,434 sites (1,354 calls, 1,053 imports), 159 removed (152 locals
  called or passed as values, 6 `Result`s std's, 1 import through a path
  attribute module); ripgrep +248 / -149; axum +710 / -60; clap +730 / -112;
  serde +155 / -207 (192 calls to a closure named `test`); dioxus
  +2,950 / -272; gleam +1,573 / -359. Groups and samples inspected, every
  changed site's crate checked against the manifests' dependencies;
  express and flask byte-identical.
- Cost (kernel, medians of 3 interleaved): tokio CPU 34.5 -> 33.0 s, peak
  RSS 991 -> 1,007 MB; gleam 32.5 -> 30.6 s, 1,022 -> 1,012 MB; dioxus
  45.4 -> 45.4 s, 1,101 -> 1,107 MB.
- Original PR #2308 by @danusha2345; its module-path part landed as PR #2955.

Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry added a commit that referenced this pull request Oct 29, 2026
…he crate's modules (#3014)

A bare Rust name took the file's `use`s all at once: a `use` inside one
function (`fn first() { use crate::util::take; take() }`) bound `take` for
every other function too, and for the file's own `fn take`; a renamed one
(`use crate::util::take as consume; consume()`) linked nothing, since no
item carries the alias; a parameter or closure of the function's name
(`fn run(take: fn()) { take() }`, `let take = || {};`) linked to the
imported function; and `use crate::models::E::Good; Good(1)` took any
`E::Good`. Paths through modules (`resolveRustPathReference`) walked the
directories instead of the modules: `super::helper()` and `crate::config()`
with no module between linked nothing, `super` inside an inline
`mod tests { }` went to the parent directory's module, and an item a module
passes on (`pub use`, a glob) or another workspace crate's was not found.

The fix keeps one `use` reader, rustUsesOf:
- each `use` keeps the block it is written in (a function's body or a
  block; an inline `mod` and a macro's braces, `cfg_if!`'s nested ones
  included, are the module's), and std `use`s are kept by position;
- a reference reads the binding of its innermost scope (rustVisibleUse);
  isRustNameInScope uses it, so a block's `use` shadows the file's own item
  and a variant `use` names its enum's variant (rustUsePathItems' enum tail);
- matchRustUseBoundName, ahead of the pre-filter, resolves a rename to the
  item its path names, and a call to a name a local binds (rust-scopes'
  bindings, nearer than any `use`) to nothing, unless the local is a `let`
  holding a function by its path (serde's `let test = test::<i8>;`);
- resolveRustPathReference is rustWalkModules + rustModuleItems from the
  module the path is written in (rustPathTarget), with today's crate-root
  fallback; resolveRustModuleFile, collectRustUseBindings and
  rustItemPlacement are gone, and isBoundToOutOfRepoImport reads
  rustUsesOf's std `use`s;
- `use`s are read with strings blanked (blankRustCode), so a string's
  "use crate::x;" binds nothing;
- the crate graph reads a package's name by its section (axum-core writes
  `categories = [..]` first) and a `[lib]`/`[[bin]]` path of its own
  (ripgrep's `crates/core/main.rs`), which `crate::` then starts from; a
  file no manifest covers starts it from the nearest lib.rs or main.rs
  above it, as the directory walk did.

Validation:
- rust-use-call.test.ts (PR 2308's 15 cases), rust-use-scopes.test.ts,
  rust-crate-roots.test.ts and 5 new rust-module-path-call cases: on main's
  source 18 of 32 fail; all pass. Every rust-* suite passes.
- A/B on main 01e0ac3, 7 repos, the same sites on the kernel and on wasm:
  tokio +2,434 sites (1,354 calls, 1,053 imports), 159 removed (152 locals
  called or passed as values, 6 `Result`s std's, 1 import through a path
  attribute module); ripgrep +248 / -149; axum +710 / -60; clap +730 / -112;
  serde +155 / -207 (192 calls to a closure named `test`); dioxus
  +2,950 / -272; gleam +1,573 / -359. Groups and samples inspected, every
  changed site's crate checked against the manifests' dependencies;
  express and flask byte-identical.
- Cost (kernel, medians of 3 interleaved): tokio CPU 34.5 -> 33.0 s, peak
  RSS 991 -> 1,007 MB; gleam 32.5 -> 30.6 s, 1,022 -> 1,012 MB; dioxus
  45.4 -> 45.4 s, 1,101 -> 1,107 MB.
- Original PR #2308 by @danusha2345; its module-path part landed as PR #2955.

Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@colbymchenry

Copy link
Copy Markdown
Owner

Thank you for this careful work, @danusha2345! The rest of your PR has landed in #3014, built on main's own use model rather than a second parser. Renamed uses like use crate::util::take as consume; consume(), block-local uses, locals that shadow an import, and enum variants through a use now resolve the way Rust scopes them. Module paths now walk the crate's modules, so super::x(), crate::x() and super inside mod tests { } land right. Your call-level tests are in, with you as co-author (your parser-level cases became call-level ones), and the module-path part landed earlier as #2955, so we're closing this one.

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.

2 participants