diff options
| author | Brad Gibson <b2b@humanenginuity.com> | 2018-07-29 20:03:15 -0700 |
|---|---|---|
| committer | Brad Gibson <b2b@humanenginuity.com> | 2018-07-29 22:13:12 -0700 |
| commit | 56016cb1e02ece29f25c619b297f9c9797db821c (patch) | |
| tree | 70dd7a5658527a54769c7603e6e17bfe27985f86 /src/libsyntax/parse | |
| parent | 6a78c0a10f2e719117fe4bb929bfb38549acfeec (diff) | |
| parent | 866a713258915e6cbb212d135f751a6a8c9e1c0a (diff) | |
| download | rust-56016cb1e02ece29f25c619b297f9c9797db821c.tar.gz rust-56016cb1e02ece29f25c619b297f9c9797db821c.zip | |
resolved upstream merge conflicts
Diffstat (limited to 'src/libsyntax/parse')
| -rw-r--r-- | src/libsyntax/parse/attr.rs | 15 | ||||
| -rw-r--r-- | src/libsyntax/parse/classify.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/parse/common.rs | 36 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/comments.rs | 22 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/mod.rs | 266 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/tokentrees.rs | 25 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/unicode_chars.rs | 12 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 294 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 1716 | ||||
| -rw-r--r-- | src/libsyntax/parse/token.rs | 206 |
10 files changed, 1442 insertions, 1152 deletions
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index cceed589212..4d59f64bb6b 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -11,13 +11,12 @@ use attr; use ast; use codemap::respan; -use parse::common::SeqSep; -use parse::PResult; +use parse::{SeqSep, PResult}; use parse::token::{self, Nonterminal}; use parse::parser::{Parser, TokenType, PathStyle}; use tokenstream::TokenStream; -#[derive(PartialEq, Eq, Debug)] +#[derive(Debug)] enum InnerAttributeParsePolicy<'a> { Permitted, NotPermitted { reason: &'a str }, @@ -28,7 +27,7 @@ const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &'static str = "an inner attribute impl<'a> Parser<'a> { /// Parse attributes that appear before an item - pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> { + crate fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> { let mut attrs: Vec<ast::Attribute> = Vec::new(); let mut just_parsed_doc_comment = false; loop { @@ -95,7 +94,7 @@ impl<'a> Parser<'a> { let lo = self.span; self.bump(); - if inner_parse_policy == InnerAttributeParsePolicy::Permitted { + if let InnerAttributeParsePolicy::Permitted = inner_parse_policy { self.expected_tokens.push(TokenType::Token(token::Not)); } let style = if self.token == token::Not { @@ -139,7 +138,7 @@ impl<'a> Parser<'a> { }) } - pub fn parse_path_and_tokens(&mut self) -> PResult<'a, (ast::Path, TokenStream)> { + crate fn parse_path_and_tokens(&mut self) -> PResult<'a, (ast::Path, TokenStream)> { let meta = match self.token { token::Interpolated(ref nt) => match nt.0 { Nonterminal::NtMeta(ref meta) => Some(meta.clone()), @@ -160,7 +159,7 @@ impl<'a> Parser<'a> { /// terminated by a semicolon. /// matches inner_attrs* - pub fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> { + crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> { let mut attrs: Vec<ast::Attribute> = vec![]; loop { match self.token { @@ -231,7 +230,7 @@ impl<'a> Parser<'a> { Ok(ast::MetaItem { ident, node, span }) } - pub fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { + crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { Ok(if self.eat(&token::Eq) { ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?) } else if self.eat(&token::OpenDelim(token::Paren)) { diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index b8e02556625..531483e7de1 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -26,7 +26,7 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { ast::ExprKind::If(..) | ast::ExprKind::IfLet(..) | ast::ExprKind::Match(..) | - ast::ExprKind::Block(_) | + ast::ExprKind::Block(..) | ast::ExprKind::While(..) | ast::ExprKind::WhileLet(..) | ast::ExprKind::Loop(..) | diff --git a/src/libsyntax/parse/common.rs b/src/libsyntax/parse/common.rs deleted file mode 100644 index fe931f7cf6a..00000000000 --- a/src/libsyntax/parse/common.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2012 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. - -//! Common routines shared by parser mods - -use parse::token; - -/// `SeqSep` : a sequence separator (token) -/// and whether a trailing separator is allowed. -pub struct SeqSep { - pub sep: Option<token::Token>, - pub trailing_sep_allowed: bool, -} - -impl SeqSep { - pub fn trailing_allowed(t: token::Token) -> SeqSep { - SeqSep { - sep: Some(t), - trailing_sep_allowed: true, - } - } - - pub fn none() -> SeqSep { - SeqSep { - sep: None, - trailing_sep_allowed: false, - } - } -} diff --git a/src/libsyntax/parse/lexer/comments.rs b/src/libsyntax/parse/lexer/comments.rs index 63aa5d28ce8..20a585b6601 100644 --- a/src/libsyntax/parse/lexer/comments.rs +++ b/src/libsyntax/parse/lexer/comments.rs @@ -40,7 +40,7 @@ pub struct Comment { pub pos: BytePos, } -pub fn is_doc_comment(s: &str) -> bool { +fn is_doc_comment(s: &str) -> bool { (s.starts_with("///") && super::is_doc_comment(s)) || s.starts_with("//!") || (s.starts_with("/**") && is_block_doc_comment(s)) || s.starts_with("/*!") } @@ -238,7 +238,21 @@ fn read_block_comment(rdr: &mut StringReader, debug!(">>> block comment"); let p = rdr.pos; let mut lines: Vec<String> = Vec::new(); - let col = rdr.col; + + // Count the number of chars since the start of the line by rescanning. + let mut src_index = rdr.src_index(rdr.filemap.line_begin_pos(rdr.pos)); + let end_src_index = rdr.src_index(rdr.pos); + assert!(src_index <= end_src_index, + "src_index={}, end_src_index={}, line_begin_pos={}", + src_index, end_src_index, rdr.filemap.line_begin_pos(rdr.pos).to_u32()); + let mut n = 0; + while src_index < end_src_index { + let c = char_at(&rdr.src, src_index); + src_index += c.len_utf8(); + n += 1; + } + let col = CharPos(n); + rdr.bump(); rdr.bump(); @@ -343,14 +357,14 @@ pub struct Literal { // it appears this function is called only from pprust... that's // probably not a good thing. -pub fn gather_comments_and_literals(sess: &ParseSess, path: FileName, srdr: &mut Read) +pub fn gather_comments_and_literals(sess: &ParseSess, path: FileName, srdr: &mut dyn Read) -> (Vec<Comment>, Vec<Literal>) { let mut src = Vec::new(); srdr.read_to_end(&mut src).unwrap(); let src = String::from_utf8(src).unwrap(); let cm = CodeMap::new(sess.codemap().path_mapping().clone()); let filemap = cm.new_filemap(path, src); - let mut rdr = lexer::StringReader::new_raw(sess, filemap); + let mut rdr = lexer::StringReader::new_raw(sess, filemap, None); let mut comments: Vec<Comment> = Vec::new(); let mut literals: Vec<Literal> = Vec::new(); diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 22a0261d8c6..f9b9e95ead1 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -11,7 +11,7 @@ use ast::{self, Ident}; use syntax_pos::{self, BytePos, CharPos, Pos, Span, NO_EXPANSION}; use codemap::{CodeMap, FilePathMapping}; -use errors::{FatalError, DiagnosticBuilder}; +use errors::{Applicability, FatalError, DiagnosticBuilder}; use parse::{token, ParseSess}; use str::char_at; use symbol::{Symbol, keywords}; @@ -26,7 +26,7 @@ pub mod comments; mod tokentrees; mod unicode_chars; -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Debug)] pub struct TokenAndSpan { pub tok: token::Token, pub sp: Span, @@ -34,7 +34,10 @@ pub struct TokenAndSpan { impl Default for TokenAndSpan { fn default() -> Self { - TokenAndSpan { tok: token::Whitespace, sp: syntax_pos::DUMMY_SP } + TokenAndSpan { + tok: token::Whitespace, + sp: syntax_pos::DUMMY_SP, + } } } @@ -44,34 +47,36 @@ pub struct StringReader<'a> { pub next_pos: BytePos, /// The absolute offset within the codemap of the current character pub pos: BytePos, - /// The column of the next character to read - pub col: CharPos, /// The current character (which has been read from self.pos) pub ch: Option<char>, pub filemap: Lrc<syntax_pos::FileMap>, - /// If Some, stop reading the source at this position (inclusive). - pub terminator: Option<BytePos>, - /// Whether to record new-lines and multibyte chars in filemap. - /// This is only necessary the first time a filemap is lexed. - /// If part of a filemap is being re-lexed, this should be set to false. - pub save_new_lines_and_multibyte: bool, + /// Stop reading src at this index. + pub end_src_index: usize, // cached: - pub peek_tok: token::Token, - pub peek_span: Span, - pub fatal_errs: Vec<DiagnosticBuilder<'a>>, + peek_tok: token::Token, + peek_span: Span, + peek_span_src_raw: Span, + fatal_errs: Vec<DiagnosticBuilder<'a>>, // cache a direct reference to the source text, so that we don't have to // retrieve it via `self.filemap.src.as_ref().unwrap()` all the time. - source_text: Lrc<String>, + src: Lrc<String>, /// Stack of open delimiters and their spans. Used for error message. token: token::Token, span: Span, + /// The raw source span which *does not* take `override_span` into account + span_src_raw: Span, open_braces: Vec<(token::DelimToken, Span)>, - pub override_span: Option<Span>, + crate override_span: Option<Span>, } impl<'a> StringReader<'a> { fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span { - unwrap_or!(self.override_span, Span::new(lo, hi, NO_EXPANSION)) + self.mk_sp_and_raw(lo, hi).0 + } + fn mk_sp_and_raw(&self, lo: BytePos, hi: BytePos) -> (Span, Span) { + let raw = Span::new(lo, hi, NO_EXPANSION); + let real = unwrap_or!(self.override_span, raw); + (real, raw) } fn mk_ident(&self, string: &str) -> Ident { let mut ident = Ident::from_str(string); @@ -113,14 +118,7 @@ impl<'a> StringReader<'a> { self.unwrap_or_abort(res) } fn is_eof(&self) -> bool { - if self.ch.is_none() { - return true; - } - - match self.terminator { - Some(t) => self.next_pos > t, - None => false, - } + self.ch.is_none() } /// Return the next token. EFFECT: advances the string_reader. pub fn try_next_token(&mut self) -> Result<TokenAndSpan, ()> { @@ -130,6 +128,7 @@ impl<'a> StringReader<'a> { sp: self.peek_span, }; self.advance_token()?; + self.span_src_raw = self.peek_span_src_raw; Ok(ret_val) } @@ -160,47 +159,48 @@ impl<'a> StringReader<'a> { sp: self.peek_span, } } -} -impl<'a> StringReader<'a> { /// For comments.rs, which hackily pokes into next_pos and ch - pub fn new_raw(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>) -> Self { - let mut sr = StringReader::new_raw_internal(sess, filemap); + fn new_raw(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>, + override_span: Option<Span>) -> Self { + let mut sr = StringReader::new_raw_internal(sess, filemap, override_span); sr.bump(); sr } - fn new_raw_internal(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>) -> Self { + fn new_raw_internal(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>, + override_span: Option<Span>) -> Self { if filemap.src.is_none() { sess.span_diagnostic.bug(&format!("Cannot lex filemap without source: {}", filemap.name)); } - let source_text = (*filemap.src.as_ref().unwrap()).clone(); + let src = (*filemap.src.as_ref().unwrap()).clone(); StringReader { sess, next_pos: filemap.start_pos, pos: filemap.start_pos, - col: CharPos(0), ch: Some('\n'), filemap, - terminator: None, - save_new_lines_and_multibyte: true, + end_src_index: src.len(), // dummy values; not read peek_tok: token::Eof, peek_span: syntax_pos::DUMMY_SP, - source_text, + peek_span_src_raw: syntax_pos::DUMMY_SP, + src, fatal_errs: Vec::new(), token: token::Eof, span: syntax_pos::DUMMY_SP, + span_src_raw: syntax_pos::DUMMY_SP, open_braces: Vec::new(), - override_span: None, + override_span, } } - pub fn new(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>) -> Self { - let mut sr = StringReader::new_raw(sess, filemap); + pub fn new(sess: &'a ParseSess, filemap: Lrc<syntax_pos::FileMap>, override_span: Option<Span>) + -> Self { + let mut sr = StringReader::new_raw(sess, filemap, override_span); if sr.advance_token().is_err() { sr.emit_fatal_errors(); FatalError.raise(); @@ -217,12 +217,11 @@ impl<'a> StringReader<'a> { span = span.shrink_to_lo(); } - let mut sr = StringReader::new_raw_internal(sess, begin.fm); + let mut sr = StringReader::new_raw_internal(sess, begin.fm, None); // Seek the lexer to the right byte range. - sr.save_new_lines_and_multibyte = false; sr.next_pos = span.lo(); - sr.terminator = Some(span.hi()); + sr.end_src_index = sr.src_index(span.hi()); sr.bump(); @@ -233,17 +232,17 @@ impl<'a> StringReader<'a> { sr } - pub fn ch_is(&self, c: char) -> bool { + fn ch_is(&self, c: char) -> bool { self.ch == Some(c) } /// Report a fatal lexical error with a given span. - pub fn fatal_span(&self, sp: Span, m: &str) -> FatalError { + fn fatal_span(&self, sp: Span, m: &str) -> FatalError { self.sess.span_diagnostic.span_fatal(sp, m) } /// Report a lexical error with a given span. - pub fn err_span(&self, sp: Span, m: &str) { + fn err_span(&self, sp: Span, m: &str) { self.sess.span_diagnostic.span_err(sp, m) } @@ -261,14 +260,12 @@ impl<'a> StringReader<'a> { /// Pushes a character to a message string for error reporting fn push_escaped_char_for_msg(m: &mut String, c: char) { match c { - '\u{20}'...'\u{7e}' => { + '\u{20}'..='\u{7e}' => { // Don't escape \, ' or " for user-facing messages m.push(c); } _ => { - for c in c.escape_default() { - m.push(c); - } + m.extend(c.escape_default()); } } } @@ -326,9 +323,7 @@ impl<'a> StringReader<'a> { /// offending string to the error message fn fatal_span_verbose(&self, from_pos: BytePos, to_pos: BytePos, mut m: String) -> FatalError { m.push_str(": "); - let from = self.byte_offset(from_pos).to_usize(); - let to = self.byte_offset(to_pos).to_usize(); - m.push_str(&self.source_text[from..to]); + m.push_str(&self.src[self.src_index(from_pos)..self.src_index(to_pos)]); self.fatal_span_(from_pos, to_pos, &m[..]) } @@ -337,31 +332,40 @@ impl<'a> StringReader<'a> { fn advance_token(&mut self) -> Result<(), ()> { match self.scan_whitespace_or_comment() { Some(comment) => { + self.peek_span_src_raw = comment.sp; self.peek_span = comment.sp; self.peek_tok = comment.tok; } None => { if self.is_eof() { self.peek_tok = token::Eof; - self.peek_span = self.mk_sp(self.filemap.end_pos, self.filemap.end_pos); + let (real, raw) = self.mk_sp_and_raw( + self.filemap.end_pos, + self.filemap.end_pos, + ); + self.peek_span = real; + self.peek_span_src_raw = raw; } else { let start_bytepos = self.pos; self.peek_tok = self.next_token_inner()?; - self.peek_span = self.mk_sp(start_bytepos, self.pos); + let (real, raw) = self.mk_sp_and_raw(start_bytepos, self.pos); + self.peek_span = real; + self.peek_span_src_raw = raw; }; } } Ok(()) } - fn byte_offset(&self, pos: BytePos) -> BytePos { - (pos - self.filemap.start_pos) + #[inline] + fn src_index(&self, pos: BytePos) -> usize { + (pos - self.filemap.start_pos).to_usize() } /// Calls `f` with a string slice of the source text spanning from `start` /// up to but excluding `self.pos`, meaning the slice does not include /// the character `self.ch`. - pub fn with_str_from<T, F>(&self, start: BytePos, f: F) -> T + fn with_str_from<T, F>(&self, start: BytePos, f: F) -> T where F: FnOnce(&str) -> T { self.with_str_from_to(start, self.pos, f) @@ -370,13 +374,13 @@ impl<'a> StringReader<'a> { /// Create a Name from a given offset to the current offset, each /// adjusted 1 towards each other (assumes that on either side there is a /// single-byte delimiter). - pub fn name_from(&self, start: BytePos) -> ast::Name { + fn name_from(&self, start: BytePos) -> ast::Name { debug!("taking an ident from {:?} to {:?}", start, self.pos); self.with_str_from(start, Symbol::intern) } /// As name_from, with an explicit endpoint. - pub fn name_from_to(&self, start: BytePos, end: BytePos) -> ast::Name { + fn name_from_to(&self, start: BytePos, end: BytePos) -> ast::Name { debug!("taking an ident from {:?} to {:?}", start, end); self.with_str_from_to(start, end, Symbol::intern) } @@ -386,7 +390,7 @@ impl<'a> StringReader<'a> { fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T where F: FnOnce(&str) -> T { - f(&self.source_text[self.byte_offset(start).to_usize()..self.byte_offset(end).to_usize()]) + f(&self.src[self.src_index(start)..self.src_index(end)]) } /// Converts CRLF to LF in the given string, raising an error on bare CR. @@ -438,71 +442,49 @@ impl<'a> StringReader<'a> { } } - /// Advance the StringReader by one character. If a newline is /// discovered, add it to the FileMap's list of line start offsets. - pub fn bump(&mut self) { - let new_pos = self.next_pos; - let new_byte_offset = self.byte_offset(new_pos).to_usize(); - let end = self.terminator.map_or(self.source_text.len(), |t| { - self.byte_offset(t).to_usize() - }); - if new_byte_offset < end { - let old_ch_is_newline = self.ch.unwrap() == '\n'; - let new_ch = char_at(&self.source_text, new_byte_offset); - let new_ch_len = new_ch.len_utf8(); - - self.ch = Some(new_ch); - self.pos = new_pos; - self.next_pos = new_pos + Pos::from_usize(new_ch_len); - if old_ch_is_newline { - if self.save_new_lines_and_multibyte { - self.filemap.next_line(self.pos); - } - self.col = CharPos(0); - } else { - self.col = self.col + CharPos(1); - } - if new_ch_len > 1 { - if self.save_new_lines_and_multibyte { - self.filemap.record_multibyte_char(self.pos, new_ch_len); - } - } - self.filemap.record_width(self.pos, new_ch); + crate fn bump(&mut self) { + let next_src_index = self.src_index(self.next_pos); + if next_src_index < self.end_src_index { + let next_ch = char_at(&self.src, next_src_index); + let next_ch_len = next_ch.len_utf8(); + + self.ch = Some(next_ch); + self.pos = self.next_pos; + self.next_pos = self.next_pos + Pos::from_usize(next_ch_len); } else { self.ch = None; - self.pos = new_pos; + self.pos = self.next_pos; } } - pub fn nextch(&self) -> Option<char> { - let offset = self.byte_offset(self.next_pos).to_usize(); - if offset < self.source_text.len() { - Some(char_at(&self.source_text, offset)) + fn nextch(&self) -> Option<char> { + let next_src_index = self.src_index(self.next_pos); + if next_src_index < self.end_src_index { + Some(char_at(&self.src, next_src_index)) } else { None } } - pub fn nextch_is(&self, c: char) -> bool { + fn nextch_is(&self, c: char) -> bool { self.nextch() == Some(c) } - pub fn nextnextch(&self) -> Option<char> { - let offset = self.byte_offset(self.next_pos).to_usize(); - let s = &self.source_text[..]; - if offset >= s.len() { - return None; - } - let next = offset + char_at(s, offset).len_utf8(); - if next < s.len() { - Some(char_at(s, next)) - } else { - None + fn nextnextch(&self) -> Option<char> { + let next_src_index = self.src_index(self.next_pos); + if next_src_index < self.end_src_index { + let next_next_src_index = + next_src_index + char_at(&self.src, next_src_index).len_utf8(); + if next_next_src_index < self.end_src_index { + return Some(char_at(&self.src, next_next_src_index)); + } } + None } - pub fn nextnextch_is(&self, c: char) -> bool { + fn nextnextch_is(&self, c: char) -> bool { self.nextnextch() == Some(c) } @@ -512,6 +494,7 @@ impl<'a> StringReader<'a> { return None; } let start = self.pos; + self.bump(); while ident_continue(self.ch) { self.bump(); } @@ -776,7 +759,7 @@ impl<'a> StringReader<'a> { base = 16; num_digits = self.scan_digits(16, 16); } - '0'...'9' | '_' | '.' | 'e' | 'E' => { + '0'..='9' | '_' | '.' | 'e' | 'E' => { num_digits = self.scan_digits(10, 10) + 1; } _ => { @@ -1071,9 +1054,18 @@ impl<'a> StringReader<'a> { self.bump(); } if self.scan_digits(10, 10) == 0 { - self.err_span_(self.pos, - self.next_pos, - "expected at least one digit in exponent") + let mut err = self.struct_span_fatal( + self.pos, self.next_pos, + "expected at least one digit in exponent" + ); + if let Some(ch) = self.ch { + // check for e.g. Unicode minus '−' (Issue #49746) + if unicode_chars::check_for_substitution(self, ch, &mut err) { + self.bump(); + self.scan_digits(10, 10); + } + } + err.emit(); } } } @@ -1142,6 +1134,7 @@ impl<'a> StringReader<'a> { } let start = self.pos; + self.bump(); while ident_continue(self.ch) { self.bump(); } @@ -1149,7 +1142,7 @@ impl<'a> StringReader<'a> { return Ok(self.with_str_from(start, |string| { // FIXME: perform NFKC normalization here. (Issue #2253) let ident = self.mk_ident(string); - if is_raw_ident && (token::is_path_segment_keyword(ident) || + if is_raw_ident && (ident.is_path_segment_keyword() || ident.name == keywords::Underscore.name()) { self.fatal_span_(raw_start, self.pos, &format!("`r#{}` is not currently supported.", ident.name) @@ -1287,11 +1280,7 @@ impl<'a> StringReader<'a> { } '-' => { self.bump(); - match self.ch.unwrap_or('\x00') { - _ => { - Ok(token::LArrow) - } - } + Ok(token::LArrow) } _ => { Ok(token::Lt) @@ -1359,19 +1348,19 @@ impl<'a> StringReader<'a> { loop { self.bump(); if self.ch_is('\'') { - let start = self.byte_offset(start).to_usize(); - let end = self.byte_offset(self.pos).to_usize(); + let start = self.src_index(start); + let end = self.src_index(self.pos); self.bump(); let span = self.mk_sp(start_with_quote, self.pos); self.sess.span_diagnostic .struct_span_err(span, "character literal may only contain one codepoint") - .span_suggestion(span, - "if you meant to write a `str` literal, \ - use double quotes", - format!("\"{}\"", - &self.source_text[start..end])) - .emit(); + .span_suggestion_with_applicability( + span, + "if you meant to write a `str` literal, use double quotes", + format!("\"{}\"", &self.src[start..end]), + Applicability::MachineApplicable + ).emit(); return Ok(token::Literal(token::Str_(Symbol::intern("??")), None)) } if self.ch_is('\n') || self.is_eof() || self.ch_is('/') { @@ -1441,6 +1430,13 @@ impl<'a> StringReader<'a> { self.bump(); let mut hash_count: u16 = 0; while self.ch_is('#') { + if hash_count == 65535 { + let bpos = self.next_pos; + self.fatal_span_(start_bpos, + bpos, + "too many `#` symbols: raw strings may be \ + delimited by up to 65535 `#` symbols").raise(); + } self.bump(); hash_count += 1; } @@ -1671,6 +1667,13 @@ impl<'a> StringReader<'a> { self.bump(); let mut hash_count = 0; while self.ch_is('#') { + if hash_count == 65535 { + let bpos = self.next_pos; + self.fatal_span_(start_bpos, + bpos, + "too many `#` symbols: raw byte strings may be \ + delimited by up to 65535 `#` symbols").raise(); + } self.bump(); hash_count += 1; } @@ -1721,7 +1724,7 @@ impl<'a> StringReader<'a> { // This tests the character for the unicode property 'PATTERN_WHITE_SPACE' which // is guaranteed to be forward compatible. http://unicode.org/reports/tr31/#R3 -pub fn is_pattern_whitespace(c: Option<char>) -> bool { +crate fn is_pattern_whitespace(c: Option<char>) -> bool { c.map_or(false, Pattern_White_Space) } @@ -1736,14 +1739,14 @@ fn is_dec_digit(c: Option<char>) -> bool { in_range(c, '0', '9') } -pub fn is_doc_comment(s: &str) -> bool { +fn is_doc_comment(s: &str) -> bool { let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/') || s.starts_with("//!"); debug!("is {:?} a doc comment? {}", s, res); res } -pub fn is_block_doc_comment(s: &str) -> bool { +fn is_block_doc_comment(s: &str) -> bool { // Prevent `/**/` from being parsed as a doc comment let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*') || s.starts_with("/*!")) && s.len() >= 5; @@ -1802,6 +1805,7 @@ mod tests { raw_identifier_spans: Lock::new(Vec::new()), registered_diagnostics: Lock::new(ErrorMap::new()), non_modrs_mods: Lock::new(vec![]), + buffered_lints: Lock::new(vec![]), } } @@ -1811,7 +1815,7 @@ mod tests { teststr: String) -> StringReader<'a> { let fm = cm.new_filemap(PathBuf::from("zebra.rs").into(), teststr); - StringReader::new(sess, fm) + StringReader::new(sess, fm, None) } #[test] @@ -1831,7 +1835,8 @@ mod tests { tok: token::Ident(id, false), sp: Span::new(BytePos(21), BytePos(23), NO_EXPANSION), }; - assert_eq!(tok1, tok2); + assert_eq!(tok1.tok, tok2.tok); + assert_eq!(tok1.sp, tok2.sp); assert_eq!(string_reader.next_token().tok, token::Whitespace); // the 'main' id is already read: assert_eq!(string_reader.pos.clone(), BytePos(28)); @@ -1841,7 +1846,8 @@ mod tests { tok: mk_ident("main"), sp: Span::new(BytePos(24), BytePos(28), NO_EXPANSION), }; - assert_eq!(tok3, tok4); + assert_eq!(tok3.tok, tok4.tok); + assert_eq!(tok3.sp, tok4.sp); // the lparen is already read: assert_eq!(string_reader.pos.clone(), BytePos(29)) }) diff --git a/src/libsyntax/parse/lexer/tokentrees.rs b/src/libsyntax/parse/lexer/tokentrees.rs index a2c81e24754..36c220fa0d9 100644 --- a/src/libsyntax/parse/lexer/tokentrees.rs +++ b/src/libsyntax/parse/lexer/tokentrees.rs @@ -15,12 +15,10 @@ use tokenstream::{Delimited, TokenStream, TokenTree}; impl<'a> StringReader<'a> { // Parse a stream of tokens into a list of `TokenTree`s, up to an `Eof`. - pub fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> { + crate fn parse_all_token_trees(&mut self) -> PResult<'a, TokenStream> { let mut tts = Vec::new(); while self.token != token::Eof { - let tree = self.parse_token_tree()?; - let is_joint = tree.span().hi() == self.span.lo() && token::is_op(&self.token); - tts.push(if is_joint { tree.joint() } else { tree.into() }); + tts.push(self.parse_token_tree()?); } Ok(TokenStream::concat(tts)) } @@ -32,19 +30,17 @@ impl<'a> StringReader<'a> { if let token::CloseDelim(..) = self.token { return TokenStream::concat(tts); } - let tree = match self.parse_token_tree() { - Ok(tree) => tree, + match self.parse_token_tree() { + Ok(tree) => tts.push(tree), Err(mut e) => { e.emit(); return TokenStream::concat(tts); } - }; - let is_joint = tree.span().hi() == self.span.lo() && token::is_op(&self.token); - tts.push(if is_joint { tree.joint() } else { tree.into() }); + } } } - fn parse_token_tree(&mut self) -> PResult<'a, TokenTree> { + fn parse_token_tree(&mut self) -> PResult<'a, TokenStream> { match self.token { token::Eof => { let msg = "this file contains an un-closed delimiter"; @@ -115,7 +111,7 @@ impl<'a> StringReader<'a> { Ok(TokenTree::Delimited(span, Delimited { delim, tts: tts.into(), - })) + }).into()) }, token::CloseDelim(_) => { // An unexpected closing delimiter (i.e., there is no @@ -127,8 +123,13 @@ impl<'a> StringReader<'a> { }, _ => { let tt = TokenTree::Token(self.span, self.token.clone()); + // Note that testing for joint-ness here is done via the raw + // source span as the joint-ness is a property of the raw source + // rather than wanting to take `override_span` into account. + let raw = self.span_src_raw; self.real_token(); - Ok(tt) + let is_joint = raw.hi() == self.span_src_raw.lo() && token::is_op(&self.token); + Ok(if is_joint { tt.joint() } else { tt.into() }) } } } diff --git a/src/libsyntax/parse/lexer/unicode_chars.rs b/src/libsyntax/parse/lexer/unicode_chars.rs index 35afe8dd56d..a32b515672e 100644 --- a/src/libsyntax/parse/lexer/unicode_chars.rs +++ b/src/libsyntax/parse/lexer/unicode_chars.rs @@ -333,9 +333,9 @@ const ASCII_ARRAY: &'static [(char, &'static str)] = &[ ('=', "Equals Sign"), ('>', "Greater-Than Sign"), ]; -pub fn check_for_substitution<'a>(reader: &StringReader<'a>, +crate fn check_for_substitution<'a>(reader: &StringReader<'a>, ch: char, - err: &mut DiagnosticBuilder<'a>) { + err: &mut DiagnosticBuilder<'a>) -> bool { UNICODE_ARRAY .iter() .find(|&&(c, _, _)| c == ch) @@ -344,14 +344,16 @@ pub fn check_for_substitution<'a>(reader: &StringReader<'a>, match ASCII_ARRAY.iter().find(|&&(c, _)| c == ascii_char) { Some(&(ascii_char, ascii_name)) => { let msg = - format!("unicode character '{}' ({}) looks like '{}' ({}), but it's not", + format!("Unicode character '{}' ({}) looks like '{}' ({}), but it is not", ch, u_name, ascii_char, ascii_name); - err.span_help(span, &msg); + err.span_suggestion(span, &msg, ascii_char.to_string()); + true }, None => { let msg = format!("substitution character not found for '{}'", ch); reader.sess.span_diagnostic.span_bug_no_panic(span, &msg); + false } } - }); + }).unwrap_or(false) } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index f252020bc31..d029509f0c1 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -11,9 +11,10 @@ //! The main parser interface use rustc_data_structures::sync::{Lrc, Lock}; -use ast::{self, CrateConfig}; +use ast::{self, CrateConfig, NodeId}; +use early_buffered_lints::{BufferedEarlyLint, BufferedEarlyLintId}; use codemap::{CodeMap, FilePathMapping}; -use syntax_pos::{self, Span, FileMap, NO_EXPANSION, FileName}; +use syntax_pos::{Span, FileMap, FileName, MultiSpan}; use errors::{Handler, ColorConfig, DiagnosticBuilder}; use feature_gate::UnstableFeatures; use parse::parser::Parser; @@ -23,6 +24,7 @@ use symbol::Symbol; use tokenstream::{TokenStream, TokenTree}; use diagnostics::plugin::ErrorMap; +use std::borrow::Cow; use std::collections::HashSet; use std::iter; use std::path::{Path, PathBuf}; @@ -37,7 +39,6 @@ pub mod lexer; pub mod token; pub mod attr; -pub mod common; pub mod classify; /// Info about a parsing session. @@ -50,13 +51,14 @@ pub struct ParseSess { /// raw identifiers pub raw_identifier_spans: Lock<Vec<Span>>, /// The registered diagnostics codes - pub registered_diagnostics: Lock<ErrorMap>, + crate registered_diagnostics: Lock<ErrorMap>, // Spans where a `mod foo;` statement was included in a non-mod.rs file. // These are used to issue errors if the non_modrs_mods feature is not enabled. pub non_modrs_mods: Lock<Vec<(ast::Ident, Span)>>, /// Used to determine and report recursive mod inclusions included_mod_stack: Lock<Vec<PathBuf>>, code_map: Lrc<CodeMap>, + pub buffered_lints: Lock<Vec<BufferedEarlyLint>>, } impl ParseSess { @@ -80,17 +82,34 @@ impl ParseSess { included_mod_stack: Lock::new(vec![]), code_map, non_modrs_mods: Lock::new(vec![]), + buffered_lints: Lock::new(vec![]), } } pub fn codemap(&self) -> &CodeMap { &self.code_map } + + pub fn buffer_lint<S: Into<MultiSpan>>(&self, + lint_id: BufferedEarlyLintId, + span: S, + id: NodeId, + msg: &str, + ) { + self.buffered_lints.with_lock(|buffered_lints| { + buffered_lints.push(BufferedEarlyLint{ + span: span.into(), + id, + msg: msg.into(), + lint_id, + }); + }); + } } #[derive(Clone)] -pub struct Directory { - pub path: PathBuf, +pub struct Directory<'a> { + pub path: Cow<'a, Path>, pub ownership: DirectoryOwnership, } @@ -130,7 +149,7 @@ pub fn parse_crate_attrs_from_source_str(name: FileName, source: String, sess: & new_parser_from_source_str(sess, name, source).parse_inner_attributes() } -pub fn parse_expr_from_source_str(name: FileName, source: String, sess: &ParseSess) +crate fn parse_expr_from_source_str(name: FileName, source: String, sess: &ParseSess) -> PResult<P<ast::Expr>> { new_parser_from_source_str(sess, name, source).parse_expr() } @@ -139,17 +158,12 @@ pub fn parse_expr_from_source_str(name: FileName, source: String, sess: &ParseSe /// /// Returns `Ok(Some(item))` when successful, `Ok(None)` when no item was found, and `Err` /// when a syntax error occurred. -pub fn parse_item_from_source_str(name: FileName, source: String, sess: &ParseSess) +crate fn parse_item_from_source_str(name: FileName, source: String, sess: &ParseSess) -> PResult<Option<P<ast::Item>>> { new_parser_from_source_str(sess, name, source).parse_item() } -pub fn parse_meta_from_source_str(name: FileName, source: String, sess: &ParseSess) - -> PResult<ast::MetaItem> { - new_parser_from_source_str(sess, name, source).parse_meta_item() -} - -pub fn parse_stmt_from_source_str(name: FileName, source: String, sess: &ParseSess) +crate fn parse_stmt_from_source_str(name: FileName, source: String, sess: &ParseSess) -> PResult<Option<ast::Stmt>> { new_parser_from_source_str(sess, name, source).parse_stmt() } @@ -177,7 +191,7 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path) -> Parser<'a> /// Given a session, a crate config, a path, and a span, add /// the file at the given path to the codemap, and return a parser. /// On an error, use the given span as the source of the problem. -pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess, +crate fn new_sub_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, directory_ownership: DirectoryOwnership, module_name: Option<String>, @@ -189,12 +203,12 @@ pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess, } /// Given a filemap and config, return a parser -pub fn filemap_to_parser(sess: & ParseSess, filemap: Lrc<FileMap>) -> Parser { +fn filemap_to_parser(sess: & ParseSess, filemap: Lrc<FileMap>) -> Parser { let end_pos = filemap.end_pos; let mut parser = stream_to_parser(sess, filemap_to_stream(sess, filemap, None)); - if parser.token == token::Eof && parser.span == syntax_pos::DUMMY_SP { - parser.span = Span::new(end_pos, end_pos, NO_EXPANSION); + if parser.token == token::Eof && parser.span.is_dummy() { + parser.span = Span::new(end_pos, end_pos, parser.span.ctxt()); } parser @@ -228,8 +242,7 @@ fn file_to_filemap(sess: &ParseSess, path: &Path, spanopt: Option<Span>) /// Given a filemap, produce a sequence of token-trees pub fn filemap_to_stream(sess: &ParseSess, filemap: Lrc<FileMap>, override_span: Option<Span>) -> TokenStream { - let mut srdr = lexer::StringReader::new(sess, filemap); - srdr.override_span = override_span; + let mut srdr = lexer::StringReader::new(sess, filemap, override_span); srdr.real_token(); panictry!(srdr.parse_all_token_trees()) } @@ -243,7 +256,7 @@ pub fn stream_to_parser(sess: &ParseSess, stream: TokenStream) -> Parser { /// Rather than just accepting/rejecting a given literal, unescapes it as /// well. Can take any slice prefixed by a character escape. Returns the /// character and the number of characters consumed. -pub fn char_lit(lit: &str, diag: Option<(Span, &Handler)>) -> (char, isize) { +fn char_lit(lit: &str, diag: Option<(Span, &Handler)>) -> (char, isize) { use std::char; // Handle non-escaped chars first. @@ -369,7 +382,7 @@ pub fn str_lit(lit: &str, diag: Option<(Span, &Handler)>) -> String { /// Parse a string representing a raw string literal into its final form. The /// only operation this does is convert embedded CRLF into a single LF. -pub fn raw_str_lit(lit: &str) -> String { +fn raw_str_lit(lit: &str) -> String { debug!("raw_str_lit: given {}", lit.escape_default()); let mut res = String::with_capacity(lit.len()); @@ -406,7 +419,7 @@ macro_rules! err { } } -pub fn lit_token(lit: token::Lit, suf: Option<Symbol>, diag: Option<(Span, &Handler)>) +crate fn lit_token(lit: token::Lit, suf: Option<Symbol>, diag: Option<(Span, &Handler)>) -> (bool /* suffix illegal? */, Option<ast::LitKind>) { use ast::LitKind; @@ -419,13 +432,24 @@ pub fn lit_token(lit: token::Lit, suf: Option<Symbol>, diag: Option<(Span, &Hand token::Integer(s) => (false, integer_lit(&s.as_str(), suf, diag)), token::Float(s) => (false, float_lit(&s.as_str(), suf, diag)), - token::Str_(s) => { - let s = Symbol::intern(&str_lit(&s.as_str(), diag)); - (true, Some(LitKind::Str(s, ast::StrStyle::Cooked))) + token::Str_(mut sym) => { + // If there are no characters requiring special treatment we can + // reuse the symbol from the Token. Otherwise, we must generate a + // new symbol because the string in the LitKind is different to the + // string in the Token. + let s = &sym.as_str(); + if s.as_bytes().iter().any(|&c| c == b'\\' || c == b'\r') { + sym = Symbol::intern(&str_lit(s, diag)); + } + (true, Some(LitKind::Str(sym, ast::StrStyle::Cooked))) } - token::StrRaw(s, n) => { - let s = Symbol::intern(&raw_str_lit(&s.as_str())); - (true, Some(LitKind::Str(s, ast::StrStyle::Raw(n)))) + token::StrRaw(mut sym, n) => { + // Ditto. + let s = &sym.as_str(); + if s.contains('\r') { + sym = Symbol::intern(&raw_str_lit(s)); + } + (true, Some(LitKind::Str(sym, ast::StrStyle::Raw(n)))) } token::ByteStr(i) => { (true, Some(LitKind::ByteStr(byte_str_lit(&i.as_str())))) @@ -465,7 +489,7 @@ fn filtered_float_lit(data: Symbol, suffix: Option<Symbol>, diag: Option<(Span, } }) } -pub fn float_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>) +fn float_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>) -> Option<ast::LitKind> { debug!("float_lit: {:?}, {:?}", s, suffix); // FIXME #2252: bounds checking float literals is deferred until trans @@ -474,7 +498,7 @@ pub fn float_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler)> } /// Parse a string representing a byte literal into its final form. Similar to `char_lit` -pub fn byte_lit(lit: &str) -> (u8, usize) { +fn byte_lit(lit: &str) -> (u8, usize) { let err = |i| format!("lexer accepted invalid byte literal {} step {}", lit, i); if lit.len() == 1 { @@ -505,7 +529,7 @@ pub fn byte_lit(lit: &str) -> (u8, usize) { } } -pub fn byte_str_lit(lit: &str) -> Lrc<Vec<u8>> { +fn byte_str_lit(lit: &str) -> Lrc<Vec<u8>> { let mut res = Vec::with_capacity(lit.len()); let error = |i| format!("lexer should have rejected {} at {}", lit, i); @@ -564,7 +588,7 @@ pub fn byte_str_lit(lit: &str) -> Lrc<Vec<u8>> { Lrc::new(res) } -pub fn integer_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>) +fn integer_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler)>) -> Option<ast::LitKind> { // s can only be ascii, byte indexing is fine @@ -668,22 +692,40 @@ pub fn integer_lit(s: &str, suffix: Option<Symbol>, diag: Option<(Span, &Handler }) } +/// `SeqSep` : a sequence separator (token) +/// and whether a trailing separator is allowed. +pub struct SeqSep { + pub sep: Option<token::Token>, + pub trailing_sep_allowed: bool, +} + +impl SeqSep { + pub fn trailing_allowed(t: token::Token) -> SeqSep { + SeqSep { + sep: Some(t), + trailing_sep_allowed: true, + } + } + + pub fn none() -> SeqSep { + SeqSep { + sep: None, + trailing_sep_allowed: false, + } + } +} + #[cfg(test)] mod tests { use super::*; - use syntax_pos::{self, Span, BytePos, Pos, NO_EXPANSION}; - use codemap::{respan, Spanned}; + use syntax_pos::{Span, BytePos, Pos, NO_EXPANSION}; use ast::{self, Ident, PatKind}; - use rustc_target::spec::abi::Abi; use attr::first_attr_value_str_by_name; use parse; - use parse::parser::Parser; use print::pprust::item_to_string; - use ptr::P; use tokenstream::{self, TokenTree}; - use util::parser_testing::{string_to_stream, string_to_parser}; - use util::parser_testing::{string_to_expr, string_to_item, string_to_stmt}; - use util::ThinVec; + use util::parser_testing::string_to_stream; + use util::parser_testing::{string_to_expr, string_to_item}; use with_globals; // produce a syntax_pos::span @@ -691,42 +733,6 @@ mod tests { Span::new(BytePos(a), BytePos(b), NO_EXPANSION) } - fn str2seg(s: &str, lo: u32, hi: u32) -> ast::PathSegment { - ast::PathSegment::from_ident(Ident::new(Symbol::intern(s), sp(lo, hi))) - } - - #[test] fn path_exprs_1() { - with_globals(|| { - assert!(string_to_expr("a".to_string()) == - P(ast::Expr{ - id: ast::DUMMY_NODE_ID, - node: ast::ExprKind::Path(None, ast::Path { - span: sp(0, 1), - segments: vec![str2seg("a", 0, 1)], - }), - span: sp(0, 1), - attrs: ThinVec::new(), - })) - }) - } - - #[test] fn path_exprs_2 () { - with_globals(|| { - assert!(string_to_expr("::a::b".to_string()) == - P(ast::Expr { - id: ast::DUMMY_NODE_ID, - node: ast::ExprKind::Path(None, ast::Path { - span: sp(0, 6), - segments: vec![ast::PathSegment::crate_root(sp(0, 0)), - str2seg("a", 2, 3), - str2seg("b", 5, 6)] - }), - span: sp(0, 6), - attrs: ThinVec::new(), - })) - }) - } - #[should_panic] #[test] fn bad_path_expr_1() { with_globals(|| { @@ -827,140 +833,6 @@ mod tests { }) } - #[test] fn ret_expr() { - with_globals(|| { - assert!(string_to_expr("return d".to_string()) == - P(ast::Expr{ - id: ast::DUMMY_NODE_ID, - node:ast::ExprKind::Ret(Some(P(ast::Expr{ - id: ast::DUMMY_NODE_ID, - node:ast::ExprKind::Path(None, ast::Path{ - span: sp(7, 8), - segments: vec![str2seg("d", 7, 8)], - }), - span:sp(7,8), - attrs: ThinVec::new(), - }))), - span:sp(0,8), - attrs: ThinVec::new(), - })) - }) - } - - #[test] fn parse_stmt_1 () { - with_globals(|| { - assert!(string_to_stmt("b;".to_string()) == - Some(ast::Stmt { - node: ast::StmtKind::Expr(P(ast::Expr { - id: ast::DUMMY_NODE_ID, - node: ast::ExprKind::Path(None, ast::Path { - span:sp(0,1), - segments: vec![str2seg("b", 0, 1)], - }), - span: sp(0,1), - attrs: ThinVec::new()})), - id: ast::DUMMY_NODE_ID, - span: sp(0,1)})) - }) - } - - fn parser_done(p: Parser){ - assert_eq!(p.token.clone(), token::Eof); - } - - #[test] fn parse_ident_pat () { - with_globals(|| { - let sess = ParseSess::new(FilePathMapping::empty()); - let mut parser = string_to_parser(&sess, "b".to_string()); - assert!(panictry!(parser.parse_pat()) - == P(ast::Pat{ - id: ast::DUMMY_NODE_ID, - node: PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Immutable), - Ident::new(Symbol::intern("b"), sp(0, 1)), - None), - span: sp(0,1)})); - parser_done(parser); - }) - } - - // check the contents of the tt manually: - #[test] fn parse_fundecl () { - with_globals(|| { - // this test depends on the intern order of "fn" and "i32" - let item = string_to_item("fn a (b : i32) { b; }".to_string()).map(|m| { - m.map(|mut m| { - m.tokens = None; - m - }) - }); - assert_eq!(item, - Some( - P(ast::Item{ident:Ident::from_str("a"), - attrs:Vec::new(), - id: ast::DUMMY_NODE_ID, - tokens: None, - node: ast::ItemKind::Fn(P(ast::FnDecl { - inputs: vec![ast::Arg{ - ty: P(ast::Ty{id: ast::DUMMY_NODE_ID, - node: ast::TyKind::Path(None, ast::Path{ - span:sp(10,13), - segments: vec![str2seg("i32", 10, 13)], - }), - span:sp(10,13) - }), - pat: P(ast::Pat { - id: ast::DUMMY_NODE_ID, - node: PatKind::Ident( - ast::BindingMode::ByValue( - ast::Mutability::Immutable), - Ident::new(Symbol::intern("b"), sp(6, 7)), - None - ), - span: sp(6,7) - }), - id: ast::DUMMY_NODE_ID - }], - output: ast::FunctionRetTy::Default(sp(15, 15)), - variadic: false - }), - ast::Unsafety::Normal, - Spanned { - span: sp(0,2), - node: ast::Constness::NotConst, - }, - Abi::Rust, - ast::Generics{ - params: Vec::new(), - where_clause: ast::WhereClause { - id: ast::DUMMY_NODE_ID, - predicates: Vec::new(), - span: syntax_pos::DUMMY_SP, - }, - span: syntax_pos::DUMMY_SP, - }, - P(ast::Block { - stmts: vec![ast::Stmt { - node: ast::StmtKind::Semi(P(ast::Expr{ - id: ast::DUMMY_NODE_ID, - node: ast::ExprKind::Path(None, - ast::Path{ - span:sp(17,18), - segments: vec![str2seg("b", 17, 18)], - }), - span: sp(17,18), - attrs: ThinVec::new()})), - id: ast::DUMMY_NODE_ID, - span: sp(17,19)}], - id: ast::DUMMY_NODE_ID, - rules: ast::BlockCheckMode::Default, // no idea - span: sp(15,21), - recovered: false, - })), - vis: respan(sp(0, 0), ast::VisibilityKind::Inherited), - span: sp(0,21)}))); - }) - } - #[test] fn parse_use() { with_globals(|| { let use_s = "use foo::bar::baz;"; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index bf4a68679df..9011b6e48b9 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -9,24 +9,25 @@ // except according to those terms. use rustc_target::spec::abi::{self, Abi}; -use ast::{AngleBracketedParameterData, ParenthesizedParameterData, AttrStyle, BareFnTy}; -use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; +use ast::{AngleBracketedArgs, ParenthesisedArgs, AttrStyle, BareFnTy}; +use ast::{GenericBound, TraitBoundModifier}; use ast::Unsafety; -use ast::{Mod, Arg, Arm, Attribute, BindingMode, TraitItemKind}; +use ast::{Mod, AnonConst, Arg, Arm, Attribute, BindingMode, TraitItemKind}; use ast::Block; use ast::{BlockCheckMode, CaptureBy, Movability}; use ast::{Constness, Crate}; use ast::Defaultness; use ast::EnumDef; use ast::{Expr, ExprKind, RangeLimits}; -use ast::{Field, FnDecl}; +use ast::{Field, FnDecl, FnHeader}; use ast::{ForeignItem, ForeignItemKind, FunctionRetTy}; -use ast::GenericParam; -use ast::{Ident, ImplItem, IsAuto, Item, ItemKind}; -use ast::{Label, Lifetime, LifetimeDef, Lit, LitKind, UintTy}; +use ast::{GenericParam, GenericParamKind}; +use ast::GenericArg; +use ast::{Ident, ImplItem, IsAsync, IsAuto, Item, ItemKind}; +use ast::{Label, Lifetime, Lit, LitKind}; use ast::Local; use ast::MacStmtStyle; -use ast::{Mac, Mac_}; +use ast::{Mac, Mac_, MacDelimiter}; use ast::{MutTy, Mutability}; use ast::{Pat, PatKind, PathSegment}; use ast::{PolyTraitRef, QSelf}; @@ -35,17 +36,16 @@ use ast::{VariantData, StructField}; use ast::StrStyle; use ast::SelfKind; use ast::{TraitItem, TraitRef, TraitObjectSyntax}; -use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds}; +use ast::{Ty, TyKind, TypeBinding, GenericBounds}; use ast::{Visibility, VisibilityKind, WhereClause, CrateSugar}; use ast::{UseTree, UseTreeKind}; use ast::{BinOpKind, UnOp}; use ast::{RangeEnd, RangeSyntax}; use {ast, attr}; use codemap::{self, CodeMap, Spanned, respan}; -use syntax_pos::{self, Span, MultiSpan, BytePos, FileName, DUMMY_SP}; -use errors::{self, DiagnosticBuilder}; -use parse::{self, classify, token}; -use parse::common::SeqSep; +use syntax_pos::{self, Span, MultiSpan, BytePos, FileName, edition::Edition}; +use errors::{self, Applicability, DiagnosticBuilder}; +use parse::{self, SeqSep, classify, token}; use parse::lexer::TokenAndSpan; use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; use parse::{new_sub_parser_from_file, ParseSess, Directory, DirectoryOwnership}; @@ -57,13 +57,23 @@ use tokenstream::{self, Delimited, ThinTokenStream, TokenTree, TokenStream}; use symbol::{Symbol, keywords}; use util::ThinVec; +use std::borrow::Cow; use std::cmp; use std::mem; use std::path::{self, Path, PathBuf}; use std::slice; +#[derive(Debug)] +/// Whether the type alias or associated type is a concrete type or an existential type +pub enum AliasKind { + /// Just a new name for the same type + Weak(P<Ty>), + /// Only trait impls of the type will be usable, not the actual type itself + Existential(GenericBounds), +} + bitflags! { - pub struct Restrictions: u8 { + struct Restrictions: u8 { const STMT_EXPR = 1 << 0; const NO_STRUCT_LITERAL = 1 << 1; } @@ -94,14 +104,14 @@ pub enum PathStyle { Mod, } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum SemiColonMode { +#[derive(Clone, Copy, PartialEq, Debug)] +enum SemiColonMode { Break, Ignore, } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum BlockMode { +#[derive(Clone, Copy, PartialEq, Debug)] +enum BlockMode { Break, Ignore, } @@ -115,7 +125,7 @@ macro_rules! maybe_whole_expr { ($p:expr) => { if let token::Interpolated(nt) = $p.token.clone() { match nt.0 { - token::NtExpr(ref e) => { + token::NtExpr(ref e) | token::NtLiteral(ref e) => { $p.bump(); return Ok((*e).clone()); } @@ -128,7 +138,7 @@ macro_rules! maybe_whole_expr { token::NtBlock(ref block) => { $p.bump(); let span = $p.span; - let kind = ExprKind::Block((*block).clone()); + let kind = ExprKind::Block((*block).clone(), None); return Ok($p.mk_expr(span, kind, ThinVec::new())); } _ => {}, @@ -222,22 +232,22 @@ pub struct Parser<'a> { /// the span of the current token: pub span: Span, /// the span of the previous token: - pub meta_var_span: Option<Span>, + meta_var_span: Option<Span>, pub prev_span: Span, /// the previous token kind prev_token_kind: PrevTokenKind, - pub restrictions: Restrictions, + restrictions: Restrictions, /// Used to determine the path to externally loaded source files - pub directory: Directory, + crate directory: Directory<'a>, /// Whether to parse sub-modules in other files. pub recurse_into_file_modules: bool, /// Name of the root module this parser originated from. If `None`, then the /// name is not known. This does not change while the parser is descending /// into modules, and sub-parsers have new values for this name. pub root_module_name: Option<String>, - pub expected_tokens: Vec<TokenType>, + crate expected_tokens: Vec<TokenType>, token_cursor: TokenCursor, - pub desugar_doc_comments: bool, + desugar_doc_comments: bool, /// Whether we should configure out of line modules as we parse. pub cfg_mods: bool, } @@ -278,8 +288,8 @@ struct TokenCursorFrame { /// on the parser. #[derive(Clone)] enum LastToken { - Collecting(Vec<TokenTree>), - Was(Option<TokenTree>), + Collecting(Vec<TokenStream>), + Was(Option<TokenStream>), } impl TokenCursorFrame { @@ -316,8 +326,8 @@ impl TokenCursor { }; match self.frame.last_token { - LastToken::Collecting(ref mut v) => v.push(tree.clone()), - LastToken::Was(ref mut t) => *t = Some(tree.clone()), + LastToken::Collecting(ref mut v) => v.push(tree.clone().into()), + LastToken::Was(ref mut t) => *t = Some(tree.clone().into()), } match tree { @@ -375,8 +385,8 @@ impl TokenCursor { } } -#[derive(PartialEq, Eq, Clone)] -pub enum TokenType { +#[derive(Clone, PartialEq)] +crate enum TokenType { Token(token::Token), Keyword(keywords::Keyword), Operator, @@ -389,7 +399,7 @@ pub enum TokenType { impl TokenType { fn to_string(&self) -> String { match *self { - TokenType::Token(ref t) => format!("`{}`", Parser::token_to_string(t)), + TokenType::Token(ref t) => format!("`{}`", pprust::token_to_string(t)), TokenType::Keyword(kw) => format!("`{}`", kw.name()), TokenType::Operator => "an operator".to_string(), TokenType::Lifetime => "lifetime".to_string(), @@ -412,8 +422,8 @@ fn can_continue_type_after_non_fn_ident(t: &token::Token) -> bool { /// Information about the path to a module. pub struct ModulePath { - pub name: String, - pub path_exists: bool, + name: String, + path_exists: bool, pub result: Result<ModulePathSuccess, Error>, } @@ -423,11 +433,6 @@ pub struct ModulePathSuccess { warn: bool, } -pub struct ModulePathError { - pub err_msg: String, - pub help_msg: String, -} - pub enum Error { FileNotFoundForModule { mod_name: String, @@ -445,7 +450,7 @@ pub enum Error { } impl Error { - pub fn span_err<S: Into<MultiSpan>>(self, + fn span_err<S: Into<MultiSpan>>(self, sp: S, handler: &errors::Handler) -> DiagnosticBuilder { match self { @@ -488,7 +493,7 @@ impl Error { } #[derive(Debug)] -pub enum LhsExpr { +enum LhsExpr { NotYetParsed, AttributesParsed(ThinVec<Attribute>), AlreadyParsed(P<Expr>), @@ -526,7 +531,7 @@ fn dummy_arg(span: Span) -> Arg { Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug)] enum TokenExpectType { Expect, NoExpect, @@ -535,7 +540,7 @@ enum TokenExpectType { impl<'a> Parser<'a> { pub fn new(sess: &'a ParseSess, tokens: TokenStream, - directory: Option<Directory>, + directory: Option<Directory<'a>>, recurse_into_file_modules: bool, desugar_doc_comments: bool) -> Self { @@ -549,7 +554,7 @@ impl<'a> Parser<'a> { restrictions: Restrictions::empty(), recurse_into_file_modules, directory: Directory { - path: PathBuf::new(), + path: Cow::from(PathBuf::new()), ownership: DirectoryOwnership::Owned { relative: None } }, root_module_name: None, @@ -571,10 +576,10 @@ impl<'a> Parser<'a> { if let Some(directory) = directory { parser.directory = directory; - } else if !parser.span.source_equal(&DUMMY_SP) { - if let FileName::Real(path) = sess.codemap().span_to_unmapped_path(parser.span) { - parser.directory.path = path; - parser.directory.path.pop(); + } else if !parser.span.is_dummy() { + if let FileName::Real(mut path) = sess.codemap().span_to_unmapped_path(parser.span) { + path.pop(); + parser.directory.path = Cow::from(path); } } @@ -588,24 +593,19 @@ impl<'a> Parser<'a> { } else { self.token_cursor.next() }; - if next.sp == syntax_pos::DUMMY_SP { + if next.sp.is_dummy() { // Tweak the location for better diagnostics, but keep syntactic context intact. next.sp = self.prev_span.with_ctxt(next.sp.ctxt()); } next } - /// Convert a token to a string using self's reader - pub fn token_to_string(token: &token::Token) -> String { - pprust::token_to_string(token) - } - /// Convert the current token to a string using self's reader pub fn this_token_to_string(&self) -> String { - Parser::token_to_string(&self.token) + pprust::token_to_string(&self.token) } - pub fn token_descr(&self) -> Option<&'static str> { + fn token_descr(&self) -> Option<&'static str> { Some(match &self.token { t if t.is_special_ident() => "reserved identifier", t if t.is_used_keyword() => "keyword", @@ -614,7 +614,7 @@ impl<'a> Parser<'a> { }) } - pub fn this_token_descr(&self) -> String { + fn this_token_descr(&self) -> String { if let Some(prefix) = self.token_descr() { format!("{} `{}`", prefix, self.this_token_to_string()) } else { @@ -622,12 +622,12 @@ impl<'a> Parser<'a> { } } - pub fn unexpected_last<T>(&self, t: &token::Token) -> PResult<'a, T> { - let token_str = Parser::token_to_string(t); + fn unexpected_last<T>(&self, t: &token::Token) -> PResult<'a, T> { + let token_str = pprust::token_to_string(t); Err(self.span_fatal(self.prev_span, &format!("unexpected token: `{}`", token_str))) } - pub fn unexpected<T>(&mut self) -> PResult<'a, T> { + crate fn unexpected<T>(&mut self) -> PResult<'a, T> { match self.expect_one_of(&[], &[]) { Err(e) => Err(e), Ok(_) => unreachable!(), @@ -642,23 +642,42 @@ impl<'a> Parser<'a> { self.bump(); Ok(()) } else { - let token_str = Parser::token_to_string(t); + let token_str = pprust::token_to_string(t); let this_token_str = self.this_token_to_string(); let mut err = self.fatal(&format!("expected `{}`, found `{}`", token_str, this_token_str)); - err.span_label(self.span, format!("expected `{}`", token_str)); + + let sp = if self.token == token::Token::Eof { + // EOF, don't want to point at the following char, but rather the last token + self.prev_span + } else { + self.sess.codemap().next_point(self.prev_span) + }; + let label_exp = format!("expected `{}`", token_str); + let cm = self.sess.codemap(); + match (cm.lookup_line(self.span.lo()), cm.lookup_line(sp.lo())) { + (Ok(ref a), Ok(ref b)) if a.line == b.line => { + // When the spans are in the same line, it means that the only content + // between them is whitespace, point only at the found token. + err.span_label(self.span, label_exp); + } + _ => { + err.span_label(sp, label_exp); + err.span_label(self.span, "unexpected token"); + } + } Err(err) } } else { - self.expect_one_of(unsafe { slice::from_raw_parts(t, 1) }, &[]) + self.expect_one_of(slice::from_ref(t), &[]) } } /// Expect next token to be edible or inedible token. If edible, /// then consume it; if inedible, then return without consuming /// anything. Signal a fatal error if next token is unexpected. - pub fn expect_one_of(&mut self, + fn expect_one_of(&mut self, edible: &[token::Token], inedible: &[token::Token]) -> PResult<'a, ()>{ fn tokens_to_string(tokens: &[TokenType]) -> String { @@ -766,6 +785,9 @@ impl<'a> Parser<'a> { err.span_label(self.span, format!("expected identifier, found {}", token_descr)); } else { err.span_label(self.span, "expected identifier"); + if self.token == token::Comma && self.look_ahead(1, |t| t.is_ident()) { + err.span_suggestion(self.span, "remove this comma", "".into()); + } } err } @@ -803,7 +825,7 @@ impl<'a> Parser<'a> { /// /// This method will automatically add `tok` to `expected_tokens` if `tok` is not /// encountered. - pub fn check(&mut self, tok: &token::Token) -> bool { + fn check(&mut self, tok: &token::Token) -> bool { let is_present = self.token == *tok; if !is_present { self.expected_tokens.push(TokenType::Token(tok.clone())); } is_present @@ -817,7 +839,7 @@ impl<'a> Parser<'a> { is_present } - pub fn check_keyword(&mut self, kw: keywords::Keyword) -> bool { + fn check_keyword(&mut self, kw: keywords::Keyword) -> bool { self.expected_tokens.push(TokenType::Keyword(kw)); self.token.is_keyword(kw) } @@ -833,7 +855,7 @@ impl<'a> Parser<'a> { } } - pub fn eat_keyword_noexpect(&mut self, kw: keywords::Keyword) -> bool { + fn eat_keyword_noexpect(&mut self, kw: keywords::Keyword) -> bool { if self.token.is_keyword(kw) { self.bump(); true @@ -845,7 +867,7 @@ impl<'a> Parser<'a> { /// If the given word is not a keyword, signal an error. /// If the next token is not the given word, signal an error. /// Otherwise, eat it. - pub fn expect_keyword(&mut self, kw: keywords::Keyword) -> PResult<'a, ()> { + fn expect_keyword(&mut self, kw: keywords::Keyword) -> PResult<'a, ()> { if !self.eat_keyword(kw) { self.unexpected() } else { @@ -880,6 +902,40 @@ impl<'a> Parser<'a> { } } + /// Expect and consume a `+`. if `+=` is seen, replace it with a `=` + /// and continue. If a `+` is not seen, return false. + /// + /// This is using when token splitting += into +. + /// See issue 47856 for an example of when this may occur. + fn eat_plus(&mut self) -> bool { + self.expected_tokens.push(TokenType::Token(token::BinOp(token::Plus))); + match self.token { + token::BinOp(token::Plus) => { + self.bump(); + true + } + token::BinOpEq(token::Plus) => { + let span = self.span.with_lo(self.span.lo() + BytePos(1)); + self.bump_with(token::Eq, span); + true + } + _ => false, + } + } + + + /// Checks to see if the next token is either `+` or `+=`. + /// Otherwise returns false. + fn check_plus(&mut self) -> bool { + if self.token.is_like_plus() { + true + } + else { + self.expected_tokens.push(TokenType::Token(token::BinOp(token::Plus))); + false + } + } + /// Expect and consume an `&`. If `&&` is seen, replace it with a single /// `&` and continue. If an `&` is not seen, signal an error. fn expect_and(&mut self) -> PResult<'a, ()> { @@ -914,7 +970,7 @@ impl<'a> Parser<'a> { } } - pub fn expect_no_suffix(&self, sp: Span, kind: &str, suffix: Option<ast::Name>) { + fn expect_no_suffix(&self, sp: Span, kind: &str, suffix: Option<ast::Name>) { match suffix { None => {/* everything ok */} Some(suf) => { @@ -959,7 +1015,7 @@ impl<'a> Parser<'a> { /// Expect and consume a GT. if a >> is seen, replace it /// with a single > and continue. If a GT is not seen, /// signal an error. - pub fn expect_gt(&mut self) -> PResult<'a, ()> { + fn expect_gt(&mut self) -> PResult<'a, ()> { self.expected_tokens.push(TokenType::Token(token::Gt)); match self.token { token::Gt => { @@ -982,83 +1038,9 @@ impl<'a> Parser<'a> { } } - pub fn parse_seq_to_before_gt_or_return<T, F>(&mut self, - sep: Option<token::Token>, - mut f: F) - -> PResult<'a, (Vec<T>, bool)> - where F: FnMut(&mut Parser<'a>) -> PResult<'a, Option<T>>, - { - let mut v = Vec::new(); - // This loop works by alternating back and forth between parsing types - // and commas. For example, given a string `A, B,>`, the parser would - // first parse `A`, then a comma, then `B`, then a comma. After that it - // would encounter a `>` and stop. This lets the parser handle trailing - // commas in generic parameters, because it can stop either after - // parsing a type or after parsing a comma. - for i in 0.. { - if self.check(&token::Gt) - || self.token == token::BinOp(token::Shr) - || self.token == token::Ge - || self.token == token::BinOpEq(token::Shr) { - break; - } - - if i % 2 == 0 { - match f(self)? { - Some(result) => v.push(result), - None => return Ok((v, true)) - } - } else { - if let Some(t) = sep.as_ref() { - self.expect(t)?; - } - - } - } - return Ok((v, false)); - } - - /// Parse a sequence bracketed by '<' and '>', stopping - /// before the '>'. - pub fn parse_seq_to_before_gt<T, F>(&mut self, - sep: Option<token::Token>, - mut f: F) - -> PResult<'a, Vec<T>> where - F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, - { - let (result, returned) = self.parse_seq_to_before_gt_or_return(sep, - |p| Ok(Some(f(p)?)))?; - assert!(!returned); - return Ok(result); - } - - pub fn parse_seq_to_gt<T, F>(&mut self, - sep: Option<token::Token>, - f: F) - -> PResult<'a, Vec<T>> where - F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, - { - let v = self.parse_seq_to_before_gt(sep, f)?; - self.expect_gt()?; - return Ok(v); - } - - pub fn parse_seq_to_gt_or_return<T, F>(&mut self, - sep: Option<token::Token>, - f: F) - -> PResult<'a, (Vec<T>, bool)> where - F: FnMut(&mut Parser<'a>) -> PResult<'a, Option<T>>, - { - let (v, returned) = self.parse_seq_to_before_gt_or_return(sep, f)?; - if !returned { - self.expect_gt()?; - } - return Ok((v, returned)); - } - /// Eat and discard tokens until one of `kets` is encountered. Respects token trees, /// passes through any errors encountered. Used for error recovery. - pub fn eat_to_tokens(&mut self, kets: &[&token::Token]) { + fn eat_to_tokens(&mut self, kets: &[&token::Token]) { let handler = self.diagnostic(); if let Err(ref mut err) = self.parse_seq_to_before_tokens(kets, @@ -1107,7 +1089,12 @@ impl<'a> Parser<'a> { { let mut first: bool = true; let mut v = vec![]; - while !kets.contains(&&self.token) { + while !kets.iter().any(|k| { + match expect { + TokenExpectType::Expect => self.check(k), + TokenExpectType::NoExpect => self.token == **k, + } + }) { match self.token { token::CloseDelim(..) | token::Eof => break, _ => {} @@ -1157,7 +1144,7 @@ impl<'a> Parser<'a> { /// Parse a sequence, including the closing delimiter. The function /// f must consume tokens until reaching the next separator or /// closing bracket. - pub fn parse_unspanned_seq<T, F>(&mut self, + fn parse_unspanned_seq<T, F>(&mut self, bra: &token::Token, ket: &token::Token, sep: SeqSep, @@ -1173,24 +1160,6 @@ impl<'a> Parser<'a> { Ok(result) } - // NB: Do not use this function unless you actually plan to place the - // spanned list in the AST. - pub fn parse_seq<T, F>(&mut self, - bra: &token::Token, - ket: &token::Token, - sep: SeqSep, - f: F) - -> PResult<'a, Spanned<Vec<T>>> where - F: FnMut(&mut Parser<'a>) -> PResult<'a, T>, - { - let lo = self.span; - self.expect(bra)?; - let result = self.parse_seq_to_before_end(ket, sep, f)?; - let hi = self.span; - self.bump(); - Ok(respan(lo.to(hi), result)) - } - /// Advance the parser by one token pub fn bump(&mut self) { if self.prev_token_kind == PrevTokenKind::Eof { @@ -1221,7 +1190,7 @@ impl<'a> Parser<'a> { /// Advance the parser using provided token as a next one. Use this when /// consuming a part of a token. For example a single `<` from `<<`. - pub fn bump_with(&mut self, next: token::Token, span: Span) { + fn bump_with(&mut self, next: token::Token, span: Span) { self.prev_span = self.span.with_hi(span.lo()); // It would be incorrect to record the kind of the current token, but // fortunately for tokens currently using `bump_with`, the @@ -1264,41 +1233,22 @@ impl<'a> Parser<'a> { pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> { self.sess.span_diagnostic.struct_span_fatal(sp, m) } - pub fn span_fatal_err<S: Into<MultiSpan>>(&self, sp: S, err: Error) -> DiagnosticBuilder<'a> { + fn span_fatal_err<S: Into<MultiSpan>>(&self, sp: S, err: Error) -> DiagnosticBuilder<'a> { err.span_err(sp, self.diagnostic()) } - pub fn span_fatal_help<S: Into<MultiSpan>>(&self, - sp: S, - m: &str, - help: &str) -> DiagnosticBuilder<'a> { - let mut err = self.sess.span_diagnostic.struct_span_fatal(sp, m); - err.help(help); - err - } - pub fn bug(&self, m: &str) -> ! { + fn bug(&self, m: &str) -> ! { self.sess.span_diagnostic.span_bug(self.span, m) } - pub fn warn(&self, m: &str) { - self.sess.span_diagnostic.span_warn(self.span, m) - } - pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, m: &str) { - self.sess.span_diagnostic.span_warn(sp, m) - } - pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) { + fn span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) { self.sess.span_diagnostic.span_err(sp, m) } - pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> { + fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> { self.sess.span_diagnostic.struct_span_err(sp, m) } - pub fn span_err_help<S: Into<MultiSpan>>(&self, sp: S, m: &str, h: &str) { - let mut err = self.sess.span_diagnostic.mut_span_err(sp, m); - err.help(h); - err.emit(); - } - pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> ! { + crate fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> ! { self.sess.span_diagnostic.span_bug(sp, m) } - pub fn abort_if_errors(&self) { + crate fn abort_if_errors(&self) { self.sess.span_diagnostic.abort_if_errors(); } @@ -1306,21 +1256,20 @@ impl<'a> Parser<'a> { self.sess.span_diagnostic.cancel(err) } - pub fn diagnostic(&self) -> &'a errors::Handler { + crate fn diagnostic(&self) -> &'a errors::Handler { &self.sess.span_diagnostic } /// Is the current token one of the keywords that signals a bare function /// type? - pub fn token_is_bare_fn_keyword(&mut self) -> bool { + fn token_is_bare_fn_keyword(&mut self) -> bool { self.check_keyword(keywords::Fn) || self.check_keyword(keywords::Unsafe) || self.check_keyword(keywords::Extern) && self.is_extern_non_path() } /// parse a TyKind::BareFn type: - pub fn parse_ty_bare_fn(&mut self, generic_params: Vec<GenericParam>) - -> PResult<'a, TyKind> { + fn parse_ty_bare_fn(&mut self, generic_params: Vec<GenericParam>) -> PResult<'a, TyKind> { /* [unsafe] [extern "ABI"] fn (S) -> T @@ -1356,6 +1305,18 @@ impl<'a> Parser<'a> { }))) } + /// Parse asyncness: `async` or nothing + fn parse_asyncness(&mut self) -> IsAsync { + if self.eat_keyword(keywords::Async) { + IsAsync::Async { + closure_id: ast::DUMMY_NODE_ID, + return_impl_trait_id: ast::DUMMY_NODE_ID, + } + } else { + IsAsync::NotAsync + } + } + /// Parse unsafety: `unsafe` or nothing. fn parse_unsafety(&mut self) -> Unsafety { if self.eat_keyword(keywords::Unsafe) { @@ -1385,9 +1346,7 @@ impl<'a> Parser<'a> { let lo = self.span; let (name, node, generics) = if self.eat_keyword(keywords::Type) { - let (generics, TyParam {ident, bounds, default, ..}) = - self.parse_trait_item_assoc_ty(vec![])?; - (ident, TraitItemKind::Type(bounds, default), generics) + self.parse_trait_item_assoc_ty()? } else if self.is_const_item() { self.expect_keyword(keywords::Const)?; let ident = self.parse_ident()?; @@ -1407,7 +1366,7 @@ impl<'a> Parser<'a> { // trait item macro. (keywords::Invalid.ident(), ast::TraitItemKind::Macro(mac), ast::Generics::default()) } else { - let (constness, unsafety, abi) = self.parse_fn_front_matter()?; + let (constness, unsafety, asyncness, abi) = self.parse_fn_front_matter()?; let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; @@ -1421,10 +1380,13 @@ impl<'a> Parser<'a> { generics.where_clause = self.parse_where_clause()?; let sig = ast::MethodSig { - unsafety, - constness, + header: FnHeader { + unsafety, + constness, + abi, + asyncness, + }, decl: d, - abi, }; let body = match self.token { @@ -1511,7 +1473,7 @@ impl<'a> Parser<'a> { if ts.len() == 1 && !last_comma { let ty = ts.into_iter().nth(0).unwrap().into_inner(); - let maybe_bounds = allow_plus && self.token == token::BinOp(token::Plus); + let maybe_bounds = allow_plus && self.token.is_like_plus(); match ty.node { // `(TY_BOUND_NOPAREN) + BOUND + ...`. TyKind::Path(None, ref path) if maybe_bounds => { @@ -1520,7 +1482,7 @@ impl<'a> Parser<'a> { TyKind::TraitObject(ref bounds, TraitObjectSyntax::None) if maybe_bounds && bounds.len() == 1 && !trailing_plus => { let path = match bounds[0] { - TraitTyParamBound(ref pt, ..) => pt.trait_ref.path.clone(), + GenericBound::Trait(ref pt, ..) => pt.trait_ref.path.clone(), _ => self.bug("unexpected lifetime bound"), }; self.parse_remaining_bounds(Vec::new(), path, lo, true)? @@ -1543,7 +1505,10 @@ impl<'a> Parser<'a> { // Parse optional `; EXPR` in `[TYPE; EXPR]` let t = match self.maybe_parse_fixed_length_of_vec()? { None => TyKind::Slice(t), - Some(suffix) => TyKind::Array(t, suffix), + Some(length) => TyKind::Array(t, AnonConst { + id: ast::DUMMY_NODE_ID, + value: length, + }), }; self.expect(&token::CloseDelim(token::Bracket))?; t @@ -1555,7 +1520,10 @@ impl<'a> Parser<'a> { // `typeof(EXPR)` // In order to not be ambiguous, the type must be surrounded by parens. self.expect(&token::OpenDelim(token::Paren))?; - let e = self.parse_expr()?; + let e = AnonConst { + id: ast::DUMMY_NODE_ID, + value: self.parse_expr()?, + }; self.expect(&token::CloseDelim(token::Paren))?; TyKind::Typeof(e) } else if self.eat_keyword(keywords::Underscore) { @@ -1574,26 +1542,26 @@ impl<'a> Parser<'a> { self.parse_ty_bare_fn(lifetime_defs)? } else { let path = self.parse_path(PathStyle::Type)?; - let parse_plus = allow_plus && self.check(&token::BinOp(token::Plus)); + let parse_plus = allow_plus && self.check_plus(); self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)? } } else if self.eat_keyword(keywords::Impl) { // Always parse bounds greedily for better error recovery. - let bounds = self.parse_ty_param_bounds()?; + let bounds = self.parse_generic_bounds()?; impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus; - TyKind::ImplTrait(bounds) + TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds) } else if self.check_keyword(keywords::Dyn) && self.look_ahead(1, |t| t.can_begin_bound() && !can_continue_type_after_non_fn_ident(t)) { self.bump(); // `dyn` // Always parse bounds greedily for better error recovery. - let bounds = self.parse_ty_param_bounds()?; + let bounds = self.parse_generic_bounds()?; impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus; TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn) } else if self.check(&token::Question) || - self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)) { + self.check_lifetime() && self.look_ahead(1, |t| t.is_like_plus()) { // Bound list (trait object type) - TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?, + TyKind::TraitObject(self.parse_generic_bounds_common(allow_plus)?, TraitObjectSyntax::None) } else if self.eat_lt() { // Qualified path @@ -1604,13 +1572,14 @@ impl<'a> Parser<'a> { let path = self.parse_path(PathStyle::Type)?; if self.eat(&token::Not) { // Macro invocation in type position - let (_, tts) = self.expect_delimited_token_tree()?; - TyKind::Mac(respan(lo.to(self.prev_span), Mac_ { path: path, tts: tts })) + let (delim, tts) = self.expect_delimited_token_tree()?; + let node = Mac_ { path, tts, delim }; + TyKind::Mac(respan(lo.to(self.prev_span), node)) } else { // Just a type path or bound list (trait object type) starting with a trait. // `Type` // `Trait1 + Trait2 + 'a` - if allow_plus && self.check(&token::BinOp(token::Plus)) { + if allow_plus && self.check_plus() { self.parse_remaining_bounds(Vec::new(), path, lo, true)? } else { TyKind::Path(None, path) @@ -1635,10 +1604,10 @@ impl<'a> Parser<'a> { fn parse_remaining_bounds(&mut self, generic_params: Vec<GenericParam>, path: ast::Path, lo: Span, parse_plus: bool) -> PResult<'a, TyKind> { let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_span)); - let mut bounds = vec![TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None)]; + let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)]; if parse_plus { - self.bump(); // `+` - bounds.append(&mut self.parse_ty_param_bounds()?); + self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded + bounds.append(&mut self.parse_generic_bounds()?); } Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) } @@ -1647,19 +1616,23 @@ impl<'a> Parser<'a> { if !allow_plus && impl_dyn_multi { let sum_with_parens = format!("({})", pprust::ty_to_string(&ty)); self.struct_span_err(ty.span, "ambiguous `+` in a type") - .span_suggestion(ty.span, "use parentheses to disambiguate", sum_with_parens) - .emit(); + .span_suggestion_with_applicability( + ty.span, + "use parentheses to disambiguate", + sum_with_parens, + Applicability::MachineApplicable + ).emit(); } } fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> { // Do not add `+` to expected tokens. - if !allow_plus || self.token != token::BinOp(token::Plus) { + if !allow_plus || !self.token.is_like_plus() { return Ok(()) } self.bump(); // `+` - let bounds = self.parse_ty_param_bounds()?; + let bounds = self.parse_generic_bounds()?; let sum_span = ty.span.to(self.prev_span); let mut err = struct_span_err!(self.sess.span_diagnostic, sum_span, E0178, @@ -1675,10 +1648,15 @@ impl<'a> Parser<'a> { s.print_mutability(mut_ty.mutbl)?; s.popen()?; s.print_type(&mut_ty.ty)?; - s.print_bounds(" +", &bounds)?; + s.print_type_bounds(" +", &bounds)?; s.pclose() }); - err.span_suggestion(sum_span, "try adding parentheses", sum_with_parens); + err.span_suggestion_with_applicability( + sum_span, + "try adding parentheses", + sum_with_parens, + Applicability::MachineApplicable + ); } TyKind::Ptr(..) | TyKind::BareFn(..) => { err.span_label(sum_span, "perhaps you forgot parentheses?"); @@ -1708,12 +1686,17 @@ impl<'a> Parser<'a> { self.parse_path_segments(&mut segments, T::PATH_STYLE, true)?; let span = ty.span.to(self.prev_span); - let recovered = - base.to_recovered(Some(QSelf { ty, position: 0 }), ast::Path { segments, span }); + let path_span = span.to(span); // use an empty path since `position` == 0 + let recovered = base.to_recovered( + Some(QSelf { ty, path_span, position: 0 }), + ast::Path { segments, span }, + ); self.diagnostic() .struct_span_err(span, "missing angle brackets in associated item path") - .span_suggestion(span, "try", recovered.to_string()).emit(); + .span_suggestion_with_applicability( // this is a best-effort recovery + span, "try", recovered.to_string(), Applicability::MaybeIncorrect + ).emit(); Ok(recovered) } @@ -1725,7 +1708,7 @@ impl<'a> Parser<'a> { return Ok(TyKind::Rptr(opt_lifetime, MutTy { ty: ty, mutbl: mutbl })); } - pub fn parse_ptr(&mut self) -> PResult<'a, MutTy> { + fn parse_ptr(&mut self) -> PResult<'a, MutTy> { let mutbl = if self.eat_keyword(keywords::Mut) { Mutability::Mutable } else if self.eat_keyword(keywords::Const) { @@ -1758,42 +1741,42 @@ impl<'a> Parser<'a> { /// This version of parse arg doesn't necessarily require /// identifier names. - pub fn parse_arg_general(&mut self, require_name: bool) -> PResult<'a, Arg> { + fn parse_arg_general(&mut self, require_name: bool) -> PResult<'a, Arg> { maybe_whole!(self, NtArg, |x| x); - let pat = if require_name || self.is_named_argument() { + let (pat, ty) = if require_name || self.is_named_argument() { debug!("parse_arg_general parse_pat (require_name:{})", require_name); let pat = self.parse_pat()?; self.expect(&token::Colon)?; - pat + (pat, self.parse_ty()?) } else { debug!("parse_arg_general ident_to_pat"); let ident = Ident::new(keywords::Invalid.name(), self.prev_span); - P(Pat { + let ty = self.parse_ty()?; + let pat = P(Pat { id: ast::DUMMY_NODE_ID, node: PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None), - span: ident.span, - }) + span: ty.span, + }); + (pat, ty) }; - let t = self.parse_ty()?; - Ok(Arg { - ty: t, + ty, pat, id: ast::DUMMY_NODE_ID, }) } /// Parse a single function argument - pub fn parse_arg(&mut self) -> PResult<'a, Arg> { + crate fn parse_arg(&mut self) -> PResult<'a, Arg> { self.parse_arg_general(true) } /// Parse an argument in a lambda header e.g. |arg, arg| - pub fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> { + fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> { let pat = self.parse_pat()?; let t = if self.eat(&token::Colon) { self.parse_ty()? @@ -1811,7 +1794,7 @@ impl<'a> Parser<'a> { }) } - pub fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<'a, Option<P<ast::Expr>>> { + fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<'a, Option<P<ast::Expr>>> { if self.eat(&token::Semi) { Ok(Some(self.parse_expr()?)) } else { @@ -1820,10 +1803,10 @@ impl<'a> Parser<'a> { } /// Matches token_lit = LIT_INTEGER | ... - pub fn parse_lit_token(&mut self) -> PResult<'a, LitKind> { + fn parse_lit_token(&mut self) -> PResult<'a, LitKind> { let out = match self.token { token::Interpolated(ref nt) => match nt.0 { - token::NtExpr(ref v) => match v.node { + token::NtExpr(ref v) | token::NtLiteral(ref v) => match v.node { ExprKind::Lit(ref lit) => { lit.node.clone() } _ => { return self.unexpected_last(&self.token); } }, @@ -1848,7 +1831,7 @@ impl<'a> Parser<'a> { } /// Matches lit = true | false | token_lit - pub fn parse_lit(&mut self) -> PResult<'a, Lit> { + crate fn parse_lit(&mut self) -> PResult<'a, Lit> { let lo = self.span; let lit = if self.eat_keyword(keywords::True) { LitKind::Bool(true) @@ -1862,7 +1845,7 @@ impl<'a> Parser<'a> { } /// matches '-' lit | lit (cf. ast_validation::AstValidator::check_expr_within_pat) - pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> { + crate fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> { maybe_whole_expr!(self); let minus_lo = self.span; @@ -1881,7 +1864,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_path_segment_ident(&mut self) -> PResult<'a, ast::Ident> { + fn parse_path_segment_ident(&mut self) -> PResult<'a, ast::Ident> { match self.token { token::Ident(ident, _) if self.token.is_path_segment_keyword() => { let span = self.span; @@ -1898,21 +1881,32 @@ impl<'a> Parser<'a> { /// `qualified_path = <type [as trait_ref]>::path` /// /// # Examples + /// `<T>::default` /// `<T as U>::a` /// `<T as U>::F::a<S>` (without disambiguator) /// `<T as U>::F::a::<S>` (with disambiguator) fn parse_qpath(&mut self, style: PathStyle) -> PResult<'a, (QSelf, ast::Path)> { let lo = self.prev_span; let ty = self.parse_ty()?; - let mut path = if self.eat_keyword(keywords::As) { - self.parse_path(PathStyle::Type)? + + // `path` will contain the prefix of the path up to the `>`, + // if any (e.g., `U` in the `<T as U>::*` examples + // above). `path_span` has the span of that path, or an empty + // span in the case of something like `<T>::Bar`. + let (mut path, path_span); + if self.eat_keyword(keywords::As) { + let path_lo = self.span; + path = self.parse_path(PathStyle::Type)?; + path_span = path_lo.to(self.prev_span); } else { - ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP } - }; + path = ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP }; + path_span = self.span.to(self.span); + } + self.expect(&token::Gt)?; self.expect(&token::ModSep)?; - let qself = QSelf { ty, position: path.segments.len() }; + let qself = QSelf { ty, path_span, position: path.segments.len() }; self.parse_path_segments(&mut path.segments, style, true)?; Ok((qself, ast::Path { segments: path.segments, span: lo.to(self.prev_span) })) @@ -1932,11 +1926,11 @@ impl<'a> Parser<'a> { self.parse_path_common(style, true) } - pub fn parse_path_common(&mut self, style: PathStyle, enable_warning: bool) + crate fn parse_path_common(&mut self, style: PathStyle, enable_warning: bool) -> PResult<'a, ast::Path> { maybe_whole!(self, NtPath, |path| { if style == PathStyle::Mod && - path.segments.iter().any(|segment| segment.parameters.is_some()) { + path.segments.iter().any(|segment| segment.args.is_some()) { self.diagnostic().span_err(path.span, "unexpected generic arguments in path"); } path @@ -2011,12 +2005,12 @@ impl<'a> Parser<'a> { .span_label(self.prev_span, "try removing `::`").emit(); } - let parameters = if self.eat_lt() { + let args = if self.eat_lt() { // `<'a, T, A = U>` - let (lifetimes, types, bindings) = self.parse_generic_args()?; + let (args, bindings) = self.parse_generic_args()?; self.expect_gt()?; let span = lo.to(self.prev_span); - AngleBracketedParameterData { lifetimes, types, bindings, span }.into() + AngleBracketedArgs { args, bindings, span }.into() } else { // `(T, U) -> R` self.bump(); // `(` @@ -2032,23 +2026,23 @@ impl<'a> Parser<'a> { None }; let span = lo.to(self.prev_span); - ParenthesizedParameterData { inputs, output, span }.into() + ParenthesisedArgs { inputs, output, span }.into() }; - PathSegment { ident, parameters } + PathSegment { ident, args } } else { // Generic arguments are not found. PathSegment::from_ident(ident) }) } - pub fn check_lifetime(&mut self) -> bool { + crate fn check_lifetime(&mut self) -> bool { self.expected_tokens.push(TokenType::Lifetime); self.token.is_lifetime() } /// Parse single lifetime 'a or panic. - pub fn expect_lifetime(&mut self) -> Lifetime { + crate fn expect_lifetime(&mut self) -> Lifetime { if let Some(ident) = self.token.lifetime() { let span = self.span; self.bump(); @@ -2077,7 +2071,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_field_name(&mut self) -> PResult<'a, Ident> { + fn parse_field_name(&mut self) -> PResult<'a, Ident> { if let token::Literal(token::Integer(name), None) = self.token { self.bump(); Ok(Ident::new(name, self.prev_span)) @@ -2087,7 +2081,7 @@ impl<'a> Parser<'a> { } /// Parse ident (COLON expr)? - pub fn parse_field(&mut self) -> PResult<'a, Field> { + fn parse_field(&mut self) -> PResult<'a, Field> { let attrs = self.parse_outer_attributes()?; let lo = self.span; @@ -2113,27 +2107,27 @@ impl<'a> Parser<'a> { }) } - pub fn mk_expr(&mut self, span: Span, node: ExprKind, attrs: ThinVec<Attribute>) -> P<Expr> { + fn mk_expr(&mut self, span: Span, node: ExprKind, attrs: ThinVec<Attribute>) -> P<Expr> { P(Expr { node, span, attrs, id: ast::DUMMY_NODE_ID }) } - pub fn mk_unary(&mut self, unop: ast::UnOp, expr: P<Expr>) -> ast::ExprKind { + fn mk_unary(&mut self, unop: ast::UnOp, expr: P<Expr>) -> ast::ExprKind { ExprKind::Unary(unop, expr) } - pub fn mk_binary(&mut self, binop: ast::BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ast::ExprKind { + fn mk_binary(&mut self, binop: ast::BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ast::ExprKind { ExprKind::Binary(binop, lhs, rhs) } - pub fn mk_call(&mut self, f: P<Expr>, args: Vec<P<Expr>>) -> ast::ExprKind { + fn mk_call(&mut self, f: P<Expr>, args: Vec<P<Expr>>) -> ast::ExprKind { ExprKind::Call(f, args) } - pub fn mk_index(&mut self, expr: P<Expr>, idx: P<Expr>) -> ast::ExprKind { + fn mk_index(&mut self, expr: P<Expr>, idx: P<Expr>) -> ast::ExprKind { ExprKind::Index(expr, idx) } - pub fn mk_range(&mut self, + fn mk_range(&mut self, start: Option<P<Expr>>, end: Option<P<Expr>>, limits: RangeLimits) @@ -2145,7 +2139,7 @@ impl<'a> Parser<'a> { } } - pub fn mk_assign_op(&mut self, binop: ast::BinOp, + fn mk_assign_op(&mut self, binop: ast::BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ast::ExprKind { ExprKind::AssignOp(binop, lhs, rhs) } @@ -2159,34 +2153,27 @@ impl<'a> Parser<'a> { }) } - pub fn mk_lit_u32(&mut self, i: u32, attrs: ThinVec<Attribute>) -> P<Expr> { - let span = &self.span; - let lv_lit = P(codemap::Spanned { - node: LitKind::Int(i as u128, ast::LitIntType::Unsigned(UintTy::U32)), - span: *span - }); - - P(Expr { - id: ast::DUMMY_NODE_ID, - node: ExprKind::Lit(lv_lit), - span: *span, - attrs, - }) - } - - fn expect_delimited_token_tree(&mut self) -> PResult<'a, (token::DelimToken, ThinTokenStream)> { - match self.token { - token::OpenDelim(delim) => match self.parse_token_tree() { - TokenTree::Delimited(_, delimited) => Ok((delim, delimited.stream().into())), - _ => unreachable!(), - }, + fn expect_delimited_token_tree(&mut self) -> PResult<'a, (MacDelimiter, ThinTokenStream)> { + let delim = match self.token { + token::OpenDelim(delim) => delim, _ => { let msg = "expected open delimiter"; let mut err = self.fatal(msg); err.span_label(self.span, msg); - Err(err) + return Err(err) } - } + }; + let delimited = match self.parse_token_tree() { + TokenTree::Delimited(_, delimited) => delimited, + _ => unreachable!(), + }; + let delim = match delim { + token::Paren => MacDelimiter::Parenthesis, + token::Bracket => MacDelimiter::Bracket, + token::Brace => MacDelimiter::Brace, + token::NoDelim => self.bug("unexpected no delimiter"), + }; + Ok((delim, delimited.stream().into())) } /// At the bottom (top?) of the precedence hierarchy, @@ -2244,7 +2231,7 @@ impl<'a> Parser<'a> { }; } token::OpenDelim(token::Brace) => { - return self.parse_block_expr(lo, BlockCheckMode::Default, attrs); + return self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs); } token::BinOp(token::Or) | token::OrOr => { return self.parse_lambda_expr(attrs); @@ -2264,7 +2251,10 @@ impl<'a> Parser<'a> { if self.check(&token::Semi) { // Repeating array syntax: [ 0; 512 ] self.bump(); - let count = self.parse_expr()?; + let count = AnonConst { + id: ast::DUMMY_NODE_ID, + value: self.parse_expr()?, + }; self.expect(&token::CloseDelim(token::Bracket))?; ex = ExprKind::Repeat(first_expr, count); } else if self.check(&token::Comma) { @@ -2292,6 +2282,15 @@ impl<'a> Parser<'a> { hi = path.span; return Ok(self.mk_expr(lo.to(hi), ExprKind::Path(Some(qself), path), attrs)); } + if self.span.edition() >= Edition::Edition2018 && + self.check_keyword(keywords::Async) + { + if self.is_async_block() { // check for `async {` and `async move {` + return self.parse_async_block(attrs); + } else { + return self.parse_lambda_expr(attrs); + } + } if self.check_keyword(keywords::Move) || self.check_keyword(keywords::Static) { return self.parse_lambda_expr(attrs); } @@ -2318,7 +2317,13 @@ impl<'a> Parser<'a> { if self.eat_keyword(keywords::Loop) { return self.parse_loop_expr(Some(label), lo, attrs) } - let msg = "expected `while`, `for`, or `loop` after a label"; + if self.token == token::OpenDelim(token::Brace) { + return self.parse_block_expr(Some(label), + lo, + BlockCheckMode::Default, + attrs); + } + let msg = "expected `while`, `for`, `loop` or `{` after a label"; let mut err = self.fatal(msg); err.span_label(self.span, msg); return Err(err); @@ -2338,6 +2343,7 @@ impl<'a> Parser<'a> { } if self.eat_keyword(keywords::Unsafe) { return self.parse_block_expr( + None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs); @@ -2389,9 +2395,10 @@ impl<'a> Parser<'a> { // `!`, as an operator, is prefix, so we know this isn't that if self.eat(&token::Not) { // MACRO INVOCATION expression - let (_, tts) = self.expect_delimited_token_tree()?; + let (delim, tts) = self.expect_delimited_token_tree()?; let hi = self.prev_span; - return Ok(self.mk_mac_expr(lo.to(hi), Mac_ { path: pth, tts: tts }, attrs)); + let node = Mac_ { path: pth, tts, delim }; + return Ok(self.mk_mac_expr(lo.to(hi), node, attrs)) } if self.check(&token::OpenDelim(token::Brace)) { // This is a struct literal, unless we're prohibited @@ -2407,10 +2414,10 @@ impl<'a> Parser<'a> { hi = pth.span; ex = ExprKind::Path(None, pth); } else { - match self.parse_lit() { - Ok(lit) => { - hi = lit.span; - ex = ExprKind::Lit(P(lit)); + match self.parse_literal_maybe_minus() { + Ok(expr) => { + hi = expr.span; + ex = expr.node.clone(); } Err(mut err) => { self.cancel(&mut err); @@ -2457,7 +2464,12 @@ impl<'a> Parser<'a> { exp_span.to(self.prev_span), "cannot use a comma after the base struct", ); - err.span_suggestion_short(self.span, "remove this comma", "".to_owned()); + err.span_suggestion_short_with_applicability( + self.span, + "remove this comma", + "".to_owned(), + Applicability::MachineApplicable + ); err.note("the base struct must always be the last field"); err.emit(); self.recover_stmt(); @@ -2470,8 +2482,14 @@ impl<'a> Parser<'a> { Err(mut e) => { e.span_label(struct_sp, "while parsing this struct"); e.emit(); - self.recover_stmt(); - break; + + // If the next token is a comma, then try to parse + // what comes next as additional fields, rather than + // bailing out until next `}`. + if self.token != token::Comma { + self.recover_stmt(); + break; + } } } @@ -2502,7 +2520,8 @@ impl<'a> Parser<'a> { } /// Parse a block or unsafe block - pub fn parse_block_expr(&mut self, lo: Span, blk_mode: BlockCheckMode, + fn parse_block_expr(&mut self, opt_label: Option<Label>, + lo: Span, blk_mode: BlockCheckMode, outer_attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { self.expect(&token::OpenDelim(token::Brace))?; @@ -2511,11 +2530,11 @@ impl<'a> Parser<'a> { attrs.extend(self.parse_inner_attributes()?); let blk = self.parse_block_tail(lo, blk_mode)?; - return Ok(self.mk_expr(blk.span, ExprKind::Block(blk), attrs)); + return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs)); } /// parse a.b or a(13) or a[4] or just a - pub fn parse_dot_or_call_expr(&mut self, + fn parse_dot_or_call_expr(&mut self, already_parsed_attrs: Option<ThinVec<Attribute>>) -> PResult<'a, P<Expr>> { let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; @@ -2525,7 +2544,7 @@ impl<'a> Parser<'a> { self.parse_dot_or_call_expr_with(b, span, attrs) } - pub fn parse_dot_or_call_expr_with(&mut self, + fn parse_dot_or_call_expr_with(&mut self, e0: P<Expr>, lo: Span, mut attrs: ThinVec<Attribute>) @@ -2575,8 +2594,8 @@ impl<'a> Parser<'a> { } _ => { // Field access `expr.f` - if let Some(parameters) = segment.parameters { - self.span_err(parameters.span(), + if let Some(args) = segment.args { + self.span_err(args.span(), "field expressions may not have generic arguments"); } @@ -2629,10 +2648,12 @@ impl<'a> Parser<'a> { s.s.word(".")?; s.s.word(fstr.splitn(2, ".").last().unwrap()) }); - err.span_suggestion( + err.span_suggestion_with_applicability( lo.to(self.prev_span), "try parenthesizing the first index", - sugg); + sugg, + Applicability::MachineApplicable + ); } return Err(err); @@ -2677,7 +2698,7 @@ impl<'a> Parser<'a> { return Ok(e); } - pub fn process_potential_macro_variable(&mut self) { + crate fn process_potential_macro_variable(&mut self) { let (token, span) = match self.token { token::Dollar if self.span.ctxt() != syntax_pos::hygiene::SyntaxContext::empty() && self.look_ahead(1, |t| t.is_ident()) => { @@ -2708,7 +2729,7 @@ impl<'a> Parser<'a> { } /// parse a single token tree from the input. - pub fn parse_token_tree(&mut self) -> TokenTree { + crate fn parse_token_tree(&mut self) -> TokenTree { match self.token { token::OpenDelim(..) => { let frame = mem::replace(&mut self.token_cursor.frame, @@ -2751,7 +2772,7 @@ impl<'a> Parser<'a> { } /// Parse a prefix-unary-operator expr - pub fn parse_prefix_expr(&mut self, + fn parse_prefix_expr(&mut self, already_parsed_attrs: Option<ThinVec<Attribute>>) -> PResult<'a, P<Expr>> { let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; @@ -2772,9 +2793,12 @@ impl<'a> Parser<'a> { let span_of_tilde = lo; let mut err = self.diagnostic().struct_span_err(span_of_tilde, "`~` cannot be used as a unary operator"); - err.span_suggestion_short(span_of_tilde, - "use `!` to perform bitwise negation", - "!".to_owned()); + err.span_suggestion_short_with_applicability( + span_of_tilde, + "use `!` to perform bitwise negation", + "!".to_owned(), + Applicability::MachineApplicable + ); err.emit(); (lo.to(span), self.mk_unary(UnOp::Not, e)) } @@ -2797,6 +2821,17 @@ impl<'a> Parser<'a> { let (span, e) = self.interpolated_or_expr_span(e)?; (lo.to(span), ExprKind::AddrOf(m, e)) } + token::Ident(..) if self.token.is_keyword(keywords::In) => { + self.bump(); + let place = self.parse_expr_res( + Restrictions::NO_STRUCT_LITERAL, + None, + )?; + let blk = self.parse_block()?; + let span = blk.span; + let blk_expr = self.mk_expr(span, ExprKind::Block(blk, None), ThinVec::new()); + (lo.to(span), ExprKind::ObsoleteInPlace(place, blk_expr)) + } token::Ident(..) if self.token.is_keyword(keywords::Box) => { self.bump(); let e = self.parse_prefix_expr(None); @@ -2831,9 +2866,12 @@ impl<'a> Parser<'a> { // trailing whitespace after the `!` in our suggestion let to_replace = self.sess.codemap() .span_until_non_whitespace(lo.to(self.span)); - err.span_suggestion_short(to_replace, - "use `!` to perform logical negation", - "!".to_owned()); + err.span_suggestion_short_with_applicability( + to_replace, + "use `!` to perform logical negation", + "!".to_owned(), + Applicability::MachineApplicable + ); err.emit(); // —and recover! (just as if we were in the block // for the `token::Not` arm) @@ -2853,14 +2891,14 @@ impl<'a> Parser<'a> { /// /// This parses an expression accounting for associativity and precedence of the operators in /// the expression. - pub fn parse_assoc_expr(&mut self, + fn parse_assoc_expr(&mut self, already_parsed_attrs: Option<ThinVec<Attribute>>) -> PResult<'a, P<Expr>> { self.parse_assoc_expr_with(0, already_parsed_attrs.into()) } /// Parse an associative expression with operators of at least `min_prec` precedence - pub fn parse_assoc_expr_with(&mut self, + fn parse_assoc_expr_with(&mut self, min_prec: usize, lhs: LhsExpr) -> PResult<'a, P<Expr>> { @@ -2928,9 +2966,12 @@ impl<'a> Parser<'a> { let cur_pos = cm.lookup_char_pos(self.span.lo()); let op_pos = cm.lookup_char_pos(cur_op_span.hi()); if cur_pos.line != op_pos.line { - err.span_suggestion_short(cur_op_span, - "did you mean to use `;` here?", - ";".to_string()); + err.span_suggestion_with_applicability( + cur_op_span, + "try using a semicolon", + ";".to_string(), + Applicability::MaybeIncorrect // speculative + ); } return Err(err); } @@ -3000,6 +3041,8 @@ impl<'a> Parser<'a> { } AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs), ThinVec::new()), + AssocOp::ObsoleteInPlace => + self.mk_expr(span, ExprKind::ObsoleteInPlace(lhs, rhs), ThinVec::new()), AssocOp::AssignOp(k) => { let aop = match k { token::Plus => BinOpKind::Add, @@ -3082,9 +3125,12 @@ impl<'a> Parser<'a> { let expr_str = self.sess.codemap().span_to_snippet(expr.span) .unwrap_or(pprust::expr_to_string(&expr)); - err.span_suggestion(expr.span, - &format!("try {} the cast value", op_verb), - format!("({})", expr_str)); + err.span_suggestion_with_applicability( + expr.span, + &format!("try {} the cast value", op_verb), + format!("({})", expr_str), + Applicability::MachineApplicable + ); err.emit(); Ok(expr) @@ -3181,7 +3227,7 @@ impl<'a> Parser<'a> { } /// Parse an 'if' or 'if let' expression ('if' token already eaten) - pub fn parse_if_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { + fn parse_if_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { if self.check_keyword(keywords::Let) { return self.parse_if_let_expr(attrs); } @@ -3217,7 +3263,7 @@ impl<'a> Parser<'a> { } /// Parse an 'if let' expression ('if' token already eaten) - pub fn parse_if_let_expr(&mut self, attrs: ThinVec<Attribute>) + fn parse_if_let_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { let lo = self.prev_span; self.expect_keyword(keywords::Let)?; @@ -3235,7 +3281,7 @@ impl<'a> Parser<'a> { } // `move |args| expr` - pub fn parse_lambda_expr(&mut self, + fn parse_lambda_expr(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { @@ -3245,6 +3291,11 @@ impl<'a> Parser<'a> { } else { Movability::Movable }; + let asyncness = if self.span.edition() >= Edition::Edition2018 { + self.parse_asyncness() + } else { + IsAsync::NotAsync + }; let capture_clause = if self.eat_keyword(keywords::Move) { CaptureBy::Value } else { @@ -3261,28 +3312,28 @@ impl<'a> Parser<'a> { // If an explicit return type is given, require a // block to appear (RFC 968). let body_lo = self.span; - self.parse_block_expr(body_lo, BlockCheckMode::Default, ThinVec::new())? + self.parse_block_expr(None, body_lo, BlockCheckMode::Default, ThinVec::new())? } }; Ok(self.mk_expr( lo.to(body.span), - ExprKind::Closure(capture_clause, movability, decl, body, lo.to(decl_hi)), + ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)), attrs)) } // `else` token already eaten - pub fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> { + fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> { if self.eat_keyword(keywords::If) { return self.parse_if_expr(ThinVec::new()); } else { let blk = self.parse_block()?; - return Ok(self.mk_expr(blk.span, ExprKind::Block(blk), ThinVec::new())); + return Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), ThinVec::new())); } } /// Parse a 'for' .. 'in' expression ('for' token already eaten) - pub fn parse_for_expr(&mut self, opt_label: Option<Label>, + fn parse_for_expr(&mut self, opt_label: Option<Label>, span_lo: Span, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { // Parse: `for <src_pat> in <src_expr> <src_loop_block>` @@ -3292,7 +3343,11 @@ impl<'a> Parser<'a> { let in_span = self.prev_span.between(self.span); let mut err = self.sess.span_diagnostic .struct_span_err(in_span, "missing `in` in `for` loop"); - err.span_suggestion_short(in_span, "try adding `in` here", " in ".into()); + err.span_suggestion_short_with_applicability( + in_span, "try adding `in` here", " in ".into(), + // has been misleading, at least in the past (closed Issue #48492) + Applicability::MaybeIncorrect + ); err.emit(); } let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; @@ -3304,7 +3359,7 @@ impl<'a> Parser<'a> { } /// Parse a 'while' or 'while let' expression ('while' token already eaten) - pub fn parse_while_expr(&mut self, opt_label: Option<Label>, + fn parse_while_expr(&mut self, opt_label: Option<Label>, span_lo: Span, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { if self.token.is_keyword(keywords::Let) { @@ -3318,7 +3373,7 @@ impl<'a> Parser<'a> { } /// Parse a 'while let' expression ('while' token already eaten) - pub fn parse_while_let_expr(&mut self, opt_label: Option<Label>, + fn parse_while_let_expr(&mut self, opt_label: Option<Label>, span_lo: Span, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { self.expect_keyword(keywords::Let)?; @@ -3332,7 +3387,7 @@ impl<'a> Parser<'a> { } // parse `loop {...}`, `loop` token already eaten - pub fn parse_loop_expr(&mut self, opt_label: Option<Label>, + fn parse_loop_expr(&mut self, opt_label: Option<Label>, span_lo: Span, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { let (iattrs, body) = self.parse_inner_attrs_and_block()?; @@ -3341,8 +3396,26 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(span, ExprKind::Loop(body, opt_label), attrs)) } + /// Parse an `async move {...}` expression + pub fn parse_async_block(&mut self, mut attrs: ThinVec<Attribute>) + -> PResult<'a, P<Expr>> + { + let span_lo = self.span; + self.expect_keyword(keywords::Async)?; + let capture_clause = if self.eat_keyword(keywords::Move) { + CaptureBy::Value + } else { + CaptureBy::Ref + }; + let (iattrs, body) = self.parse_inner_attrs_and_block()?; + attrs.extend(iattrs); + Ok(self.mk_expr( + span_lo.to(body.span), + ExprKind::Async(capture_clause, ast::DUMMY_NODE_ID, body), attrs)) + } + /// Parse a `do catch {...}` expression (`do catch` token already eaten) - pub fn parse_catch_expr(&mut self, span_lo: Span, mut attrs: ThinVec<Attribute>) + fn parse_catch_expr(&mut self, span_lo: Span, mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> { let (iattrs, body) = self.parse_inner_attrs_and_block()?; @@ -3358,7 +3431,12 @@ impl<'a> Parser<'a> { None)?; if let Err(mut e) = self.expect(&token::OpenDelim(token::Brace)) { if self.token == token::Token::Semi { - e.span_suggestion_short(match_span, "try removing this `match`", "".to_owned()); + e.span_suggestion_short_with_applicability( + match_span, + "try removing this `match`", + "".to_owned(), + Applicability::MaybeIncorrect // speculative + ); } return Err(e) } @@ -3385,7 +3463,7 @@ impl<'a> Parser<'a> { return Ok(self.mk_expr(lo.to(hi), ExprKind::Match(discriminant, arms), attrs)); } - pub fn parse_arm(&mut self) -> PResult<'a, Arm> { + crate fn parse_arm(&mut self) -> PResult<'a, Arm> { maybe_whole!(self, NtArm, |x| x); let attrs = self.parse_outer_attributes()?; @@ -3430,10 +3508,11 @@ impl<'a> Parser<'a> { // | - ^^ self.span // | | // | parsed until here as `"y" & X` - err.span_suggestion_short( + err.span_suggestion_short_with_applicability( cm.next_point(arm_start_span), "missing a comma here to end this `match` arm", - ",".to_owned() + ",".to_owned(), + Applicability::MachineApplicable ); } _ => { @@ -3463,7 +3542,7 @@ impl<'a> Parser<'a> { /// Evaluate the closure with restrictions in place. /// /// After the closure is evaluated, restrictions are reset. - pub fn with_res<F, T>(&mut self, r: Restrictions, f: F) -> T + fn with_res<F, T>(&mut self, r: Restrictions, f: F) -> T where F: FnOnce(&mut Self) -> T { let old = self.restrictions; @@ -3475,7 +3554,7 @@ impl<'a> Parser<'a> { } /// Parse an expression, subject to the given restrictions - pub fn parse_expr_res(&mut self, r: Restrictions, + fn parse_expr_res(&mut self, r: Restrictions, already_parsed_attrs: Option<ThinVec<Attribute>>) -> PResult<'a, P<Expr>> { self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs)) @@ -3502,9 +3581,12 @@ impl<'a> Parser<'a> { if self.token == token::OrOr { let mut err = self.struct_span_err(self.span, "unexpected token `||` after pattern"); - err.span_suggestion(self.span, - "use a single `|` to specify multiple patterns", - "|".to_owned()); + err.span_suggestion_with_applicability( + self.span, + "use a single `|` to specify multiple patterns", + "|".to_owned(), + Applicability::MachineApplicable + ); err.emit(); self.bump(); } else if self.check(&token::BinOp(token::Or)) { @@ -3612,100 +3694,169 @@ impl<'a> Parser<'a> { Ok((before, slice, after)) } + fn parse_pat_field( + &mut self, + lo: Span, + attrs: Vec<Attribute> + ) -> PResult<'a, codemap::Spanned<ast::FieldPat>> { + // Check if a colon exists one ahead. This means we're parsing a fieldname. + let hi; + let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { + // Parsing a pattern of the form "fieldname: pat" + let fieldname = self.parse_field_name()?; + self.bump(); + let pat = self.parse_pat()?; + hi = pat.span; + (pat, fieldname, false) + } else { + // Parsing a pattern of the form "(box) (ref) (mut) fieldname" + let is_box = self.eat_keyword(keywords::Box); + let boxed_span = self.span; + let is_ref = self.eat_keyword(keywords::Ref); + let is_mut = self.eat_keyword(keywords::Mut); + let fieldname = self.parse_ident()?; + hi = self.prev_span; + + let bind_type = match (is_ref, is_mut) { + (true, true) => BindingMode::ByRef(Mutability::Mutable), + (true, false) => BindingMode::ByRef(Mutability::Immutable), + (false, true) => BindingMode::ByValue(Mutability::Mutable), + (false, false) => BindingMode::ByValue(Mutability::Immutable), + }; + let fieldpat = P(Pat { + id: ast::DUMMY_NODE_ID, + node: PatKind::Ident(bind_type, fieldname, None), + span: boxed_span.to(hi), + }); + + let subpat = if is_box { + P(Pat { + id: ast::DUMMY_NODE_ID, + node: PatKind::Box(fieldpat), + span: lo.to(hi), + }) + } else { + fieldpat + }; + (subpat, fieldname, true) + }; + + Ok(codemap::Spanned { + span: lo.to(hi), + node: ast::FieldPat { + ident: fieldname, + pat: subpat, + is_shorthand, + attrs: attrs.into(), + } + }) + } + /// Parse the fields of a struct-like pattern fn parse_pat_fields(&mut self) -> PResult<'a, (Vec<codemap::Spanned<ast::FieldPat>>, bool)> { let mut fields = Vec::new(); let mut etc = false; - let mut first = true; - while self.token != token::CloseDelim(token::Brace) { - if first { - first = false; - } else { - self.expect(&token::Comma)?; - // accept trailing commas - if self.check(&token::CloseDelim(token::Brace)) { break } - } + let mut ate_comma = true; + let mut delayed_err: Option<DiagnosticBuilder<'a>> = None; + let mut etc_span = None; + while self.token != token::CloseDelim(token::Brace) { let attrs = self.parse_outer_attributes()?; let lo = self.span; - let hi; + + // check that a comma comes after every field + if !ate_comma { + let err = self.struct_span_err(self.prev_span, "expected `,`"); + return Err(err); + } + ate_comma = false; if self.check(&token::DotDot) || self.token == token::DotDotDot { + etc = true; + let mut etc_sp = self.span; + if self.token == token::DotDotDot { // Issue #46718 + // Accept `...` as if it were `..` to avoid further errors let mut err = self.struct_span_err(self.span, "expected field pattern, found `...`"); - err.span_suggestion(self.span, - "to omit remaining fields, use one fewer `.`", - "..".to_owned()); + err.span_suggestion_with_applicability( + self.span, + "to omit remaining fields, use one fewer `.`", + "..".to_owned(), + Applicability::MachineApplicable + ); err.emit(); } + self.bump(); // `..` || `...`:w - self.bump(); - if self.token != token::CloseDelim(token::Brace) { - let token_str = self.this_token_to_string(); - let mut err = self.fatal(&format!("expected `{}`, found `{}`", "}", token_str)); - if self.token == token::Comma { // Issue #49257 - err.span_label(self.span, - "`..` must be in the last position, \ - and cannot have a trailing comma"); + if self.token == token::CloseDelim(token::Brace) { + etc_span = Some(etc_sp); + break; + } + let token_str = self.this_token_to_string(); + let mut err = self.fatal(&format!("expected `}}`, found `{}`", token_str)); + + err.span_label(self.span, "expected `}`"); + let mut comma_sp = None; + if self.token == token::Comma { // Issue #49257 + etc_sp = etc_sp.to(self.sess.codemap().span_until_non_whitespace(self.span)); + err.span_label(etc_sp, + "`..` must be at the end and cannot have a trailing comma"); + comma_sp = Some(self.span); + self.bump(); + ate_comma = true; + } + + etc_span = Some(etc_sp); + if self.token == token::CloseDelim(token::Brace) { + // If the struct looks otherwise well formed, recover and continue. + if let Some(sp) = comma_sp { + err.span_suggestion_short(sp, "remove this comma", "".into()); + } + err.emit(); + break; + } else if self.token.is_ident() && ate_comma { + // Accept fields coming after `..,`. + // This way we avoid "pattern missing fields" errors afterwards. + // We delay this error until the end in order to have a span for a + // suggested fix. + if let Some(mut delayed_err) = delayed_err { + delayed_err.emit(); + return Err(err); } else { - err.span_label(self.span, "expected `}`"); + delayed_err = Some(err); + } + } else { + if let Some(mut err) = delayed_err { + err.emit(); } return Err(err); } - etc = true; - break; } - // Check if a colon exists one ahead. This means we're parsing a fieldname. - let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { - // Parsing a pattern of the form "fieldname: pat" - let fieldname = self.parse_field_name()?; - self.bump(); - let pat = self.parse_pat()?; - hi = pat.span; - (pat, fieldname, false) - } else { - // Parsing a pattern of the form "(box) (ref) (mut) fieldname" - let is_box = self.eat_keyword(keywords::Box); - let boxed_span = self.span; - let is_ref = self.eat_keyword(keywords::Ref); - let is_mut = self.eat_keyword(keywords::Mut); - let fieldname = self.parse_ident()?; - hi = self.prev_span; - - let bind_type = match (is_ref, is_mut) { - (true, true) => BindingMode::ByRef(Mutability::Mutable), - (true, false) => BindingMode::ByRef(Mutability::Immutable), - (false, true) => BindingMode::ByValue(Mutability::Mutable), - (false, false) => BindingMode::ByValue(Mutability::Immutable), - }; - let fieldpat = P(Pat { - id: ast::DUMMY_NODE_ID, - node: PatKind::Ident(bind_type, fieldname, None), - span: boxed_span.to(hi), - }); - - let subpat = if is_box { - P(Pat { - id: ast::DUMMY_NODE_ID, - node: PatKind::Box(fieldpat), - span: lo.to(hi), - }) - } else { - fieldpat - }; - (subpat, fieldname, true) - }; - - fields.push(codemap::Spanned { span: lo.to(hi), - node: ast::FieldPat { - ident: fieldname, - pat: subpat, - is_shorthand, - attrs: attrs.into(), - } + fields.push(match self.parse_pat_field(lo, attrs) { + Ok(field) => field, + Err(err) => { + if let Some(mut delayed_err) = delayed_err { + delayed_err.emit(); + } + return Err(err); + } }); + ate_comma = self.eat(&token::Comma); + } + + if let Some(mut err) = delayed_err { + if let Some(etc_span) = etc_span { + err.multipart_suggestion( + "move the `..` to the end of the field list", + vec![ + (etc_span, "".into()), + (self.span, format!("{}.. }}", if ate_comma { "" } else { ", " })), + ], + ); + } + err.emit(); } return Ok((fields, etc)); } @@ -3724,7 +3875,7 @@ impl<'a> Parser<'a> { let hi = self.prev_span; Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), ThinVec::new())) } else { - self.parse_pat_literal_maybe_minus() + self.parse_literal_maybe_minus() } } @@ -3747,7 +3898,7 @@ impl<'a> Parser<'a> { /// A wrapper around `parse_pat` with some special error handling for the /// "top-level" patterns in a match arm, `for` loop, `let`, &c. (in contast /// to subpatterns within such). - pub fn parse_top_level_pat(&mut self) -> PResult<'a, P<Pat>> { + fn parse_top_level_pat(&mut self) -> PResult<'a, P<Pat>> { let pat = self.parse_pat()?; if self.token == token::Comma { // An unexpected comma after a top-level pattern is a clue that the @@ -3767,8 +3918,12 @@ impl<'a> Parser<'a> { let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern"); if let Ok(seq_snippet) = self.sess.codemap().span_to_snippet(seq_span) { - err.span_suggestion(seq_span, "try adding parentheses", - format!("({})", seq_snippet)); + err.span_suggestion_with_applicability( + seq_span, + "try adding parentheses", + format!("({})", seq_snippet), + Applicability::MachineApplicable + ); } return Err(err); } @@ -3827,8 +3982,12 @@ impl<'a> Parser<'a> { let binding_mode = if self.eat_keyword(keywords::Ref) { self.diagnostic() .struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect") - .span_suggestion(mutref_span, "try switching the order", "ref mut".into()) - .emit(); + .span_suggestion_with_applicability( + mutref_span, + "try switching the order", + "ref mut".into(), + Applicability::MachineApplicable + ).emit(); BindingMode::ByRef(Mutability::Mutable) } else { BindingMode::ByValue(Mutability::Mutable) @@ -3863,8 +4022,8 @@ impl<'a> Parser<'a> { token::Not if qself.is_none() => { // Parse macro invocation self.bump(); - let (_, tts) = self.expect_delimited_token_tree()?; - let mac = respan(lo.to(self.prev_span), Mac_ { path: path, tts: tts }); + let (delim, tts) = self.expect_delimited_token_tree()?; + let mac = respan(lo.to(self.prev_span), Mac_ { path, tts, delim }); pat = PatKind::Mac(mac); } token::DotDotDot | token::DotDotEq | token::DotDot => { @@ -3875,12 +4034,14 @@ impl<'a> Parser<'a> { _ => panic!("can only parse `..`/`...`/`..=` for ranges \ (checked above)"), }; + let op_span = self.span; // Parse range let span = lo.to(self.prev_span); let begin = self.mk_expr(span, ExprKind::Path(qself, path), ThinVec::new()); self.bump(); let end = self.parse_pat_range_end()?; - pat = PatKind::Range(begin, end, end_kind); + let op = Spanned { span: op_span, node: end_kind }; + pat = PatKind::Range(begin, end, op); } token::OpenDelim(token::Brace) => { if qself.is_some() { @@ -3914,19 +4075,24 @@ impl<'a> Parser<'a> { } } else { // Try to parse everything else as literal with optional minus - match self.parse_pat_literal_maybe_minus() { + match self.parse_literal_maybe_minus() { Ok(begin) => { - if self.eat(&token::DotDotDot) { + let op_span = self.span; + if self.check(&token::DotDot) || self.check(&token::DotDotEq) || + self.check(&token::DotDotDot) { + let end_kind = if self.eat(&token::DotDotDot) { + RangeEnd::Included(RangeSyntax::DotDotDot) + } else if self.eat(&token::DotDotEq) { + RangeEnd::Included(RangeSyntax::DotDotEq) + } else if self.eat(&token::DotDot) { + RangeEnd::Excluded + } else { + panic!("impossible case: we already matched \ + on a range-operator token") + }; let end = self.parse_pat_range_end()?; - pat = PatKind::Range(begin, end, - RangeEnd::Included(RangeSyntax::DotDotDot)); - } else if self.eat(&token::DotDotEq) { - let end = self.parse_pat_range_end()?; - pat = PatKind::Range(begin, end, - RangeEnd::Included(RangeSyntax::DotDotEq)); - } else if self.eat(&token::DotDot) { - let end = self.parse_pat_range_end()?; - pat = PatKind::Range(begin, end, RangeEnd::Excluded); + let op = Spanned { span: op_span, node: end_kind }; + pat = PatKind::Range(begin, end, op); } else { pat = PatKind::Lit(begin); } @@ -3947,16 +4113,20 @@ impl<'a> Parser<'a> { if !allow_range_pat { match pat.node { - PatKind::Range(_, _, RangeEnd::Included(RangeSyntax::DotDotDot)) => {} + PatKind::Range( + _, _, Spanned { node: RangeEnd::Included(RangeSyntax::DotDotDot), .. } + ) => {}, PatKind::Range(..) => { let mut err = self.struct_span_err( pat.span, "the range pattern here has ambiguous interpretation", ); - err.span_suggestion( + err.span_suggestion_with_applicability( pat.span, "add parentheses to clarify the precedence", format!("({})", pprust::pat_to_string(&pat)), + // "ambiguous interpretation" implies that we have to be guessing + Applicability::MaybeIncorrect ); return Err(err); } @@ -4027,9 +4197,12 @@ impl<'a> Parser<'a> { (Ok(init), Some((_, colon_sp, mut err))) => { // init parsed, ty error // Could parse the type as if it were the initializer, it is likely there was a // typo in the code: `:` instead of `=`. Add suggestion and emit the error. - err.span_suggestion_short(colon_sp, - "use `=` if you meant to assign", - "=".to_string()); + err.span_suggestion_short_with_applicability( + colon_sp, + "use `=` if you meant to assign", + "=".to_string(), + Applicability::MachineApplicable + ); err.emit(); // As this was parsed successfully, continue as if the code has been fixed for the // rest of the file. It will still fail due to the emitted error, but we avoid @@ -4184,6 +4357,18 @@ impl<'a> Parser<'a> { }) } + fn is_async_block(&mut self) -> bool { + self.token.is_keyword(keywords::Async) && + ( + ( // `async move {` + self.look_ahead(1, |t| t.is_keyword(keywords::Move)) && + self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace)) + ) || ( // `async {` + self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) + ) + ) + } + fn is_catch_expr(&mut self) -> bool { self.token.is_keyword(keywords::Do) && self.look_ahead(1, |t| t.is_keyword(keywords::Catch)) && @@ -4206,6 +4391,11 @@ impl<'a> Parser<'a> { self.token.is_keyword(keywords::Extern) && self.look_ahead(1, |t| t != &token::ModSep) } + fn is_existential_type_decl(&self) -> bool { + self.token.is_keyword(keywords::Existential) && + self.look_ahead(1, |t| t.is_keyword(keywords::Type)) + } + fn is_auto_trait_item(&mut self) -> bool { // auto trait (self.token.is_keyword(keywords::Auto) @@ -4257,7 +4447,7 @@ impl<'a> Parser<'a> { let ident = self.parse_ident()?; let (delim, tokens) = self.expect_delimited_token_tree()?; - if delim != token::Brace { + if delim != MacDelimiter::Brace { if !self.eat(&token::Semi) { let msg = "macros that expand to items must either \ be surrounded with braces or followed by a semicolon"; @@ -4310,6 +4500,7 @@ impl<'a> Parser<'a> { !self.is_union_item() && !self.is_crate_vis() && !self.is_extern_non_path() && + !self.is_existential_type_decl() && !self.is_auto_trait_item() { let pth = self.parse_path(PathStyle::Expr)?; @@ -4342,8 +4533,8 @@ impl<'a> Parser<'a> { // check that we're pointing at delimiters (need to check // again after the `if`, because of `parse_ident` // consuming more tokens). - let delim = match self.token { - token::OpenDelim(delim) => delim, + match self.token { + token::OpenDelim(_) => {} _ => { // we only expect an ident if we didn't parse one // above. @@ -4359,20 +4550,20 @@ impl<'a> Parser<'a> { err.span_label(self.span, format!("expected {}`(` or `{{`", ident_str)); return Err(err) }, - }; + } - let (_, tts) = self.expect_delimited_token_tree()?; + let (delim, tts) = self.expect_delimited_token_tree()?; let hi = self.prev_span; - let style = if delim == token::Brace { + let style = if delim == MacDelimiter::Brace { MacStmtStyle::Braces } else { MacStmtStyle::NoBraces }; if id.name == keywords::Invalid.name() { - let mac = respan(lo.to(hi), Mac_ { path: pth, tts: tts }); - let node = if delim == token::Brace || + let mac = respan(lo.to(hi), Mac_ { path: pth, tts, delim }); + let node = if delim == MacDelimiter::Brace || self.token == token::Semi || self.token == token::Eof { StmtKind::Mac(P((mac, style, attrs.into()))) } @@ -4420,7 +4611,7 @@ impl<'a> Parser<'a> { node: StmtKind::Item({ self.mk_item( span, id /*id is good here*/, - ItemKind::Mac(respan(span, Mac_ { path: pth, tts: tts })), + ItemKind::Mac(respan(span, Mac_ { path: pth, tts, delim })), respan(lo, VisibilityKind::Inherited), attrs) }), @@ -4517,7 +4708,13 @@ impl<'a> Parser<'a> { s.print_stmt(&stmt)?; s.bclose_maybe_open(stmt.span, INDENT_UNIT, false) }); - e.span_suggestion(stmt_span, "try placing this code inside a block", sugg); + e.span_suggestion_with_applicability( + stmt_span, + "try placing this code inside a block", + sugg, + // speculative, has been misleading in the past (closed Issue #46836) + Applicability::MaybeIncorrect + ); } Err(mut e) => { self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); @@ -4577,7 +4774,7 @@ impl<'a> Parser<'a> { } /// Parse a statement, including the trailing semicolon. - pub fn parse_full_stmt(&mut self, macro_legacy_warnings: bool) -> PResult<'a, Option<Stmt>> { + crate fn parse_full_stmt(&mut self, macro_legacy_warnings: bool) -> PResult<'a, Option<Stmt>> { // skip looking for a trailing semicolon when we have an interpolated statement maybe_whole!(self, NtStmt, |x| Some(x)); @@ -4604,7 +4801,7 @@ impl<'a> Parser<'a> { if macro_legacy_warnings && self.token != token::Semi { self.warn_missing_semicolon(); } else { - self.expect_one_of(&[token::Semi], &[])?; + self.expect_one_of(&[], &[token::Semi])?; } } _ => {} @@ -4628,12 +4825,14 @@ impl<'a> Parser<'a> { fn err_dotdotdot_syntax(&self, span: Span) { self.diagnostic().struct_span_err(span, { - "`...` syntax cannot be used in expressions" - }).help({ - "Use `..` if you need an exclusive range (a < b)" - }).help({ - "or `..=` if you need an inclusive range (a <= b)" - }).emit(); + "unexpected token: `...`" + }).span_suggestion_with_applicability( + span, "use `..` for an exclusive range", "..".to_owned(), + Applicability::MaybeIncorrect + ).span_suggestion_with_applicability( + span, "or `..=` for an inclusive range", "..=".to_owned(), + Applicability::MaybeIncorrect + ).emit(); } // Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`. @@ -4641,7 +4840,7 @@ impl<'a> Parser<'a> { // LT_BOUND = LIFETIME (e.g. `'a`) // TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN) // TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`) - fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> { + fn parse_generic_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, GenericBounds> { let mut bounds = Vec::new(); loop { // This needs to be syncronized with `Token::can_begin_bound`. @@ -4650,6 +4849,7 @@ impl<'a> Parser<'a> { self.check_keyword(keywords::For) || self.check(&token::OpenDelim(token::Paren)); if is_bound_start { + let lo = self.span; let has_parens = self.eat(&token::OpenDelim(token::Paren)); let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None }; if self.token.is_lifetime() { @@ -4657,31 +4857,31 @@ impl<'a> Parser<'a> { self.span_err(question_span, "`?` may only modify trait bounds, not lifetime bounds"); } - bounds.push(RegionTyParamBound(self.expect_lifetime())); + bounds.push(GenericBound::Outlives(self.expect_lifetime())); + if has_parens { + self.expect(&token::CloseDelim(token::Paren))?; + self.span_err(self.prev_span, + "parenthesized lifetime bounds are not supported"); + } } else { - let lo = self.span; let lifetime_defs = self.parse_late_bound_lifetime_defs()?; let path = self.parse_path(PathStyle::Type)?; + if has_parens { + self.expect(&token::CloseDelim(token::Paren))?; + } let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span)); let modifier = if question.is_some() { TraitBoundModifier::Maybe } else { TraitBoundModifier::None }; - bounds.push(TraitTyParamBound(poly_trait, modifier)); - } - if has_parens { - self.expect(&token::CloseDelim(token::Paren))?; - if let Some(&RegionTyParamBound(..)) = bounds.last() { - self.span_err(self.prev_span, - "parenthesized lifetime bounds are not supported"); - } + bounds.push(GenericBound::Trait(poly_trait, modifier)); } } else { break } - if !allow_plus || !self.eat(&token::BinOp(token::Plus)) { + if !allow_plus || !self.eat_plus() { break } } @@ -4689,18 +4889,18 @@ impl<'a> Parser<'a> { return Ok(bounds); } - fn parse_ty_param_bounds(&mut self) -> PResult<'a, TyParamBounds> { - self.parse_ty_param_bounds_common(true) + fn parse_generic_bounds(&mut self) -> PResult<'a, GenericBounds> { + self.parse_generic_bounds_common(true) } // Parse bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`. // BOUND = LT_BOUND (e.g. `'a`) - fn parse_lt_param_bounds(&mut self) -> Vec<Lifetime> { + fn parse_lt_param_bounds(&mut self) -> GenericBounds { let mut lifetimes = Vec::new(); while self.check_lifetime() { - lifetimes.push(self.expect_lifetime()); + lifetimes.push(ast::GenericBound::Outlives(self.expect_lifetime())); - if !self.eat(&token::BinOp(token::Plus)) { + if !self.eat_plus() { break } } @@ -4708,12 +4908,14 @@ impl<'a> Parser<'a> { } /// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )? - fn parse_ty_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, TyParam> { + fn parse_ty_param(&mut self, + preceding_attrs: Vec<Attribute>) + -> PResult<'a, GenericParam> { let ident = self.parse_ident()?; // Parse optional colon and param bounds. let bounds = if self.eat(&token::Colon) { - self.parse_ty_param_bounds()? + self.parse_generic_bounds()? } else { Vec::new() }; @@ -4724,25 +4926,27 @@ impl<'a> Parser<'a> { None }; - Ok(TyParam { - attrs: preceding_attrs.into(), + Ok(GenericParam { ident, id: ast::DUMMY_NODE_ID, + attrs: preceding_attrs.into(), bounds, - default, + kind: GenericParamKind::Type { + default, + } }) } /// Parses the following grammar: - /// TraitItemAssocTy = Ident ["<"...">"] [":" [TyParamBounds]] ["where" ...] ["=" Ty] - fn parse_trait_item_assoc_ty(&mut self, preceding_attrs: Vec<Attribute>) - -> PResult<'a, (ast::Generics, TyParam)> { + /// TraitItemAssocTy = Ident ["<"...">"] [":" [GenericBounds]] ["where" ...] ["=" Ty] + fn parse_trait_item_assoc_ty(&mut self) + -> PResult<'a, (Ident, TraitItemKind, ast::Generics)> { let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; // Parse optional colon and param bounds. let bounds = if self.eat(&token::Colon) { - self.parse_ty_param_bounds()? + self.parse_generic_bounds()? } else { Vec::new() }; @@ -4755,18 +4959,12 @@ impl<'a> Parser<'a> { }; self.expect(&token::Semi)?; - Ok((generics, TyParam { - attrs: preceding_attrs.into(), - ident, - id: ast::DUMMY_NODE_ID, - bounds, - default, - })) + Ok((ident, TraitItemKind::Type(bounds, default), generics)) } /// Parses (possibly empty) list of lifetime and type parameters, possibly including /// trailing comma and erroneous trailing attributes. - pub fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> { + crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> { let mut params = Vec::new(); let mut seen_ty_param = false; loop { @@ -4779,18 +4977,20 @@ impl<'a> Parser<'a> { } else { Vec::new() }; - params.push(ast::GenericParam::Lifetime(LifetimeDef { + params.push(ast::GenericParam { + ident: lifetime.ident, + id: lifetime.id, attrs: attrs.into(), - lifetime, bounds, - })); + kind: ast::GenericParamKind::Lifetime, + }); if seen_ty_param { self.span_err(self.prev_span, "lifetime parameters must be declared prior to type parameters"); } } else if self.check_ident() { // Parse type parameter. - params.push(ast::GenericParam::Type(self.parse_ty_param(attrs)?)); + params.push(self.parse_ty_param(attrs)?); seen_ty_param = true; } else { // Check for trailing attributes and stop parsing. @@ -4816,7 +5016,7 @@ impl<'a> Parser<'a> { /// matches generics = ( ) | ( < > ) | ( < typaramseq ( , )? > ) | ( < lifetimes ( , )? > ) /// | ( < lifetimes , typaramseq ( , )? > ) /// where typaramseq = ( typaram ) | ( typaram , typaramseq ) - pub fn parse_generics(&mut self) -> PResult<'a, ast::Generics> { + fn parse_generics(&mut self) -> PResult<'a, ast::Generics> { maybe_whole!(self, NtGenerics, |x| x); let span_lo = self.span; @@ -4839,16 +5039,16 @@ impl<'a> Parser<'a> { /// Parses (possibly empty) list of lifetime and type arguments and associated type bindings, /// possibly including trailing comma. - fn parse_generic_args(&mut self) -> PResult<'a, (Vec<Lifetime>, Vec<P<Ty>>, Vec<TypeBinding>)> { - let mut lifetimes = Vec::new(); - let mut types = Vec::new(); + fn parse_generic_args(&mut self) + -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> { + let mut args = Vec::new(); let mut bindings = Vec::new(); let mut seen_type = false; let mut seen_binding = false; loop { - if self.check_lifetime() && self.look_ahead(1, |t| t != &token::BinOp(token::Plus)) { + if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. - lifetimes.push(self.expect_lifetime()); + args.push(GenericArg::Lifetime(self.expect_lifetime())); if seen_type || seen_binding { self.span_err(self.prev_span, "lifetime parameters must be declared prior to type parameters"); @@ -4868,11 +5068,12 @@ impl<'a> Parser<'a> { seen_binding = true; } else if self.check_type() { // Parse type argument. - types.push(self.parse_ty()?); + let ty_param = self.parse_ty()?; if seen_binding { - self.span_err(types[types.len() - 1].span, + self.span_err(ty_param.span, "type parameters must be declared prior to associated type bindings"); } + args.push(GenericArg::Type(ty_param)); seen_type = true; } else { break @@ -4882,7 +5083,7 @@ impl<'a> Parser<'a> { break } } - Ok((lifetimes, types, bindings)) + Ok((args, bindings)) } /// Parses an optional `where` clause and places it in `generics`. @@ -4890,7 +5091,7 @@ impl<'a> Parser<'a> { /// ```ignore (only-for-syntax-highlight) /// where T : Trait<U, V> + 'b, 'a : 'b /// ``` - pub fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> { + fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> { maybe_whole!(self, NtWhereClause, |x| x); let mut where_clause = WhereClause { @@ -4915,7 +5116,7 @@ impl<'a> Parser<'a> { loop { let lo = self.span; - if self.check_lifetime() && self.look_ahead(1, |t| t != &token::BinOp(token::Plus)) { + if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { let lifetime = self.expect_lifetime(); // Bounds starting with a colon are mandatory, but possibly empty. self.expect(&token::Colon)?; @@ -4940,7 +5141,7 @@ impl<'a> Parser<'a> { // or with mandatory equality sign and the second type. let ty = self.parse_ty()?; if self.eat(&token::Colon) { - let bounds = self.parse_ty_param_bounds()?; + let bounds = self.parse_generic_bounds()?; where_clause.predicates.push(ast::WherePredicate::BoundPredicate( ast::WhereBoundPredicate { span: lo.to(self.prev_span), @@ -5041,7 +5242,7 @@ impl<'a> Parser<'a> { } /// Parse the argument list and result type of a function declaration - pub fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<'a, P<FnDecl>> { + fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<'a, P<FnDecl>> { let (args, variadic) = self.parse_fn_args(true, allow_variadic)?; let ret_ty = self.parse_ret_ty(true)?; @@ -5070,36 +5271,36 @@ impl<'a> Parser<'a> { // Only a limited set of initial token sequences is considered self parameters, anything // else is parsed as a normal function parameter list, so some lookahead is required. let eself_lo = self.span; - let (eself, eself_ident) = match self.token { + let (eself, eself_ident, eself_hi) = match self.token { token::BinOp(token::And) => { // &self // &mut self // &'lt self // &'lt mut self // ¬_self - if isolated_self(self, 1) { + (if isolated_self(self, 1) { self.bump(); - (SelfKind::Region(None, Mutability::Immutable), expect_ident(self)) + SelfKind::Region(None, Mutability::Immutable) } else if self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) && isolated_self(self, 2) { self.bump(); self.bump(); - (SelfKind::Region(None, Mutability::Mutable), expect_ident(self)) + SelfKind::Region(None, Mutability::Mutable) } else if self.look_ahead(1, |t| t.is_lifetime()) && isolated_self(self, 2) { self.bump(); let lt = self.expect_lifetime(); - (SelfKind::Region(Some(lt), Mutability::Immutable), expect_ident(self)) + SelfKind::Region(Some(lt), Mutability::Immutable) } else if self.look_ahead(1, |t| t.is_lifetime()) && self.look_ahead(2, |t| t.is_keyword(keywords::Mut)) && isolated_self(self, 3) { self.bump(); let lt = self.expect_lifetime(); self.bump(); - (SelfKind::Region(Some(lt), Mutability::Mutable), expect_ident(self)) + SelfKind::Region(Some(lt), Mutability::Mutable) } else { return Ok(None); - } + }, expect_ident(self), self.prev_span) } token::BinOp(token::Star) => { // *self @@ -5107,43 +5308,45 @@ impl<'a> Parser<'a> { // *mut self // *not_self // Emit special error for `self` cases. - if isolated_self(self, 1) { + (if isolated_self(self, 1) { self.bump(); self.span_err(self.span, "cannot pass `self` by raw pointer"); - (SelfKind::Value(Mutability::Immutable), expect_ident(self)) + SelfKind::Value(Mutability::Immutable) } else if self.look_ahead(1, |t| t.is_mutability()) && isolated_self(self, 2) { self.bump(); self.bump(); self.span_err(self.span, "cannot pass `self` by raw pointer"); - (SelfKind::Value(Mutability::Immutable), expect_ident(self)) + SelfKind::Value(Mutability::Immutable) } else { return Ok(None); - } + }, expect_ident(self), self.prev_span) } token::Ident(..) => { if isolated_self(self, 0) { // self // self: TYPE let eself_ident = expect_ident(self); - if self.eat(&token::Colon) { + let eself_hi = self.prev_span; + (if self.eat(&token::Colon) { let ty = self.parse_ty()?; - (SelfKind::Explicit(ty, Mutability::Immutable), eself_ident) + SelfKind::Explicit(ty, Mutability::Immutable) } else { - (SelfKind::Value(Mutability::Immutable), eself_ident) - } + SelfKind::Value(Mutability::Immutable) + }, eself_ident, eself_hi) } else if self.token.is_keyword(keywords::Mut) && isolated_self(self, 1) { // mut self // mut self: TYPE self.bump(); let eself_ident = expect_ident(self); - if self.eat(&token::Colon) { + let eself_hi = self.prev_span; + (if self.eat(&token::Colon) { let ty = self.parse_ty()?; - (SelfKind::Explicit(ty, Mutability::Mutable), eself_ident) + SelfKind::Explicit(ty, Mutability::Mutable) } else { - (SelfKind::Value(Mutability::Mutable), eself_ident) - } + SelfKind::Value(Mutability::Mutable) + }, eself_ident, eself_hi) } else { return Ok(None); } @@ -5151,7 +5354,7 @@ impl<'a> Parser<'a> { _ => return Ok(None), }; - let eself = codemap::respan(eself_lo.to(self.prev_span), eself); + let eself = codemap::respan(eself_lo.to(eself_hi), eself); Ok(Some(Arg::from_self(eself, eself_ident))) } @@ -5240,6 +5443,7 @@ impl<'a> Parser<'a> { /// Parse an item-position function declaration. fn parse_item_fn(&mut self, unsafety: Unsafety, + asyncness: IsAsync, constness: Spanned<Constness>, abi: Abi) -> PResult<'a, ItemInfo> { @@ -5247,11 +5451,12 @@ impl<'a> Parser<'a> { let decl = self.parse_fn_decl(false)?; generics.where_clause = self.parse_where_clause()?; let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; - Ok((ident, ItemKind::Fn(decl, unsafety, constness, abi, generics, body), Some(inner_attrs))) + let header = FnHeader { unsafety, asyncness, constness, abi }; + Ok((ident, ItemKind::Fn(decl, header, generics, body), Some(inner_attrs))) } /// true if we are looking at `const ID`, false for things like `const fn` etc - pub fn is_const_item(&mut self) -> bool { + fn is_const_item(&mut self) -> bool { self.token.is_keyword(keywords::Const) && !self.look_ahead(1, |t| t.is_keyword(keywords::Fn)) && !self.look_ahead(1, |t| t.is_keyword(keywords::Unsafe)) @@ -5265,10 +5470,18 @@ impl<'a> Parser<'a> { /// - `const unsafe fn` /// - `extern fn` /// - etc - pub fn parse_fn_front_matter(&mut self) -> PResult<'a, (Spanned<Constness>, Unsafety, Abi)> { + fn parse_fn_front_matter(&mut self) + -> PResult<'a, ( + Spanned<Constness>, + Unsafety, + IsAsync, + Abi + )> + { let is_const_fn = self.eat_keyword(keywords::Const); let const_span = self.prev_span; let unsafety = self.parse_unsafety(); + let asyncness = self.parse_asyncness(); let (constness, unsafety, abi) = if is_const_fn { (respan(const_span, Constness::Const), unsafety, Abi::Rust) } else { @@ -5280,7 +5493,7 @@ impl<'a> Parser<'a> { (respan(self.prev_span, Constness::NotConst), unsafety, abi) }; self.expect_keyword(keywords::Fn)?; - Ok((constness, unsafety, abi)) + Ok((constness, unsafety, asyncness, abi)) } /// Parse an impl item. @@ -5304,16 +5517,13 @@ impl<'a> Parser<'a> { let lo = self.span; let vis = self.parse_visibility(false)?; let defaultness = self.parse_defaultness(); - let (name, node, generics) = if self.eat_keyword(keywords::Type) { - // This parses the grammar: - // ImplItemAssocTy = Ident ["<"...">"] ["where" ...] "=" Ty ";" - let name = self.parse_ident()?; - let mut generics = self.parse_generics()?; - generics.where_clause = self.parse_where_clause()?; - self.expect(&token::Eq)?; - let typ = self.parse_ty()?; - self.expect(&token::Semi)?; - (name, ast::ImplItemKind::Type(typ), generics) + let (name, node, generics) = if let Some(type_) = self.eat_type() { + let (name, alias, generics) = type_?; + let kind = match alias { + AliasKind::Weak(typ) => ast::ImplItemKind::Type(typ), + AliasKind::Existential(bounds) => ast::ImplItemKind::Existential(bounds), + }; + (name, kind, generics) } else if self.is_const_item() { // This parses the grammar: // ImplItemConst = "const" Ident ":" Ty "=" Expr ";" @@ -5361,9 +5571,12 @@ impl<'a> Parser<'a> { if is_macro_rules { let mut err = self.diagnostic() .struct_span_err(sp, "can't qualify macro_rules invocation with `pub`"); - err.span_suggestion(sp, - "try exporting the macro", - "#[macro_export]".to_owned()); + err.span_suggestion_with_applicability( + sp, + "try exporting the macro", + "#[macro_export]".to_owned(), + Applicability::MaybeIncorrect // speculative + ); Err(err) } else { let mut err = self.diagnostic() @@ -5412,19 +5625,18 @@ impl<'a> Parser<'a> { Ok((keywords::Invalid.ident(), vec![], ast::Generics::default(), ast::ImplItemKind::Macro(mac))) } else { - let (constness, unsafety, abi) = self.parse_fn_front_matter()?; + let (constness, unsafety, asyncness, abi) = self.parse_fn_front_matter()?; let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; let decl = self.parse_fn_decl_with_self(|p| p.parse_arg())?; generics.where_clause = self.parse_where_clause()?; *at_end = true; let (inner_attrs, body) = self.parse_inner_attrs_and_block()?; - Ok((ident, inner_attrs, generics, ast::ImplItemKind::Method(ast::MethodSig { - abi, - unsafety, - constness, - decl, - }, body))) + let header = ast::FnHeader { abi, unsafety, constness, asyncness }; + Ok((ident, inner_attrs, generics, ast::ImplItemKind::Method( + ast::MethodSig { header, decl }, + body + ))) } } @@ -5435,14 +5647,14 @@ impl<'a> Parser<'a> { // Parse optional colon and supertrait bounds. let bounds = if self.eat(&token::Colon) { - self.parse_ty_param_bounds()? + self.parse_generic_bounds()? } else { Vec::new() }; if self.eat(&token::Eq) { // it's a trait alias - let bounds = self.parse_ty_param_bounds()?; + let bounds = self.parse_generic_bounds()?; tps.where_clause = self.parse_where_clause()?; self.expect(&token::Semi)?; if unsafety != Unsafety::Normal { @@ -5701,7 +5913,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_record_struct_body(&mut self) -> PResult<'a, Vec<StructField>> { + fn parse_record_struct_body(&mut self) -> PResult<'a, Vec<StructField>> { let mut fields = Vec::new(); if self.eat(&token::OpenDelim(token::Brace)) { while self.token != token::CloseDelim(token::Brace) { @@ -5728,7 +5940,7 @@ impl<'a> Parser<'a> { Ok(fields) } - pub fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<StructField>> { + fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<StructField>> { // This is the case where we find `struct Foo<T>(T) where T: Copy;` // Unit like structs are handled in parse_item_struct function let fields = self.parse_unspanned_seq( @@ -5741,7 +5953,7 @@ impl<'a> Parser<'a> { let vis = p.parse_visibility(true)?; let ty = p.parse_ty()?; Ok(StructField { - span: lo.to(p.span), + span: lo.to(ty.span), vis, ident: None, id: ast::DUMMY_NODE_ID, @@ -5754,7 +5966,7 @@ impl<'a> Parser<'a> { } /// Parse a structure field declaration - pub fn parse_single_struct_field(&mut self, + fn parse_single_struct_field(&mut self, lo: Span, vis: Visibility, attrs: Vec<Attribute> ) @@ -5784,14 +5996,28 @@ impl<'a> Parser<'a> { } else { if seen_comma == false { let sp = self.sess.codemap().next_point(previous_span); - err.span_suggestion(sp, "missing comma here", ",".into()); + err.span_suggestion_with_applicability( + sp, + "missing comma here", + ",".into(), + Applicability::MachineApplicable + ); } return Err(err); } } - _ => return Err(self.span_fatal_help(self.span, - &format!("expected `,`, or `}}`, found `{}`", self.this_token_to_string()), - "struct fields should be separated by commas")), + _ => { + let sp = self.sess.codemap().next_point(self.prev_span); + let mut err = self.struct_span_err(sp, &format!("expected `,`, or `}}`, found `{}`", + self.this_token_to_string())); + if self.token.is_ident() { + // This is likely another field; emit the diagnostic and keep going + err.span_suggestion(sp, "try adding a comma", ",".into()); + err.emit(); + } else { + return Err(err) + } + } } Ok(a_var) } @@ -5818,7 +6044,10 @@ impl<'a> Parser<'a> { } if !self.eat_keyword(keywords::Pub) { - return Ok(respan(self.prev_span, VisibilityKind::Inherited)) + // We need a span for our `Spanned<VisibilityKind>`, but there's inherently no + // keyword to grab a span from for inherited visibility; an empty span at the + // beginning of the current token would seem to be the "Schelling span". + return Ok(respan(self.span.shrink_to_lo(), VisibilityKind::Inherited)) } let lo = self.prev_span; @@ -5870,11 +6099,14 @@ impl<'a> Parser<'a> { `pub(super)`: visible only in the current module's parent `pub(in path::to::module)`: visible only on the specified path"##; let path = self.parse_path(PathStyle::Mod)?; - let path_span = self.prev_span; + let sp = self.prev_span; let help_msg = format!("make this visible only to module `{}` with `in`", path); self.expect(&token::CloseDelim(token::Paren))?; // `)` - let mut err = self.span_fatal_help(path_span, msg, suggestion); - err.span_suggestion(path_span, &help_msg, format!("in {}", path)); + let mut err = struct_span_err!(self.sess.span_diagnostic, sp, E0704, "{}", msg); + err.help(suggestion); + err.span_suggestion_with_applicability( + sp, &help_msg, format!("in {}", path), Applicability::MachineApplicable + ); err.emit(); // emit diagnostic, but continue with public visibility } } @@ -5912,14 +6144,32 @@ impl<'a> Parser<'a> { let mut err = self.fatal(&format!("expected item, found `{}`", token_str)); if token_str == ";" { let msg = "consider removing this semicolon"; - err.span_suggestion_short(self.span, msg, "".to_string()); + err.span_suggestion_short_with_applicability( + self.span, msg, "".to_string(), Applicability::MachineApplicable + ); + if !items.is_empty() { // Issue #51603 + let previous_item = &items[items.len()-1]; + let previous_item_kind_name = match previous_item.node { + // say "braced struct" because tuple-structs and + // braceless-empty-struct declarations do take a semicolon + ItemKind::Struct(..) => Some("braced struct"), + ItemKind::Enum(..) => Some("enum"), + ItemKind::Trait(..) => Some("trait"), + ItemKind::Union(..) => Some("union"), + _ => None, + }; + if let Some(name) = previous_item_kind_name { + err.help(&format!("{} declarations are not followed by a semicolon", + name)); + } + } } else { err.span_label(self.span, "expected item"); } return Err(err); } - let hi = if self.span == syntax_pos::DUMMY_SP { + let hi = if self.span.is_dummy() { inner_lo } else { self.prev_span @@ -6000,15 +6250,27 @@ impl<'a> Parser<'a> { fn push_directory(&mut self, id: Ident, attrs: &[Attribute]) { if let Some(path) = attr::first_attr_value_str_by_name(attrs, "path") { - self.directory.path.push(&path.as_str()); + self.directory.path.to_mut().push(&path.as_str()); self.directory.ownership = DirectoryOwnership::Owned { relative: None }; } else { - self.directory.path.push(&id.name.as_str()); + self.directory.path.to_mut().push(&id.as_str()); } } pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> { - attr::first_attr_value_str_by_name(attrs, "path").map(|d| dir_path.join(&d.as_str())) + if let Some(s) = attr::first_attr_value_str_by_name(attrs, "path") { + let s = s.as_str(); + + // On windows, the base path might have the form + // `\\?\foo\bar` in which case it does not tolerate + // mixed `/` and `\` separators, so canonicalize + // `/` to `\`. + #[cfg(windows)] + let s = s.replace("/", "\\"); + Some(dir_path.join(s)) + } else { + None + } } /// Returns either a path to a module, or . @@ -6024,7 +6286,7 @@ impl<'a> Parser<'a> { // `./<id>.rs` and `./<id>/mod.rs`. let relative_prefix_string; let relative_prefix = if let Some(ident) = relative { - relative_prefix_string = format!("{}{}", ident.name.as_str(), path::MAIN_SEPARATOR); + relative_prefix_string = format!("{}{}", ident.as_str(), path::MAIN_SEPARATOR); &relative_prefix_string } else { "" @@ -6058,7 +6320,7 @@ impl<'a> Parser<'a> { mod_name: mod_name.clone(), default_path: default_path_str, secondary_path: secondary_path_str, - dir_path: format!("{}", dir_path.display()), + dir_path: dir_path.display().to_string(), }), (true, true) => Err(Error::DuplicatePaths { mod_name: mod_name.clone(), @@ -6138,7 +6400,7 @@ impl<'a> Parser<'a> { } let mut err = self.diagnostic().struct_span_err(id_sp, "cannot declare a new module at this location"); - if id_sp != syntax_pos::DUMMY_SP { + if !id_sp.is_dummy() { let src_path = self.sess.codemap().span_to_filename(id_sp); if let FileName::Real(src_path) = src_path { if let Some(stem) = src_path.file_stem() { @@ -6252,6 +6514,39 @@ impl<'a> Parser<'a> { }) } + fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, ast::Ident> { + let error_msg = "crate name using dashes are not valid in `extern crate` statements"; + let suggestion_msg = "if the original crate name uses dashes you need to use underscores \ + in the code"; + let mut ident = self.parse_ident()?; + let mut idents = vec![]; + let mut replacement = vec![]; + let mut fixed_crate_name = false; + // Accept `extern crate name-like-this` for better diagnostics + let dash = token::Token::BinOp(token::BinOpToken::Minus); + if self.token == dash { // Do not include `-` as part of the expected tokens list + while self.eat(&dash) { + fixed_crate_name = true; + replacement.push((self.prev_span, "_".to_string())); + idents.push(self.parse_ident()?); + } + } + if fixed_crate_name { + let fixed_name_sp = ident.span.to(idents.last().unwrap().span); + let mut fixed_name = format!("{}", ident.name); + for part in idents { + fixed_name.push_str(&format!("_{}", part.name)); + } + ident = Ident::from_str(&fixed_name).with_span_pos(fixed_name_sp); + + let mut err = self.struct_span_err(fixed_name_sp, error_msg); + err.span_label(fixed_name_sp, "dash-separated idents are not valid"); + err.multipart_suggestion(suggestion_msg, replacement); + err.emit(); + } + Ok(ident) + } + /// Parse extern crate links /// /// # Examples @@ -6263,7 +6558,8 @@ impl<'a> Parser<'a> { visibility: Visibility, attrs: Vec<Attribute>) -> PResult<'a, P<Item>> { - let orig_name = self.parse_ident()?; + // Accept `extern crate name-like-this` for better diagnostics + let orig_name = self.parse_crate_name_with_dashes()?; let (item_name, orig_name) = if let Some(rename) = self.parse_rename()? { (rename, Some(orig_name.name)) } else { @@ -6313,14 +6609,43 @@ impl<'a> Parser<'a> { } /// Parse type Foo = Bar; - fn parse_item_type(&mut self) -> PResult<'a, ItemInfo> { + /// or + /// existential type Foo: Bar; + /// or + /// return None without modifying the parser state + fn eat_type(&mut self) -> Option<PResult<'a, (Ident, AliasKind, ast::Generics)>> { + // This parses the grammar: + // Ident ["<"...">"] ["where" ...] ("=" | ":") Ty ";" + if self.check_keyword(keywords::Type) || + self.check_keyword(keywords::Existential) && + self.look_ahead(1, |t| t.is_keyword(keywords::Type)) { + let existential = self.eat_keyword(keywords::Existential); + assert!(self.eat_keyword(keywords::Type)); + Some(self.parse_existential_or_alias(existential)) + } else { + None + } + } + + /// Parse type alias or existential type + fn parse_existential_or_alias( + &mut self, + existential: bool, + ) -> PResult<'a, (Ident, AliasKind, ast::Generics)> { let ident = self.parse_ident()?; let mut tps = self.parse_generics()?; tps.where_clause = self.parse_where_clause()?; - self.expect(&token::Eq)?; - let ty = self.parse_ty()?; + let alias = if existential { + self.expect(&token::Colon)?; + let bounds = self.parse_generic_bounds()?; + AliasKind::Existential(bounds) + } else { + self.expect(&token::Eq)?; + let ty = self.parse_ty()?; + AliasKind::Weak(ty) + }; self.expect(&token::Semi)?; - Ok((ident, ItemKind::Ty(ty, tps), None)) + Ok((ident, alias, tps)) } /// Parse the part of an "enum" decl following the '{' @@ -6345,8 +6670,11 @@ impl<'a> Parser<'a> { struct_def = VariantData::Tuple(self.parse_tuple_struct_body()?, ast::DUMMY_NODE_ID); } else if self.eat(&token::Eq) { - disr_expr = Some(self.parse_expr()?); - any_disr = disr_expr.as_ref().map(|expr| expr.span); + disr_expr = Some(AnonConst { + id: ast::DUMMY_NODE_ID, + value: self.parse_expr()?, + }); + any_disr = disr_expr.as_ref().map(|c| c.value.span); struct_def = VariantData::Unit(ast::DUMMY_NODE_ID); } else { struct_def = VariantData::Unit(ast::DUMMY_NODE_ID); @@ -6400,12 +6728,15 @@ impl<'a> Parser<'a> { Some(abi) => Ok(Some(abi)), None => { let prev_span = self.prev_span; - self.span_err( + let mut err = struct_span_err!( + self.sess.span_diagnostic, prev_span, - &format!("invalid ABI: expected one of [{}], \ - found `{}`", - abi::all_names().join(", "), - s)); + E0703, + "invalid ABI: found `{}`", + s); + err.span_label(prev_span, "invalid ABI"); + err.help(&format!("valid ABIs: {}", abi::all_names().join(", "))); + err.emit(); Ok(None) } } @@ -6432,11 +6763,49 @@ impl<'a> Parser<'a> { } } + fn parse_item_( + &mut self, + attrs: Vec<Attribute>, + macros_allowed: bool, + attributes_allowed: bool, + ) -> PResult<'a, Option<P<Item>>> { + let (ret, tokens) = self.collect_tokens(|this| { + this.parse_item_implementation(attrs, macros_allowed, attributes_allowed) + })?; + + // Once we've parsed an item and recorded the tokens we got while + // parsing we may want to store `tokens` into the item we're about to + // return. Note, though, that we specifically didn't capture tokens + // related to outer attributes. The `tokens` field here may later be + // used with procedural macros to convert this item back into a token + // stream, but during expansion we may be removing attributes as we go + // along. + // + // If we've got inner attributes then the `tokens` we've got above holds + // these inner attributes. If an inner attribute is expanded we won't + // actually remove it from the token stream, so we'll just keep yielding + // it (bad!). To work around this case for now we just avoid recording + // `tokens` if we detect any inner attributes. This should help keep + // expansion correct, but we should fix this bug one day! + Ok(ret.map(|item| { + item.map(|mut i| { + if !i.attrs.iter().any(|attr| attr.style == AttrStyle::Inner) { + i.tokens = Some(tokens); + } + i + }) + })) + } + /// Parse one of the items allowed by the flags. /// NB: this function no longer parses the items inside an /// extern crate. - fn parse_item_(&mut self, attrs: Vec<Attribute>, - macros_allowed: bool, attributes_allowed: bool) -> PResult<'a, Option<P<Item>>> { + fn parse_item_implementation( + &mut self, + attrs: Vec<Attribute>, + macros_allowed: bool, + attributes_allowed: bool, + ) -> PResult<'a, Option<P<Item>>> { maybe_whole!(self, NtItem, |item| { let mut item = item.into_inner(); let mut attrs = attrs; @@ -6473,6 +6842,7 @@ impl<'a> Parser<'a> { let abi = opt_abi.unwrap_or(Abi::C); let (ident, item_, extra_attrs) = self.parse_item_fn(Unsafety::Normal, + IsAsync::NotAsync, respan(fn_span, Constness::NotConst), abi)?; let prev_span = self.prev_span; @@ -6516,6 +6886,7 @@ impl<'a> Parser<'a> { self.bump(); let (ident, item_, extra_attrs) = self.parse_item_fn(unsafety, + IsAsync::NotAsync, respan(const_span, Constness::Const), Abi::Rust)?; let prev_span = self.prev_span; @@ -6543,6 +6914,37 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } + + // `unsafe async fn` or `async fn` + if ( + self.check_keyword(keywords::Unsafe) && + self.look_ahead(1, |t| t.is_keyword(keywords::Async)) + ) || ( + self.check_keyword(keywords::Async) && + self.look_ahead(1, |t| t.is_keyword(keywords::Fn)) + ) + { + // ASYNC FUNCTION ITEM + let unsafety = self.parse_unsafety(); + self.expect_keyword(keywords::Async)?; + self.expect_keyword(keywords::Fn)?; + let fn_span = self.prev_span; + let (ident, item_, extra_attrs) = + self.parse_item_fn(unsafety, + IsAsync::Async { + closure_id: ast::DUMMY_NODE_ID, + return_impl_trait_id: ast::DUMMY_NODE_ID, + }, + respan(fn_span, Constness::NotConst), + Abi::Rust)?; + let prev_span = self.prev_span; + let item = self.mk_item(lo.to(prev_span), + ident, + item_, + visibility, + maybe_append(attrs, extra_attrs)); + return Ok(Some(item)); + } if self.check_keyword(keywords::Unsafe) && (self.look_ahead(1, |t| t.is_keyword(keywords::Trait)) || self.look_ahead(1, |t| t.is_keyword(keywords::Auto))) @@ -6588,6 +6990,7 @@ impl<'a> Parser<'a> { let fn_span = self.prev_span; let (ident, item_, extra_attrs) = self.parse_item_fn(Unsafety::Normal, + IsAsync::NotAsync, respan(fn_span, Constness::NotConst), Abi::Rust)?; let prev_span = self.prev_span; @@ -6613,6 +7016,7 @@ impl<'a> Parser<'a> { let fn_span = self.prev_span; let (ident, item_, extra_attrs) = self.parse_item_fn(Unsafety::Unsafe, + IsAsync::NotAsync, respan(fn_span, Constness::NotConst), abi)?; let prev_span = self.prev_span; @@ -6635,15 +7039,19 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if self.eat_keyword(keywords::Type) { + if let Some(type_) = self.eat_type() { + let (ident, alias, generics) = type_?; // TYPE ITEM - let (ident, item_, extra_attrs) = self.parse_item_type()?; + let item_ = match alias { + AliasKind::Weak(ty) => ItemKind::Ty(ty, generics), + AliasKind::Existential(bounds) => ItemKind::Existential(bounds, generics), + }; let prev_span = self.prev_span; let item = self.mk_item(lo.to(prev_span), ident, item_, visibility, - maybe_append(attrs, extra_attrs)); + attrs); return Ok(Some(item)); } if self.eat_keyword(keywords::Enum) { @@ -6708,7 +7116,7 @@ impl<'a> Parser<'a> { // Verify whether we have encountered a struct or method definition where the user forgot to // add the `struct` or `fn` keyword after writing `pub`: `pub S {}` - if visibility.node == VisibilityKind::Public && + if visibility.node.is_pub() && self.check_ident() && self.look_ahead(1, |t| *t != token::Not) { @@ -6726,7 +7134,9 @@ impl<'a> Parser<'a> { ident); let mut err = self.diagnostic() .struct_span_err(sp, "missing `struct` for struct definition"); - err.span_suggestion_short(sp, &msg, " struct ".into()); + err.span_suggestion_short_with_applicability( + sp, &msg, " struct ".into(), Applicability::MaybeIncorrect // speculative + ); return Err(err); } else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) { let ident = self.parse_ident().unwrap(); @@ -6749,13 +7159,18 @@ impl<'a> Parser<'a> { kw, ident, kw_name); - err.span_suggestion_short(sp, &suggestion, format!(" {} ", kw)); + err.span_suggestion_short_with_applicability( + sp, &suggestion, format!(" {} ", kw), Applicability::MachineApplicable + ); } else { if let Ok(snippet) = self.sess.codemap().span_to_snippet(ident_sp) { - err.span_suggestion( + err.span_suggestion_with_applicability( full_sp, - "if you meant to call a macro, write instead", - format!("{}!", snippet)); + "if you meant to call a macro, try", + format!("{}!", snippet), + // this is the `ambiguous` conditional branch + Applicability::MaybeIncorrect + ); } else { err.help("if you meant to call a macro, remove the `pub` \ and add a trailing `!` after the identifier"); @@ -6768,7 +7183,7 @@ impl<'a> Parser<'a> { } /// Parse a foreign item. - pub fn parse_foreign_item(&mut self) -> PResult<'a, Option<ForeignItem>> { + crate fn parse_foreign_item(&mut self) -> PResult<'a, Option<ForeignItem>> { maybe_whole!(self, NtForeignItem, |ni| Some(ni)); let attrs = self.parse_outer_attributes()?; @@ -6781,8 +7196,12 @@ impl<'a> Parser<'a> { if self.token.is_keyword(keywords::Const) { self.diagnostic() .struct_span_err(self.span, "extern items cannot be `const`") - .span_suggestion(self.span, "instead try using", "static".to_owned()) - .emit(); + .span_suggestion_with_applicability( + self.span, + "try using a static value", + "static".to_owned(), + Applicability::MachineApplicable + ).emit(); } self.bump(); // `static` or `const` return Ok(Some(self.parse_item_foreign_static(visibility, lo, attrs)?)); @@ -6850,7 +7269,7 @@ impl<'a> Parser<'a> { }; // eat a matched-delimiter token tree: let (delim, tts) = self.expect_delimited_token_tree()?; - if delim != token::Brace { + if delim != MacDelimiter::Brace { if !self.eat(&token::Semi) { self.span_err(self.prev_span, "macros that expand to items must either \ @@ -6860,7 +7279,7 @@ impl<'a> Parser<'a> { } let hi = self.prev_span; - let mac = respan(mac_lo.to(hi), Mac_ { path: pth, tts: tts }); + let mac = respan(mac_lo.to(hi), Mac_ { path: pth, tts, delim }); let item = self.mk_item(lo.to(hi), id, ItemKind::Mac(mac), visibility, attrs); return Ok(Some(item)); } @@ -6904,11 +7323,11 @@ impl<'a> Parser<'a> { // eat a matched-delimiter token tree: let (delim, tts) = self.expect_delimited_token_tree()?; - if delim != token::Brace { + if delim != MacDelimiter::Brace { self.expect(&token::Semi)? } - Ok(Some(respan(lo.to(self.prev_span), Mac_ { path: pth, tts: tts }))) + Ok(Some(respan(lo.to(self.prev_span), Mac_ { path: pth, tts, delim }))) } else { Ok(None) } @@ -6919,12 +7338,15 @@ impl<'a> Parser<'a> { { // Record all tokens we parse when parsing this item. let mut tokens = Vec::new(); - match self.token_cursor.frame.last_token { - LastToken::Collecting(_) => { - panic!("cannot collect tokens recursively yet") + let prev_collecting = match self.token_cursor.frame.last_token { + LastToken::Collecting(ref mut list) => { + Some(mem::replace(list, Vec::new())) } - LastToken::Was(ref mut last) => tokens.extend(last.take()), - } + LastToken::Was(ref mut last) => { + tokens.extend(last.take()); + None + } + }; self.token_cursor.frame.last_token = LastToken::Collecting(tokens); let prev = self.token_cursor.stack.len(); let ret = f(self); @@ -6933,7 +7355,9 @@ impl<'a> Parser<'a> { } else { &mut self.token_cursor.stack[prev].last_token }; - let mut tokens = match *last_token { + + // Pull our the toekns that we've collected from the call to `f` above + let mut collected_tokens = match *last_token { LastToken::Collecting(ref mut v) => mem::replace(v, Vec::new()), LastToken::Was(_) => panic!("our vector went away?"), }; @@ -6941,44 +7365,34 @@ impl<'a> Parser<'a> { // If we're not at EOF our current token wasn't actually consumed by // `f`, but it'll still be in our list that we pulled out. In that case // put it back. - if self.token == token::Eof { - *last_token = LastToken::Was(None); + let extra_token = if self.token != token::Eof { + collected_tokens.pop() } else { - *last_token = LastToken::Was(tokens.pop()); + None + }; + + // If we were previously collecting tokens, then this was a recursive + // call. In that case we need to record all the tokens we collected in + // our parent list as well. To do that we push a clone of our stream + // onto the previous list. + let stream = collected_tokens.into_iter().collect::<TokenStream>(); + match prev_collecting { + Some(mut list) => { + list.push(stream.clone()); + list.extend(extra_token); + *last_token = LastToken::Collecting(list); + } + None => { + *last_token = LastToken::Was(extra_token); + } } - Ok((ret?, tokens.into_iter().collect())) + Ok((ret?, stream)) } pub fn parse_item(&mut self) -> PResult<'a, Option<P<Item>>> { let attrs = self.parse_outer_attributes()?; - - let (ret, tokens) = self.collect_tokens(|this| { - this.parse_item_(attrs, true, false) - })?; - - // Once we've parsed an item and recorded the tokens we got while - // parsing we may want to store `tokens` into the item we're about to - // return. Note, though, that we specifically didn't capture tokens - // related to outer attributes. The `tokens` field here may later be - // used with procedural macros to convert this item back into a token - // stream, but during expansion we may be removing attributes as we go - // along. - // - // If we've got inner attributes then the `tokens` we've got above holds - // these inner attributes. If an inner attribute is expanded we won't - // actually remove it from the token stream, so we'll just keep yielding - // it (bad!). To work around this case for now we just avoid recording - // `tokens` if we detect any inner attributes. This should help keep - // expansion correct, but we should fix this bug one day! - Ok(ret.map(|item| { - item.map(|mut i| { - if !i.attrs.iter().any(|attr| attr.style == AttrStyle::Inner) { - i.tokens = Some(tokens); - } - i - }) - })) + self.parse_item_(attrs, true, false) } /// `::{` or `::*` @@ -7023,7 +7437,7 @@ impl<'a> Parser<'a> { UseTreeKind::Nested(self.parse_use_tree_list()?) } } else { - UseTreeKind::Simple(self.parse_rename()?) + UseTreeKind::Simple(self.parse_rename()?, ast::DUMMY_NODE_ID, ast::DUMMY_NODE_ID) } }; @@ -7046,7 +7460,7 @@ impl<'a> Parser<'a> { match self.token { token::Ident(ident, false) if ident.name == keywords::Underscore.name() => { self.bump(); // `_` - Ok(Some(Ident::new(ident.name.gensymed(), ident.span))) + Ok(Some(ident.gensym())) } _ => self.parse_ident().map(Some), } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 938711ca1d4..fd8f394a600 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -22,6 +22,7 @@ use serialize::{Decodable, Decoder, Encodable, Encoder}; use symbol::keywords; use syntax::parse::parse_stream_from_source_str; use syntax_pos::{self, Span, FileName}; +use syntax_pos::symbol::{self, Symbol}; use tokenstream::{TokenStream, TokenTree}; use tokenstream; @@ -29,7 +30,7 @@ use std::{cmp, fmt}; use std::mem; use rustc_data_structures::sync::{Lrc, Lock}; -#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] pub enum BinOpToken { Plus, Minus, @@ -44,7 +45,7 @@ pub enum BinOpToken { } /// A delimiter token -#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] pub enum DelimToken { /// A round parenthesis: `(` or `)` Paren, @@ -66,7 +67,7 @@ impl DelimToken { } } -#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] pub enum Lit { Byte(ast::Name), Char(ast::Name), @@ -79,7 +80,7 @@ pub enum Lit { } impl Lit { - pub fn short_name(&self) -> &'static str { + crate fn short_name(&self) -> &'static str { match *self { Byte(_) => "byte", Char(_) => "char", @@ -138,45 +139,7 @@ fn ident_can_begin_type(ident: ast::Ident, is_raw: bool) -> bool { ].contains(&ident.name) } -pub fn is_path_segment_keyword(id: ast::Ident) -> bool { - id.name == keywords::Super.name() || - id.name == keywords::SelfValue.name() || - id.name == keywords::SelfType.name() || - id.name == keywords::Extern.name() || - id.name == keywords::Crate.name() || - id.name == keywords::CrateRoot.name() || - id.name == keywords::DollarCrate.name() -} - -// We see this identifier in a normal identifier position, like variable name or a type. -// How was it written originally? Did it use the raw form? Let's try to guess. -pub fn is_raw_guess(ident: ast::Ident) -> bool { - ident.name != keywords::Invalid.name() && - is_reserved_ident(ident) && !is_path_segment_keyword(ident) -} - -// Returns true for reserved identifiers used internally for elided lifetimes, -// unnamed method parameters, crate root module, error recovery etc. -pub fn is_special_ident(id: ast::Ident) -> bool { - id.name <= keywords::Underscore.name() -} - -/// Returns `true` if the token is a keyword used in the language. -pub fn is_used_keyword(id: ast::Ident) -> bool { - id.name >= keywords::As.name() && id.name <= keywords::While.name() -} - -/// Returns `true` if the token is a keyword reserved for possible future use. -pub fn is_unused_keyword(id: ast::Ident) -> bool { - id.name >= keywords::Abstract.name() && id.name <= keywords::Yield.name() -} - -/// Returns `true` if the token is either a special identifier or a keyword. -pub fn is_reserved_ident(id: ast::Ident) -> bool { - is_special_ident(id) || is_used_keyword(id) || is_unused_keyword(id) -} - -#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug)] pub enum Token { /* Expression-operator symbols. */ Eq, @@ -210,6 +173,8 @@ pub enum Token { Pound, Dollar, Question, + /// Used by proc macros for representing lifetimes, not generated by lexer right now. + SingleQuote, /// An opening delimiter, eg. `{` OpenDelim(DelimToken), /// A closing delimiter, eg. `}` @@ -249,19 +214,18 @@ impl Token { /// Recovers a `Token` from an `ast::Ident`. This creates a raw identifier if necessary. pub fn from_ast_ident(ident: ast::Ident) -> Token { - Ident(ident, is_raw_guess(ident)) + Ident(ident, ident.is_raw_guess()) } - /// Returns `true` if the token starts with '>'. - pub fn is_like_gt(&self) -> bool { + crate fn is_like_plus(&self) -> bool { match *self { - BinOp(Shr) | BinOpEq(Shr) | Gt | Ge => true, + BinOp(Plus) | BinOpEq(Plus) => true, _ => false, } } /// Returns `true` if the token can appear at the start of an expression. - pub fn can_begin_expr(&self) -> bool { + crate fn can_begin_expr(&self) -> bool { match *self { Ident(ident, is_raw) => ident_can_begin_expr(ident, is_raw), // value name or keyword @@ -280,7 +244,12 @@ impl Token { Lifetime(..) | // labeled loop Pound => true, // expression attributes Interpolated(ref nt) => match nt.0 { - NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) | NtLifetime(..) => true, + NtLiteral(..) | + NtIdent(..) | + NtExpr(..) | + NtBlock(..) | + NtPath(..) | + NtLifetime(..) => true, _ => false, }, _ => false, @@ -288,7 +257,7 @@ impl Token { } /// Returns `true` if the token can appear at the start of a type. - pub fn can_begin_type(&self) -> bool { + crate fn can_begin_type(&self) -> bool { match *self { Ident(ident, is_raw) => ident_can_begin_type(ident, is_raw), // type name or keyword @@ -311,19 +280,31 @@ impl Token { } /// Returns `true` if the token can appear at the start of a generic bound. - pub fn can_begin_bound(&self) -> bool { + crate fn can_begin_bound(&self) -> bool { self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) || self == &Question || self == &OpenDelim(Paren) } /// Returns `true` if the token is any literal - pub fn is_lit(&self) -> bool { + crate fn is_lit(&self) -> bool { match *self { Literal(..) => true, _ => false, } } + /// Returns `true` if the token is any literal, a minus (which can follow a literal, + /// for example a '-42', or one of the boolean idents). + crate fn can_begin_literal_or_bool(&self) -> bool { + match *self { + Literal(..) => true, + BinOp(Minus) => true, + Ident(ident, false) if ident.name == keywords::True.name() => true, + Ident(ident, false) if ident.name == keywords::False.name() => true, + _ => false, + } + } + /// Returns an identifier if this token is an identifier. pub fn ident(&self) -> Option<(ast::Ident, /* is_raw */ bool)> { match *self { @@ -351,37 +332,21 @@ impl Token { self.ident().is_some() } /// Returns `true` if the token is a lifetime. - pub fn is_lifetime(&self) -> bool { + crate fn is_lifetime(&self) -> bool { self.lifetime().is_some() } /// Returns `true` if the token is a identifier whose name is the given /// string slice. - pub fn is_ident_named(&self, name: &str) -> bool { + crate fn is_ident_named(&self, name: &str) -> bool { match self.ident() { - Some((ident, _)) => ident.name.as_str() == name, + Some((ident, _)) => ident.as_str() == name, None => false } } - /// Returns `true` if the token is a documentation comment. - pub fn is_doc_comment(&self) -> bool { - match *self { - DocComment(..) => true, - _ => false, - } - } - - /// Returns `true` if the token is interpolated. - pub fn is_interpolated(&self) -> bool { - match *self { - Interpolated(..) => true, - _ => false, - } - } - /// Returns `true` if the token is an interpolated path. - pub fn is_path(&self) -> bool { + fn is_path(&self) -> bool { if let Interpolated(ref nt) = *self { if let NtPath(..) = nt.0 { return true; @@ -391,16 +356,16 @@ impl Token { } /// Returns `true` if the token is either the `mut` or `const` keyword. - pub fn is_mutability(&self) -> bool { + crate fn is_mutability(&self) -> bool { self.is_keyword(keywords::Mut) || self.is_keyword(keywords::Const) } - pub fn is_qpath_start(&self) -> bool { + crate fn is_qpath_start(&self) -> bool { self == &Lt || self == &BinOp(Shl) } - pub fn is_path_start(&self) -> bool { + crate fn is_path_start(&self) -> bool { self == &ModSep || self.is_qpath_start() || self.is_path() || self.is_path_segment_keyword() || self.is_ident() && !self.is_reserved_ident() } @@ -412,7 +377,7 @@ impl Token { pub fn is_path_segment_keyword(&self) -> bool { match self.ident() { - Some((id, false)) => is_path_segment_keyword(id), + Some((id, false)) => id.is_path_segment_keyword(), _ => false, } } @@ -421,23 +386,23 @@ impl Token { // unnamed method parameters, crate root module, error recovery etc. pub fn is_special_ident(&self) -> bool { match self.ident() { - Some((id, false)) => is_special_ident(id), + Some((id, false)) => id.is_special(), _ => false, } } /// Returns `true` if the token is a keyword used in the language. - pub fn is_used_keyword(&self) -> bool { + crate fn is_used_keyword(&self) -> bool { match self.ident() { - Some((id, false)) => is_used_keyword(id), + Some((id, false)) => id.is_used_keyword(), _ => false, } } /// Returns `true` if the token is a keyword reserved for possible future use. - pub fn is_unused_keyword(&self) -> bool { + crate fn is_unused_keyword(&self) -> bool { match self.ident() { - Some((id, false)) => is_unused_keyword(id), + Some((id, false)) => id.is_unused_keyword(), _ => false, } } @@ -445,12 +410,12 @@ impl Token { /// Returns `true` if the token is either a special identifier or a keyword. pub fn is_reserved_ident(&self) -> bool { match self.ident() { - Some((id, false)) => is_reserved_ident(id), + Some((id, false)) => id.is_reserved(), _ => false, } } - pub fn glue(self, joint: Token) -> Option<Token> { + crate fn glue(self, joint: Token) -> Option<Token> { Some(match self { Eq => match joint { Eq => EqEq, @@ -496,6 +461,16 @@ impl Token { Colon => ModSep, _ => return None, }, + SingleQuote => match joint { + Ident(ident, false) => { + let name = Symbol::intern(&format!("'{}", ident)); + Lifetime(symbol::Ident { + name, + span: ident.span, + }) + } + _ => return None, + }, Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq | DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar | @@ -508,7 +483,7 @@ impl Token { /// Returns tokens that are likely to be typed accidentally instead of the current token. /// Enables better error recovery when the wrong token is found. - pub fn similar_tokens(&self) -> Option<Vec<Token>> { + crate fn similar_tokens(&self) -> Option<Vec<Token>> { match *self { Comma => Some(vec![Dot, Lt]), Semi => Some(vec![Colon]), @@ -596,13 +571,15 @@ impl Token { if tokens.probably_equal_for_proc_macro(&tokens_for_real) { return tokens } + info!("cached tokens found, but they're not \"probably equal\", \ + going with stringified version"); } return tokens_for_real } // See comments in `interpolated_to_tokenstream` for why we care about // *probably* equal here rather than actual equality - pub fn probably_equal_for_proc_macro(&self, other: &Token) -> bool { + crate fn probably_equal_for_proc_macro(&self, other: &Token) -> bool { if mem::discriminant(self) != mem::discriminant(other) { return false } @@ -661,7 +638,7 @@ impl Token { } } -#[derive(Clone, RustcEncodable, RustcDecodable, Eq, Hash)] +#[derive(Clone, RustcEncodable, RustcDecodable)] /// For interpolation during macro expansion. pub enum Nonterminal { NtItem(P<ast::Item>), @@ -672,6 +649,7 @@ pub enum Nonterminal { NtTy(P<ast::Ty>), NtIdent(ast::Ident, /* is_raw */ bool), NtLifetime(ast::Ident), + NtLiteral(P<ast::Expr>), /// Stuff inside brackets for attributes NtMeta(ast::MetaItem), NtPath(ast::Path), @@ -713,6 +691,7 @@ impl fmt::Debug for Nonterminal { NtExpr(..) => f.pad("NtExpr(..)"), NtTy(..) => f.pad("NtTy(..)"), NtIdent(..) => f.pad("NtIdent(..)"), + NtLiteral(..) => f.pad("NtLiteral(..)"), NtMeta(..) => f.pad("NtMeta(..)"), NtPath(..) => f.pad("NtPath(..)"), NtTT(..) => f.pad("NtTT(..)"), @@ -729,7 +708,7 @@ impl fmt::Debug for Nonterminal { } } -pub fn is_op(tok: &Token) -> bool { +crate fn is_op(tok: &Token) -> bool { match *tok { OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..) | Lifetime(..) | Interpolated(..) | @@ -755,11 +734,11 @@ impl fmt::Debug for LazyTokenStream { } impl LazyTokenStream { - pub fn new() -> Self { + fn new() -> Self { LazyTokenStream(Lock::new(None)) } - pub fn force<F: FnOnce() -> TokenStream>(&self, f: F) -> TokenStream { + fn force<F: FnOnce() -> TokenStream>(&self, f: F) -> TokenStream { let mut opt_stream = self.0.lock(); if opt_stream.is_none() { *opt_stream = Some(f()); @@ -798,11 +777,50 @@ fn prepend_attrs(sess: &ParseSess, for attr in attrs { assert_eq!(attr.style, ast::AttrStyle::Outer, "inner attributes should prevent cached tokens from existing"); - // FIXME: Avoid this pretty-print + reparse hack as bove - let name = FileName::MacroExpansion; - let source = pprust::attr_to_string(attr); - let stream = parse_stream_from_source_str(name, source, sess, Some(span)); - builder.push(stream); + + if attr.is_sugared_doc { + let stream = parse_stream_from_source_str( + FileName::MacroExpansion, + pprust::attr_to_string(attr), + sess, + Some(span), + ); + builder.push(stream); + continue + } + + // synthesize # [ $path $tokens ] manually here + let mut brackets = tokenstream::TokenStreamBuilder::new(); + + // For simple paths, push the identifier directly + if attr.path.segments.len() == 1 && attr.path.segments[0].args.is_none() { + let ident = attr.path.segments[0].ident; + let token = Ident(ident, ident.as_str().starts_with("r#")); + brackets.push(tokenstream::TokenTree::Token(ident.span, token)); + + // ... and for more complicated paths, fall back to a reparse hack that + // should eventually be removed. + } else { + let stream = parse_stream_from_source_str( + FileName::MacroExpansion, + pprust::path_to_string(&attr.path), + sess, + Some(span), + ); + brackets.push(stream); + } + + brackets.push(attr.tokens.clone()); + + let tokens = tokenstream::Delimited { + delim: DelimToken::Bracket, + tts: brackets.build().into(), + }; + // The span we list here for `#` and for `[ ... ]` are both wrong in + // that it encompasses more than each token, but it hopefully is "good + // enough" for now at least. + builder.push(tokenstream::TokenTree::Token(attr.span, Pound)); + builder.push(tokenstream::TokenTree::Delimited(attr.span, tokens)); } builder.push(tokens.clone()); Some(builder.build()) |
