Skip to content

Expand macros in extern {} blocks #49350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 5, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
@@ -2724,6 +2724,7 @@ impl<'a> LoweringContext<'a> {
hir::ForeignItemStatic(this.lower_ty(t, ImplTraitContext::Disallowed), m)
}
ForeignItemKind::Ty => hir::ForeignItemType,
ForeignItemKind::Macro(_) => panic!("shouldn't exist here"),
},
vis: this.lower_visibility(&i.vis, None),
span: i.span,
4 changes: 4 additions & 0 deletions src/librustc/hir/map/def_collector.rs
Original file line number Diff line number Diff line change
@@ -181,6 +181,10 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
}

fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) {
if let ForeignItemKind::Macro(_) = foreign_item.node {
return self.visit_macro_invoc(foreign_item.id, false);
}

let def = self.create_def(foreign_item.id,
DefPathData::ValueNs(foreign_item.ident.name.as_str()),
REGULAR_SPACE,
18 changes: 17 additions & 1 deletion src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
@@ -381,7 +381,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
.span_label(span, "pattern not allowed in foreign function").emit();
});
}
ForeignItemKind::Static(..) | ForeignItemKind::Ty => {}
ForeignItemKind::Static(..) | ForeignItemKind::Ty | ForeignItemKind::Macro(..) => {}
}

visit::walk_foreign_item(self, fi)
@@ -460,6 +460,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
visit::walk_poly_trait_ref(self, t, m);
}

fn visit_mac(&mut self, mac: &Spanned<Mac_>) {
// when a new macro kind is added but the author forgets to set it up for expansion
// because that's the only part that won't cause a compiler error
self.session.diagnostic()
.span_bug(mac.span, "macro invocation missed in expansion; did you forget to override \
the relevant `fold_*()` method in `PlaceholderExpander`?");
}
}

// Bans nested `impl Trait`, e.g. `impl Into<impl Debug>`.
@@ -522,6 +530,10 @@ impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> {
}
}
}

fn visit_mac(&mut self, _mac: &Spanned<Mac_>) {
// covered in AstValidator
}
}

// Bans `impl Trait` in path projections like `<impl Iterator>::Item` or `Foo::Bar<impl Trait>`.
@@ -583,6 +595,10 @@ impl<'a> Visitor<'a> for ImplTraitProjectionVisitor<'a> {
_ => visit::walk_ty(self, t),
}
}

fn visit_mac(&mut self, _mac: &Spanned<Mac_>) {
// covered in AstValidator
}
}

pub fn check_crate(session: &Session, krate: &Crate) {
6 changes: 6 additions & 0 deletions src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
@@ -456,6 +456,7 @@ impl<'a> Resolver<'a> {
ForeignItemKind::Ty => {
(Def::TyForeign(self.definitions.local_def_id(item.id)), TypeNS)
}
ForeignItemKind::Macro(_) => unreachable!(),
};
let parent = self.current_module;
let vis = self.resolve_visibility(&item.vis);
@@ -816,6 +817,11 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> {
}

fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) {
if let ForeignItemKind::Macro(_) = foreign_item.node {
self.visit_invoc(foreign_item.id);
return;
}

self.resolver.build_reduced_graph_for_foreign_item(foreign_item, self.expansion);
visit::walk_foreign_item(self, foreign_item);
}
1 change: 1 addition & 0 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
@@ -863,6 +863,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
}
ForeignItemKind::Static(..) => NoTypeParameters,
ForeignItemKind::Ty => NoTypeParameters,
ForeignItemKind::Macro(..) => NoTypeParameters,
};
self.with_type_parameter_rib(type_parameters, |this| {
visit::walk_foreign_item(this, foreign_item);
1 change: 1 addition & 0 deletions src/librustc_save_analysis/dump_visitor.rs
Original file line number Diff line number Diff line change
@@ -1812,6 +1812,7 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc
self.dumper.dump_def(&access, var_data);
}
}
ast::ForeignItemKind::Macro(..) => {}
}
}
}
1 change: 1 addition & 0 deletions src/librustc_save_analysis/lib.rs
Original file line number Diff line number Diff line change
@@ -182,6 +182,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
}
// FIXME(plietar): needs a new DefKind in rls-data
ast::ForeignItemKind::Ty => None,
ast::ForeignItemKind::Macro(..) => None,
}
}

1 change: 1 addition & 0 deletions src/librustc_save_analysis/sig.rs
Original file line number Diff line number Diff line change
@@ -822,6 +822,7 @@ impl Sig for ast::ForeignItem {
refs: vec![],
})
}
ast::ForeignItemKind::Macro(..) => Err("macro"),
}
}
}
3 changes: 3 additions & 0 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
@@ -2195,6 +2195,8 @@ pub enum ForeignItemKind {
Static(P<Ty>, bool),
/// A foreign type
Ty,
/// A macro invocation
Macro(Mac),
}

