Skip to content

Commit 0f5d4a2

Browse files
committed
add unqualified_local_imports lint
1 parent 6106b05 commit 0f5d4a2

File tree

7 files changed

+108
-0
lines changed

7 files changed

+108
-0
lines changed

compiler/rustc_lint/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,8 @@ lint_tykind = usage of `ty::TyKind`
761761
lint_tykind_kind = usage of `ty::TyKind::<kind>`
762762
.suggestion = try using `ty::<kind>` directly
763763
764+
lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::`
765+
764766
lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing
765767
.label = argument has type `{$arg_ty}`
766768
.suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ mod shadowed_into_iter;
8080
mod traits;
8181
mod types;
8282
mod unit_bindings;
83+
mod unqualified_local_imports;
8384
mod unused;
8485

8586
pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
@@ -118,6 +119,7 @@ use shadowed_into_iter::ShadowedIntoIter;
118119
use traits::*;
119120
use types::*;
120121
use unit_bindings::*;
122+
use unqualified_local_imports::*;
121123
use unused::*;
122124

123125
pub use builtin::{MissingDoc, SoftLints};
@@ -234,6 +236,7 @@ late_lint_methods!(
234236
AsyncFnInTrait: AsyncFnInTrait,
235237
NonLocalDefinitions: NonLocalDefinitions::default(),
236238
ImplTraitOvercaptures: ImplTraitOvercaptures,
239+
UnqualifiedLocalImports: UnqualifiedLocalImports,
237240
]
238241
]
239242
);

compiler/rustc_lint/src/lints.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2953,3 +2953,7 @@ pub struct UnsafeAttrOutsideUnsafeSuggestion {
29532953
pub struct OutOfScopeMacroCalls {
29542954
pub path: String,
29552955
}
2956+
2957+
#[derive(LintDiagnostic)]
2958+
#[diag(lint_unqualified_local_imports)]
2959+
pub struct UnqualifiedLocalImportsDiag {}

compiler/rustc_lint/src/passes.rs

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ macro_rules! late_lint_methods {
1414
fn check_mod(a: &'tcx rustc_hir::Mod<'tcx>, b: rustc_hir::HirId);
1515
fn check_foreign_item(a: &'tcx rustc_hir::ForeignItem<'tcx>);
1616
fn check_item(a: &'tcx rustc_hir::Item<'tcx>);
17+
/// This is called *after* recursing into the item
18+
/// (in contrast to `check_item`, which is checked before).
1719
fn check_item_post(a: &'tcx rustc_hir::Item<'tcx>);
1820
fn check_local(a: &'tcx rustc_hir::LetStmt<'tcx>);
1921
fn check_block(a: &'tcx rustc_hir::Block<'tcx>);
@@ -135,6 +137,8 @@ macro_rules! early_lint_methods {
135137
fn check_crate(a: &rustc_ast::Crate);
136138
fn check_crate_post(a: &rustc_ast::Crate);
137139
fn check_item(a: &rustc_ast::Item);
140+
/// This is called *after* recursing into the item
141+
/// (in contrast to `check_item`, which is checked before).
138142
fn check_item_post(a: &rustc_ast::Item);
139143
fn check_local(a: &rustc_ast::Local);
140144
fn check_block(a: &rustc_ast::Block);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use rustc_hir as hir;
2+
use rustc_session::{declare_lint, declare_lint_pass};
3+
use rustc_span::symbol::kw;
4+
5+
use crate::{lints, LateContext, LateLintPass, LintContext};
6+
7+
declare_lint! {
8+
/// The `unqualified_local_imports` lint checks for `use` items that import a local item using a
9+
/// path that does not start with `self::`, `super::`, or `crate::`.
10+
///
11+
/// ### Example
12+
///
13+
/// ```rust,edition2018
14+
/// #![warn(unqualified_local_imports)]
15+
///
16+
/// mod localmod {
17+
/// pub struct S;
18+
/// }
19+
///
20+
/// use localmod::S;
21+
/// ```
22+
///
23+
/// {{produces}}
24+
///
25+
/// ### Explanation
26+
///
27+
/// This lint is meant to be used with the (unstable) rustfmt setting `group_imports = "StdExternalCrate"`.
28+
/// That setting makes rustfmt group `self::`, `super::`, and `crate::` imports separately from those
29+
/// refering to other crates. However, rustfmt cannot know whether `use c::S;` refers to a local module `c`
30+
/// or an external crate `c`, so it always gets categorized as an import from another crate.
31+
/// To ensure consistent grouping of imports from the local crate, all local imports must
32+
/// start with `self::`, `super::`, or `crate::`. This lint can be used to enforce that style.
33+
pub UNQUALIFIED_LOCAL_IMPORTS,
34+
Allow,
35+
"`use` of a local item without leading `self::`, `super::`, or `crate::`"
36+
}
37+
38+
declare_lint_pass!(UnqualifiedLocalImports => [UNQUALIFIED_LOCAL_IMPORTS]);
39+
40+
impl<'tcx> LateLintPass<'tcx> for UnqualifiedLocalImports {
41+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
42+
let hir::ItemKind::Use(path, _kind) = item.kind else { return };
43+
// `path` has three resolutions for the type, module, value namespaces.
44+
// However, it shouldn't be possible for those to be in different crates so we only check the first.
45+
let Some(hir::def::Res::Def(_def_kind, def_id)) = path.res.first() else { return };
46+
if !def_id.is_local() {
47+
return;
48+
}
49+
// So this does refer to something local. Let's check whether it starts with `self`,
50+
// `super`, or `crate`. If the path is empty, that means we have a `use *`, which is
51+
// equivalent to `use crate::*` so we don't fire the lint in that case.
52+
let Some(first_seg) = path.segments.first() else { return };
53+
if matches!(first_seg.ident.name, kw::SelfLower | kw::Super | kw::Crate) {
54+
return;
55+
}
56+
57+
// This `use` qualifies for our lint!
58+
cx.emit_span_lint(
59+
UNQUALIFIED_LOCAL_IMPORTS,
60+
first_seg.ident.span,
61+
lints::UnqualifiedLocalImportsDiag {},
62+
);
63+
}
64+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![deny(unclear_local_imports)]
2+
3+
mod localmod {
4+
pub struct S;
5+
pub struct T;
6+
}
7+
8+
// Not a local import, so no lint.
9+
use std::cell::Cell;
10+
11+
// Implicitly local import, gets lint.
12+
use localmod::S; //~ERROR: unclear
13+
14+
// Explicitly local import, no lint.
15+
use self::localmod::T;
16+
17+
fn main() {}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: `use` of a local item without leading `self::`, `super::`, or `crate::`
2+
--> $DIR/unclear_local_imports.rs:12:5
3+
|
4+
LL | use localmod::S;
5+
| ^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unclear_local_imports.rs:1:9
9+
|
10+
LL | #![deny(unclear_local_imports)]
11+
| ^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+

0 commit comments

Comments
 (0)