A feature that compiles in the demo isn’t the same as a feature that compiles on every input. 0.3.4 closes the gap between the two for --split-nested-mods.
Today we released SplitRS 0.3.4 — a correctness release. A focused review of 0.3.3’s recursive --split-nested-mods descent turned up five distinct ways the generated code could fail to compile, and all five are fixed.
SplitRS is the AST-based Rust refactoring tool that splits oversized source files into clean, compilable modules: it parses with syn, clusters methods and types, infers visibility, and writes the imports and mod.rs facades for you. No C, no external formatter shelling out to system tools — it links prettyplease in-process and compiles to a single static binary.
Why 0.3.4 matters
Recursive descent multiplies the ways a splitter can get visibility and imports wrong — every level down, super:: paths shift, private items cross new module boundaries, and macro names compete with type names in the same import map. 0.3.4 fixes the five failure modes a targeted review actually found in generated output:
- Relocated private
moditems now compile. When a private inlinemodis moved into a generated bucket module, it’s now widened topub(super)— closing anE0603(private item) against theuse self::<bucket>::<mod>;re-binding the generator itself emits. - The parent-scope glob import survives. Previously,
use super::*;could get silently dropped from a descended module body whenever an unresolved bare function call or method belonged to something the parent scope actually provides.compute_parent_scope_itemsnow tracks provided names and trait methods, soE0425/E0599in descended bodies are gone. - Macros no longer collide with type imports.
macro_rules!names are now excluded from the type-to-module import map, eliminating bogususe super::macros::<name>;lines that triggered both anE0432(unresolved import) and a follow-onE0659ambiguity against#[macro_use]-expanded macros. - Cleaner generated imports. Parent-scope import pruning now also subtracts names the nested body already binds through its own non-
superuse trees, so generatedmod.rsfiles stop emitting duplicateunused_importswarnings — whilesuper::-path targets are correctly never subtracted by local bindings.
None of this changes the CLI surface — --split-nested-mods, --max-mod-depth, and --facade all work exactly as documented in 0.3.3. This release makes the code they generate compile more often.
Technical Deep Dive
upgrade_type_visibilitygains anItem::Modarm. Visibility widening previously handled types and functions relocated across module boundaries but had no case for inlinemoditems themselves — so a private nested module, once moved, stayed invisible to theuse self::...binding the generator wrote to reach it.ParentScopeItems(compute_parent_scope_items). Rather than a single “does the parent provide this name” check, the parent-scope analysis now returns a structured result covering both plain provided names and trait methods reachable only via method-call syntax — so step 2 of the descent pipeline can decide to keep the glob import instead of dropping it.Module::importable_exported_namesfilters out macro names. The type-to-module import map that drives auto-generateduse super::module::Name;lines now excludes anything that is amacro_rules!definition, so macros keep resolving through their normal#[macro_use]path instead of being shadowed by a bogus module-qualified import.collect_use_bound_nameswidenedpub(super)→pub(crate)(src/module_generator/functions.rs) sonested_mod_splitter— a sibling module, not a child — can call it to enumerate exactly which bindings a generatedmod.rsneeds to recreate for its own descended children.- Five new regression tests, one per review finding, plus a
rustccompile probe wired directly into the main nested-mod end-to-end test — so a future regression here fails a test, not just a downstreamcargo build.
Getting Started
Install the latest SplitRS:
cargo install splitrs
Recursive descent works exactly as in 0.3.3 — you don’t need to change any flags to get the fixes:
splitrs \
--input src/large_file.rs \
--output src/large_file/ \
--split-nested-mods true \
--max-mod-depth 4
If you hit E0603, E0425, E0599, E0432, or E0659 against generated mod.rs output on 0.3.3, upgrading to 0.3.4 and re-running the same command should resolve it without any config changes.
What’s New in 0.3.4
- Nested-mod descent (
--split-nested-mods): private inlinemoditems relocated into generated buckets are now widened topub(super), fixingE0603. - Parent-scope binding recreation: the forwarded
use super::*;glob is no longer dropped when an unresolved name or method belongs to something the parent scope provides; fixesE0425/E0599. #[macro_use]-firstmod.rsordering:macro_rules!names excluded from the type-to-module import map, eliminatingE0432/E0659.collect_use_bound_nameswidenedpub(super)→pub(crate)sonested_mod_splittercan enumerate bindings a generatedmod.rsrecreates for descended children.- Parent-scope import pruning also subtracts names the nested body binds through its own non-
superuse trees, removing duplicatedunused_importswarnings. - Test suite grew to 570 tests passing with
--all-features(499 with default features), up from 565 in 0.3.3 — five new regression tests, one per review finding, plus a compile probe.
Tips
- No config changes needed to pick up these fixes — if you’re already using
--split-nested-mods, just upgrade and re-run. - Hitting a private-item or macro-import error in generated
mod.rs? File it against 0.3.4 with the specificE0xxxcode — the fix set here specifically targets the visibility and import-resolution edge cases recursive descent can hit. - The compile probe test is a good pattern to copy if you maintain your own codegen: asserting
rustcactually accepts the generated output catches classes of bugs that pure AST-shape assertions miss. --facade namedremains the safest choice when you want to audit exactly what a generated module re-exports, independent of these fixes.
This is the foundation
SplitRS remains the development workhorse behind the COOLJAPAN policy of keeping every file under 2,000 lines, used across the ecosystem’s Rust codebases — OxiZ, NumRS2, SciRS2, and dozens of oxi* crates — whenever a module outgrows its budget. 0.3.4 is a small release by line count, but it’s the kind of hardening that keeps recursive descent trustworthy as a default, not just a demo-able flag.
Repository: https://github.com/cool-japan/splitrs
Star the repo if you want a refactoring tool that treats “the generated code doesn’t compile” as a bug worth a dedicated release. Pure Rust refactoring is here — fast, safe, and increasingly battle-tested.
— KitaSan at COOLJAPAN OÜ July 7, 2026