Skip to content

Commit 9b86d54

Browse files
authored
Rollup merge of rust-lang#106407 - mejrs:attr_check, r=compiler-errors
Improve proc macro attribute diagnostics Closes rust-lang#102923
2 parents c18a5e8 + 8e43414 commit 9b86d54

16 files changed

+542
-22
lines changed

compiler/rustc_error_messages/locales/en-US/passes.ftl

+21
Original file line numberDiff line numberDiff line change
@@ -710,3 +710,24 @@ passes_ignored_derived_impls =
710710
[one] trait {$trait_list}, but this is
711711
*[other] traits {$trait_list}, but these are
712712
} intentionally ignored during dead code analysis
713+
714+
passes_proc_macro_typeerror = mismatched {$kind} signature
715+
.label = found {$found}, expected type `proc_macro::TokenStream`
716+
.note = {$kind}s must have a signature of `{$expected_signature}`
717+
718+
passes_proc_macro_diff_arg_count = mismatched {$kind} signature
719+
.label = found unexpected {$count ->
720+
[one] argument
721+
*[other] arguments
722+
}
723+
.note = {$kind}s must have a signature of `{$expected_signature}`
724+
725+
passes_proc_macro_missing_args = mismatched {$kind} signature
726+
.label = {$kind} must have {$expected_input_count ->
727+
[one] one argument
728+
*[other] two arguments
729+
} of type `proc_macro::TokenStream`
730+
731+
passes_proc_macro_invalid_abi = proc macro functions may not be `extern "{$abi}"`
732+
733+
passes_proc_macro_unsafe = proc macro functions may not be `unsafe`

compiler/rustc_passes/src/check_attr.rs

+140-6
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
77
use crate::errors::{
88
self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr,
9-
OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint,
9+
OnlyHasEffectOn, ProcMacroDiffArguments, ProcMacroInvalidAbi, ProcMacroMissingArguments,
10+
ProcMacroTypeError, ProcMacroUnsafe, TransparentIncompatible, UnrecognizedReprHint,
1011
};
1112
use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
1213
use rustc_data_structures::fx::FxHashMap;
13-
use rustc_errors::{fluent, Applicability, MultiSpan};
14+
use rustc_errors::{fluent, Applicability, IntoDiagnosticArg, MultiSpan};
1415
use rustc_expand::base::resolve_path;
1516
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
1617
use rustc_hir as hir;
@@ -19,18 +20,20 @@ use rustc_hir::intravisit::{self, Visitor};
1920
use rustc_hir::{
2021
self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
2122
};
22-
use rustc_hir::{MethodKind, Target};
23+
use rustc_hir::{MethodKind, Target, Unsafety};
2324
use rustc_middle::hir::nested_filter;
2425
use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
26+
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
2527
use rustc_middle::ty::query::Providers;
26-
use rustc_middle::ty::TyCtxt;
28+
use rustc_middle::ty::{ParamEnv, TyCtxt};
2729
use rustc_session::lint::builtin::{
2830
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
2931
};
3032
use rustc_session::parse::feature_err;
3133
use rustc_span::symbol::{kw, sym, Symbol};
3234
use rustc_span::{Span, DUMMY_SP};
3335
use rustc_target::spec::abi::Abi;
36+
use std::cell::Cell;
3437
use std::collections::hash_map::Entry;
3538

3639
pub(crate) fn target_from_impl_item<'tcx>(
@@ -62,8 +65,29 @@ enum ItemLike<'tcx> {
6265
ForeignItem,
6366
}
6467

68+
#[derive(Copy, Clone)]
69+
pub(crate) enum ProcMacroKind {
70+
FunctionLike,
71+
Derive,
72+
Attribute,
73+
}
74+
75+
impl IntoDiagnosticArg for ProcMacroKind {
76+
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
77+
match self {
78+
ProcMacroKind::Attribute => "attribute proc macro",
79+
ProcMacroKind::Derive => "derive proc macro",
80+
ProcMacroKind::FunctionLike => "function-like proc macro",
81+
}
82+
.into_diagnostic_arg()
83+
}
84+
}
85+
6586
struct CheckAttrVisitor<'tcx> {
6687
tcx: TyCtxt<'tcx>,
88+
89+
// Whether or not this visitor should abort after finding errors
90+
abort: Cell<bool>,
6791
}
6892

6993
impl CheckAttrVisitor<'_> {
@@ -173,7 +197,7 @@ impl CheckAttrVisitor<'_> {
173197
sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod),
174198
sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
175199
sym::macro_export => self.check_macro_export(hir_id, attr, target),
176-
sym::ignore | sym::should_panic | sym::proc_macro_derive => {
200+
sym::ignore | sym::should_panic => {
177201
self.check_generic_attr(hir_id, attr, target, Target::Fn)
178202
}
179203
sym::automatically_derived => {
@@ -183,6 +207,16 @@ impl CheckAttrVisitor<'_> {
183207
self.check_generic_attr(hir_id, attr, target, Target::Mod)
184208
}
185209
sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id),
210+
sym::proc_macro => {
211+
self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
212+
}
213+
sym::proc_macro_attribute => {
214+
self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
215+
}
216+
sym::proc_macro_derive => {
217+
self.check_generic_attr(hir_id, attr, target, Target::Fn);
218+
self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
219+
}
186220
_ => {}
187221
}
188222

