Repository navigation
fix(rust): a module a #[path] attribute loads is the module that declares it - #3064
Merged
Merged
Conversation
…ares it
A Rust module loaded with `#[path = "…"]` was found by its file's directory:
fast-mlsirm's `#[path = "../../../tests/unit/scaling_tests.rs"] mod tests;`
in `crates/core/src/scaling.rs` made `tests/unit/scaling_tests.rs`'s
`use super::*` walk to a `tests/unit` module that doesn't exist, so its
`predict_rating_multi(1)` linked to nothing, or to a same-named function of
another crate; its `crate::` had no crate, and the parent's private items
looked out of its reach. From the declaring side, a path through such a
module (`utils::mock_event(…)` under `#[path = "./utils.rs"] mod utils;`)
reached nothing.
The fix reads each file's `#[path]` declarations as the Rust Reference
reads the attribute (from the declaring file's directory, or inside an
inline `mod` from that module's own; a path that leaves the project, by
escapesRoot, or that a `cfg_attr` sets, names no file):
- rustSubmodule: a module the module declares once with a `#[path]` is
the file it names, whatever else loads that file too (a helper several
tests load is each test's); cfg alternatives that name a file each stay
unknown, and a path's 2015 crate-root fallback no longer reaches past
such a module to a namesake at the crate root.
- rustPathClaim: a file the regular rules don't place is the module of the
one `#[path]` declaration that loads it, if exactly one does. Placed
(rustPlacedByName) are crate roots, files with a `fn main` or
`#![no_main]` of their own (trybuild cases, fuzz targets), and files a
plain `mod name;` declares in the module owning their directory or in an
inline module of one above it (dioxus's `mod components { pub mod home; }`
in main.rs). Only for a file left over are the project's Rust files that
write `path =` read (a byte check first), once per resolver. With a
claim, rustSuperModule is the declaring module, rustFileModule owns the
file's directory (rustc reads every `#[path]` file as a mod.rs),
rustCrateRootModule is the declaring file's crate, and rustModuleWithin,
isRustItemVisibleFrom and isVisibleAcrossFiles see the file inside the
declaring module, so `use super::*` brings in its private items.
- rustCrateGraph: `[[example]]`, `[[test]]` and `[[bench]]` paths are
target roots, as `[lib]`/`[[bin]]` ones are since PR #3014, and such an
example is its own crate's root (dioxus's
`examples/07-fullstack/handling_errors.rs`).
Validation:
- rust-path-super-glob.test.ts (PR 1892's case, credited) and
rust-path-attribute-modules.test.ts (7 cases): on main all 8 fail; all
pass. Every rust-* suite passes.
- A/B against main, native kernel: fast-mlsirm (its Rust files) +2,796
links (1,839 calls, 427 references, 334 constructions, 196 imports), 71
retargeted (exposure_tests.rs's `Lcg::next_f64` from cdm_tests.rs's
private `Lcg` to exposure.rs's, the declaring module's helpers instead
of bifactor_grm.rs's), 87 reaching the same item through the declaring
module (confidence 0.4 to 0.9), 0 removed; dioxus +27 (each headless
test's `utils::mock_event(…)` into the helper five tests load); tokio +2
(`use super::AtomicU64` in the files atomic_u64_as_mutex.rs loads);
ripgrep, axum, clap, serde, gleam, express and flask unchanged.
- Cost, native kernel, medians of 3 interleaved runs: fast-mlsirm CPU 15.3
-> 15.8 s, wall 4.2 -> 5.2 s, peak RSS 825 -> 882 MB; tokio CPU 26.1 ->
26.7 s, wall 10.4 -> 11.2 s, peak RSS 1,023 -> 1,028 MB; dioxus CPU 34.8
-> 35.6 s, wall 10.3 -> 11.2 s, peak RSS 1,125 -> 1,120 MB; gleam CPU
24.1 -> 23.8 s, wall 6.6 -> 6.7 s, peak RSS 1,027 -> 1,023 MB.
Builds on PR #1892 by @seonghobae.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 tasks done
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
Builds on #1892 by @seonghobae, on the module walk of #3014 and the cross-crate paths of #3025: a Rust module loaded with
#[path = "…"]is now the module that declares it.#[path = "../../../tests/unit/scaling_tests.rs"] mod tests;incrates/core/src/scaling.rs: the test file'suse super::*brings inscaling.rs's items, private ones included, sopredict_rating_multi(1)links toscaling.rs's and not to another crate's namesake; itscrate::iscore's.#[path = "./utils.rs"] mod utils;and callsutils::mock_event(…).atomic_u64_as_mutex.rsloadsatomic_u64_static_const_new.rs(or…_once_cell.rs) with a#[path]: theiruse super::AtomicU64is itsAtomicU64.modfrom that module's own. A module whose cfg alternatives each name a file (tokio'sprocess::imp:unix/mod.rsorwindows.rs), or whose path acfg_attrsets, depends on the build and stays unknown; a path that leaves the project (escapesRoot) names no file.Cargo.tomlplaces at a path of its own ([[example]] path = "examples/07-fullstack/handling_errors.rs") is its own crate's root, like[lib]/[[bin]]paths since fix(rust): use bindings follow Rust's scopes, and module paths walk the crate's modules #3014.Root cause
rustSuperModule,rustFileModule,rustCrateRootModuleand the visibility checks placed every file by its directory, andrustSubmodulereturned nothing for a module with a#[path]. A#[path]file's directory says nothing about where it sits in the module tree:tests/unit/scaling_tests.rsisscaling.rs'smod tests.Fix
rustPathDeclsIn: a file's own#[path]declarations, read from its text (strings and comments blanked) after a byte check forpath, with each target resolved per the Reference.rustSubmodule: a module declared once with a#[path]is the file it names.rustPathClaim: a file the regular rules don't place is the module of the one#[path]declaration that loads it, if exactly one does.rustPlacedByNameplaces crate roots, files with afn main/#![no_main]of their own (trybuild cases, fuzz targets), and files a plainmod name;declares in the module owning their directory or in an inline module of one above it (dioxus'smod components { pub mod home; }). Only for a file left over does the resolver read the project's Rust files that writepath =for their declarations, once per resolver. With a claim,rustSuperModuleis the declaring module (the inline one around the attribute, if any),rustFileModuleowns the file's directory (rustc reads every#[path]file as amod.rs),rustCrateRootModuleis the declaring file's crate, andrustModuleWithin,isRustItemVisibleFromandisVisibleAcrossFilessee the file inside the declaring module.#[path]to a namesake at the crate root.rustCrateGraph:[[example]],[[test]]and[[bench]]paths are target roots, kept inside the project through the samejoin.superis the same walk since fix(rust): use bindings follow Rust's scopes, and module paths walk the crate's modules #3014.Validation
Tests: fix(rust): resolve use super::* callees in #[path] modules #1892's
rust-path-super-glob.test.ts(credited) andrust-path-attribute-modules.test.ts(private items throughuse super::*,crate::, an inline module's own directory, a helper two tests load, a folder named..generated, an example at its own path, cfg alternatives andcfg_attrleft unknown): on main all 8 fail; all pass. Everyrust-*suite passes.A/B against main, native kernel, the 7 Rust repos, fast-mlsirm's Rust files and 2 controls:
#[path]modules)fast-mlsirm: 1,839 calls, 427 references, 334 constructions and 196 imports added, mostly names
use super::*brings in from the declaring module; retargets are the declaring module's own items (exposure_tests.rs'sLcg::next_f64from cdm_tests.rs's privateLcgto exposure.rs's, the declaring module'sgh_ruleand helpers instead of bifactor_grm.rs's); the 87 reach the same item as before at confidence 0.9 instead of a 0.4 name guess. dioxus: the five headless tests'utils::…calls into their shared helper. tokio: the twouse super::AtomicU64. Sampled and checked; every other repo is site-for-site identical.Cost (native kernel, medians of 3 interleaved runs):
The extra is reading the project’s
#[path]declarations once per resolver: the main thread and each pool worker that reaches a file the regular rules don’t place (tokio’satomic_u64_*.rsandprocess/unix/mod.rs, dioxus’s headless-test helper, fast-mlsirm’s test files). On this Windows machine that read is about 0.4 ms a file (file opens), 0.1–0.6 s per resolver; axum and ripgrep never do it, serde and gleam once on the main thread. With the resolver pool off (measured one revision earlier), fast-mlsirm is 9.8 → 10.4 s CPU, 4.7 → 5.0 s wall and 513 → 512 MB.Full suite (native kernel rebuilt from main,
CODEGRAPH_KERNEL_EXPECT=1, daemon allowed, 4 workers) on this commit, on 2d98c1b: 815 files (809 passed, 6 skipped); 9,690 tests: 9,604 passed, 0 failed, 86 skipped.Left out
#[path]declarations load (a shared test helper) has no single module, so its ownsuper::andcrate::stay unknown; calls into it from each declaring file resolve.serde_core'scrate_root!) or byautomod::dir!(clap's test suites) are not read as declarations, so those files still send the resolver to read the project's#[path]declarations once; they keep their directory's module.#[path]modules.🤖 Generated with Claude Code