Skip to content

Commit 8034536

Browse files
committed
Implement RFC3373 non local definitions lint
1 parent 69db514 commit 8034536

File tree

6 files changed

+343
-0
lines changed

6 files changed

+343
-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+
}
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// check-pass
2+
// edition:2021
3+
4+
use std::fmt::{Debug, Display};
5+
6+
struct Test;
7+
8+
impl Debug for Test {
9+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10+
todo!()
11+
}
12+
}
13+
14+
mod do_not_lint_mod {
15+
pub trait Tait {}
16+
17+
impl super::Test {
18+
fn hugo() {}
19+
}
20+
21+
impl Tait for super::Test {}
22+
}
23+
24+
const _: () = {
25+
trait Uto {}
26+
27+
impl Uto for Test {}
28+
};
29+
30+
trait Uto1 {}
31+
enum Enum {
32+
Discr = {
33+
impl Uto1 for Test {}
34+
//~^ WARN non local definition
35+
36+
1
37+
}
38+
}
39+
40+
trait Uto2 {}
41+
static A: u32 = {
42+
impl Uto2 for Test {}
43+
//~^ WARN non local definition
44+
45+
1
46+
};
47+
48+
trait Uto3 {}
49+
const B: u32 = {
50+
impl Uto3 for Test {}
51+
//~^ WARN non local definition
52+
53+
#[macro_export]
54+
macro_rules! m0 { () => { } };
55+
//~^ WARN non local definition
56+
57+
trait Uto4 {}
58+
impl Uto4 for Test {}
59+
60+
1
61+
};
62+
63+
trait Uto5 {}
64+
fn main() {
65+
#[macro_export]
66+
macro_rules! m { () => { } };
67+
//~^ WARN non local definition
68+
69+
impl Test {
70+
//~^ WARN non local definition
71+
fn foo() {}
72+
}
73+
74+
impl Display for Test {
75+
//~^ WARN non local definition
76+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77+
todo!()
78+
}
79+
}
80+
81+
let _a = || {
82+
impl Uto5 for Test {}
83+
//~^ WARN non local definition
84+
85+
1
86+
};
87+
88+
struct InsideMain;
89+
90+
impl Debug for InsideMain {
91+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92+
todo!()
93+
}
94+
}
95+
96+
impl InsideMain {
97+
fn foo() {}
98+
}
99+
100+
fn inside_inside() {
101+
impl Display for InsideMain {
102+
//~^ WARN non local definition
103+
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104+
todo!()
105+
}
106+
}
107+
108+
impl InsideMain {
109+
//~^ WARN non local definition
110+
fn bar() {
111+
#[macro_export]
112+
macro_rules! m2 { () => { } };
113+
//~^ WARN non local definition
114+
}
115+
}
116+
}
117+
}
+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
warning: non local definition should be avoided as they go against expectation
2+
--> $DIR/non_local_definitions.rs:33:9
3+
|
4+
LL | impl Uto1 for Test {}
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= help: move this declaration outside the of the 1 outermost bodies
8+
= note: this may become deny-by-default in the Edition 2024 and higher
9+
= note: `#[warn(non_local_definitions)]` on by default
10+
11+
warning: non local definition should be avoided as they go against expectation
12+
--> $DIR/non_local_definitions.rs:42:5
13+
|
14+
LL | impl Uto2 for Test {}
15+
| ^^^^^^^^^^^^^^^^^^^^^
16+
|
17+
= help: move this declaration outside the of the 1 outermost bodies
18+
= note: this may become deny-by-default in the Edition 2024 and higher
19+
20+
warning: non local definition should be avoided as they go against expectation
21+
--> $DIR/non_local_definitions.rs:50:5
22+
|
23+
LL | impl Uto3 for Test {}
24+
| ^^^^^^^^^^^^^^^^^^^^^
25+
|
26+
= help: move this declaration outside the of the 1 outermost bodies
27+
= note: this may become deny-by-default in the Edition 2024 and higher
28+
29+
warning: non local definition should be avoided as they go against expectation
30+
--> $DIR/non_local_definitions.rs:54:5
31+
|
32+
LL | macro_rules! m0 { () => { } };
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
34+
|
35+
= help: move this declaration outside the of the 1 outermost bodies
36+
= note: this may become deny-by-default in the Edition 2024 and higher
37+
38+
warning: non local definition should be avoided as they go against expectation
39+
--> $DIR/non_local_definitions.rs:66:5
40+
|
41+
LL | macro_rules! m { () => { } };
42+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
43+
|
44+
= help: move this declaration outside the of the 1 outermost bodies
45+
= note: this may become deny-by-default in the Edition 2024 and higher
46+
47+
warning: non local definition should be avoided as they go against expectation
48+
--> $DIR/non_local_definitions.rs:69:5
49+
|
50+
LL | / impl Test {
51+
LL | |
52+
LL | | fn foo() {}
53+
LL | | }
54+
| |_____^
55+
|
56+
= help: move this declaration outside the of the 1 outermost bodies
57+
= note: this may become deny-by-default in the Edition 2024 and higher
58+
59+
warning: non local definition should be avoided as they go against expectation
60+
--> $DIR/non_local_definitions.rs:74:5
61+
|
62+
LL | / impl Display for Test {
63+
LL | |
64+
LL | | fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65+
LL | | todo!()
66+
LL | | }
67+
LL | | }
68+
| |_____^
69+
|
70+
= help: move this declaration outside the of the 1 outermost bodies
71+
= note: this may become deny-by-default in the Edition 2024 and higher
72+
73+
warning: non local definition should be avoided as they go against expectation
74+
--> $DIR/non_local_definitions.rs:82:9
75+
|
76+
LL | impl Uto5 for Test {}
77+
| ^^^^^^^^^^^^^^^^^^^^^
78+
|
79+
= help: move this declaration outside the of the 2 outermost bodies
80+
= note: this may become deny-by-default in the Edition 2024 and higher
81+
82+
warning: non local definition should be avoided as they go against expectation
83+
--> $DIR/non_local_definitions.rs:101:9
84+
|
85+
LL | / impl Display for InsideMain {
86+
LL | |
87+
LL | | fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88+
LL | | todo!()
89+
LL | | }
90+
LL | | }
91+
| |_________^
92+
|
93+
= help: move this declaration outside the of the 2 outermost bodies
94+
= note: this may become deny-by-default in the Edition 2024 and higher
95+
96+
warning: non local definition should be avoided as they go against expectation
97+
--> $DIR/non_local_definitions.rs:108:9
98+
|
99+
LL | / impl InsideMain {
100+
LL | |
101+
LL | | fn bar() {
102+
LL | | #[macro_export]
103+
... |
104+
LL | | }
105+
LL | | }
106+
| |_________^
107+
|
108+
= help: move this declaration outside the of the 2 outermost bodies
109+
= note: this may become deny-by-default in the Edition 2024 and higher
110+
111+
warning: non local definition should be avoided as they go against expectation
112+
--> $DIR/non_local_definitions.rs:112:17
113+
|
114+
LL | macro_rules! m2 { () => { } };
115+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
116+
|
117+
= help: move this declaration outside the of the 3 outermost bodies
118+
= note: this may become deny-by-default in the Edition 2024 and higher
119+
120+
warning: 11 warnings emitted
121+

0 commit comments

Comments
 (0)