Skip to content

Commit d6bac6e

Browse files
committed
Implement RFC3373 non local definitions lint
1 parent e7bbe8c commit d6bac6e

File tree

6 files changed

+387
-0
lines changed

6 files changed

+387
-0
lines changed

compiler/rustc_lint/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,10 @@ lint_non_fmt_panic_unused =
406406
}
407407
.add_fmt_suggestion = or add a "{"{"}{"}"}" format string to use the message literally
408408
409+
lint_non_local_definitions = non local definition should be avoided as they go against expectation
410+
.help = move this declaration outside the of the {$depth} outermost bodies
411+
.deprecation = this may become deny-by-default in the Edition 2024 and higher
412+
409413
lint_non_snake_case = {$sort} `{$name}` should have a snake case name
410414
.rename_or_convert_suggestion = rename the identifier or convert it to a snake case raw identifier
411415
.cannot_convert_note = `{$sc}` cannot be used as a raw identifier

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ mod methods;
7272
mod multiple_supertrait_upcastable;
7373
mod non_ascii_idents;
7474
mod non_fmt_panic;
75+
mod non_local_def;
7576
mod nonstandard_style;
7677
mod noop_method_call;
7778
mod opaque_hidden_inferred_bound;
@@ -107,6 +108,7 @@ use methods::*;
107108
use multiple_supertrait_upcastable::*;
108109
use non_ascii_idents::*;
109110
use non_fmt_panic::NonPanicFmt;
111+
use non_local_def::*;
110112
use nonstandard_style::*;
111113
use noop_method_call::*;
112114
use opaque_hidden_inferred_bound::*;
@@ -233,6 +235,7 @@ late_lint_methods!(
233235
MissingDebugImplementations: MissingDebugImplementations,
234236
MissingDoc: MissingDoc,
235237
AsyncFnInTrait: AsyncFnInTrait,
238+
NonLocalDefinitions: NonLocalDefinitions::default(),
236239
]
237240
]
238241
);

compiler/rustc_lint/src/lints.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,15 @@ pub struct SuspiciousDoubleRefCloneDiag<'a> {
13051305
pub ty: Ty<'a>,
13061306
}
13071307