impl ForeignItemKind {
@@ -2203,6 +2205,7 @@ impl ForeignItemKind {
ForeignItemKind::Fn(..) => "foreign function",
ForeignItemKind::Static(..) => "foreign static item",
ForeignItemKind::Ty => "foreign type",
ForeignItemKind::Macro(..) => "macro in foreign module",
}
}
}
28 changes: 28 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@ pub enum Annotatable {
Item(P<ast::Item>),
TraitItem(P<ast::TraitItem>),
ImplItem(P<ast::ImplItem>),
ForeignItem(P<ast::ForeignItem>),
Stmt(P<ast::Stmt>),
Expr(P<ast::Expr>),
}
@@ -48,6 +49,7 @@ impl HasAttrs for Annotatable {
Annotatable::Item(ref item) => &item.attrs,
Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
Annotatable::ImplItem(ref impl_item) => &impl_item.attrs,
Annotatable::ForeignItem(ref foreign_item) => &foreign_item.attrs,
Annotatable::Stmt(ref stmt) => stmt.attrs(),
Annotatable::Expr(ref expr) => &expr.attrs,
}
@@ -58,6 +60,8 @@ impl HasAttrs for Annotatable {
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)),
Annotatable::ForeignItem(foreign_item) =>
Annotatable::ForeignItem(foreign_item.map_attrs(f)),
Annotatable::Stmt(stmt) => Annotatable::Stmt(stmt.map_attrs(f)),
Annotatable::Expr(expr) => Annotatable::Expr(expr.map_attrs(f)),
}
@@ -70,6 +74,7 @@ impl Annotatable {
Annotatable::Item(ref item) => item.span,
Annotatable::TraitItem(ref trait_item) => trait_item.span,
Annotatable::ImplItem(ref impl_item) => impl_item.span,
Annotatable::ForeignItem(ref foreign_item) => foreign_item.span,
Annotatable::Stmt(ref stmt) => stmt.span,
Annotatable::Expr(ref expr) => expr.span,
}
@@ -106,6 +111,13 @@ impl Annotatable {
}
}

pub fn expect_foreign_item(self) -> ast::ForeignItem {
match self {
Annotatable::ForeignItem(i) => i.into_inner(),
_ => panic!("expected foreign item")
}
}

