Skip to content

Commit 344bbb7

Browse files
committed
uplift module inspect
1 parent 9725c4b commit 344bbb7

File tree

16 files changed

+158
-22
lines changed

16 files changed

+158
-22
lines changed

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ a type parameter).
6363
*/
6464

6565
pub mod always_applicable;
66+
#[cfg_attr(bootstrap, allow(unknown_lints))]
67+
#[allow(module_inception)]
6668
mod check;
6769
mod compare_impl_item;
6870
mod entry;

compiler/rustc_lint/src/late.rs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -405,14 +405,6 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
405405
}
406406

407407
fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
408-
// Note: `passes` is often empty.
409-
let passes: Vec<_> =
410-
unerased_lint_store(tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
411-
412-
if passes.is_empty() {
413-
return;
414-
}
415-
416408
let context = LateContext {
417409
tcx,
418410
enclosing_body: None,
@@ -426,19 +418,23 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
426418

427419
let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(());
428420

429-
let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes
430-
.into_iter()
431-
.filter(|pass| {
432-
let lints = (**pass).get_lints();
433-
// Lintless passes are always in
434-
lints.is_empty() ||
435-
// If the pass doesn't have a single needed lint, omit it
436-
!lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint)))
437-
})
438-
.collect();
421+
let mut passes: Vec<Box<dyn LateLintPass<'tcx>>> =
422+
vec![Box::new(crate::BuiltinCombinedLateLintPass::new()), Box::new(HardwiredLints)];
423+
424+
// Filter out passes whose lints are all in the "don't need to run" set
425+
passes.retain(|pass| {
426+
let lints = (**pass).get_lints();
427+
// Lintless passes are always in
428+
lints.is_empty() ||
429+
// If the pass doesn't have a single needed lint, omit it
430+
!lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint)))
431+
});
432+
433+
if passes.is_empty() {
434+
return;
435+
}
439436

440-
filtered_passes.push(Box::new(HardwiredLints));
441-
let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] };
437+
let pass = RuntimeCombinedLateLintPass { passes: &mut passes };
442438
late_lint_crate_inner(tcx, context, pass);
443439
}
444440

compiler/rustc_lint/src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub mod lifetime_syntax;
5959
mod lints;
6060
mod macro_expr_fragment_specifier_2024_migration;
6161
mod map_unit_fn;
62+
mod module_inception;
6263
mod multiple_supertrait_upcastable;
6364
mod non_ascii_idents;
6465
mod non_fmt_panic;
@@ -100,6 +101,7 @@ use let_underscore::*;
100101
use lifetime_syntax::*;
101102
use macro_expr_fragment_specifier_2024_migration::*;
102103
use map_unit_fn::*;
104+
use module_inception::*;
103105
use multiple_supertrait_upcastable::*;
104106
use non_ascii_idents::*;
105107
use non_fmt_panic::NonPanicFmt;
@@ -185,6 +187,16 @@ early_lint_methods!(
185187
]
186188
);
187189

