Skip to content

Commit 64868d3

Browse files
committed
Emit some extern "custom" errors in ast_validation:wq
1 parent a1f6e7b commit 64868d3

File tree

12 files changed

+418
-157
lines changed

12 files changed

+418
-157
lines changed

compiler/rustc_ast/src/ast.rs

+31
Original file line numberDiff line numberDiff line change
@@ -3480,6 +3480,37 @@ impl FnHeader {
34803480
|| matches!(constness, Const::Yes(_))
34813481
|| !matches!(ext, Extern::None)
34823482
}
3483+
3484+
pub fn span(&self) -> Option<Span> {
3485+
fn append(a: &mut Option<Span>, b: Span) {
3486+
*a = match a {
3487+
None => Some(b),
3488+
Some(x) => Some(x.to(b)),
3489+
}
3490+
}
3491+
3492+
let mut full_span = None;
3493+
3494+
match self.safety {
3495+
Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span),
3496+
Safety::Default => {}
3497+
};
3498+
3499+
if let Some(coroutine_kind) = self.coroutine_kind {
3500+
append(&mut full_span, coroutine_kind.span());
3501+
}
3502+
3503+
if let Const::Yes(span) = self.constness {
3504+
append(&mut full_span, span);
3505+
}
3506+
3507+
match self.ext {
3508+
Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span),
3509+
Extern::None => {}
3510+
}
3511+
3512+
full_span
3513+
}
34833514
}
34843515

