diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 91c9a1524e144..ad621d8ae537a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -454,6 +454,7 @@ pub struct Crate { pub module: Mod, pub attrs: Vec, pub span: Span, + pub tokens: Option, } /// A spanned compile-time attribute list item. diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index c68a743303a27..f822f68c9ebb9 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -1383,6 +1383,6 @@ macro_rules! derive_has_attrs { } derive_has_attrs! { - Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm, - ast::Field, ast::FieldPat, ast::Variant_ + Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, + ast::Arm, ast::Field, ast::FieldPat, ast::Variant_, ast::Crate } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 0c313ab14899f..4640d575d65e1 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -35,6 +35,7 @@ use tokenstream::{self, TokenStream}; #[derive(Debug,Clone)] pub enum Annotatable { + Crate(P), Item(P), TraitItem(P), ImplItem(P), @@ -46,6 +47,7 @@ pub enum Annotatable { impl HasAttrs for Annotatable { fn attrs(&self) -> &[Attribute] { match *self { + Annotatable::Crate(ref krate) => &krate.attrs, Annotatable::Item(ref item) => &item.attrs, Annotatable::TraitItem(ref trait_item) => &trait_item.attrs, Annotatable::ImplItem(ref impl_item) => &impl_item.attrs, @@ -57,6 +59,7 @@ impl HasAttrs for Annotatable { fn map_attrs) -> Vec>(self, f: F) -> Self { match self { + Annotatable::Crate(krate) => Annotatable::Crate(krate.map_attrs(f)), Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)), Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)), Annotatable::ImplItem(impl_item) => Annotatable::ImplItem(impl_item.map_attrs(f)), @@ -71,6 +74,7 @@ impl HasAttrs for Annotatable { impl Annotatable { pub fn span(&self) -> Span { match *self { + Annotatable::Crate(ref krate) => krate.span, Annotatable::Item(ref item) => item.span, Annotatable::TraitItem(ref trait_item) => trait_item.span, Annotatable::ImplItem(ref impl_item) => impl_item.span, @@ -87,6 +91,13 @@ impl Annotatable { } } + pub fn expect_crate(self) -> ast::Crate { + match self { + Annotatable::Crate(c) => c.into_inner(), + _ => panic!("expected crate"), + } + } + pub fn map_item_or(self, mut f: F, mut or: G) -> Annotatable where F: FnMut(P) -> P, G: FnMut(Annotatable) -> Annotatable @@ -118,6 +129,20 @@ impl Annotatable { } } + pub fn expect_stmt(self) -> ast::Stmt { + match self { + Annotatable::Stmt(stmt) => stmt.into_inner(), + _ => panic!("expected statement"), + } + } + + pub fn expect_expr(self) -> P { + match self { + Annotatable::Expr(expr) => expr, + _ => panic!("expected expression"), + } + } + pub fn derive_allowed(&self) -> bool { match *self { Annotatable::Item(ref item) => match item.node { @@ -310,6 +335,11 @@ macro_rules! make_stmts_default { /// The result of a macro expansion. The return values of the various /// methods are spliced into the AST at the callsite of the macro. pub trait MacResult { + /// Create a whole crate. + fn make_crate(self: Box) -> Option> { + None + } + /// Create an expression. fn make_expr(self: Box) -> Option> { None @@ -375,6 +405,7 @@ macro_rules! make_MacEager { } make_MacEager! { + krate: P, expr: P, pat: P, items: SmallVector>, @@ -386,6 +417,10 @@ make_MacEager! { } impl MacResult for MacEager { + fn make_crate(self: Box) -> Option> { + self.krate + } + fn make_expr(self: Box) -> Option> { self.expr } @@ -489,6 +524,20 @@ impl DummyResult { } impl MacResult for DummyResult { + fn make_crate(self: Box) -> Option> { + Some(P( + ast::Crate{ + attrs: vec![], + module: ast::Mod { + inner: self.span, + items: vec![], + }, + span: self.span, + tokens: None, + } + )) + } + fn make_expr(self: Box) -> Option> { Some(DummyResult::raw_expr(self.span)) } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 678c20402d6f4..cecee927260d6 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -45,13 +45,19 @@ macro_rules! expansions { $(.$fold:ident)* $(lift .$fold_elt:ident)*, $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { #[derive(Copy, Clone, PartialEq, Eq)] - pub enum ExpansionKind { OptExpr, $( $kind, )* } - pub enum Expansion { OptExpr(Option>), $( $kind($ty), )* } + pub enum ExpansionKind { OptExpr, Crate, $( $kind, )* } + pub enum Expansion { + OptExpr(Option>), + // we don't box `ast::Crate` in most places but we do it here to keep `Expansion` small + Crate(P), + $( $kind($ty), )* + } impl ExpansionKind { pub fn name(self) -> &'static str { match self { ExpansionKind::OptExpr => "expression", + ExpansionKind::Crate => "crate", $( ExpansionKind::$kind => $kind_name, )* } } @@ -59,6 +65,7 @@ macro_rules! expansions { fn make_from<'a>(self, result: Box) -> Option { match self { ExpansionKind::OptExpr => result.make_expr().map(Some).map(Expansion::OptExpr), + ExpansionKind::Crate => result.make_crate().map(Expansion::Crate), $( ExpansionKind::$kind => result.$make().map(Expansion::$kind), )* } } @@ -71,6 +78,14 @@ macro_rules! expansions { _ => panic!("Expansion::make_* called on the wrong kind of expansion"), } } + + pub fn make_crate(self) -> ast::Crate { + match self { + Expansion::Crate(krate) => krate.into_inner(), + _ => panic!("Expansion::make_* called on the wrong kind of expansion"), + } + } + $( pub fn $make(self) -> $ty { match self { Expansion::$kind(ast) => ast, @@ -82,6 +97,7 @@ macro_rules! expansions { use self::Expansion::*; match self { OptExpr(expr) => OptExpr(expr.and_then(|expr| folder.fold_opt_expr(expr))), + Crate(krate) => Crate(krate.map(|k| folder.fold_crate(k))), $($( $kind(ast) => $kind(folder.$fold(ast)), )*)* $($( $kind(ast) => { $kind(ast.into_iter().flat_map(|ast| folder.$fold_elt(ast)).collect()) @@ -91,6 +107,7 @@ macro_rules! expansions { pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { match *self { + Expansion::Crate(ref krate) => visitor.visit_crate(krate), Expansion::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), Expansion::OptExpr(None) => {} $($( Expansion::$kind(ref ast) => visitor.$visit(ast), )*)* @@ -105,6 +122,11 @@ macro_rules! expansions { fn fold_opt_expr(&mut self, expr: P) -> Option> { self.expand(Expansion::OptExpr(Some(expr))).make_opt_expr() } + + fn fold_crate(&mut self, krate: ast::Crate) -> ast::Crate { + self.expand(Expansion::Crate(P(krate))).make_crate() + } + $($(fn $fold(&mut self, node: $ty) -> $ty { self.expand(Expansion::$kind(node)).$make() })*)* @@ -143,8 +165,12 @@ impl ExpansionKind { } fn expect_from_annotatables>(self, items: I) -> Expansion { - let items = items.into_iter(); + let mut items = items.into_iter(); match self { + ExpansionKind::Crate => Expansion::Crate( + // we only box `ast::Crate` for `Expansion` + P(items.next().expect("expected exactly one crate").expect_crate()), + ), ExpansionKind::Items => Expansion::Items(items.map(Annotatable::expect_item).collect()), ExpansionKind::ImplItems => @@ -153,7 +179,14 @@ impl ExpansionKind { Expansion::TraitItems(items.map(Annotatable::expect_trait_item).collect()), ExpansionKind::ForeignItems => Expansion::ForeignItems(items.map(Annotatable::expect_foreign_item).collect()), - _ => unreachable!(), + ExpansionKind::Stmts => Expansion::Stmts(items.map(Annotatable::expect_stmt).collect()), + ExpansionKind::Expr => Expansion::Expr( + items.next().expect("expected exactly one expression").expect_expr() + ), + ExpansionKind::OptExpr => + Expansion::OptExpr(items.next().map(Annotatable::expect_expr)), + ExpansionKind::Pat | ExpansionKind::Ty => + panic!("patterns and types aren't annotatable"), } } } @@ -235,33 +268,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.current_expansion.module = Rc::new(module); self.cx.current_expansion.crate_span = Some(krate.span); - let orig_mod_span = krate.module.inner; - - let krate_item = Expansion::Items(SmallVector::one(P(ast::Item { - attrs: krate.attrs, - span: krate.span, - node: ast::ItemKind::Mod(krate.module), - ident: keywords::Invalid.ident(), - id: ast::DUMMY_NODE_ID, - vis: respan(krate.span.shrink_to_lo(), ast::VisibilityKind::Public), - tokens: None, - }))); - - match self.expand(krate_item).make_items().pop().map(P::into_inner) { - Some(ast::Item { attrs, node: ast::ItemKind::Mod(module), .. }) => { - krate.attrs = attrs; - krate.module = module; - }, - None => { - // Resolution failed so we return an empty expansion - krate.attrs = vec![]; - krate.module = ast::Mod { - inner: orig_mod_span, - items: vec![], - }; - }, - _ => unreachable!(), - }; + // we only box `ast::Crate` for `Expansion` to keep it small + let krate_item = Expansion::Crate(P(krate)); + krate = self.expand(krate_item).make_crate(); + self.cx.trace_macros_diag(); krate } @@ -430,6 +440,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // Since the item itself has already been configured by the InvocationCollector, // we know that fold result vector will contain exactly one element match item { + Annotatable::Crate(krate) => { + Annotatable::Crate(krate.map(|k| cfg.fold_crate(k))) + } Annotatable::Item(item) => { Annotatable::Item(cfg.fold_item(item).pop().unwrap()) } @@ -515,6 +528,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } AttrProcMacro(ref mac) => { let item_tok = TokenTree::Token(DUMMY_SP, Token::interpolated(match item { + Annotatable::Crate(krate) => token::NtCrate(krate), Annotatable::Item(item) => token::NtItem(item), Annotatable::TraitItem(item) => token::NtTraitItem(item.into_inner()), Annotatable::ImplItem(item) => token::NtImplItem(item.into_inner()), @@ -782,6 +796,9 @@ impl<'a> Parser<'a> { pub fn parse_expansion(&mut self, kind: ExpansionKind, macro_legacy_warnings: bool) -> PResult<'a, Expansion> { Ok(match kind { + ExpansionKind::Crate => { + Expansion::Crate(P(self.parse_crate_mod()?)) + } ExpansionKind::Items => { let mut items = SmallVector::new(); while let Some(item) = self.parse_item()? { diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index 9f60882ca29fc..ead2f839a5f38 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -42,6 +42,14 @@ pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion { }); match kind { + ExpansionKind::Crate => Expansion::Crate(P(ast::Crate { + attrs, module: ast::Mod { + inner: span, + items: vec![], + }, + span, + tokens: None, + })), ExpansionKind::Expr => Expansion::Expr(expr_placeholder()), ExpansionKind::OptExpr => Expansion::OptExpr(Some(expr_placeholder())), ExpansionKind::Items => Expansion::Items(SmallVector::one(P(ast::Item { diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index a0cd831a9ba08..512f6ae22c9d3 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -617,6 +617,7 @@ pub fn noop_fold_token(t: token::Token, fld: &mut T) -> token::Token pub fn noop_fold_interpolated(nt: token::Nonterminal, fld: &mut T) -> token::Nonterminal { match nt { + token::NtCrate(krate) => token::NtCrate(krate.map(|k| fld.fold_crate(k))), token::NtItem(item) => token::NtItem(fld.fold_item(item) // this is probably okay, because the only folds likely @@ -1017,7 +1018,7 @@ pub fn noop_fold_mod(Mod {inner, items}: Mod, folder: &mut T) -> Mod } } -pub fn noop_fold_crate(Crate {module, attrs, span}: Crate, +pub fn noop_fold_crate(Crate {module, attrs, span, tokens}: Crate, folder: &mut T) -> Crate { let mut items = folder.fold_item(P(ast::Item { ident: keywords::Invalid.ident(), @@ -1026,16 +1027,16 @@ pub fn noop_fold_crate(Crate {module, attrs, span}: Crate, vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Public), span, node: ast::ItemKind::Mod(module), - tokens: None, + tokens, })).into_iter(); - let (module, attrs, span) = match items.next() { + let (module, attrs, span, tokens) = match items.next() { Some(item) => { assert!(items.next().is_none(), "a crate cannot expand to more than one item"); - item.and_then(|ast::Item { attrs, span, node, .. }| { + item.and_then(|ast::Item { attrs, span, node, tokens, .. }| { match node { - ast::ItemKind::Mod(m) => (m, attrs, span), + ast::ItemKind::Mod(m) => (m, attrs, span, tokens), _ => panic!("fold converted a module to not a module"), } }) @@ -1043,13 +1044,14 @@ pub fn noop_fold_crate(Crate {module, attrs, span}: Crate, None => (ast::Mod { inner: span, items: vec![], - }, vec![], span) + }, vec![], span, None) }; Crate { module, attrs, span, + tokens, } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a7a9ce745122c..422148c55e501 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -7047,6 +7047,7 @@ impl<'a> Parser<'a> { attrs: self.parse_inner_attributes()?, module: self.parse_mod_items(&token::Eof, lo)?, span: lo.to(self.span), + tokens: None, }) } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 0913ed8614792..fb26d71e5a113 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -541,6 +541,9 @@ impl Token { let mut tokens = None; match nt.0 { + Nonterminal::NtCrate(ref krate) => { + tokens = prepend_attrs(sess, &krate.attrs, krate.tokens.as_ref(), span); + } Nonterminal::NtItem(ref item) => { tokens = prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span); } @@ -581,6 +584,7 @@ impl Token { #[derive(Clone, RustcEncodable, RustcDecodable, Eq, Hash)] /// For interpolation during macro expansion. pub enum Nonterminal { + NtCrate(P), NtItem(P), NtBlock(P), NtStmt(ast::Stmt), @@ -623,6 +627,7 @@ impl PartialEq for Nonterminal { impl fmt::Debug for Nonterminal { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { + NtCrate(..) => f.pad("NtCrate(..)"), NtItem(..) => f.pad("NtItem(..)"), NtBlock(..) => f.pad("NtBlock(..)"), NtStmt(..) => f.pad("NtStmt(..)"), diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 3741850b8a974..4615bd437a595 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -262,6 +262,7 @@ pub fn token_to_string(tok: &Token) -> String { token::Shebang(s) => format!("/* shebang: {}*/", s), token::Interpolated(ref nt) => match nt.0 { + token::NtCrate(ref k) => crate_to_string(k), token::NtExpr(ref e) => expr_to_string(e), token::NtMeta(ref e) => meta_item_to_string(e), token::NtTy(ref e) => ty_to_string(e), @@ -286,6 +287,10 @@ pub fn token_to_string(tok: &Token) -> String { } } +pub fn crate_to_string(krate: &ast::Crate) -> String { + to_string(|s| s.print_mod(&krate.module, &krate.attrs)) +} + pub fn ty_to_string(ty: &ast::Ty) -> String { to_string(|s| s.print_type(ty)) } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 8743840e44393..df4e932e7fe48 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -150,6 +150,10 @@ pub trait Visitor<'ast>: Sized { fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FunctionRetTy) { walk_fn_ret_ty(self, ret_ty) } + + fn visit_crate(&mut self, krate: &'ast Crate) { + walk_crate(self, krate) + } } #[macro_export] diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs index 5fd5e29948852..13352c26d8e8e 100644 --- a/src/libsyntax_ext/deriving/custom.rs +++ b/src/libsyntax_ext/deriving/custom.rs @@ -57,7 +57,8 @@ impl MultiItemModifier for ProcMacroDerive { Annotatable::TraitItem(_) | Annotatable::ForeignItem(_) | Annotatable::Stmt(_) | - Annotatable::Expr(_) => { + Annotatable::Expr(_) | + Annotatable::Crate(_) => { ecx.span_err(span, "proc-macro derives may only be \ applied to struct/enum items"); return Vec::new() diff --git a/src/test/compile-fail/issue-49934.rs b/src/test/compile-fail/issue-49934.rs new file mode 100644 index 0000000000000..996ddcce3a4ed --- /dev/null +++ b/src/test/compile-fail/issue-49934.rs @@ -0,0 +1,23 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(stmt_expr_attributes)] + +fn main() { + #[derive(Debug)] //~ ERROR `derive` + println!("Hello, world!"); + + let _ = #[derive(Debug)] "Hello, world!"; + //~^ ERROR `derive` + + let _ = [ + #[derive(Debug)] //~ ERROR `derive` + "Hello, world!" + ]; +}