pub fn derive_allowed(&self) -> bool {
match *self {
Annotatable::Item(ref item) => match item.node {
@@ -317,6 +329,9 @@ pub trait MacResult {
None
}

/// Create zero or more items in an `extern {}` block
fn make_foreign_items(self: Box<Self>) -> Option<SmallVector<ast::ForeignItem>> { None }

/// Create a pattern.
fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> {
None
@@ -365,6 +380,7 @@ make_MacEager! {
items: SmallVector<P<ast::Item>>,
impl_items: SmallVector<ast::ImplItem>,
trait_items: SmallVector<ast::TraitItem>,
foreign_items: SmallVector<ast::ForeignItem>,
stmts: SmallVector<ast::Stmt>,
ty: P<ast::Ty>,
}
@@ -386,6 +402,10 @@ impl MacResult for MacEager {
self.trait_items
}

fn make_foreign_items(self: Box<Self>) -> Option<SmallVector<ast::ForeignItem>> {
self.foreign_items
}

fn make_stmts(self: Box<Self>) -> Option<SmallVector<ast::Stmt>> {
match self.stmts.as_ref().map_or(0, |s| s.len()) {
0 => make_stmts_default!(self),
@@ -502,6 +522,14 @@ impl MacResult for DummyResult {
}
}

fn make_foreign_items(self: Box<Self>) -> Option<SmallVector<ast::ForeignItem>> {
if self.expr_only {
None
} else {
Some(SmallVector::new())
}
}

fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<ast::Stmt>> {
Some(SmallVector::one(ast::Stmt {
id: ast::DUMMY_NODE_ID,
58 changes: 58 additions & 0 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
@@ -133,6 +133,8 @@ expansions! {
"trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item;
ImplItems: SmallVector<ast::ImplItem> [SmallVector, ast::ImplItem],
"impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item;
ForeignItems: SmallVector<ast::ForeignItem> [SmallVector, ast::ForeignItem],
"foreign item", .make_foreign_items, lift .fold_foreign_item, lift .visit_foreign_item;
}

impl ExpansionKind {
@@ -149,6 +151,8 @@ impl ExpansionKind {
Expansion::ImplItems(items.map(Annotatable::expect_impl_item).collect()),
ExpansionKind::TraitItems =>
Expansion::TraitItems(items.map(Annotatable::expect_trait_item).collect()),
ExpansionKind::ForeignItems =>
Expansion::ForeignItems(items.map(Annotatable::expect_foreign_item).collect()),
_ => unreachable!(),
}
}
@@ -435,6 +439,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
Annotatable::ImplItem(item) => {
Annotatable::ImplItem(item.map(|item| cfg.fold_impl_item(item).pop().unwrap()))
}
Annotatable::ForeignItem(item) => {
Annotatable::ForeignItem(
item.map(|item| cfg.fold_foreign_item(item).pop().unwrap())
)
}
Annotatable::Stmt(stmt) => {
Annotatable::Stmt(stmt.map(|stmt| cfg.fold_stmt(stmt).pop().unwrap()))
}
@@ -509,6 +518,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
Annotatable::Item(item) => token::NtItem(item),
Annotatable::TraitItem(item) => token::NtTraitItem(item.into_inner()),
Annotatable::ImplItem(item) => token::NtImplItem(item.into_inner()),
Annotatable::ForeignItem(item) => token::NtForeignItem(item.into_inner()),
Annotatable::Stmt(stmt) => token::NtStmt(stmt.into_inner()),
Annotatable::Expr(expr) => token::NtExpr(expr),
})).into();
@@ -793,6 +803,15 @@ impl<'a> Parser<'a> {
}
Expansion::ImplItems(items)
}
ExpansionKind::ForeignItems => {
let mut items = SmallVector::new();
while self.token != token::Eof {
if let Some(item) = self.parse_foreign_item()? {
items.push(item);
}
}
Expansion::ForeignItems(items)
}
ExpansionKind::Stmts => {
let mut stmts = SmallVector::new();
while self.token != token::Eof &&
@@ -1166,6 +1185,44 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
noop_fold_foreign_mod(self.cfg.configure_foreign_mod(foreign_mod), self)
}

fn fold_foreign_item(&mut self,
foreign_item: ast::ForeignItem) -> SmallVector<ast::ForeignItem> {
let (attr, traits, foreign_item) = self.classify_item(foreign_item);

let explain = if self.cx.ecfg.proc_macro_enabled() {
feature_gate::EXPLAIN_PROC_MACROS_IN_EXTERN
} else {
feature_gate::EXPLAIN_MACROS_IN_EXTERN
};

if attr.is_some() || !traits.is_empty() {
if !self.cx.ecfg.macros_in_extern_enabled() {
if let Some(ref attr) = attr {
emit_feature_err(&self.cx.parse_sess, "macros_in_extern", attr.span,
GateIssue::Language, explain);
}
}

let item = Annotatable::ForeignItem(P(foreign_item));
return self.collect_attr(attr, traits, item, ExpansionKind::ForeignItems)
.make_foreign_items();
}

if let ast::ForeignItemKind::Macro(mac) = foreign_item.node {
self.check_attributes(&foreign_item.attrs);

if !self.cx.ecfg.macros_in_extern_enabled() {
emit_feature_err(&self.cx.parse_sess, "macros_in_extern", foreign_item.span,
GateIssue::Language, explain);
}

return self.collect_bang(mac, foreign_item.span, ExpansionKind::ForeignItems)
.make_foreign_items();
}

noop_fold_foreign_item(foreign_item, self)
}

fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
match item {
ast::ItemKind::MacroDef(..) => item,
@@ -1311,6 +1368,7 @@ impl<'feat> ExpansionConfig<'feat> {
fn enable_allow_internal_unstable = allow_internal_unstable,
fn enable_custom_derive = custom_derive,
fn proc_macro_enabled = proc_macro,
fn macros_in_extern_enabled = macros_in_extern,
}
}

11 changes: 11 additions & 0 deletions src/libsyntax/ext/placeholders.rs
Original file line number Diff line number Diff line change
@@ -60,6 +60,10 @@ pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion {
defaultness: ast::Defaultness::Final,
tokens: None,
})),
ExpansionKind::ForeignItems => Expansion::ForeignItems(SmallVector::one(ast::ForeignItem {
id, span, ident, vis, attrs,
node: ast::ForeignItemKind::Macro(mac_placeholder()),
})),
ExpansionKind::Pat => Expansion::Pat(P(ast::Pat {
id, span, node: ast::PatKind::Mac(mac_placeholder()),
})),
@@ -132,6 +136,13 @@ impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> {
}
}

fn fold_foreign_item(&mut self, item: ast::ForeignItem) -> SmallVector<ast::ForeignItem> {
match item.node {
ast::ForeignItemKind::Macro(_) => self.remove(item.id).make_foreign_items(),
_ => noop_fold_foreign_item(item, self),
}
}

fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
match expr.node {
ast::ExprKind::Mac(_) => self.remove(expr.id).make_expr(),
11 changes: 11 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -447,6 +447,9 @@ declare_features! (

// Allows keywords to be escaped for use as identifiers
(active, raw_identifiers, "1.26.0", Some(48589), None),

// Allows macro invocations in `extern {}` blocks
(active, macros_in_extern, "1.27.0", Some(49476), None),
);

declare_features! (
@@ -1296,6 +1299,13 @@ pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str =
pub const EXPLAIN_MACRO_AT_MOST_ONCE_REP: &'static str =
"Using the `?` macro Kleene operator for \"at most one\" repetition is unstable";

pub const EXPLAIN_MACROS_IN_EXTERN: &'static str =
"Macro invocations in `extern {}` blocks are experimental.";

// mention proc-macros when enabled
pub const EXPLAIN_PROC_MACROS_IN_EXTERN: &'static str =
"Macro and proc-macro invocations in `extern {}` blocks are experimental.";

struct PostExpansionVisitor<'a> {
context: &'a Context<'a>,
}
@@ -1600,6 +1610,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
gate_feature_post!(&self, extern_types, i.span,
"extern types are experimental");
}
ast::ForeignItemKind::Macro(..) => {}
}

visit::walk_foreign_item(self, i)
20 changes: 17 additions & 3 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
@@ -60,10 +60,14 @@ pub trait Folder : Sized {
noop_fold_use_tree(use_tree, self)
}

fn fold_foreign_item(&mut self, ni: ForeignItem) -> ForeignItem {
fn fold_foreign_item(&mut self, ni: ForeignItem) -> SmallVector<ForeignItem> {
noop_fold_foreign_item(ni, self)
}

fn fold_foreign_item_simple(&mut self, ni: ForeignItem) -> ForeignItem {
noop_fold_foreign_item_simple(ni, self)
}

fn fold_item(&mut self, i: P<Item>) -> SmallVector<P<Item>> {
noop_fold_item(i, self)
}
@@ -414,7 +418,7 @@ pub fn noop_fold_foreign_mod<T: Folder>(ForeignMod {abi, items}: ForeignMod,
fld: &mut T) -> ForeignMod {
ForeignMod {
abi,
items: items.move_map(|x| fld.fold_foreign_item(x)),
items: items.move_flat_map(|x| fld.fold_foreign_item(x)),
}
}

@@ -648,6 +652,10 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T)
token::NtArg(arg) => token::NtArg(fld.fold_arg(arg)),
token::NtVis(vis) => token::NtVis(fld.fold_vis(vis)),
token::NtLifetime(lifetime) => token::NtLifetime(fld.fold_lifetime(lifetime)),
token::NtForeignItem(ni) =>
token::NtForeignItem(fld.fold_foreign_item(ni)
// see reasoning above
.expect_one("expected fold to produce exactly one item")),
}
}

