Skip to content

Commit 0afbdb1

Browse files
committed
add unclear_local_imports lint
1 parent b0f8618 commit 0afbdb1

File tree

7 files changed

+106
-0
lines changed

7 files changed

+106
-0
lines changed

compiler/rustc_lint/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,8 @@ lint_tykind = usage of `ty::TyKind`
728728
lint_tykind_kind = usage of `ty::TyKind::<kind>`
729729
.suggestion = try using `ty::<kind>` directly
730730
731+
lint_unclear_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::`
732+
731733
lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing
732734
.label = argument has type `{$arg_ty}`
733735
.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
@@ -75,6 +75,7 @@ mod reference_casting;
7575
mod shadowed_into_iter;
7676
mod traits;
7777
mod types;
78+
mod unclear_local_imports;
7879
mod unit_bindings;
7980
mod unused;
8081

@@ -111,6 +112,7 @@ use reference_casting::*;
111112
use shadowed_into_iter::ShadowedIntoIter;
112113
use traits::*;
113114
use types::*;
115+
use unclear_local_imports::*;
114116
use unit_bindings::*;
115117
use unused::*;
116118

@@ -228,6 +230,7 @@ late_lint_methods!(
228230
AsyncFnInTrait: AsyncFnInTrait,
229231
NonLocalDefinitions: NonLocalDefinitions::default(),
230232
ImplTraitOvercaptures: ImplTraitOvercaptures,
233+
UnclearLocalImports: UnclearLocalImports,
231234
]
232235
]
233236
);

compiler/rustc_lint/src/lints.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2768,3 +2768,7 @@ pub struct RedundantImportVisibility {
27682768
pub import_vis: String,
27692769
pub max_vis: String,
27702770
}
2771+
2772+
#[derive(LintDiagnostic)]
2773+
#[diag(lint_unclear_local_imports)]
2774+
pub struct UnclearLocalImportsDiag {}

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,62 @@
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 `unclear_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(unclear_local_imports)]
15+
///
16+
/// mod localmod {
17+
/// pub struct S;
18+
/// }
19+
///
20+
/// use localmode::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 UNCLEAR_LOCAL_IMPORTS,
34+
Allow,
35+
"`use` of a local item without leading `self::`, `super::`, or `crate::`"
36+
}
37+
38+
declare_lint_pass!(UnclearLocalImports => [UNCLEAR_LOCAL_IMPORTS]);
39+
40+
impl<'tcx> LateLintPass<'tcx> for UnclearLocalImports {
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 type, module, value namespace.
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.get(0) else { return };
46+
if !def_id.is_local() {
47+
return;
48+
}
49+
// So this does refer to something local. Let's check whether it start with `self` or `crate`.
50+
let first_seg = path.segments[0];
51+
if matches!(first_seg.ident.name, kw::SelfLower | kw::Super | kw::Crate) {
52+
return;
53+
}
54+
55+
// This `use` qualifies for our lint!
56+
cx.emit_span_lint(
57+
UNCLEAR_LOCAL_IMPORTS,
58+
first_seg.ident.span,
59+
lints::UnclearLocalImportsDiag {},
60+
);
61+
}
62+
}
+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)