Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) ->

pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::Result {
let Item { owner_id: _, kind, span: _, vis_span: _, has_delayed_lints: _ } = item;

try_visit!(visitor.visit_id(item.hir_id()));
match *kind {
ItemKind::ExternCrate(orig_name, ident) => {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ a type parameter).
*/

pub mod always_applicable;
#[cfg_attr(bootstrap, allow(unknown_lints))]
#[allow(module_inception)]
mod check;
mod compare_impl_item;
mod entry;
Expand Down
36 changes: 16 additions & 20 deletions compiler/rustc_lint/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,14 +405,6 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
}

fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
// Note: `passes` is often empty.
let passes: Vec<_> =
unerased_lint_store(tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();

if passes.is_empty() {
return;
}

let context = LateContext {
tcx,
enclosing_body: None,
Expand All @@ -426,19 +418,23 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {

let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(());

let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes
.into_iter()
.filter(|pass| {
let lints = (**pass).get_lints();
// Lintless passes are always in
lints.is_empty() ||
// If the pass doesn't have a single needed lint, omit it
!lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint)))
})
.collect();
let mut passes: Vec<Box<dyn LateLintPass<'tcx>>> =
vec![Box::new(crate::BuiltinCombinedLateLintPass::new()), Box::new(HardwiredLints)];

// Filter out passes whose lints are all in the "don't need to run" set
passes.retain(|pass| {
let lints = (**pass).get_lints();
// Lintless passes are always in
lints.is_empty() ||
// If the pass doesn't have a single needed lint, omit it
!lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint)))
});

if passes.is_empty() {
return;
}

filtered_passes.push(Box::new(HardwiredLints));
let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] };
let pass = RuntimeCombinedLateLintPass { passes: &mut passes };
late_lint_crate_inner(tcx, context, pass);
}

Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub mod lifetime_syntax;
mod lints;
mod macro_expr_fragment_specifier_2024_migration;
mod map_unit_fn;
mod module_inception;
mod multiple_supertrait_upcastable;
mod non_ascii_idents;
mod non_fmt_panic;
Expand Down Expand Up @@ -100,6 +101,7 @@ use let_underscore::*;
use lifetime_syntax::*;
use macro_expr_fragment_specifier_2024_migration::*;
use map_unit_fn::*;
use module_inception::*;
use multiple_supertrait_upcastable::*;
use non_ascii_idents::*;
use non_fmt_panic::NonPanicFmt;
Expand Down Expand Up @@ -185,6 +187,16 @@ early_lint_methods!(
]
);

late_lint_methods!(
declare_combined_late_lint_pass,
[
BuiltinCombinedLateLintPass,
[
ModuleInception: ModuleInception::new(),
]
]
);

late_lint_methods!(
declare_combined_late_lint_pass,
[
Expand Down Expand Up @@ -273,6 +285,7 @@ fn register_builtins(store: &mut LintStore) {

store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints());
store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints());
store.register_lints(&BuiltinCombinedLateLintPass::get_lints());
store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints());
store.register_lints(&foreign_modules::get_lints());
store.register_lints(&HardwiredLints::lint_vec());
Expand Down
92 changes: 92 additions & 0 deletions compiler/rustc_lint/src/module_inception.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! Lint for detecting modules that have the same name as their parent module.

use rustc_hir::{Body, Item, ItemKind};
use rustc_session::{declare_lint, impl_lint_pass};
use rustc_span::Symbol;

use crate::{LateContext, LateLintPass, LintContext};

declare_lint! {
/// The `module_inception` lint detects modules that have the same name as their parent module.
///
/// ### Example
///
/// ```rust,compile_fail
/// mod foo {
/// mod foo {
/// pub fn bar() {}
/// }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// A typical beginner mistake is to have `mod foo;` and again `mod foo { .. }`
/// in `foo.rs`. The expectation is that items inside the inner `mod foo { .. }`
/// are then available through `foo::x`, but they are only available through
/// `foo::foo::x`. If this is done on purpose, it would be better to choose a
/// more representative module name.
pub MODULE_INCEPTION,
Warn,
"modules that have the same name as their parent module"
}