@@ -2063,6 +2097,103 @@ impl CheckAttrVisitor<'_> {
20632097
errors::Unused { attr_span: attr.span, note },
20642098
);
20652099
}
2100+
2101+
/// A best effort attempt to create an error for a mismatching proc macro signature.
2102+
///
2103+
/// If this best effort goes wrong, it will just emit a worse error later (see #102923)
2104+
fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
2105+
let expected_input_count = match kind {
2106+
ProcMacroKind::Attribute => 2,
2107+
ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,
2108+
};
2109+
2110+
let expected_signature = match kind {
2111+
ProcMacroKind::Attribute => "fn(TokenStream, TokenStream) -> TokenStream",
2112+
ProcMacroKind::Derive | ProcMacroKind::FunctionLike => "fn(TokenStream) -> TokenStream",
2113+
};
2114+
2115+
let tcx = self.tcx;
2116+
if target == Target::Fn {
2117+
let Some(tokenstream) = tcx.get_diagnostic_item(sym::TokenStream) else {return};
2118+
let tokenstream = tcx.type_of(tokenstream);
2119+
2120+
let id = hir_id.expect_owner();
2121+
let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap();
2122+
2123+
let sig = tcx.liberate_late_bound_regions(id.to_def_id(), tcx.fn_sig(id));
2124+
let sig = tcx.normalize_erasing_regions(ParamEnv::empty(), sig);
2125+
2126+
// We don't currently require that the function signature is equal to
2127+
// `fn(TokenStream) -> TokenStream`, but instead monomorphizes to
2128+
// `fn(TokenStream) -> TokenStream` after some substitution of generic arguments.
2129+
//
2130+
// Properly checking this means pulling in additional `rustc` crates, so we don't.
2131+
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer };
2132+
2133+
if sig.abi != Abi::Rust {
2134+
tcx.sess.emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi.name() });
2135+
self.abort.set(true);
2136+
}
2137+
2138+
if sig.unsafety == Unsafety::Unsafe {
2139+
tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span });
2140+
self.abort.set(true);
2141+
}
2142+
2143+
let output = sig.output();
2144+
2145+
// Typecheck the output
2146+
if !drcx.types_may_unify(output, tokenstream) {
2147+
tcx.sess.emit_err(ProcMacroTypeError {
2148+
span: hir_sig.decl.output.span(),
2149+
found: output,
2150+
kind,
2151+
expected_signature,
2152+
});
2153+
self.abort.set(true);
2154+
}
2155+
2156+
if sig.inputs().len() < expected_input_count {
2157+
tcx.sess.emit_err(ProcMacroMissingArguments {
2158+
expected_input_count,
2159+
span: hir_sig.span,
2160+
kind,
2161+
expected_signature,
2162+
});
2163+
self.abort.set(true);
2164+
}
2165+
2166+
// Check that the inputs are correct, if there are enough.
2167+
if sig.inputs().len() >= expected_input_count {
2168+
for (arg, input) in
2169+
sig.inputs().iter().zip(hir_sig.decl.inputs).take(expected_input_count)
2170+
{
2171+
if !drcx.types_may_unify(*arg, tokenstream) {
2172+
tcx.sess.emit_err(ProcMacroTypeError {
2173+
span: input.span,
2174+
found: *arg,
2175+
kind,
2176+
expected_signature,
2177+
});
2178+
self.abort.set(true);
2179+
}
2180+
}
2181+
}
2182+
2183+
// Check that there are not too many arguments
2184+
let body_id = tcx.hir().body_owned_by(id.def_id);
2185+
let excess = tcx.hir().body(body_id).params.get(expected_input_count..);
2186+
if let Some(excess @ [begin @ end] | excess @ [begin, .., end]) = excess {
2187+
tcx.sess.emit_err(ProcMacroDiffArguments {
2188+
span: begin.span.to(end.span),
2189+
count: excess.len(),
2190+
kind,
2191+
expected_signature,
2192+
});
2193+
self.abort.set(true);
2194+
}
2195+
}
2196+
}
20662197
}
20672198

20682199
impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
@@ -2225,12 +2356,15 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>)
22252356
}
22262357

22272358
fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
2228-
let check_attr_visitor = &mut CheckAttrVisitor { tcx };
2359+
let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) };
22292360
tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor);
22302361
if module_def_id.is_top_level_module() {
22312362
check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
22322363
check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
22332364
}
2365+
if check_attr_visitor.abort.get() {
2366+
tcx.sess.abort_if_errors()
2367+
}
22342368
}
22352369