34853516
impl Default for FnHeader {

compiler/rustc_ast_passes/messages.ftl

+13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
ast_passes_abi_custom_invalid_signature =
2+
invalid signature for `extern "custom"` function
3+
.note = functions with the `"custom"` ABI cannot have any parameters or return type
4+
.suggestion = remove the parameters and return type
5+
6+
ast_passes_abi_custom_safe_foreign_function =
7+
foreign functions with the `"custom"` ABI cannot be safe
8+
.suggestion = remove the `safe` keyword from this definition
9+
10+
ast_passes_abi_custom_safe_function =
11+
functions with the `"custom"` ABI must be unsafe
12+
.suggestion = add the `unsafe` keyword to function definition
13+
114
ast_passes_assoc_const_without_body =
215
associated constant in `impl` without body
316
.suggestion = provide a definition for the constant

compiler/rustc_ast_passes/src/ast_validation.rs

+69-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
1919
use std::mem;
2020
use std::ops::{Deref, DerefMut};
21+
use std::str::FromStr;
2122

2223
use itertools::{Either, Itertools};
2324
use rustc_abi::ExternAbi;
@@ -81,6 +82,7 @@ struct AstValidator<'a> {
8182

8283
/// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
8384
extern_mod_safety: Option<Safety>,
85+
extern_mod_abi: Option<ExternAbi>,
8486

8587
lint_node_id: NodeId,
8688

@@ -121,10 +123,17 @@ impl<'a> AstValidator<'a> {
121123
self.outer_trait_or_trait_impl = old;
122124
}
123125

124-
fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) {
125-
let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
126+
fn with_in_extern_mod(
127+
&mut self,
128+
extern_mod_safety: Safety,
129+
abi: Option<ExternAbi>,
130+
f: impl FnOnce(&mut Self),
131+
) {
132+
let old_safety = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
133+
let old_abi = mem::replace(&mut self.extern_mod_abi, abi);
126134
f(self);
127-
self.extern_mod_safety = old;
135+
self.extern_mod_safety = old_safety;
136+
self.extern_mod_abi = old_abi;
128137
}
129138

130139
fn with_tilde_const(
@@ -370,6 +379,49 @@ impl<'a> AstValidator<'a> {
370379
}
371380
}
372381

382+
/// An `extern "custom"` function must be unsafe, and must not have any parameters or return
383+
/// type.
384+
fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
385+
let dcx = self.dcx();
386+
387+
// An `extern "custom"` function must be unsafe.
388+
match sig.header.safety {
389+
Safety::Unsafe(_) => { /* all good */ }
390+
Safety::Safe(safe_span) => {
391+
let safe_span = safe_span.with_hi(safe_span.hi() + rustc_span::BytePos(1));
392+
dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span });
393+
}
394+
Safety::Default => match ctxt {
395+
FnCtxt::Foreign => { /* all good */ }
396+
FnCtxt::Free | FnCtxt::Assoc(_) => {
397+
self.dcx().emit_err(errors::AbiCustomSafeFunction {
398+
span: sig.span,
399+
unsafe_span: sig.span.shrink_to_lo(),
400+
});
401+
}
402+
},
403+
}
404+
405+
// An `extern "custom"` function must not have any parameters or return type.
406+
let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
407+
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
408+
spans.push(ret_ty.span);
409+
}
410+
411+
if !spans.is_empty() {
412+
let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo());
413+
let suggestion_span = sig.span.with_lo(header_span.hi());
414+
let padding = if header_span.is_empty() { "" } else { " " };
415+
416+
self.dcx().emit_err(errors::AbiCustomInvalidSignature {
417+
spans,
418+
symbol: ident.name,
419+
suggestion_span,
420+
padding,
421+
});
422+
}
423+
}
424+
373425
/// This ensures that items can only be `unsafe` (or unmarked) outside of extern
374426
/// blocks.
375427
///
@@ -1005,7 +1057,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10051057
if abi.is_none() {
10061058
self.handle_missing_abi(*extern_span, item.id);
10071059
}
1008-
self.with_in_extern_mod(*safety, |this| {
1060+
1061+
let extern_abi = abi.and_then(|abi| ExternAbi::from_str(abi.symbol.as_str()).ok());
1062+
self.with_in_extern_mod(*safety, extern_abi, |this| {
10091063
visit::walk_item(this, item);
10101064
});
10111065
self.extern_mod_span = old_item;
@@ -1145,6 +1199,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11451199
self.check_foreign_fn_bodyless(*ident, body.as_deref());
11461200
self.check_foreign_fn_headerless(sig.header);
11471201
self.check_foreign_item_ascii_only(*ident);
1202+
if self.extern_mod_abi == Some(ExternAbi::Custom) {
1203+
self.check_custom_abi(FnCtxt::Foreign, ident, sig);
1204+
}
11481205
}
11491206
ForeignItemKind::TyAlias(box TyAlias {
11501207
defaultness,
@@ -1352,6 +1409,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13521409
self.check_item_safety(span, safety);
13531410
}
13541411

1412+
if let FnKind::Fn(ctxt, _, fun) = fk
1413+
&& let Extern::Explicit(str_lit, _) = fun.sig.header.ext
1414+
&& let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str())
1415+
{
1416+
self.check_custom_abi(ctxt, &fun.ident, &fun.sig);
1417+
}
1418+
13551419
self.check_c_variadic_type(fk);
13561420

13571421
// Functions cannot both be `const async` or `const gen`
@@ -1703,6 +1767,7 @@ pub fn check_crate(
17031767
outer_impl_trait_span: None,
17041768
disallow_tilde_const: Some(TildeConstReason::Item),
17051769
extern_mod_safety: None,
1770+
extern_mod_abi: None,
17061771
lint_node_id: CRATE_NODE_ID,
17071772
is_sdylib_interface,
17081773
lint_buffer: lints,

compiler/rustc_ast_passes/src/errors.rs

+48
Original file line numberDiff line numberDiff line change
@@ -824,3 +824,51 @@ pub(crate) struct MissingAbi {
824824
#[suggestion(code = "extern \"<abi>\"", applicability = "has-placeholders")]
825825
pub span: Span,
826826
}
827+
828+
#[derive(Diagnostic)]
829+
#[diag(ast_passes_abi_custom_safe_foreign_function)]
830+
pub(crate) struct AbiCustomSafeForeignFunction {
831+
#[primary_span]
832+
pub span: Span,
833+
834+
#[suggestion(
835+
ast_passes_suggestion,
836+
applicability = "maybe-incorrect",
837+
code = "",
838+
style = "verbose"
839+
)]
840+
pub safe_span: Span,
841+
}
842+
843+
#[derive(Diagnostic)]
844+
#[diag(ast_passes_abi_custom_safe_function)]
845+
pub(crate) struct AbiCustomSafeFunction {
846+
#[primary_span]
847+
pub span: Span,
848+
849+
#[suggestion(
850+
ast_passes_suggestion,
851+
applicability = "maybe-incorrect",
852+
code = "unsafe ",
853+
style = "verbose"
854+
)]
855+
pub unsafe_span: Span,
856+
}
857+
858+
#[derive(Diagnostic)]
859+
#[diag(ast_passes_abi_custom_invalid_signature)]
860+
#[note]
861+
pub(crate) struct AbiCustomInvalidSignature {
862+
#[primary_span]
863+
pub spans: Vec<Span>,
864+
865+
#[suggestion(
866+
ast_passes_suggestion,
867+
applicability = "maybe-incorrect",
868+
code = "{padding}fn {symbol}()",
869+
style = "verbose"
870+
)]
871+
pub suggestion_span: Span,
872+
pub symbol: Symbol,
873+
pub padding: &'static str,
874+
}