@@ -1072,7 +1080,12 @@ pub fn noop_fold_item_simple<T: Folder>(Item {id, ident, attrs, node, vis, span,
}
}

pub fn noop_fold_foreign_item<T: Folder>(ni: ForeignItem, folder: &mut T) -> ForeignItem {
pub fn noop_fold_foreign_item<T: Folder>(ni: ForeignItem, folder: &mut T)
-> SmallVector<ForeignItem> {
SmallVector::one(folder.fold_foreign_item_simple(ni))
}

pub fn noop_fold_foreign_item_simple<T: Folder>(ni: ForeignItem, folder: &mut T) -> ForeignItem {
ForeignItem {
id: folder.new_id(ni.id),
vis: folder.fold_vis(ni.vis),
@@ -1086,6 +1099,7 @@ pub fn noop_fold_foreign_item<T: Folder>(ni: ForeignItem, folder: &mut T) -> For
ForeignItemKind::Static(folder.fold_ty(t), m)
}
ForeignItemKind::Ty => ForeignItemKind::Ty,
ForeignItemKind::Macro(mac) => ForeignItemKind::Macro(folder.fold_mac(mac)),
},
span: folder.new_span(ni.span)
}
124 changes: 69 additions & 55 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ use ast::{Ident, ImplItem, IsAuto, Item, ItemKind};
use ast::{Label, Lifetime, LifetimeDef, Lit, LitKind, UintTy};
use ast::Local;
use ast::MacStmtStyle;
use ast::Mac_;
use ast::{Mac, Mac_};
use ast::{MutTy, Mutability};
use ast::{Pat, PatKind, PathSegment};
use ast::{PolyTraitRef, QSelf};
@@ -1417,28 +1417,8 @@ impl<'a> Parser<'a> {
None
};
(ident, TraitItemKind::Const(ty, default), ast::Generics::default())
} else if self.token.is_path_start() && !self.is_extern_non_path() {
} else if let Some(mac) = self.parse_assoc_macro_invoc("trait", None, &mut false)? {
// trait item macro.
// code copied from parse_macro_use_or_failure... abstraction!
let prev_span = self.prev_span;
let lo = self.span;
let pth = self.parse_path(PathStyle::Mod)?;

if pth.segments.len() == 1 {
if !self.eat(&token::Not) {
return Err(self.missing_assoc_item_kind_err("trait", prev_span));
}
} else {
self.expect(&token::Not)?;
}

// eat a matched-delimiter token tree:
let (delim, tts) = self.expect_delimited_token_tree()?;
if delim != token::Brace {
self.expect(&token::Semi)?
}

let mac = respan(lo.to(self.prev_span), Mac_ { path: pth, tts: tts });
(keywords::Invalid.ident(), ast::TraitItemKind::Macro(mac), ast::Generics::default())
} else {
let (constness, unsafety, abi) = self.parse_fn_front_matter()?;
@@ -5406,6 +5386,12 @@ impl<'a> Parser<'a> {
fn missing_assoc_item_kind_err(&mut self, item_type: &str, prev_span: Span)
-> DiagnosticBuilder<'a>
{
let expected_kinds = if item_type == "extern" {
"missing `fn`, `type`, or `static`"
} else {
"missing `fn`, `type`, or `const`"
};

// Given this code `path(`, it seems like this is not
// setting the visibility of a macro invocation, but rather
// a mistyped method declaration.
@@ -5418,9 +5404,9 @@ impl<'a> Parser<'a> {
let sp = prev_span.between(self.prev_span);
let mut err = self.diagnostic().struct_span_err(
sp,
&format!("missing `fn`, `type`, or `const` for {}-item declaration",
item_type));
err.span_label(sp, "missing `fn`, `type`, or `const`");
&format!("{} for {}-item declaration",
expected_kinds, item_type));
err.span_label(sp, expected_kinds);
err
}

@@ -5429,31 +5415,8 @@ impl<'a> Parser<'a> {
-> PResult<'a, (Ident, Vec<Attribute>, ast::Generics,
ast::ImplItemKind)> {
// code copied from parse_macro_use_or_failure... abstraction!
if self.token.is_path_start() && !self.is_extern_non_path() {
if let Some(mac) = self.parse_assoc_macro_invoc("impl", Some(vis), at_end)? {
// Method macro.

let prev_span = self.prev_span;

let lo = self.span;
let pth = self.parse_path(PathStyle::Mod)?;
if pth.segments.len() == 1 {
if !self.eat(&token::Not) {
return Err(self.missing_assoc_item_kind_err("impl", prev_span));
}
} else {
self.expect(&token::Not)?;
}

self.complain_if_pub_macro(&vis.node, prev_span);

// eat a matched-delimiter token tree:
*at_end = true;
let (delim, tts) = self.expect_delimited_token_tree()?;
if delim != token::Brace {
self.expect(&token::Semi)?
}

let mac = respan(lo.to(self.prev_span), Mac_ { path: pth, tts: tts });
Ok((keywords::Invalid.ident(), vec![], ast::Generics::default(),
ast::ImplItemKind::Macro(mac)))
} else {
@@ -6799,7 +6762,9 @@ impl<'a> Parser<'a> {
}

/// Parse a foreign item.
fn parse_foreign_item(&mut self) -> PResult<'a, Option<ForeignItem>> {
pub fn parse_foreign_item(&mut self) -> PResult<'a, Option<ForeignItem>> {
Copy link
Contributor

@petrochenkov petrochenkov Mar 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parser support for macros in foreign blocks is missing (the disambiguation is a bit tricky, but the approach can be copied from macros in traits/impls as well).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had covered this already but I lost a bunch of work somewhere.

maybe_whole!(self, NtForeignItem, |ni| Some(ni));

let attrs = self.parse_outer_attributes()?;
let lo = self.span;
let visibility = self.parse_visibility(false)?;
@@ -6825,12 +6790,26 @@ impl<'a> Parser<'a> {
return Ok(Some(self.parse_item_foreign_type(visibility, lo, attrs)?));
}

// FIXME #5668: this will occur for a macro invocation:
match self.parse_macro_use_or_failure(attrs, true, false, lo, visibility)? {
Some(item) => {
return Err(self.span_fatal(item.span, "macros cannot expand to foreign items"));
match self.parse_assoc_macro_invoc("extern", Some(&visibility), &mut false)? {
Some(mac) => {
Ok(Some(
ForeignItem {
ident: keywords::Invalid.ident(),
span: lo.to(self.prev_span),
id: ast::DUMMY_NODE_ID,
attrs,
vis: visibility,
node: ForeignItemKind::Macro(mac),
}
))
}
None => {
if !attrs.is_empty() {
self.expected_item_err(&attrs);
}

Ok(None)
}
None => Ok(None)
}
}

@@ -6894,6 +6873,41 @@ impl<'a> Parser<'a> {
Ok(None)
}

/// Parse a macro invocation inside a `trait`, `impl` or `extern` block
fn parse_assoc_macro_invoc(&mut self, item_kind: &str, vis: Option<&Visibility>,
at_end: &mut bool) -> PResult<'a, Option<Mac>>
{
if self.token.is_path_start() && !self.is_extern_non_path() {
let prev_span = self.prev_span;
let lo = self.span;
let pth = self.parse_path(PathStyle::Mod)?;

if pth.segments.len() == 1 {
if !self.eat(&token::Not) {
return Err(self.missing_assoc_item_kind_err(item_kind, prev_span));
}
} else {
self.expect(&token::Not)?;
}

if let Some(vis) = vis {
self.complain_if_pub_macro(&vis.node, prev_span);
}

*at_end = true;

// eat a matched-delimiter token tree:
let (delim, tts) = self.expect_delimited_token_tree()?;
if delim != token::Brace {
self.expect(&token::Semi)?
}

Ok(Some(respan(lo.to(self.prev_span), Mac_ { path: pth, tts: tts })))
} else {
Ok(None)
}
}

fn collect_tokens<F, R>(&mut self, f: F) -> PResult<'a, (R, TokenStream)>
where F: FnOnce(&mut Self) -> PResult<'a, R>
{
2 changes: 2 additions & 0 deletions src/libsyntax/parse/token.rs
Original file line number Diff line number Diff line change
@@ -581,6 +581,7 @@ pub enum Nonterminal {
NtArm(ast::Arm),
NtImplItem(ast::ImplItem),
NtTraitItem(ast::TraitItem),
NtForeignItem(ast::ForeignItem),
NtGenerics(ast::Generics),
NtWhereClause(ast::WhereClause),
NtArg(ast::Arg),
@@ -603,6 +604,7 @@ impl fmt::Debug for Nonterminal {
NtArm(..) => f.pad("NtArm(..)"),
NtImplItem(..) => f.pad("NtImplItem(..)"),
NtTraitItem(..) => f.pad("NtTraitItem(..)"),
NtForeignItem(..) => f.pad("NtForeignItem(..)"),
NtGenerics(..) => f.pad("NtGenerics(..)"),
NtWhereClause(..) => f.pad("NtWhereClause(..)"),
NtArg(..) => f.pad("NtArg(..)"),
9 changes: 9 additions & 0 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
@@ -281,6 +281,7 @@ pub fn token_to_string(tok: &Token) -> String {
token::NtArg(ref e) => arg_to_string(e),
token::NtVis(ref e) => vis_to_string(e),
token::NtLifetime(ref e) => lifetime_to_string(e),
token::NtForeignItem(ref ni) => foreign_item_to_string(ni),
}
}
}
@@ -422,6 +423,10 @@ pub fn mac_to_string(arg: &ast::Mac) -> String {
to_string(|s| s.print_mac(arg, ::parse::token::Paren))
}

pub fn foreign_item_to_string(arg: &ast::ForeignItem) -> String {
to_string(|s| s.print_foreign_item(arg))
}

pub fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
format!("{}{}", to_string(|s| s.print_visibility(vis)), s)
}
@@ -1127,6 +1132,10 @@ impl<'a> State<'a> {
self.end()?; // end the head-ibox
self.end() // end the outer cbox
}
ast::ForeignItemKind::Macro(ref m) => {
self.print_mac(m, token::Paren)?;
self.s.word(";")
}
}
}

1 change: 1 addition & 0 deletions src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
@@ -460,6 +460,7 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, foreign_item: &'a
}
ForeignItemKind::Static(ref typ, _) => visitor.visit_ty(typ),
ForeignItemKind::Ty => (),
ForeignItemKind::Macro(ref mac) => visitor.visit_mac(mac),
}

walk_list!(visitor, visit_attribute, &foreign_item.attrs);
1 change: 1 addition & 0 deletions src/libsyntax_ext/deriving/custom.rs
Original file line number Diff line number Diff line change
@@ -55,6 +55,7 @@ impl MultiItemModifier for ProcMacroDerive {
Annotatable::Item(item) => item,
Annotatable::ImplItem(_) |
Annotatable::TraitItem(_) |
Annotatable::ForeignItem(_) |
Annotatable::Stmt(_) |
Annotatable::Expr(_) => {
ecx.span_err(span, "proc-macro derives may only be \
6 changes: 5 additions & 1 deletion src/test/compile-fail-fulldeps/auxiliary/macro_crate_test.rs
Original file line number Diff line number Diff line change
@@ -93,7 +93,9 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt,
}
})
}
// these are covered in proc_macro/attr-stmt-expr.rs
// covered in proc_macro/macros-in-extern.rs
Annotatable::ForeignItem(_) => unimplemented!(),
// covered in proc_macro/attr-stmt-expr.rs
Annotatable::Stmt(_) | Annotatable::Expr(_) => panic!("expected item")
}
}
@@ -147,6 +149,8 @@ fn expand_duplicate(cx: &mut ExtCtxt,
new_it.ident = copy_name;
push(Annotatable::TraitItem(P(new_it)));
}
// covered in proc_macro/macros-in-extern.rs
Annotatable::ForeignItem(_) => unimplemented!(),
// covered in proc_macro/attr-stmt-expr.rs
Annotatable::Stmt(_) | Annotatable::Expr(_) => panic!("expected item")
}
36 changes: 36 additions & 0 deletions src/test/compile-fail-fulldeps/proc-macro/auxiliary/test-macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// no-prefer-dynamic

