From 5d06c890fececc6f6779cd65ca83cef4647b8fdd Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 8 Mar 2018 14:27:23 +0300 Subject: [PATCH 1/2] syntax: Make `_` an identifier --- src/libproc_macro/lib.rs | 2 - src/librustc/hir/mod.rs | 2 +- src/librustc/ich/impls_syntax.rs | 1 - src/librustc_passes/ast_validation.rs | 6 +- src/librustdoc/html/highlight.rs | 2 +- src/libsyntax/diagnostics/plugin.rs | 4 +- src/libsyntax/ext/quote.rs | 1 - src/libsyntax/ext/tt/macro_parser.rs | 3 +- src/libsyntax/feature_gate.rs | 2 +- src/libsyntax/parse/lexer/mod.rs | 14 +- src/libsyntax/parse/parser.rs | 36 +++-- src/libsyntax/parse/token.rs | 9 +- src/libsyntax/print/pprust.rs | 1 - src/libsyntax_ext/env.rs | 6 +- src/libsyntax_pos/symbol.rs | 126 +++++++++--------- src/test/parse-fail/issue-32501.rs | 4 +- src/test/parse-fail/recover-enum2.rs | 2 +- .../parse-fail/underscore-suffix-for-float.rs | 3 +- src/test/run-pass/macro-pat.rs | 2 + src/test/ui/cross-file-errors/main.stderr | 2 +- 20 files changed, 109 insertions(+), 119 deletions(-) diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 1163c1a98d5ac..b239f8018bebb 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -680,7 +680,6 @@ impl TokenTree { Pound => op!('#'), Dollar => op!('$'), Question => op!('?'), - Underscore => op!('_'), Ident(ident) | Lifetime(ident) => TokenNode::Term(Term(ident.name)), Literal(..) | DocComment(..) => TokenNode::Literal(self::Literal(token)), @@ -743,7 +742,6 @@ impl TokenTree { '#' => Pound, '$' => Dollar, '?' => Question, - '_' => Underscore, _ => panic!("unsupported character {}", op), }; diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index f4638c23c5f4b..83e855178dbe7 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -214,7 +214,7 @@ impl LifetimeName { use self::LifetimeName::*; match *self { Implicit => keywords::Invalid.name(), - Underscore => Symbol::intern("'_"), + Underscore => keywords::UnderscoreLifetime.name(), Static => keywords::StaticLifetime.name(), Name(name) => name, } diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 289bc753d7fec..513b6c835f982 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -287,7 +287,6 @@ fn hash_token<'a, 'gcx, W: StableHasherResult>( token::Token::Pound | token::Token::Dollar | token::Token::Question | - token::Token::Underscore | token::Token::Whitespace | token::Token::Comment | token::Token::Eof => {} diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 55d00f92e4dac..4215bf306a4fd 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -37,7 +37,9 @@ impl<'a> AstValidator<'a> { } fn check_lifetime(&self, lifetime: &Lifetime) { - let valid_names = [keywords::StaticLifetime.name(), keywords::Invalid.name()]; + let valid_names = [keywords::UnderscoreLifetime.name(), + keywords::StaticLifetime.name(), + keywords::Invalid.name()]; if !valid_names.contains(&lifetime.ident.name) && token::Ident(lifetime.ident.without_first_quote()).is_reserved_ident() { self.err_handler().span_err(lifetime.span, "lifetimes cannot use keyword names"); @@ -45,7 +47,7 @@ impl<'a> AstValidator<'a> { } fn check_label(&self, label: Ident, span: Span) { - if token::Ident(label.without_first_quote()).is_reserved_ident() || label.name == "'_" { + if token::Ident(label.without_first_quote()).is_reserved_ident() { self.err_handler().span_err(span, &format!("invalid label name `{}`", label.name)); } } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index ef01c3e6bdb0c..659ec8a993dc8 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -352,7 +352,7 @@ impl<'a> Classifier<'a> { token::Lifetime(..) => Class::Lifetime, - token::Underscore | token::Eof | token::Interpolated(..) | + token::Eof | token::Interpolated(..) | token::Tilde | token::At | token::DotEq => Class::None, }; diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs index e8c2d325bd653..6c0fe525f55b0 100644 --- a/src/libsyntax/diagnostics/plugin.rs +++ b/src/libsyntax/diagnostics/plugin.rs @@ -19,7 +19,7 @@ use ext::base::{ExtCtxt, MacEager, MacResult}; use ext::build::AstBuilder; use parse::token; use ptr::P; -use symbol::Symbol; +use symbol::{keywords, Symbol}; use tokenstream::{TokenTree}; use util::small_vector::SmallVector; @@ -192,7 +192,7 @@ pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt, (descriptions.len(), ecx.expr_vec(span, descriptions)) }); - let static_ = ecx.lifetime(span, Ident::from_str("'static")); + let static_ = ecx.lifetime(span, keywords::StaticLifetime.ident()); let ty_str = ecx.ty_rptr( span, ecx.ty_ident(span, ecx.ident_of("str")), diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 7a024dbad8830..d6642b7b6c260 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -709,7 +709,6 @@ fn expr_mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P { token::Pound => "Pound", token::Dollar => "Dollar", token::Question => "Question", - token::Underscore => "Underscore", token::Eof => "Eof", token::Whitespace | token::Comment | token::Shebang(_) => { diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 0621f728e2a9d..beefdb3a6eac4 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -765,8 +765,7 @@ fn may_begin_with(name: &str, token: &Token) -> bool { Token::DotDotDot | // range pattern (future compat) Token::ModSep | // path Token::Lt | // path (UFCS constant) - Token::BinOp(token::Shl) | // path (double UFCS) - Token::Underscore => true, // placeholder + Token::BinOp(token::Shl) => true, // path (double UFCS) Token::Interpolated(ref nt) => may_be_ident(&nt.0), _ => false, }, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index f42cb8a258314..0098f2ae89b55 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1790,7 +1790,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) { - if lt.ident.name == "'_" { + if lt.ident.name == keywords::UnderscoreLifetime.name() { gate_feature_post!(&self, underscore_lifetimes, lt.span, "underscore lifetimes are unstable"); } diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 815ba49a60a72..9d1bfba7b9446 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -34,7 +34,7 @@ pub struct TokenAndSpan { impl Default for TokenAndSpan { fn default() -> Self { - TokenAndSpan { tok: token::Underscore, sp: syntax_pos::DUMMY_SP } + TokenAndSpan { tok: token::Whitespace, sp: syntax_pos::DUMMY_SP } } } @@ -126,7 +126,7 @@ impl<'a> StringReader<'a> { pub fn try_next_token(&mut self) -> Result { assert!(self.fatal_errs.is_empty()); let ret_val = TokenAndSpan { - tok: replace(&mut self.peek_tok, token::Underscore), + tok: replace(&mut self.peek_tok, token::Whitespace), sp: self.peek_span, }; self.advance_token()?; @@ -1133,14 +1133,8 @@ impl<'a> StringReader<'a> { self.bump(); } - return Ok(self.with_str_from(start, |string| { - if string == "_" { - token::Underscore - } else { - // FIXME: perform NFKC normalization here. (Issue #2253) - token::Ident(self.mk_ident(string)) - } - })); + // FIXME: perform NFKC normalization here. (Issue #2253) + return Ok(self.with_str_from(start, |string| token::Ident(self.mk_ident(string)))); } if is_dec_digit(c) { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a3a6489fe8b14..aa2a6f1cb47f1 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -549,7 +549,7 @@ impl<'a> Parser<'a> { -> Self { let mut parser = Parser { sess, - token: token::Underscore, + token: token::Whitespace, span: syntax_pos::DUMMY_SP, prev_span: syntax_pos::DUMMY_SP, meta_var_span: None, @@ -800,11 +800,7 @@ impl<'a> Parser<'a> { Err(if self.prev_token_kind == PrevTokenKind::DocComment { self.span_fatal_err(self.prev_span, Error::UselessDocComment) } else { - let mut err = self.expected_ident_found(); - if self.token == token::Underscore { - err.note("`_` is a wildcard pattern, not an identifier"); - } - err + self.expected_ident_found() }) } } @@ -1602,7 +1598,7 @@ impl<'a> Parser<'a> { let e = self.parse_expr()?; self.expect(&token::CloseDelim(token::Paren))?; TyKind::Typeof(e) - } else if self.eat(&token::Underscore) { + } else if self.eat_keyword(keywords::Underscore) { // A type to be inferred `_` TyKind::Infer } else if self.token_is_bare_fn_keyword() { @@ -1796,7 +1792,7 @@ impl<'a> Parser<'a> { _ => 0, }; - self.look_ahead(offset, |t| t.is_ident() || t == &token::Underscore) && + self.look_ahead(offset, |t| t.is_ident()) && self.look_ahead(offset + 1, |t| t == &token::Colon) } @@ -2782,7 +2778,7 @@ impl<'a> Parser<'a> { }, token::CloseDelim(_) | token::Eof => unreachable!(), _ => { - let (token, span) = (mem::replace(&mut self.token, token::Underscore), self.span); + let (token, span) = (mem::replace(&mut self.token, token::Whitespace), self.span); self.bump(); TokenTree::Token(span, token) } @@ -3815,11 +3811,6 @@ impl<'a> Parser<'a> { let lo = self.span; let pat; match self.token { - token::Underscore => { - // Parse _ - self.bump(); - pat = PatKind::Wild; - } token::BinOp(token::And) | token::AndAnd => { // Parse &pat / &mut pat self.expect_and()?; @@ -3849,8 +3840,11 @@ impl<'a> Parser<'a> { self.expect(&token::CloseDelim(token::Bracket))?; pat = PatKind::Slice(before, slice, after); } - // At this point, token != _, &, &&, (, [ - _ => if self.eat_keyword(keywords::Mut) { + // At this point, token != &, &&, (, [ + _ => if self.eat_keyword(keywords::Underscore) { + // Parse _ + pat = PatKind::Wild; + } else if self.eat_keyword(keywords::Mut) { // Parse mut ident @ pat / mut ref ident @ pat let mutref_span = self.prev_span.to(self.span); let binding_mode = if self.eat_keyword(keywords::Ref) { @@ -7065,10 +7059,12 @@ impl<'a> Parser<'a> { fn parse_rename(&mut self) -> PResult<'a, Option> { if self.eat_keyword(keywords::As) { - if self.eat(&token::Underscore) { - Ok(Some(Ident::with_empty_ctxt(Symbol::gensym("_")))) - } else { - self.parse_ident().map(Some) + match self.token { + token::Ident(ident) if ident.name == keywords::Underscore.name() => { + self.bump(); // `_` + Ok(Some(Ident { name: ident.name.gensymed(), ..ident })) + } + _ => self.parse_ident().map(Some), } } else { Ok(None) diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 097a2eb89fdf4..5c051e9b3584d 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -122,6 +122,7 @@ fn ident_can_begin_type(ident: ast::Ident) -> bool { !ident_token.is_reserved_ident() || ident_token.is_path_segment_keyword() || [ + keywords::Underscore.name(), keywords::For.name(), keywords::Impl.name(), keywords::Fn.name(), @@ -175,7 +176,6 @@ pub enum Token { /* Name components */ Ident(ast::Ident), - Underscore, Lifetime(ast::Ident), // The `LazyTokenStream` is a pure function of the `Nonterminal`, @@ -242,7 +242,6 @@ impl Token { Ident(ident) => ident_can_begin_type(ident), // type name or keyword OpenDelim(Paren) | // tuple OpenDelim(Bracket) | // array - Underscore | // placeholder Not | // never BinOp(Star) | // raw pointer BinOp(And) | // reference @@ -371,7 +370,7 @@ impl Token { // unnamed method parameters, crate root module, error recovery etc. pub fn is_special_ident(&self) -> bool { match self.ident() { - Some(id) => id.name <= keywords::DollarCrate.name(), + Some(id) => id.name <= keywords::Underscore.name(), _ => false, } } @@ -441,7 +440,7 @@ impl Token { Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq | DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar | - Question | OpenDelim(..) | CloseDelim(..) | Underscore => return None, + Question | OpenDelim(..) | CloseDelim(..) => return None, Literal(..) | Ident(..) | Lifetime(..) | Interpolated(..) | DocComment(..) | Whitespace | Comment | Shebang(..) | Eof => return None, @@ -573,7 +572,7 @@ impl fmt::Debug for Nonterminal { pub fn is_op(tok: &Token) -> bool { match *tok { OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | - Ident(..) | Underscore | Lifetime(..) | Interpolated(..) | + Ident(..) | Lifetime(..) | Interpolated(..) | Whitespace | Comment | Shebang(..) | Eof => false, _ => true, } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 1cf2b7a44bc17..36698a8637451 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -252,7 +252,6 @@ pub fn token_to_string(tok: &Token) -> String { /* Name components */ token::Ident(s) => s.to_string(), token::Lifetime(s) => s.to_string(), - token::Underscore => "_".to_string(), /* Other */ token::DocComment(s) => s.to_string(), diff --git a/src/libsyntax_ext/env.rs b/src/libsyntax_ext/env.rs index fcad065be52bc..ba6d25f7a60a4 100644 --- a/src/libsyntax_ext/env.rs +++ b/src/libsyntax_ext/env.rs @@ -17,7 +17,7 @@ use syntax::ast::{self, Ident}; use syntax::ext::base::*; use syntax::ext::base; use syntax::ext::build::AstBuilder; -use syntax::symbol::Symbol; +use syntax::symbol::{keywords, Symbol}; use syntax_pos::Span; use syntax::tokenstream; @@ -35,14 +35,14 @@ pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, let sp = sp.with_ctxt(sp.ctxt().apply_mark(cx.current_expansion.mark)); let e = match env::var(&*var.as_str()) { Err(..) => { + let lt = cx.lifetime(sp, keywords::StaticLifetime.ident()); cx.expr_path(cx.path_all(sp, true, cx.std_path(&["option", "Option", "None"]), Vec::new(), vec![cx.ty_rptr(sp, cx.ty_ident(sp, Ident::from_str("str")), - Some(cx.lifetime(sp, - Ident::from_str("'static"))), + Some(lt), ast::Mutability::Immutable)], Vec::new())) } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index e95079f7c88dd..0cba094da641d 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -261,73 +261,77 @@ macro_rules! declare_keywords {( declare_keywords! { // Special reserved identifiers used internally for elided lifetimes, // unnamed method parameters, crate root module, error recovery etc. - (0, Invalid, "") - (1, CrateRoot, "{{root}}") - (2, DollarCrate, "$crate") + (0, Invalid, "") + (1, CrateRoot, "{{root}}") + (2, DollarCrate, "$crate") + (3, Underscore, "_") // Keywords used in the language. - (3, As, "as") - (4, Box, "box") - (5, Break, "break") - (6, Const, "const") - (7, Continue, "continue") - (8, Crate, "crate") - (9, Else, "else") - (10, Enum, "enum") - (11, Extern, "extern") - (12, False, "false") - (13, Fn, "fn") - (14, For, "for") - (15, If, "if") - (16, Impl, "impl") - (17, In, "in") - (18, Let, "let") - (19, Loop, "loop") - (20, Match, "match") - (21, Mod, "mod") - (22, Move, "move") - (23, Mut, "mut") - (24, Pub, "pub") - (25, Ref, "ref") - (26, Return, "return") - (27, SelfValue, "self") - (28, SelfType, "Self") - (29, Static, "static") - (30, Struct, "struct") - (31, Super, "super") - (32, Trait, "trait") - (33, True, "true") - (34, Type, "type") - (35, Unsafe, "unsafe") - (36, Use, "use") - (37, Where, "where") - (38, While, "while") + (4, As, "as") + (5, Box, "box") + (6, Break, "break") + (7, Const, "const") + (8, Continue, "continue") + (9, Crate, "crate") + (10, Else, "else") + (11, Enum, "enum") + (12, Extern, "extern") + (13, False, "false") + (14, Fn, "fn") + (15, For, "for") + (16, If, "if") + (17, Impl, "impl") + (18, In, "in") + (19, Let, "let") + (20, Loop, "loop") + (21, Match, "match") + (22, Mod, "mod") + (23, Move, "move") + (24, Mut, "mut") + (25, Pub, "pub") + (26, Ref, "ref") + (27, Return, "return") + (28, SelfValue, "self") + (29, SelfType, "Self") + (30, Static, "static") + (31, Struct, "struct") + (32, Super, "super") + (33, Trait, "trait") + (34, True, "true") + (35, Type, "type") + (36, Unsafe, "unsafe") + (37, Use, "use") + (38, Where, "where") + (39, While, "while") // Keywords reserved for future use. - (39, Abstract, "abstract") - (40, Alignof, "alignof") - (41, Become, "become") - (42, Do, "do") - (43, Final, "final") - (44, Macro, "macro") - (45, Offsetof, "offsetof") - (46, Override, "override") - (47, Priv, "priv") - (48, Proc, "proc") - (49, Pure, "pure") - (50, Sizeof, "sizeof") - (51, Typeof, "typeof") - (52, Unsized, "unsized") - (53, Virtual, "virtual") - (54, Yield, "yield") + (40, Abstract, "abstract") + (41, Alignof, "alignof") + (42, Become, "become") + (43, Do, "do") + (44, Final, "final") + (45, Macro, "macro") + (46, Offsetof, "offsetof") + (47, Override, "override") + (48, Priv, "priv") + (49, Proc, "proc") + (50, Pure, "pure") + (51, Sizeof, "sizeof") + (52, Typeof, "typeof") + (53, Unsized, "unsized") + (54, Virtual, "virtual") + (55, Yield, "yield") + + // Special lifetime names + (56, UnderscoreLifetime, "'_") + (57, StaticLifetime, "'static") // Weak keywords, have special meaning only in specific contexts. - (55, Auto, "auto") - (56, Catch, "catch") - (57, Default, "default") - (58, Dyn, "dyn") - (59, StaticLifetime, "'static") - (60, Union, "union") + (58, Auto, "auto") + (59, Catch, "catch") + (60, Default, "default") + (61, Dyn, "dyn") + (62, Union, "union") } // If an interner exists, return it. Otherwise, prepare a fresh one. diff --git a/src/test/parse-fail/issue-32501.rs b/src/test/parse-fail/issue-32501.rs index f29c1fa279400..21db2f5051703 100644 --- a/src/test/parse-fail/issue-32501.rs +++ b/src/test/parse-fail/issue-32501.rs @@ -16,7 +16,5 @@ fn main() { let _ = 0; let mut b = 0; let mut _b = 0; - let mut _ = 0; //~ ERROR expected identifier, found `_` - //~^ NOTE `_` is a wildcard pattern, not an identifier - //~| NOTE expected identifier + let mut _ = 0; //~ ERROR expected identifier, found reserved identifier `_` } diff --git a/src/test/parse-fail/recover-enum2.rs b/src/test/parse-fail/recover-enum2.rs index 49380a03e156a..6fd32f842f135 100644 --- a/src/test/parse-fail/recover-enum2.rs +++ b/src/test/parse-fail/recover-enum2.rs @@ -39,5 +39,5 @@ fn main() { } } // still recover later - let bad_syntax = _; //~ ERROR: found `_` + let bad_syntax = _; //~ ERROR: expected expression, found reserved identifier `_` } diff --git a/src/test/parse-fail/underscore-suffix-for-float.rs b/src/test/parse-fail/underscore-suffix-for-float.rs index df7d9aa374dce..8327217e6f286 100644 --- a/src/test/parse-fail/underscore-suffix-for-float.rs +++ b/src/test/parse-fail/underscore-suffix-for-float.rs @@ -9,5 +9,6 @@ // except according to those terms. fn main() { - let a = 42._; //~ ERROR unexpected token: `_` + let a = 42._; //~ ERROR expected identifier, found reserved identifier `_` + //~^ ERROR `{integer}` is a primitive type and therefore doesn't have fields } diff --git a/src/test/run-pass/macro-pat.rs b/src/test/run-pass/macro-pat.rs index 48e521de57e90..8de3996245f86 100644 --- a/src/test/run-pass/macro-pat.rs +++ b/src/test/run-pass/macro-pat.rs @@ -71,4 +71,6 @@ pub fn main() { let ident_pat!(x) = 2; x+1 }); + + let ident_pat!(_) = 2; // OK } diff --git a/src/test/ui/cross-file-errors/main.stderr b/src/test/ui/cross-file-errors/main.stderr index a9db5214e6a2e..41a2172d29580 100644 --- a/src/test/ui/cross-file-errors/main.stderr +++ b/src/test/ui/cross-file-errors/main.stderr @@ -1,4 +1,4 @@ -error: expected expression, found `_` +error: expected expression, found reserved identifier `_` --> $DIR/underscore.rs:18:9 | LL | _ From ed5ea5c705ccea7d2eef1558530e87d2dbb88be7 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 17 Mar 2018 22:08:18 +0300 Subject: [PATCH 2/2] Reject `_` in `ident` matcher --- src/libsyntax/ext/tt/macro_parser.rs | 36 +++++++++++---------- src/test/run-pass/macro-pat.rs | 2 -- src/test/ui/underscore-ident-matcher.rs | 19 +++++++++++ src/test/ui/underscore-ident-matcher.stderr | 8 +++++ 4 files changed, 46 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/underscore-ident-matcher.rs create mode 100644 src/test/ui/underscore-ident-matcher.stderr diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index beefdb3a6eac4..667653b5f7f26 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 8de3996245f86..48e521de57e90 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 0000000000000..eee99296c7941 --- /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 0000000000000..7f2b6ac30b0da --- /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 +