22362370
pub(crate) fn provide(providers: &mut Providers) {

compiler/rustc_passes/src/errors.rs

+50
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
1212
use rustc_middle::ty::{MainDefinition, Ty};
1313
use rustc_span::{Span, Symbol, DUMMY_SP};
1414

15+
use crate::check_attr::ProcMacroKind;
1516
use crate::lang_items::Duplicate;
1617

1718
#[derive(Diagnostic)]
@@ -1515,3 +1516,52 @@ pub struct ChangeFieldsToBeOfUnitType {
15151516
#[suggestion_part(code = "()")]
15161517
pub spans: Vec<Span>,
15171518
}
1519+
1520+
#[derive(Diagnostic)]
1521+
#[diag(passes_proc_macro_typeerror)]
1522+
#[note]
1523+
pub(crate) struct ProcMacroTypeError<'tcx> {
1524+
#[primary_span]
1525+
#[label]
1526+
pub span: Span,
1527+
pub found: Ty<'tcx>,
1528+
pub kind: ProcMacroKind,
1529+
pub expected_signature: &'static str,
1530+
}
1531+
1532+
#[derive(Diagnostic)]
1533+
#[diag(passes_proc_macro_diff_arg_count)]
1534+
pub(crate) struct ProcMacroDiffArguments {
1535+
#[primary_span]
1536+
#[label]
1537+
pub span: Span,
1538+
pub count: usize,
1539+
pub kind: ProcMacroKind,
1540+
pub expected_signature: &'static str,
1541+
}
1542+
1543+
#[derive(Diagnostic)]
1544+
#[diag(passes_proc_macro_missing_args)]
1545+
pub(crate) struct ProcMacroMissingArguments {
1546+
#[primary_span]
1547+
#[label]
1548+
pub span: Span,
1549+
pub expected_input_count: usize,
1550+
pub kind: ProcMacroKind,
1551+
pub expected_signature: &'static str,
1552+
}
1553+
1554+
#[derive(Diagnostic)]
1555+
#[diag(passes_proc_macro_invalid_abi)]
1556+
pub(crate) struct ProcMacroInvalidAbi {
1557+
#[primary_span]
1558+
pub span: Span,
1559+
pub abi: &'static str,
1560+
}
1561+
1562+
#[derive(Diagnostic)]
1563+
#[diag(passes_proc_macro_unsafe)]
1564+
pub(crate) struct ProcMacroUnsafe {
1565+
#[primary_span]
1566+
pub span: Span,
1567+
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ symbols! {
288288
Target,
289289
ToOwned,
290290
ToString,
291+
TokenStream,
291292
Try,
292293
TryCaptureGeneric,
293294
TryCapturePrintable,

library/proc_macro/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ pub fn is_available() -> bool {
7474
///
7575
/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]`
7676
/// and `#[proc_macro_derive]` definitions.
77+
#[rustc_diagnostic_item = "TokenStream"]
7778
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
7879
#[derive(Clone)]
7980
pub struct TokenStream(Option<bridge::client::TokenStream>);
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// check-pass
2+
// force-host
3+
// no-prefer-dynamic
4+
5+
#![crate_type = "proc-macro"]
6+
#![allow(private_in_public)]
7+
extern crate proc_macro;
8+
use proc_macro::TokenStream;
9+
10+
#[proc_macro]
11+
pub fn foo<T>(t: T) -> TokenStream {
12+
TokenStream::new()
13+
}
14+
15+
trait Project {
16+
type Assoc;
17+
}
18+
19+
impl Project for () {
20+
type Assoc = TokenStream;
21+
}
22+
23+
#[proc_macro]
24+
pub fn uwu(_input: <() as Project>::Assoc) -> <() as Project>::Assoc {
25+
TokenStream::new()
26+
}

tests/ui/proc-macro/proc-macro-abi.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// force-host
2+
// no-prefer-dynamic
3+
4+
#![crate_type = "proc-macro"]
5+
#![allow(warnings)]
6+
7+
extern crate proc_macro;
8+
use proc_macro::TokenStream;
9+
10+
#[proc_macro]
11+
pub extern "C" fn abi(a: TokenStream) -> TokenStream {
12+
//~^ ERROR proc macro functions may not be `extern "C"`
13+
a
14+
}
15+
16+
#[proc_macro]
17+
pub extern "system" fn abi2(a: TokenStream) -> TokenStream {
18+
//~^ ERROR proc macro functions may not be `extern "system"`
19+
a
20+
}
21+
22+
#[proc_macro]
23+
pub extern fn abi3(a: TokenStream) -> TokenStream {
24+
//~^ ERROR proc macro functions may not be `extern "C"`
25+
a
26+
}
27+
28+
#[proc_macro]
29+
pub extern "Rust" fn abi4(a: TokenStream) -> TokenStream {
30+
a
31+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: proc macro functions may not be `extern "C"`
2+
--> $DIR/proc-macro-abi.rs:11:1
3+
|
4+
LL | pub extern "C" fn abi(a: TokenStream) -> TokenStream {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: proc macro functions may not be `extern "system"`
8+
--> $DIR/proc-macro-abi.rs:17:1
9+
|
10+
LL | pub extern "system" fn abi2(a: TokenStream) -> TokenStream {
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: proc macro functions may not be `extern "C"`
14+
--> $DIR/proc-macro-abi.rs:23:1
15+
|
16+
LL | pub extern fn abi3(a: TokenStream) -> TokenStream {
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 3 previous errors
20+

0 commit comments

Comments
 (0)