1308+
// non_local_defs.rs
1309+
#[derive(LintDiagnostic)]
1310+
#[diag(lint_non_local_definitions)]
1311+
#[help(lint_help)]
1312+
#[note(lint_deprecation)]
1313+
pub struct NonLocalDefinitionsDiag {
1314+
pub depth: u32,
1315+
}
1316+
13081317
// pass_by_value.rs
13091318
#[derive(LintDiagnostic)]
13101319
#[diag(lint_pass_by_value)]
+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use rustc_hir::{Body, Item, ItemKind, QPath, TyKind};
2+
use rustc_span::{sym, MacroKind};
3+
4+
use crate::{lints::NonLocalDefinitionsDiag, LateContext, LateLintPass, LintContext};
5+
6+
declare_lint! {
7+
/// The `non_local_definitions` lint checks for `impl` blocks and `#[macro_export]`
8+
/// macro inside bodies (functions, enum discriminant, ...).
9+
///
10+
/// ### Example
11+
///
12+
/// ```rust
13+
/// trait MyTrait {}
14+
/// struct MyStruct;
15+
///
16+
/// fn foo() {
17+
/// impl MyTrait for MyStruct {}
18+
/// }
19+
/// ```
20+
///
21+
/// {{produces}}
22+
///
23+
/// ### Explanation
24+
///
25+
/// Creating non local definitions go against expectation and can create decrepencies
26+
/// in tooling. In should be avoided.
27+
pub NON_LOCAL_DEFINITIONS,
28+
Warn,
29+
"checks for non local definitions"
30+
}
31+
32+
#[derive(Default)]
33+
pub struct NonLocalDefinitions {
34+
is_in_body: u32,
35+
}
36+
37+
impl_lint_pass!(NonLocalDefinitions => [NON_LOCAL_DEFINITIONS]);
38+
39+
impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
40+
fn check_body(&mut self, _cx: &LateContext<'tcx>, _body: &'tcx Body<'tcx>) {
41+
self.is_in_body += 1;
42+
}
43+
44+
fn check_body_post(&mut self, _cx: &LateContext<'tcx>, _body: &'tcx Body<'tcx>) {
45+
self.is_in_body -= 1;
46+
}
47+
48+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
49+
if self.is_in_body > 0 {
50+
match item.kind {
51+
ItemKind::Impl(impl_) => 'block: {
52+
let TyKind::Path(QPath::Resolved(_, self_ty_path)) = impl_.self_ty.kind else {
53+
break 'block;
54+
};
55+
let Some(self_ty_did) = self_ty_path.res.opt_def_id() else {
56+
break 'block;
57+
};
58+
let parent_self_ty = cx.tcx.parent(self_ty_did);
59+
60+
let parent_impl = cx.tcx.parent(item.owner_id.def_id.into());
61+
if parent_impl != parent_self_ty
62+
&& impl_
63+
.of_trait
64+
.map(|trait_| trait_.trait_def_id())
65+
.flatten()
66+
.map(|did| cx.tcx.parent(did) != parent_impl)
67+
.unwrap_or(true)
68+
{
69+
cx.emit_span_lint(
70+
NON_LOCAL_DEFINITIONS,
71+
item.span,
72+
NonLocalDefinitionsDiag { depth: self.is_in_body },
73+
);
74+
}
75+
}
76+
ItemKind::Macro(_macro, MacroKind::Bang) => {
77+
if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) {
78+
cx.emit_span_lint(
79+
NON_LOCAL_DEFINITIONS,
80+
item.span,
81+
NonLocalDefinitionsDiag { depth: self.is_in_body },
82+
);
83+
}
84+
}
85+
_ => {}
86+
}
87+
}
88+
}
89+
}
+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// check-pass
2+
// edition:2021
3+
4+
#![feature(inline_const)]
5+
6+
use std::fmt::{Debug, Display};
7+
8+
struct Test;
9+
10+
impl Debug for Test {
11+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12+
todo!()
13+
}
14+
}
15+
16+
mod do_not_lint_mod {
17+
pub trait Tait {}
18+
19+
impl super::Test {
20+
fn hugo() {}
21+
}
22+
23+
impl Tait for super::Test {}
24+
}
25+
26+
const _: () = {
27+
trait Uto {}
28+
29+
impl Uto for Test {} // the trait is local, don't lint
30+
};
31+
32+
trait Uto1 {}
33+
enum Enum {
34+
Discr = {
35+
impl Uto1 for Test {}
36+
//~^ WARN non local definition
37+
38+
1
39+
}
40+
}
41+
42+
trait Uto2 {}
43+
static A: u32 = {
44+
impl Uto2 for Test {}
45+
//~^ WARN non local definition
46+
47+
1
48+
};
49+
50+
trait Uto3 {}
51+
const B: u32 = {
52+
impl Uto3 for Test {}
53+
//~^ WARN non local definition
54+
55+
#[macro_export]
56+
macro_rules! m0 { () => { } };
57+
//~^ WARN non local definition
58+
59+
trait Uto4 {}
60+
impl Uto4 for Test {}
61+
62+
1
63+
};
64+
65+
trait Uto5 {}
66+
fn main() {
67+
#[macro_export]
68+
macro_rules! m { () => { } };
69+
//~^ WARN non local definition
70+
71+
impl Test {
72+
//~^ WARN non local definition
73+
fn foo() {}
74+
}
75+
76+
let _array = [0i32; {
77+
impl Test {
78+
//~^ WARN non local definition
79+
fn bar() {}
80+
}
81+
82+
1
83+
}];
84+
85+
const {
86+
impl Test {
87+
//~^ WARN non local definition
88+
fn hoo() {}
89+
}
90+
91+
1
92+
};
93+
94+
impl Display for Test {
95+
//~^ WARN non local definition
96+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97+
todo!()
98+
}
99+
}
100+
101+
let _a = || {
102+
impl Uto5 for Test {}
103+
//~^ WARN non local definition
104+
105+
1
106+
};
107+
108+
struct InsideMain;
109+
110+
impl Debug for InsideMain {
111+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112+
todo!()
113+
}
114+
}
115+
116+
impl InsideMain {
117+
fn foo() {}
118+
}
119+
120+
fn inside_inside() {
121+
impl Display for InsideMain {
122+
//~^ WARN non local definition
123+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124+
todo!()
125+
}
126+
}
127+
128+
impl InsideMain {
129+
//~^ WARN non local definition
130+
fn bar() {
131+
#[macro_export]
132+
macro_rules! m2 { () => { } };
133+
//~^ WARN non local definition
134+
}
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)