#![crate_type = "proc-macro"]
#![feature(proc_macro)]

extern crate proc_macro;

use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn nop_attr(_attr: TokenStream, input: TokenStream) -> TokenStream {
assert!(_attr.to_string().is_empty());
input
}

#[proc_macro_attribute]
pub fn no_output(_attr: TokenStream, _input: TokenStream) -> TokenStream {
assert!(_attr.to_string().is_empty());
assert!(!_input.to_string().is_empty());
"".parse().unwrap()
}

#[proc_macro]
pub fn emit_input(input: TokenStream) -> TokenStream {
input
}
38 changes: 38 additions & 0 deletions src/test/compile-fail-fulldeps/proc-macro/macros-in-extern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// aux-build:test-macros.rs
// ignore-stage1
// ignore-wasm32

#![feature(proc_macro)]

extern crate test_macros;

use test_macros::{nop_attr, no_output, emit_input};

fn main() {
assert_eq!(unsafe { rust_get_test_int() }, 0isize);
assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEF);
}

#[link(name = "rust_test_helpers", kind = "static")]
extern {
#[no_output]
//~^ ERROR Macro and proc-macro invocations in `extern {}` blocks are experimental.
fn some_definitely_unknown_symbol_which_should_be_removed();

#[nop_attr]
//~^ ERROR Macro and proc-macro invocations in `extern {}` blocks are experimental.
fn rust_get_test_int() -> isize;

emit_input!(fn rust_dbg_extern_identity_u32(arg: u32) -> u32;);
//~^ ERROR Macro and proc-macro invocations in `extern {}` blocks are experimental.
}
42 changes: 42 additions & 0 deletions src/test/compile-fail/macros-in-extern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-wasm32