190+
late_lint_methods!(
191+
declare_combined_late_lint_pass,
192+
[
193+
BuiltinCombinedLateLintPass,
194+
[
195+
ModuleInception: ModuleInception::new(),
196+
]
197+
]
198+
);
199+
188200
late_lint_methods!(
189201
declare_combined_late_lint_pass,
190202
[
@@ -273,6 +285,7 @@ fn register_builtins(store: &mut LintStore) {
273285

274286
store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints());
275287
store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints());
288+
store.register_lints(&BuiltinCombinedLateLintPass::get_lints());
276289
store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints());
277290
store.register_lints(&foreign_modules::get_lints());
278291
store.register_lints(&HardwiredLints::lint_vec());
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//! Lint for detecting modules that have the same name as their parent module.
2+
3+
use rustc_hir::{Body, Item, ItemKind};
4+
use rustc_session::{declare_lint, impl_lint_pass};
5+
use rustc_span::Symbol;
6+
7+
use crate::{LateContext, LateLintPass, LintContext};
8+
9+
declare_lint! {
10+
/// The `module_inception` lint detects modules that have the same name as their parent module.
11+
///
12+
/// ### Example
13+
///
14+
/// ```rust,compile_fail
15+
/// mod foo {
16+
/// mod foo {
17+
/// pub fn bar() {}
18+
/// }
19+
/// }
20+
/// ```
21+
///
22+
/// {{produces}}
23+
///
24+
/// ### Explanation
25+
///
26+
/// A typical beginner mistake is to have `mod foo;` and again `mod foo { .. }`
27+
/// in `foo.rs`. The expectation is that items inside the inner `mod foo { .. }`
28+
/// are then available through `foo::x`, but they are only available through
29+
/// `foo::foo::x`. If this is done on purpose, it would be better to choose a
30+
/// more representative module name.
31+
pub MODULE_INCEPTION,
32+
Warn,
33+
"modules that have the same name as their parent module"
34+
}
35+
36+
struct ModInfo {
37+
name: Symbol,
38+
/// How many bodies are between this module and the current lint pass position.
39+
///
40+
/// Only the most recently seen module is updated when entering/exiting a body.
41+
in_body_count: u32,
42+
}
43+
44+
pub(crate) struct ModuleInception {
45+
/// The module path the lint pass is in.
46+
modules: Vec<ModInfo>,
47+
}
48+
49+
impl ModuleInception {
50+
pub(crate) fn new() -> Self {
51+
Self { modules: Vec::new() }
52+
}
53+
}
54+
55+
impl_lint_pass!(ModuleInception => [MODULE_INCEPTION]);
56+
57+
impl<'tcx> LateLintPass<'tcx> for ModuleInception {
58+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
59+
if let ItemKind::Mod(ident, _) = item.kind {
60+
// Check if this module has the same name as its parent module
61+
if let [.., prev] = &*self.modules
62+
&& prev.name == ident.name
63+
&& prev.in_body_count == 0
64+
&& !item.span.from_expansion()
65+
{
66+
cx.span_lint(MODULE_INCEPTION, item.span, |lint| {
67+
lint.primary_message("module has the same name as its containing module");
68+
});
69+
}
70+
71+
self.modules.push(ModInfo { name: ident.name, in_body_count: 0 });
72+
}
73+
}
74+
75+
fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
76+
if matches!(item.kind, ItemKind::Mod(..)) {
77+
self.modules.pop();
78+
}
79+
}
80+
81+
fn check_body(&mut self, _: &LateContext<'tcx>, _: &Body<'tcx>) {
82+
if let [.., last] = &mut *self.modules {
83+
last.in_body_count += 1;
84+
}
85+
}
86+
87+
fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &Body<'tcx>) {
88+
if let [.., last] = &mut *self.modules {
89+
last.in_body_count -= 1;
90+
}
91+
}
92+
}

library/core/src/async_iter/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
//! warning: unused result that must be used: async iterators do nothing unless polled
122122
//! ```
123123
124+
#[allow(module_inception)]
124125
mod async_iter;
125126
mod from_iter;
126127

library/core/src/future/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::ptr::NonNull;
1313
use crate::task::Context;
1414

1515
mod async_drop;
16+
#[allow(module_inception)]
1617
mod future;
1718
mod into_future;
1819
mod join;

library/std/src/sys/process/unix/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ cfg_select! {
1616
pub use unsupported::output;
1717
}
1818
_ => {
19+
#[allow(module_inception)]
1920
mod unix;
2021
use unix as imp;
2122
}

library/test/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub use self::types::TestName::*;
4040
pub use self::types::*;
4141

4242
// Module to be used by rustc to compile tests in libtest
43+
#[allow(module_inception)]
4344
pub mod test {
4445
pub use crate::bench::Bencher;
4546
pub use crate::cli::{TestOpts, parse_opts};

src/bootstrap/src/core/config/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
//! - Utility enums for specific configuration options.
2020
//! - Helper functions for managing configuration values.
2121
22-
#[expect(clippy::module_inception)]
22+
#[allow(unknown_lints, module_inception)]
2323
mod config;
2424
pub mod flags;
2525
pub mod target_selection;

src/bootstrap/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
8181

8282
/// Extra `--check-cfg` to add when building the compiler or tools
8383
/// (Mode restriction, config name, config values (if any))
84-
#[expect(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above.
8584
const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
8685
(Some(Mode::Rustc), "bootstrap", None),
8786
(Some(Mode::Codegen), "bootstrap", None),

0 commit comments

Comments
 (0)