compiler/rustc_passes/messages.ftl

-8
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,6 @@ passes_abi_custom_clothed_function =
1212
functions with the `"custom"` ABI must be naked
1313
.suggestion = add the `#[unsafe(naked)]` attribute to this function
1414
15-
passes_abi_custom_safe_foreign_function =
16-
foreign functions with the `"custom"` ABI cannot be safe
17-
.suggestion = remove the `safe` keyword from this definition
18-
19-
passes_abi_custom_safe_function =
20-
functions with the `"custom"` ABI must be unsafe
21-
.suggestion = add the `unsafe` keyword this function definition
22-
2315
passes_abi_invalid_attribute =
2416
`#[rustc_abi]` can only be applied to function items, type aliases, and associated functions
2517
passes_abi_ne =

compiler/rustc_passes/src/custom_abi.rs

+19-61
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
use rustc_abi::ExternAbi;
22
use rustc_hir::def::DefKind;
3-
use rustc_hir::def_id::{LocalDefId, LocalModDefId};
3+
use rustc_hir::def_id::LocalModDefId;
44
use rustc_hir::intravisit::Visitor;
5-
use rustc_hir::{self as hir, ExprKind, FnSig};
5+
use rustc_hir::{self as hir, ExprKind};
66
use rustc_middle::hir::nested_filter::OnlyBodies;
77
use rustc_middle::query::Providers;
88
use rustc_middle::ty::{self, TyCtxt};
9-
use rustc_span::{BytePos, sym};
9+
use rustc_span::sym;
1010

11-
use crate::errors::{
12-
AbiCustomCall, AbiCustomClothedFunction, AbiCustomSafeForeignFunction, AbiCustomSafeFunction,
13-
};
11+
use crate::errors::{AbiCustomCall, AbiCustomClothedFunction};
1412