#![feature(decl_macro)]

macro_rules! returns_isize(
($ident:ident) => (
fn $ident() -> isize;
)
);

macro takes_u32_returns_u32($ident:ident) {
fn $ident (arg: u32) -> u32;
}

macro_rules! emits_nothing(
() => ()
);

fn main() {
assert_eq!(unsafe { rust_get_test_int() }, 0isize);
assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEFu32);
}

#[link(name = "rust_test_helpers", kind = "static")]
extern {
returns_isize!(rust_get_test_int);
//~^ ERROR Macro invocations in `extern {}` blocks are experimental.
takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
//~^ ERROR Macro invocations in `extern {}` blocks are experimental.
emits_nothing!();
//~^ ERROR Macro invocations in `extern {}` blocks are experimental.
}
2 changes: 1 addition & 1 deletion src/test/parse-fail/duplicate-visibility.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@

// compile-flags: -Z parse-only

// error-pattern:unmatched visibility `pub`
// error-pattern:expected one of `(`, `fn`, `static`, `type`, or `}` here
extern {
pub pub fn foo();
}
4 changes: 2 additions & 2 deletions src/test/parse-fail/extern-no-fn.rs
Original file line number Diff line number Diff line change
@@ -10,8 +10,8 @@

// compile-flags: -Z parse-only

