diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index beefdb3a6ea..667653b5f7f 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -86,7 +86,7 @@ use self::TokenTreeOrTokenTreeVec::*; use ast::Ident; use syntax_pos::{self, BytePos, Span}; -use codemap::Spanned; +use codemap::respan; use errors::FatalError; use ext::tt::quoted::{self, TokenTree}; use parse::{Directory, ParseSess}; @@ -709,6 +709,15 @@ pub fn parse( } } +/// The token is an identifier, but not `_`. +/// We prohibit passing `_` to macros expecting `ident` for now. +fn get_macro_ident(token: &Token) -> Option { + match *token { + token::Ident(ident) if ident.name != keywords::Underscore.name() => Some(ident), + _ => None, + } +} + /// Checks whether a non-terminal may begin with a particular token. /// /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that @@ -725,7 +734,7 @@ fn may_begin_with(name: &str, token: &Token) -> bool { match name { "expr" => token.can_begin_expr(), "ty" => token.can_begin_type(), - "ident" => token.is_ident(), + "ident" => get_macro_ident(token).is_some(), "vis" => match *token { // The follow-set of :vis + "priv" keyword + interpolated Token::Comma | Token::Ident(_) | Token::Interpolated(_) => true, @@ -814,21 +823,14 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal { "expr" => token::NtExpr(panictry!(p.parse_expr())), "ty" => token::NtTy(panictry!(p.parse_ty())), // this could be handled like a token, since it is one - "ident" => match p.token { - token::Ident(sn) => { - p.bump(); - token::NtIdent(Spanned:: { - node: sn, - span: p.prev_span, - }) - } - _ => { - let token_str = pprust::token_to_string(&p.token); - p.fatal(&format!("expected ident, found {}", &token_str[..])) - .emit(); - FatalError.raise() - } - }, + "ident" => if let Some(ident) = get_macro_ident(&p.token) { + p.bump(); + token::NtIdent(respan(p.prev_span, ident)) + } else { + let token_str = pprust::token_to_string(&p.token); + p.fatal(&format!("expected ident, found {}", &token_str)).emit(); + FatalError.raise() + } "path" => token::NtPath(panictry!(p.parse_path_common(PathStyle::Type, false))), "meta" => token::NtMeta(panictry!(p.parse_meta_item())), "vis" => token::NtVis(panictry!(p.parse_visibility(true))), diff --git a/src/test/run-pass/macro-pat.rs b/src/test/run-pass/macro-pat.rs index 8de3996245f..48e521de57e 100644 --- a/src/test/run-pass/macro-pat.rs +++ b/src/test/run-pass/macro-pat.rs @@ -71,6 +71,4 @@ pub fn main() { let ident_pat!(x) = 2; x+1 }); - - let ident_pat!(_) = 2; // OK } diff --git a/src/test/ui/underscore-ident-matcher.rs b/src/test/ui/underscore-ident-matcher.rs new file mode 100644 index 00000000000..eee99296c79 --- /dev/null +++ b/src/test/ui/underscore-ident-matcher.rs @@ -0,0 +1,19 @@ +// 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. + +macro_rules! identity { + ($i: ident) => ( + $i + ) +} + +fn main() { + let identity!(_) = 10; //~ ERROR no rules expected the token `_` +} diff --git a/src/test/ui/underscore-ident-matcher.stderr b/src/test/ui/underscore-ident-matcher.stderr new file mode 100644 index 00000000000..7f2b6ac30b0 --- /dev/null +++ b/src/test/ui/underscore-ident-matcher.stderr @@ -0,0 +1,8 @@ +error: no rules expected the token `_` + --> $DIR/underscore-ident-matcher.rs:18:19 + | +LL | let identity!(_) = 10; //~ ERROR no rules expected the token `_` + | ^ + +error: aborting due to previous error +