diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 1501bd6c73e29..f9ffddf790847 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -161,6 +161,8 @@ builtin_macros_eii_only_once = `#[{$name}]` can only be specified once builtin_macros_eii_shared_macro_expected_function = `#[{$name}]` is only valid on functions builtin_macros_eii_shared_macro_expected_max_one_argument = `#[{$name}]` expected no arguments or a single argument: `#[{$name}(default)]` +builtin_macros_eii_shared_macro_in_statement_position = `#[{$name}]` can only be used on functions inside a module + .label = `#[{$name}]` is used on this item, which is part of another item's local scope builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index cec7599d68e9c..43e9dfe0092e7 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -1,8 +1,7 @@ use rustc_ast::token::{Delimiter, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::{ - Attribute, DUMMY_NODE_ID, EiiDecl, EiiImpl, ItemKind, MetaItem, Path, Stmt, StmtKind, - Visibility, ast, + Attribute, DUMMY_NODE_ID, EiiDecl, EiiImpl, ItemKind, MetaItem, Path, StmtKind, Visibility, ast, }; use rustc_ast_pretty::pprust::path_to_string; use rustc_expand::base::{Annotatable, ExtCtxt}; @@ -12,6 +11,7 @@ use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroExpectedFunction, + EiiSharedMacroInStatementPosition, }; /// ```rust @@ -55,29 +55,29 @@ fn eii_( ecx: &mut ExtCtxt<'_>, eii_attr_span: Span, meta_item: &ast::MetaItem, - item: Annotatable, + orig_item: Annotatable, impl_unsafe: bool, ) -> Vec { let eii_attr_span = ecx.with_def_site_ctxt(eii_attr_span); - let (item, wrap_item): (_, &dyn Fn(_) -> _) = if let Annotatable::Item(item) = item { - (item, &Annotatable::Item) - } else if let Annotatable::Stmt(ref stmt) = item + let item = if let Annotatable::Item(item) = orig_item { + item + } else if let Annotatable::Stmt(ref stmt) = orig_item && let StmtKind::Item(ref item) = stmt.kind + && let ItemKind::Fn(ref f) = item.kind { - (item.clone(), &|item| { - Annotatable::Stmt(Box::new(Stmt { - id: DUMMY_NODE_ID, - kind: StmtKind::Item(item), - span: eii_attr_span, - })) - }) + ecx.dcx().emit_err(EiiSharedMacroInStatementPosition { + span: eii_attr_span.to(item.span), + name: path_to_string(&meta_item.path), + item_span: f.ident.span, + }); + return vec![orig_item]; } else { ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { span: eii_attr_span, name: path_to_string(&meta_item.path), }); - return vec![item]; + return vec![orig_item]; }; let ast::Item { attrs, id: _, span: _, vis, kind: ItemKind::Fn(func), tokens: _ } = @@ -87,7 +87,7 @@ fn eii_( span: eii_attr_span, name: path_to_string(&meta_item.path), }); - return vec![wrap_item(item)]; + return vec![Annotatable::Item(item)]; }; // only clone what we need let attrs = attrs.clone(); @@ -98,17 +98,19 @@ fn eii_( filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path); let Ok(macro_name) = name_for_impl_macro(ecx, &func, &meta_item) else { - return vec![wrap_item(item)]; + // we don't need to wrap in Annotatable::Stmt conditionally since + // EII can't be used on items in statement position + return vec![Annotatable::Item(item)]; }; // span of the declaring item without attributes let item_span = func.sig.span; let foreign_item_name = func.ident; - let mut return_items = Vec::new(); + let mut module_items = Vec::new(); if func.body.is_some() { - return_items.push(generate_default_impl( + module_items.push(generate_default_impl( ecx, &func, impl_unsafe, @@ -119,7 +121,7 @@ fn eii_( )) } - return_items.push(generate_foreign_item( + module_items.push(generate_foreign_item( ecx, eii_attr_span, item_span, @@ -127,7 +129,7 @@ fn eii_( vis, &attrs_from_decl, )); - return_items.push(generate_attribute_macro_to_implement( + module_items.push(generate_attribute_macro_to_implement( ecx, eii_attr_span, macro_name, @@ -136,7 +138,9 @@ fn eii_( &attrs_from_decl, )); - return_items.into_iter().map(wrap_item).collect() + // we don't need to wrap in Annotatable::Stmt conditionally since + // EII can't be used on items in statement position + module_items.into_iter().map(Annotatable::Item).collect() } /// Decide on the name of the macro that can be used to implement the EII. @@ -213,29 +217,14 @@ fn generate_default_impl( known_eii_macro_resolution: Some(ast::EiiDecl { foreign_item: ecx.path( foreign_item_name.span, - // prefix super to escape the `dflt` module generated below - vec![Ident::from_str_and_span("super", foreign_item_name.span), foreign_item_name], + // prefix self to explicitly escape the const block generated below + // NOTE: this is why EIIs can't be used on statements + vec![Ident::from_str_and_span("self", foreign_item_name.span), foreign_item_name], ), impl_unsafe, }), }); - let item_mod = |span: Span, name: Ident, items: ThinVec>| { - ecx.item( - item_span, - ThinVec::new(), - ItemKind::Mod( - ast::Safety::Default, - name, - ast::ModKind::Loaded( - items, - ast::Inline::Yes, - ast::ModSpans { inner_span: span, inject_use_span: span }, - ), - ), - ) - }; - let anon_mod = |span: Span, stmts: ThinVec| { let unit = ecx.ty(item_span, ast::TyKind::Tup(ThinVec::new())); let underscore = Ident::new(kw::Underscore, item_span); @@ -248,33 +237,13 @@ fn generate_default_impl( }; // const _: () = { - // mod dflt { - // use super::*; - // - // } + // // } anon_mod( item_span, thin_vec![ecx.stmt_item( item_span, - item_mod( - item_span, - Ident::from_str_and_span("dflt", item_span), - thin_vec![ - ecx.item( - item_span, - thin_vec![ecx.attr_nested_word(sym::allow, sym::unused_imports, item_span)], - ItemKind::Use(ast::UseTree { - prefix: ast::Path::from_ident(Ident::from_str_and_span( - "super", item_span, - )), - kind: ast::UseTreeKind::Glob, - span: item_span, - }) - ), - ecx.item(item_span, attrs, ItemKind::Fn(Box::new(default_func))) - ] - ) + ecx.item(item_span, attrs, ItemKind::Fn(Box::new(default_func))) ),], ) } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 5d4c4e340fa1f..a9ce41c9be76e 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1039,6 +1039,16 @@ pub(crate) struct EiiSharedMacroExpectedFunction { pub name: String, } +#[derive(Diagnostic)] +#[diag(builtin_macros_eii_shared_macro_in_statement_position)] +pub(crate) struct EiiSharedMacroInStatementPosition { + #[primary_span] + pub span: Span, + pub name: String, + #[label] + pub item_span: Span, +} + #[derive(Diagnostic)] #[diag(builtin_macros_eii_only_once)] pub(crate) struct EiiOnlyOnce { diff --git a/tests/ui/eii/error_statement_position.rs b/tests/ui/eii/error_statement_position.rs index cf81e7e6a8b23..75d87595b9e39 100644 --- a/tests/ui/eii/error_statement_position.rs +++ b/tests/ui/eii/error_statement_position.rs @@ -1,11 +1,6 @@ #![feature(extern_item_impls)] -// EIIs can, despite not being super useful, be declared in statement position -// nested inside items. Items in statement position, when expanded as part of a macro, -// need to be wrapped slightly differently (in an `ast::Statement`). -// We did this on the happy path (no errors), but when there was an error, we'd -// replace it with *just* an `ast::Item` not wrapped in an `ast::Statement`. -// This caused an ICE (https://github.com/rust-lang/rust/issues/149980). -// this test fails to build, but demonstrates that no ICE is produced. +// EIIs cannot be used in statement position. +// This is also a regression test for an ICE (https://github.com/rust-lang/rust/issues/149980). fn main() { struct Bar; @@ -13,4 +8,10 @@ fn main() { #[eii] //~^ ERROR `#[eii]` is only valid on functions impl Bar {} + + + // Even on functions, eiis in statement position are rejected + #[eii] + //~^ ERROR `#[eii]` can only be used on functions inside a module + fn foo() {} } diff --git a/tests/ui/eii/error_statement_position.stderr b/tests/ui/eii/error_statement_position.stderr index 01b7394ef00fa..f14e6c33e64f5 100644 --- a/tests/ui/eii/error_statement_position.stderr +++ b/tests/ui/eii/error_statement_position.stderr @@ -1,8 +1,17 @@ error: `#[eii]` is only valid on functions - --> $DIR/error_statement_position.rs:13:5 + --> $DIR/error_statement_position.rs:8:5 | LL | #[eii] | ^^^^^^ -error: aborting due to 1 previous error +error: `#[eii]` can only be used on functions inside a module + --> $DIR/error_statement_position.rs:14:5 + | +LL | #[eii] + | ^^^^^^ +LL | +LL | fn foo() {} + | --- `#[eii]` is used on this item, which is part of another item's local scope + +error: aborting due to 2 previous errors