Repository navigation
fix(rust): a module-path call links the function its module declares - #2955
Merged
Merged
Conversation
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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A Rust path into a module, like
crate::util::take(1),super::util::take(1),util::take(1)oruse crate::util::take;, now links to the item that module declares. Before, it went to the first node of that name in the module's file, methods included, so animpl Buf { fn take }written abovepub fn takecaught every such call.Root cause
resolveRustPathReferencemapped the path's module to a file and tookgetNodesInFile(file).find(n => n.name === leaf && …), withmethodamong the accepted kinds. The first same-named node won: a method of animplabove the free function, a function written inside another function, or an item of amod tests { … }above the module's own. A path into the module's own file was refused outright, and a path whose head ausebinds from the standard library (fs::readunderuse std::fs;) went to a project module of the same name.Fix
The path's item is the module's own: never a method, an impl's or trait's associated item, or an item of a function body; an item written at the file's top level comes before one in a module the file declares inline, which the path reaches only through the module's re-export (
#[cfg(unix)] mod imp { … }+pub use imp::*;). A head ausebinds from the standard library leaves the path to it, and a path into the module's own file resolves.From the contributor, and what we changed
From @danusha2345's PR #2308: the new candidate selection in
resolveRustPathReference(no methods, lexically reachable, module-level first, inline-module fallback, same-file paths, the std-head rule) and__tests__/rust-module-path-call.test.ts.What we changed:
useparser the PR adds (rust-use-bindings.ts). Here they use what main already has: a smallrustItemPlacementhelper over main'srustUsesOf/rustBlockAt(the same testrustDeclaredItemsmakes), and main'sisBoundToOutOfRepoImportfor the std-bound head (the PR's rule, minus the source positions).asaliases, block-localuse, shadowing by locals, enum variants through ause), the new parser and its unit test, and the file-walker changes that depend on it. main'susewalk (rustUsesOf,rustWalkModules,rustModuleItems, fix(rust): a name a use brings in is the item of the module its path walks #2702/fix(rust): a name a glob use brings in is an item the globbed module passes on #2714) and scope reader (rust-scopes.ts, fix(rust): a call on a local reaches the method of the type it is declared with #2715) already derive all of that; folding those cases into them is proposed to the maintainer separately.mod testsbefore the module's item, an inline module's item reached through its re-export from its own file, andfs::readunderuse std::fs;beside a projectsrc/fs/mod.rs.Validation
__tests__/rust-module-path-call.test.ts: 8/8. On main, 7 fail:method Buf::take,Deep::get,Only::grab, noimportsedge foruse crate::util::take;, nothing for the inline-module namesake and the re-export, andfs::read→src/fs/mod.rs::read.A/B, main vs this branch, on 6 Rust repos (both arms on main 7cceba5, same kernel; after rebasing onto 2577601, express and flask were rechecked byte-identical and gleam's delta is unchanged):
fs::write(…),crate::fs::mkdir(…)move fromimpl … for ProjectIO's methods to the free functions incompiler-cli/src/fs.rs(plus 2erlang::modulefromGenerator::module); 4type_::list(…)→Type::listedges are gone (the real one istype_/prelude.rs::list, throughpub use prelude::*); 15uses of a file's own items inside its test modules linkuses), 1 more target on a shared columncrate::io::uring::open::open(…)moves fromOp::opento the freeopen; 4 links (crate::sync::watch::channelfrom watch.rs itself, test-moduleuses)use crate::{…, dir::IgnoreBuilder, …}inside dir.rs linksEvery changed site was inspected; all 162 are right. The 135 retargets were each checked to be a module-path call at the site. express and flask are byte-identical.
Kernel parity: not affected (resolution only; no extraction change).
Cost (medians of 3 interleaved index runs, both arms on main 7cceba5):
Both differences are within the runs' spread. In a CPU profile of tokio the changed functions take under 0.1 s. The standard-library check reads the file's
uses, so it is asked only when the path would otherwise link a project item; asked for every path it cost 0.3 s on tokio.Full suite on the exact commit (Windows, kernel built,
CODEGRAPH_KERNEL_EXPECT=1,--maxWorkers=4): 776 files, 9,175 passed, 0 failed, 85 skipped.Lands #2308 by @danusha2345.
🤖 Generated with Claude Code