diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 2bba7e618c0..f583825fbb3 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -785,13 +785,20 @@ impl Nonterminal { /// See issue #73345 for more details. /// FIXME(#73933): Remove this eventually. pub fn pretty_printing_compatibility_hack(&self) -> bool { - if let NtItem(item) = self { - let name = item.ident.name; - if name == sym::ProceduralMasqueradeDummyType || name == sym::ProcMacroHack { - if let ast::ItemKind::Enum(enum_def, _) = &item.kind { - if let [variant] = &*enum_def.variants { - return variant.ident.name == sym::Input; - } + let item = match self { + NtItem(item) => item, + NtStmt(stmt) => match &stmt.kind { + ast::StmtKind::Item(item) => item, + _ => return false, + }, + _ => return false, + }; + + let name = item.ident.name; + if name == sym::ProceduralMasqueradeDummyType || name == sym::ProcMacroHack { + if let ast::ItemKind::Enum(enum_def, _) = &item.kind { + if let [variant] = &*enum_def.variants { + return variant.ident.name == sym::Input; } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 72d94af4694..8c7e85f1eeb 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -54,7 +54,27 @@ impl MultiItemModifier for BuiltinDerive { // so we are doing it here in a centralized way. let span = ecx.with_def_site_ctxt(span); let mut items = Vec::new(); - (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a)); + match item { + Annotatable::Stmt(stmt) => { + if let ast::StmtKind::Item(item) = stmt.into_inner().kind { + (self.0)(ecx, span, meta_item, &Annotatable::Item(item), &mut |a| { + // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx' + // to the function + items.push(Annotatable::Stmt(P(ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(a.expect_item()), + span, + tokens: None, + }))); + }); + } else { + unreachable!("should have already errored on non-item statement") + } + } + _ => { + (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a)); + } + } ExpandResult::Ready(items) } } diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 8478fcfbf09..e976805d9dd 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -4,7 +4,7 @@ use rustc_ast::expand::allocator::{ AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS, }; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Attribute, Expr, FnHeader, FnSig, Generics, Param}; +use rustc_ast::{self as ast, Attribute, Expr, FnHeader, FnSig, Generics, Param, StmtKind}; use rustc_ast::{ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -14,7 +14,7 @@ pub fn expand( ecx: &mut ExtCtxt<'_>, _span: Span, meta_item: &ast::MetaItem, - item: Annotatable, + mut item: Annotatable, ) -> Vec { check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator); @@ -22,6 +22,17 @@ pub fn expand( ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics"); vec![item] }; + let orig_item = item.clone(); + let mut is_stmt = false; + + // Allow using `#[global_allocator]` on an item statement + if let Annotatable::Stmt(stmt) = &item { + if let StmtKind::Item(item_) = &stmt.kind { + item = Annotatable::Item(item_.clone()); + is_stmt = true; + } + } + let item = match item { Annotatable::Item(item) => match item.kind { ItemKind::Static(..) => item, @@ -41,9 +52,14 @@ pub fn expand( let const_ty = ecx.ty(span, TyKind::Tup(Vec::new())); let const_body = ecx.expr_block(ecx.block(span, stmts)); let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body); + let const_item = if is_stmt { + Annotatable::Stmt(P(ecx.stmt_item(span, const_item))) + } else { + Annotatable::Item(const_item) + }; // Return the original item and the new methods. - vec![Annotatable::Item(item), Annotatable::Item(const_item)] + vec![orig_item, const_item] } struct AllocFnFactory<'a, 'b> { diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 1de0b32f519..25d3f46da6c 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -4,6 +4,7 @@ use crate::util::check_builtin_macro_attribute; use rustc_ast as ast; use rustc_ast::attr; +use rustc_ast::ptr::P; use rustc_ast_pretty::pprust; use rustc_expand::base::*; use rustc_session::Session; @@ -78,8 +79,16 @@ pub fn expand_test_or_bench( return vec![]; } - let item = match item { - Annotatable::Item(i) => i, + let (item, is_stmt) = match item { + Annotatable::Item(i) => (i, false), + Annotatable::Stmt(stmt) if matches!(stmt.kind, ast::StmtKind::Item(_)) => { + // FIXME: Use an 'if let' guard once they are implemented + if let ast::StmtKind::Item(i) = stmt.into_inner().kind { + (i, true) + } else { + unreachable!() + } + } other => { cx.struct_span_err( other.span(), @@ -304,14 +313,25 @@ pub fn expand_test_or_bench( tracing::debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const)); - vec![ - // Access to libtest under a hygienic name - Annotatable::Item(test_extern), - // The generated test case - Annotatable::Item(test_const), - // The original item - Annotatable::Item(item), - ] + if is_stmt { + vec![ + // Access to libtest under a hygienic name + Annotatable::Stmt(P(cx.stmt_item(sp, test_extern))), + // The generated test case + Annotatable::Stmt(P(cx.stmt_item(sp, test_const))), + // The original item + Annotatable::Stmt(P(cx.stmt_item(sp, item))), + ] + } else { + vec![ + // Access to libtest under a hygienic name + Annotatable::Item(test_extern), + // The generated test case + Annotatable::Item(test_const), + // The original item + Annotatable::Item(item), + ] + } } fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String { diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 1c76c31e1a7..1b5c06a96bc 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -234,6 +234,15 @@ impl Annotatable { pub fn derive_allowed(&self) -> bool { match *self { + Annotatable::Stmt(ref stmt) => match stmt.kind { + ast::StmtKind::Item(ref item) => match item.kind { + ast::ItemKind::Struct(..) + | ast::ItemKind::Enum(..) + | ast::ItemKind::Union(..) => true, + _ => false, + }, + _ => false, + }, Annotatable::Item(ref item) => match item.kind { ast::ItemKind::Struct(..) | ast::ItemKind::Enum(..) | ast::ItemKind::Union(..) => { true diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 5be2fee8b38..ce560c6c178 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -795,7 +795,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { | Annotatable::TraitItem(_) | Annotatable::ImplItem(_) | Annotatable::ForeignItem(_) => return, - Annotatable::Stmt(_) => "statements", + Annotatable::Stmt(stmt) => { + // Attributes are stable on item statements, + // but unstable on all other kinds of statements + if stmt.is_item() { + return; + } + "statements" + } Annotatable::Expr(_) => "expressions", Annotatable::Arm(..) | Annotatable::Field(..) @@ -1266,9 +1273,19 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { // we'll expand attributes on expressions separately if !stmt.is_expr() { - // FIXME: Handle custom attributes on statements (#15701). - let attr = - if stmt.is_item() { None } else { self.take_first_attr_no_derive(&mut stmt) }; + let attr = if stmt.is_item() { + // FIXME: Implement proper token collection for statements + if let StmtKind::Item(item) = &mut stmt.kind { + stmt.tokens = item.tokens.take() + } else { + unreachable!() + }; + self.take_first_attr(&mut stmt) + } else { + // Ignore derives on non-item statements for backwards compatibility. + // This will result in a unused attribute warning + self.take_first_attr_no_derive(&mut stmt) + }; if let Some(attr) = attr { return self diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index dea167740ed..36707a1ae27 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -1,6 +1,7 @@ use crate::base::{self, *}; use crate::proc_macro_server; +use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::{self as ast, *}; @@ -74,8 +75,20 @@ impl MultiItemModifier for ProcMacroDerive { _meta_item: &ast::MetaItem, item: Annotatable, ) -> ExpandResult, Annotatable> { + // We need special handling for statement items + // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`) + let mut is_stmt = false; let item = match item { Annotatable::Item(item) => token::NtItem(item), + Annotatable::Stmt(stmt) => { + is_stmt = true; + assert!(stmt.is_item()); + + // A proc macro can't observe the fact that we're passing + // them an `NtStmt` - it can only see the underlying tokens + // of the wrapped item + token::NtStmt(stmt.into_inner()) + } _ => unreachable!(), }; let input = if item.pretty_printing_compatibility_hack() { @@ -106,7 +119,13 @@ impl MultiItemModifier for ProcMacroDerive { loop { match parser.parse_item() { Ok(None) => break, - Ok(Some(item)) => items.push(Annotatable::Item(item)), + Ok(Some(item)) => { + if is_stmt { + items.push(Annotatable::Stmt(P(ecx.stmt_item(span, item)))); + } else { + items.push(Annotatable::Item(item)); + } + } Err(mut err) => { err.emit(); break; diff --git a/src/test/ui/proc-macro/allowed-attr-stmt-expr.rs b/src/test/ui/proc-macro/allowed-attr-stmt-expr.rs new file mode 100644 index 00000000000..03c10a43248 --- /dev/null +++ b/src/test/ui/proc-macro/allowed-attr-stmt-expr.rs @@ -0,0 +1,59 @@ +// aux-build:attr-stmt-expr.rs +// aux-build:test-macros.rs +// compile-flags: -Z span-debug +// check-pass + +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] +#![feature(rustc_attrs)] +#![allow(dead_code)] + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +extern crate attr_stmt_expr; +extern crate test_macros; +use attr_stmt_expr::{expect_let, expect_print_stmt, expect_expr, expect_print_expr}; +use test_macros::print_attr; +use std::println; + +fn print_str(string: &'static str) { + // macros are handled a bit differently + #[expect_print_expr] + println!("{}", string) +} + +macro_rules! make_stmt { + ($stmt:stmt) => { + $stmt + } +} + +macro_rules! second_make_stmt { + ($stmt:stmt) => { + make_stmt!($stmt); + } +} + + +fn main() { + make_stmt!(struct Foo {}); + + #[print_attr] + #[expect_let] + let string = "Hello, world!"; + + #[print_attr] + #[expect_print_stmt] + println!("{}", string); + + #[print_attr] + second_make_stmt!(#[allow(dead_code)] struct Bar {}); + + #[print_attr] + #[rustc_dummy] + struct Other {}; + + #[expect_expr] + print_str("string") +} diff --git a/src/test/ui/proc-macro/allowed-attr-stmt-expr.stdout b/src/test/ui/proc-macro/allowed-attr-stmt-expr.stdout new file mode 100644 index 00000000000..0c7ac4fb682 --- /dev/null +++ b/src/test/ui/proc-macro/allowed-attr-stmt-expr.stdout @@ -0,0 +1,187 @@ +PRINT-ATTR INPUT (DISPLAY): #[expect_let] let string = "Hello, world!" ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "expect_let", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "let", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "string", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '=', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Literal { + kind: Str, + symbol: "Hello, world!", + suffix: None, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): #[expect_print_stmt] println ! ("{}", string) ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "expect_print_stmt", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "println", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Literal { + kind: Str, + symbol: "{}", + suffix: None, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "string", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): second_make_stmt ! (#[allow(dead_code)] struct Bar { }) ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "second_make_stmt", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "allow", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "dead_code", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "struct", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "Bar", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "struct", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "Other", + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/allowed-attr-stmt-expr.rs:1:1: 1:1 (#0), + }, +] diff --git a/src/test/ui/proc-macro/attr-stmt-expr.rs b/src/test/ui/proc-macro/attr-stmt-expr.rs index 14a392db4e1..ca1b163c986 100644 --- a/src/test/ui/proc-macro/attr-stmt-expr.rs +++ b/src/test/ui/proc-macro/attr-stmt-expr.rs @@ -1,8 +1,17 @@ // aux-build:attr-stmt-expr.rs +// aux-build:test-macros.rs +// compile-flags: -Z span-debug #![feature(proc_macro_hygiene)] +#![feature(rustc_attrs)] +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; +extern crate test_macros; extern crate attr_stmt_expr; + +use test_macros::print_attr; +use std::println; use attr_stmt_expr::{expect_let, expect_print_stmt, expect_expr, expect_print_expr}; fn print_str(string: &'static str) { @@ -13,13 +22,36 @@ fn print_str(string: &'static str) { println!("{}", string) } +macro_rules! make_stmt { + ($stmt:stmt) => { + $stmt + } +} + +macro_rules! second_make_stmt { + ($stmt:stmt) => { + make_stmt!($stmt); + } +} + fn main() { + make_stmt!(struct Foo {}); + + #[print_attr] #[expect_let] let string = "Hello, world!"; + #[print_attr] #[expect_print_stmt] println!("{}", string); + #[print_attr] + second_make_stmt!(#[allow(dead_code)] struct Bar {}); + + #[print_attr] + #[rustc_dummy] + struct Other {} + #[expect_expr] //~^ ERROR attributes on expressions are experimental //~| HELP add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable diff --git a/src/test/ui/proc-macro/attr-stmt-expr.stderr b/src/test/ui/proc-macro/attr-stmt-expr.stderr index 0d6f247cf83..7bd60e8ee77 100644 --- a/src/test/ui/proc-macro/attr-stmt-expr.stderr +++ b/src/test/ui/proc-macro/attr-stmt-expr.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/attr-stmt-expr.rs:10:5 + --> $DIR/attr-stmt-expr.rs:19:5 | LL | #[expect_print_expr] | ^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #[expect_print_expr] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/attr-stmt-expr.rs:23:5 + --> $DIR/attr-stmt-expr.rs:55:5 | LL | #[expect_expr] | ^^^^^^^^^^^^^^ diff --git a/src/test/ui/proc-macro/attr-stmt-expr.stdout b/src/test/ui/proc-macro/attr-stmt-expr.stdout new file mode 100644 index 00000000000..5c1b586725b --- /dev/null +++ b/src/test/ui/proc-macro/attr-stmt-expr.stdout @@ -0,0 +1,187 @@ +PRINT-ATTR INPUT (DISPLAY): #[expect_let] let string = "Hello, world!" ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "expect_let", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "let", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "string", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '=', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Literal { + kind: Str, + symbol: "Hello, world!", + suffix: None, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): #[expect_print_stmt] println ! ("{}", string) ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "expect_print_stmt", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "println", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Literal { + kind: Str, + symbol: "{}", + suffix: None, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "string", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): second_make_stmt ! (#[allow(dead_code)] struct Bar { }) ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "second_make_stmt", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "allow", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "dead_code", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "struct", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "Bar", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + ], + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "struct", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Ident { + ident: "Other", + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/attr-stmt-expr.rs:1:1: 1:1 (#0), + }, +] diff --git a/src/test/ui/proc-macro/keep-expr-tokens.rs b/src/test/ui/proc-macro/keep-expr-tokens.rs index 888785363cf..0bf889a855d 100644 --- a/src/test/ui/proc-macro/keep-expr-tokens.rs +++ b/src/test/ui/proc-macro/keep-expr-tokens.rs @@ -1,7 +1,12 @@ // aux-build:test-macros.rs +// compile-flags: -Z span-debug #![feature(stmt_expr_attributes)] #![feature(proc_macro_hygiene)] +#![feature(rustc_attrs)] + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; extern crate test_macros; @@ -12,4 +17,8 @@ fn main() { for item in missing_fn() {} //~ ERROR cannot find (#[recollect_attr] #[recollect_attr] ((#[recollect_attr] bad))); //~ ERROR cannot + + #[test_macros::print_attr] + #[rustc_dummy] + { 1 +1; } // Don't change the weird spacing of the '+' } diff --git a/src/test/ui/proc-macro/keep-expr-tokens.stderr b/src/test/ui/proc-macro/keep-expr-tokens.stderr index 2be8c0184da..11052d11c25 100644 --- a/src/test/ui/proc-macro/keep-expr-tokens.stderr +++ b/src/test/ui/proc-macro/keep-expr-tokens.stderr @@ -1,11 +1,11 @@ error[E0425]: cannot find function `missing_fn` in this scope - --> $DIR/keep-expr-tokens.rs:12:17 + --> $DIR/keep-expr-tokens.rs:17:17 | LL | for item in missing_fn() {} | ^^^^^^^^^^ not found in this scope error[E0425]: cannot find value `bad` in this scope - --> $DIR/keep-expr-tokens.rs:14:62 + --> $DIR/keep-expr-tokens.rs:19:62 | LL | (#[recollect_attr] #[recollect_attr] ((#[recollect_attr] bad))); | ^^^ not found in this scope diff --git a/src/test/ui/proc-macro/keep-expr-tokens.stdout b/src/test/ui/proc-macro/keep-expr-tokens.stdout new file mode 100644 index 00000000000..fcd72a0e017 --- /dev/null +++ b/src/test/ui/proc-macro/keep-expr-tokens.stdout @@ -0,0 +1,46 @@ +PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] { 1 + 1 ; } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/keep-expr-tokens.rs:22:5: 22:6 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/keep-expr-tokens.rs:22:7: 22:18 (#0), + }, + ], + span: $DIR/keep-expr-tokens.rs:22:6: 22:19 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/keep-expr-tokens.rs:23:7: 23:8 (#0), + }, + Punct { + ch: '+', + spacing: Alone, + span: $DIR/keep-expr-tokens.rs:23:9: 23:10 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/keep-expr-tokens.rs:23:10: 23:11 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/keep-expr-tokens.rs:23:11: 23:12 (#0), + }, + ], + span: $DIR/keep-expr-tokens.rs:23:5: 23:14 (#0), + }, +]