Skip to content

self_named_module_files implementation is broken #14697

Open
@TimNN

Description

@TimNN

Summary

The self_named_module_files implementation operates on path components, not paths. This leads to both, false negatives and super-confusing false positives.

The logic, as far as I can tell, is approximately this: The lint triggers if a file named foo.rs exists, a folder named foo exists, and foo/mod.rs doesn't exist. The prefixes of those paths, if any, are completely ignored.

(The lint also cannot be allowed for individual modules).


This could probably be fixed by tracking things by their full paths, not path components, though maybe a completely different implementation might be the better solution.

E.g. my first idea for how something like this could be implemented was along the lines of:

  • For every module file x.rs that is not a mod.rs file and not the crate root:
    • If x.rs contains a mod that is neither inline nor has a #[path="..."] attribute:
      • Trigger self_named_module_files for x.rs.

I think this should cover the desired semantics, with no manipulation of file paths being required.

Reproducer

False negative:

Consider this directory layout:

src
├── foo
│   └── bar.rs
├── foo.rs
├── lib.rs
└── other
    ├── foo
    │   └── mod.rs
    └── mod.rs

The lint should trigger for foo.rs, but doesn't trigger because other/foo/mod.rs exists.

If you remove other, the lint triggers correctly.

False positive:

(There's probably a simpler repro possible, but I like the absurdity of --release making the lint trigger / not trigger, and this is essentially the situation I ran into, just with build instead release).

build.rs:

use std::{env, fs};

fn main() {
    let mut out_file = env::var("OUT_DIR").unwrap();
    out_file.push_str("/data.txt");

    fs::write(&out_file, "").unwrap();

    println!("cargo::rustc-env=DATA={out_file}");

    println!("cargo::warning=DATA: {out_file}");
}

src/lib.rs:

pub static DATA: &str = include_str!(env!("DATA"));

mod release;

src/release.rs: empty file

cargo clippy -- -D clippy::self-named-module-files will not trigger the lint.

cargo clippy --release -- -D clippy::self-named-module-files will trigger the lint.

This is because for --release, $OUT_DIR contains a directory named /release/ and there's no release/mod.rs file anywhere, so release.rs triggers the lint.

Version

rustc 1.88.0-nightly (10fa3c449 2025-04-26)
binary: rustc
commit-hash: 10fa3c449f6b1613b352a6cbf78d3d91fd9a1d81
commit-date: 2025-04-26
host: aarch64-apple-darwin
release: 1.88.0-nightly
LLVM version: 20.1.2

Additional Labels

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-false-negativeIssue: The lint should have been triggered on code, but wasn'tI-false-positiveIssue: The lint was triggered on code it shouldn't have

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions