Skip to content

New lint: definition_in_module_root - #16965

Merged
samueltardieu merged 1 commit into
rust-lang:masterfrom
corygabrielsen:spike/definition-in-module-root
Jul 11, 2026
Merged

New lint: definition_in_module_root#16965
samueltardieu merged 1 commit into
rust-lang:masterfrom
corygabrielsen:spike/definition-in-module-root

Conversation

@corygabrielsen

@corygabrielsen corygabrielsen commented May 5, 2026

Copy link
Copy Markdown
Contributor

View all comments

Adds a restriction lint that flags item definitions (struct, enum, fn,
impl, trait, etc.) in mod.rs files, so mod.rs is reserved for
declarations and re-exports.

  • lib.rs and main.rs are not checked
  • #[macro_export] macros and macro-expanded items are skipped
  • #[path] is resolved via the source map

changelog: new lint: [definition_in_module_root]

@rustbot rustbot added needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels May 5, 2026
@rustbot

rustbot commented May 5, 2026

Copy link
Copy Markdown
Collaborator

r? @samueltardieu

rustbot has assigned @samueltardieu.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: 7 candidates
  • 7 candidates expanded to 7 candidates
  • Random selection from Jarcho, dswij, llogiq, samueltardieu

@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown

Lintcheck changes for 518f8ea

Lint Added Removed Changed
clippy::definition_in_module_root 7556 0 0

This comment will be updated if you push new changes

@corygabrielsen
corygabrielsen force-pushed the spike/definition-in-module-root branch 2 times, most recently from fa6e565 to 810fe1d Compare May 5, 2026 07:03

@samueltardieu samueltardieu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please trim the PR description. LLM generated description are overly verbose and take more time to review than it takes to generate, this is not normal.

View changes since this review

Comment thread clippy_lints/src/definition_in_module_root.rs Outdated
Comment thread clippy_lints/src/definition_in_module_root.rs Outdated
Comment thread clippy_lints/src/definition_in_module_root.rs Outdated
Comment thread clippy_lints/src/definition_in_module_root.rs Outdated
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels May 8, 2026
@rustbot

rustbot commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@samueltardieu samueltardieu added the lint-nominated Create an FCP-thread on Zulip for this PR label May 8, 2026
@rustbot

rustbot commented May 8, 2026

Copy link
Copy Markdown
Collaborator

This lint has been nominated for inclusion.

A FCP topic has been created on Zulip.

@rustbot

This comment has been minimized.

@corygabrielsen

Copy link
Copy Markdown
Contributor Author

Please trim the PR description. LLM generated description are overly verbose and take more time to review than it takes to generate, this is not normal.

View changes since this review

I have trimmed the PR description.

I apologize if this came off as slop. I actually spent quite a bit of time writing it because I wanted to present the mathematical motivation for the lint. I felt that it would help contextualize what I felt was the necessity of the rule and what motivated me to draft the PR.

The important snippet from the original PR description can be seen below. I wrote a good portion of this, but of course yes, the AI helped me fill it out better than I could have my self.

Mathematical Motivation for this rule ## Canonical layouts

Mathematical motivation.

This lint's value is more than stylistic. Combined with the convention of
naming each file after its primary definition, definition_in_module_root
collapses the remaining degrees of freedom in Rust's module-to-file mapping
so that the file layout is uniquely determined by the module tree.

The freedom problem

Given N symbols to organize into files, most partitions retain
combinatorial ambiguity: (N choose K) > 1 for 1 < K < N. Of all groupings,
only two are unambiguous: all symbols in one file, or one file per symbol.
The rest involve arbitrary choices about which symbols share a file with
which. The one-per-symbol extreme is the only scalable choice that
preserves canonicality.

Even under one-per-symbol, a residual freedom remains: a definition can
live in its parent's mod.rs or in a named sibling file. This lint
removes that last freedom — every definition goes in a named file. Once
both pins are in place, the filesystem tree is a direct projection of
the module tree: different authors converge on structurally identical
code, merges flatten, and automated refactoring becomes tractable.

Formal interaction with module_inception

The interaction noted in the previous section is the formal expression of
this property. Consider:

C1 (this lint):        mod.rs files hold only declarations.
C2 (module_inception): no module foo contains a child module foo.

For any type whose name matches its parent module's name, C1 ∧ C2 is
unsatisfiable:

  1. C1 forbids that type in foo/mod.rs.
  2. The one-name-per-file convention places it in a file named after the
    type — here, foo.rs.
  3. As a sibling of foo/, that file is foo/foo.rs.
  4. C2 forbids foo/foo.rs.