struct ModInfo {
name: Symbol,
/// How many bodies are between this module and the current lint pass position.
///
/// Only the most recently seen module is updated when entering/exiting a body.
in_body_count: u32,
}

pub(crate) struct ModuleInception {
/// The module path the lint pass is in.
modules: Vec<ModInfo>,
}

impl ModuleInception {
pub(crate) fn new() -> Self {
Self { modules: Vec::new() }
}
}

impl_lint_pass!(ModuleInception => [MODULE_INCEPTION]);

impl<'tcx> LateLintPass<'tcx> for ModuleInception {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Mod(ident, _) = item.kind {
// Check if this module has the same name as its parent module
if let [.., prev] = &*self.modules
&& prev.name == ident.name
&& prev.in_body_count == 0
&& !item.span.from_expansion()
{
cx.span_lint(MODULE_INCEPTION, item.span, |lint| {
lint.primary_message("module has the same name as its containing module");
});
}

self.modules.push(ModInfo { name: ident.name, in_body_count: 0 });
}
}

fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if matches!(item.kind, ItemKind::Mod(..)) {
self.modules.pop();
}
}

fn check_body(&mut self, _: &LateContext<'tcx>, _: &Body<'tcx>) {
if let [.., last] = &mut *self.modules {
last.in_body_count += 1;
}
}

fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &Body<'tcx>) {
if let [.., last] = &mut *self.modules {
last.in_body_count -= 1;
}
}
}
1 change: 1 addition & 0 deletions library/core/src/async_iter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
//! warning: unused result that must be used: async iterators do nothing unless polled
//! ```

#[allow(module_inception)]
mod async_iter;
mod from_iter;

Expand Down
1 change: 1 addition & 0 deletions library/core/src/future/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::ptr::NonNull;
use crate::task::Context;

mod async_drop;
#[allow(module_inception)]
mod future;
mod into_future;
mod join;
Expand Down
1 change: 1 addition & 0 deletions library/std/src/sys/process/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ cfg_select! {
pub use unsupported::output;
}
_ => {
#[allow(module_inception)]
mod unix;
use unix as imp;
}
Expand Down
1 change: 1 addition & 0 deletions library/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub use self::types::TestName::*;
pub use self::types::*;

// Module to be used by rustc to compile tests in libtest
#[allow(module_inception)]
pub mod test {
pub use crate::bench::Bencher;
pub use crate::cli::{TestOpts, parse_opts};
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/src/core/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
//! - Utility enums for specific configuration options.
//! - Helper functions for managing configuration values.

#[expect(clippy::module_inception)]
#[allow(unknown_lints, module_inception)]
mod config;
pub mod flags;
pub mod target_selection;
Expand Down
1 change: 1 addition & 0 deletions tests/ui/expr/weird-exprs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#![allow(uncommon_codepoints, confusable_idents)]
#![allow(unused_imports)]
#![allow(unreachable_patterns)]
#![allow(module_inception)]

#![recursion_limit = "256"]

Expand Down
1 change: 1 addition & 0 deletions tests/ui/imports/issue-62767.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//@ check-pass
#![allow(module_inception)]

// Minimized case from #62767.
mod m {
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/lint/module-inception-allow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![allow(module_inception)]

pub mod foo {
pub mod foo {
pub fn bar() {}
}
}

pub mod baz {
// This is fine - different name
pub fn qux() {}
}

pub mod outer {
pub mod outer {
pub fn inner() {}
}

pub mod different {
pub fn func() {}
}
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// Similar to https://github.com/rust-lang/rust/issues/131474

//@ check-pass
#![allow(module_inception)]

pub mod tmp {
pub mod tmp {
Expand Down
1 change: 1 addition & 0 deletions tests/ui/lint/non-local-defs/module_as_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fn simple_one() {

fn simple_two() {
mod posts {
#[allow(module_inception)]
pub mod posts {
#[allow(non_camel_case_types)]
pub struct table {}
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/structs-enums/rec-align-u64.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//@ run-pass
#![allow(dead_code)]
#![allow(unused_unsafe)]
#![allow(module_inception)]


// Issue #2303

Expand Down
Loading