extern {
f(); //~ ERROR expected one of `!` or `::`, found `(`
extern { //~ ERROR missing `fn`, `type`, or `static` for extern-item declaration
f();
}

fn main() {
8 changes: 6 additions & 2 deletions src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs
Original file line number Diff line number Diff line change
@@ -96,7 +96,9 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt,
}
})
],
// these are covered in proc_macro/attr-stmt-expr.rs
// covered in proc_macro/macros-in-extern.rs
Annotatable::ForeignItem(..) => unimplemented!(),
// covered in proc_macro/attr-stmt-expr.rs
Annotatable::Stmt(_) | Annotatable::Expr(_) => panic!("expected item"),
}
}
@@ -142,7 +144,9 @@ fn expand_duplicate(cx: &mut ExtCtxt,
new_it.ident = copy_name;
push(Annotatable::TraitItem(P(new_it)));
}
// these are covered in proc_macro/attr-stmt-expr.rs
// covered in proc_macro/macros-in-extern.rs
Annotatable::ForeignItem(..) => unimplemented!(),
// covered in proc_macro/attr-stmt-expr.rs
Annotatable::Stmt(_) | Annotatable::Expr(_) => panic!("expected item")
}
}
36 changes: 36 additions & 0 deletions src/test/run-pass-fulldeps/proc-macro/auxiliary/test-macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// no-prefer-dynamic

#![crate_type = "proc-macro"]
#![feature(proc_macro)]

extern crate proc_macro;

use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn nop_attr(_attr: TokenStream, input: TokenStream) -> TokenStream {
assert!(_attr.to_string().is_empty());
input
}

#[proc_macro_attribute]
pub fn no_output(_attr: TokenStream, _input: TokenStream) -> TokenStream {
assert!(_attr.to_string().is_empty());
assert!(!_input.to_string().is_empty());
"".parse().unwrap()
}

#[proc_macro]
pub fn emit_input(input: TokenStream) -> TokenStream {
input
}
35 changes: 35 additions & 0 deletions src/test/run-pass-fulldeps/proc-macro/macros-in-extern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// aux-build:test-macros.rs
// ignore-stage1
// ignore-wasm32

#![feature(proc_macro, macros_in_extern)]

extern crate test_macros;

use test_macros::{nop_attr, no_output, emit_input};

fn main() {
assert_eq!(unsafe { rust_get_test_int() }, 1isize);
assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEF);
}

#[link(name = "rust_test_helpers", kind = "static")]
extern {
#[no_output]
fn some_definitely_unknown_symbol_which_should_be_removed();

#[nop_attr]
fn rust_get_test_int() -> isize;

emit_input!(fn rust_dbg_extern_identity_u32(arg: u32) -> u32;);
}
39 changes: 39 additions & 0 deletions src/test/run-pass/macros-in-extern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-wasm32

#![feature(decl_macro, macros_in_extern)]

macro_rules! returns_isize(
($ident:ident) => (
fn $ident() -> isize;
)
);

macro takes_u32_returns_u32($ident:ident) {
fn $ident (arg: u32) -> u32;
}

macro_rules! emits_nothing(
() => ()
);

fn main() {
assert_eq!(unsafe { rust_get_test_int() }, 1isize);
assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEFu32);
}

#[link(name = "rust_test_helpers", kind = "static")]
extern {
returns_isize!(rust_get_test_int);
takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
emits_nothing!();
}
35 changes: 35 additions & 0 deletions src/test/ui/feature-gate-macros_in_extern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(decl_macro)]

macro_rules! returns_isize(
($ident:ident) => (
fn $ident() -> isize;
)
);

macro takes_u32_returns_u32($ident:ident) {
fn $ident (arg: u32) -> u32;
}

macro_rules! emits_nothing(
() => ()
);

#[link(name = "rust_test_helpers", kind = "static")]
extern {
returns_isize!(rust_get_test_int);
//~^ ERROR Macro invocations in `extern {}` blocks are experimental.
takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
//~^ ERROR Macro invocations in `extern {}` blocks are experimental.
emits_nothing!();
//~^ ERROR Macro invocations in `extern {}` blocks are experimental.
}
27 changes: 27 additions & 0 deletions src/test/ui/feature-gate-macros_in_extern.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error[E0658]: Macro invocations in `extern {}` blocks are experimental. (see issue #49476)
--> $DIR/feature-gate-macros_in_extern.rs:29:5
|
LL | returns_isize!(rust_get_test_int);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(macros_in_extern)] to the crate attributes to enable

error[E0658]: Macro invocations in `extern {}` blocks are experimental. (see issue #49476)
--> $DIR/feature-gate-macros_in_extern.rs:31:5
|
LL | takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(macros_in_extern)] to the crate attributes to enable

error[E0658]: Macro invocations in `extern {}` blocks are experimental. (see issue #49476)
--> $DIR/feature-gate-macros_in_extern.rs:33:5
|
LL | emits_nothing!();
| ^^^^^^^^^^^^^^^^^
|
= help: add #![feature(macros_in_extern)] to the crate attributes to enable

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0658`.