1513
pub(crate) fn provide(providers: &mut Providers) {
1614
*providers = Providers { check_mod_custom_abi, ..*providers };
@@ -25,24 +23,6 @@ fn check_mod_custom_abi(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
2523
for def_id in items.definitions() {
2624
let def_kind = tcx.def_kind(def_id);
2725

28-
// An `extern "custom"` function cannot be marked as `safe`.
29-
if let DefKind::ForeignMod = def_kind
30-
&& let hir::Node::Item(item) = tcx.hir_node_by_def_id(def_id)
31-
&& let hir::ItemKind::ForeignMod { abi: ExternAbi::Custom, items } = item.kind
32-
{
33-
for item in items {
34-
let hir_id = item.id.hir_id();
35-
if let hir::Node::ForeignItem(foreign_item) = tcx.hir_node(hir_id)
36-
&& let hir::ForeignItemKind::Fn(sig, _, _) = foreign_item.kind
37-
&& sig.header.is_safe()
38-
{
39-
let len = "safe ".len() as u32;
40-
let safe_span = sig.span.shrink_to_lo().with_hi(sig.span.lo() + BytePos(len));
41-
tcx.dcx().emit_err(AbiCustomSafeForeignFunction { span: sig.span, safe_span });
42-
}
43-
}
44-
}
45-
4626
// An `extern "custom"` function cannot be a `const fn`, because `naked_asm!` cannot be
4727
// evaluated at compile time, and `extern` blocks cannot declare `const fn` functions.
4828
// Therefore, to find all calls to `extern "custom"` functions, it suffices to traverse
@@ -51,56 +31,34 @@ fn check_mod_custom_abi(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
5131
continue;
5232
}
5333

54-
let body = match tcx.hir_node_by_def_id(def_id) {
34+
let (sig, body) = match tcx.hir_node_by_def_id(def_id) {
5535
hir::Node::Item(hir::Item {
5636
kind: hir::ItemKind::Fn { sig, body: body_id, .. },
5737
..
5838
})
5939
| hir::Node::ImplItem(hir::ImplItem {
6040
kind: hir::ImplItemKind::Fn(sig, body_id),
6141
..
62-
}) => {
63-
check_signature(tcx, def_id, sig, true);
64-
tcx.hir_body(*body_id)
65-
}
66-
hir::Node::TraitItem(hir::TraitItem {
67-
kind: hir::TraitItemKind::Fn(sig, trait_fn),
42+
})
43+
| hir::Node::TraitItem(hir::TraitItem {
44+
kind: hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)),
6845
..
69-
}) => match trait_fn {
70-
hir::TraitFn::Required(_) => {
71-
check_signature(tcx, def_id, sig, false);
72-
continue;
73-
}
74-
hir::TraitFn::Provided(body_id) => {
75-
check_signature(tcx, def_id, sig, true);
76-
tcx.hir_body(*body_id)
77-
}
78-
},
46+
}) => (sig, tcx.hir_body(*body_id)),
7947
_ => continue,
8048
};
8149

82-
let mut visitor = CheckCustomAbi { tcx };
83-
visitor.visit_body(body);
84-
}
85-
}
86-
87-
fn check_signature<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, sig: &FnSig<'tcx>, has_body: bool) {
88-
if sig.header.abi == ExternAbi::Custom {
89-
// Function definitions that use `extern "custom"` must be naked functions.
90-
if has_body && !tcx.has_attr(def_id, sym::naked) {
91-
tcx.dcx().emit_err(AbiCustomClothedFunction {
92-
span: sig.span,
93-
naked_span: sig.span.shrink_to_lo(),
94-
});
50+
if sig.header.abi == ExternAbi::Custom {
51+
// Function definitions that use `extern "custom"` must be naked functions.
52+
if !tcx.has_attr(def_id, sym::naked) {
53+
tcx.dcx().emit_err(AbiCustomClothedFunction {
54+
span: sig.span,
55+
naked_span: sig.span.shrink_to_lo(),
56+
});
57+
}
9558
}
9659

97-
// Function definitions that use `extern "custom"` must unsafe.
98-
if sig.header.is_safe() {
99-
tcx.dcx().emit_err(AbiCustomSafeFunction {
100-
span: sig.span,
101-
unsafe_span: sig.span.shrink_to_lo(),
102-
});
103-
}
60+
let mut visitor = CheckCustomAbi { tcx };
61+
visitor.visit_body(body);
10462
}
10563
}
10664

0 commit comments

Comments
 (0)