From ¬(C1 ∧ C2) it follows that any project enforcing C1 must accept ¬C2
for the relevant class — i.e., allow(module_inception) for types whose
name matches their parent module's name. The choice between dropping C1
or C2 is not symmetric: C1 is strictly stronger than C2, since
module_inception's premise (that definitions in mod.rs are
acceptable) is exactly what C1 denies.

Therefore the canonical-layout property requires this lint. C1 is
the load-bearing constraint; without enforcement of "declarations-only
in mod.rs," the whole property collapses to convention. The PR
provides C1; allow(module_inception) for the matching-name class is
the cost of that choice; canonical layouts are the payoff.

Portability and the codegen gap

The structural role of mod.rs is shared across module systems —
Python's __init__.py, TypeScript's index.ts, and Rust's mod.rs all
serve as the parent-as-table-of-contents file. The canonicality property
described above is therefore portable: enforce "declarations only" in
those slots in any language, and the layout becomes uniquely determined.

This matters for code-generation pipelines that target multiple languages
— schema- or IDL-driven generators that emit corresponding implementations
from one source of truth. For canonical layouts to hold across the
generated code, each target language needs its own enforcement of the
rule. Until this PR, Rust's slot was empty. Landing this lint fills it.

@corygabrielsen
corygabrielsen force-pushed the spike/definition-in-module-root branch from fd0d22e to 68f915c Compare May 18, 2026 21:47
@rustbot

This comment has been minimized.

@corygabrielsen
corygabrielsen force-pushed the spike/definition-in-module-root branch from 68f915c to 5071fcf Compare May 18, 2026 21:55
@corygabrielsen

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties and removed S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels May 18, 2026

@samueltardieu samueltardieu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be merged soon. However, this will be part of Rust 1.98.0.

View changes since this review

Comment thread clippy_lints/src/definition_in_module_root.rs Outdated
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels May 19, 2026
@corygabrielsen
corygabrielsen force-pushed the spike/definition-in-module-root branch from a65644b to ae0b448 Compare May 19, 2026 10:27
@rustbot

This comment has been minimized.

@corygabrielsen

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties and removed S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels May 19, 2026
@rustbot

This comment has been minimized.

@corygabrielsen
corygabrielsen force-pushed the spike/definition-in-module-root branch from ae0b448 to f4fad3c Compare June 3, 2026 23:11
@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@corygabrielsen
corygabrielsen force-pushed the spike/definition-in-module-root branch from f4fad3c to 044f840 Compare June 5, 2026 19:48
@rustbot

rustbot commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@samueltardieu samueltardieu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry this took so long.

Could you amend the commit for "1.99.0" and force-push? You can merge the PR afterwards.

@rustbot delegate

View changes since this review

Comment thread clippy_lints/src/definition_in_module_root.rs Outdated
@rustbot

rustbot commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

✌️ @corygabrielsen, you can now merge this pull request!

If @samueltardieu told you to merge after making some further change, then please make that change and post @rustbot merge.

View changes since this delegation

Flags definitions (struct, enum, fn, impl, trait, etc.) in `mod.rs`
files. Encourages putting each definition in its own named file so
filenames are descriptive and unique. `lib.rs` and `main.rs` are
not checked, since defining a small crate's API there is common and
reasonable.

Filenames are resolved through the source map so `#[path]` is
handled by the real filename. `#[macro_export]` macros and inline
modules are exempt. Items produced by macro expansion are skipped.

Projects opting into this lint will typically also `allow(module_inception)`,
since enforcing declarations-only in `mod.rs` makes `foo/foo.rs` the
expected layout.

Category: `restriction`.

changelog: new lint: [`definition_in_module_root`]

Co-authored-by: Samuel Tardieu <sam@rfc1149.net>
@corygabrielsen
corygabrielsen force-pushed the spike/definition-in-module-root branch from 044f840 to 518f8ea Compare July 11, 2026 07:31
@samueltardieu
samueltardieu enabled auto-merge July 11, 2026 07:34
@samueltardieu

Copy link
Copy Markdown
Member

(since you were so fast to push the updated commit I was able to enable the merge myself, no need to do it)

@samueltardieu
samueltardieu added this pull request to the merge queue Jul 11, 2026
Merged via the queue into rust-lang:master with commit 6412d74 Jul 11, 2026
11 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Jul 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lint-nominated Create an FCP-thread on Zulip for this PR needs-fcp PRs that add, remove, or rename lints and need an FCP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants