A Dylint lint for sibling modules that depend on each other in a cycle
Rust
0
1 commits
updated Sep 24, 2026
A Dylint lint that reports sibling modules depending on each other in a cycle.
Two modules are siblings when they share a parent, and each one counts together with everything nested inside it. So crate::report and crate::model are compared as whole subtrees: if anything in report uses model and anything in model uses report, that's a cycle. A child using its parent's items, or a parent using its children's, isn't reported, because that's one module split into files and nearly every crate does it.
A dependency is any reference the compiler resolved: paths (including use and re-exports), method calls and Type::assoc calls. A method call depends on the module holding the impl block that defines the method, which isn't always the module of the type. References written by another crate's macros are skipped. References written by the crate's own macros count and are shown at the macro call.
Each cycle is reported once, on one reference, with a note for every other step of the shortest loop through it:
warning: modules `model` and `report` depend on each other
--> src/lib.rs:9:22
|
9 | pub fn kind() -> crate::report::Row {
| ^^^^^^^^^^^^^^^^^^ `model` depends on `report` here
|
note: `report` depends on `model` here
--> src/lib.rs:16:22
|
16 | pub fn rows() -> crate::model::Id {
| ^^^^^^^^^^^^^^^^
= help: move the items they share into one of them, or into a module both depend on
Add the library to the workspace's Cargo.toml, pinned to a commit:
[workspace.metadata.dylint]
libraries = [{ git = "https://github.com/HardMax71/module-cycles", rev = "<commit>" }]
Then install Dylint and run it:
cargo install cargo-dylint dylint-link
cargo dylint --all -- --workspace --all-targets
It warns by default. To fail the run on any cycle, deny it:
DYLINT_RUSTFLAGS="-D module_cycles" cargo dylint --all -- --workspace --all-targets
To allow one cycle, put the attribute on the reported item or on its module. The cfg_attr keeps a plain cargo build from warning about a lint it doesn't know:
#[cfg_attr(dylint_lib = "module_cycles", allow(module_cycles))]
The crate then declares the cfg, so rustc doesn't flag it as unexpected:
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(dylint_lib, values(any()))"] }
The lint builds on the nightly pinned in rust-toolchain.toml. Dylint installs that toolchain itself the first time it builds the library.
1 commits
Rust
100.0%
A Dylint lint for sibling modules that depend on each other in a cycle
Rust
0
1 commits
updated Sep 24, 2026
A Dylint lint that reports sibling modules depending on each other in a cycle.
Two modules are siblings when they share a parent, and each one counts together with everything nested inside it. So crate::report and crate::model are compared as whole subtrees: if anything in report uses model and anything in model uses report, that's a cycle. A child using its parent's items, or a parent using its children's, isn't reported, because that's one module split into files and nearly every crate does it.
A dependency is any reference the compiler resolved: paths (including use and re-exports), method calls and Type::assoc calls. A method call depends on the module holding the impl block that defines the method, which isn't always the module of the type. References written by another crate's macros are skipped. References written by the crate's own macros count and are shown at the macro call.
Each cycle is reported once, on one reference, with a note for every other step of the shortest loop through it:
warning: modules `model` and `report` depend on each other
--> src/lib.rs:9:22
|
9 | pub fn kind() -> crate::report::Row {
| ^^^^^^^^^^^^^^^^^^ `model` depends on `report` here
|
note: `report` depends on `model` here
--> src/lib.rs:16:22
|
16 | pub fn rows() -> crate::model::Id {
| ^^^^^^^^^^^^^^^^
= help: move the items they share into one of them, or into a module both depend on
Add the library to the workspace's Cargo.toml, pinned to a commit:
[workspace.metadata.dylint]
libraries = [{ git = "https://github.com/HardMax71/module-cycles", rev = "<commit>" }]
Then install Dylint and run it:
cargo install cargo-dylint dylint-link
cargo dylint --all -- --workspace --all-targets
It warns by default. To fail the run on any cycle, deny it:
DYLINT_RUSTFLAGS="-D module_cycles" cargo dylint --all -- --workspace --all-targets
To allow one cycle, put the attribute on the reported item or on its module. The cfg_attr keeps a plain cargo build from warning about a lint it doesn't know:
#[cfg_attr(dylint_lib = "module_cycles", allow(module_cycles))]
The crate then declares the cfg, so rustc doesn't flag it as unexpected:
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(dylint_lib, values(any()))"] }
The lint builds on the nightly pinned in rust-toolchain.toml. Dylint installs that toolchain itself the first time it builds the library.
1 commits
Rust
100.0%