diff options
Diffstat (limited to 'compiler/rustc_parse/src')
| -rw-r--r-- | compiler/rustc_parse/src/lib.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 21 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 60 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 89 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 91 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/nonterminal.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/path.rs | 65 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 187 |
9 files changed, 291 insertions, 232 deletions
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 62fd6936d21..c365acac625 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -292,7 +292,7 @@ pub fn nt_to_tokenstream( } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) { return fake_token_stream(sess, nt); } else { - panic!("Missing tokens for nt {:?}", pprust::nonterminal_to_string(nt)); + panic!("Missing tokens for nt at {:?}: {:?}", nt.span(), pprust::nonterminal_to_string(nt)); } } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 35435baea70..f2fcce5c226 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2,14 +2,13 @@ use super::ty::AllowPlus; use super::TokenType; use super::{BlockMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType}; +use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; -use rustc_ast::{ - self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, - Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item, ItemKind, Mutability, Param, Pat, - PatKind, Path, PathSegment, QSelf, Ty, TyKind, -}; +use rustc_ast::{AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec}; +use rustc_ast::{BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item}; +use rustc_ast::{ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, TyKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err}; @@ -220,6 +219,7 @@ impl<'a> Parser<'a> { edible: &[TokenKind], inedible: &[TokenKind], ) -> PResult<'a, bool /* recovered */> { + debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible); fn tokens_to_string(tokens: &[TokenType]) -> String { let mut i = tokens.iter(); // This might be a sign we need a connect method on `Iterator`. @@ -245,6 +245,7 @@ impl<'a> Parser<'a> { .collect::<Vec<_>>(); expected.sort_by_cached_key(|x| x.to_string()); expected.dedup(); + let expect = tokens_to_string(&expected[..]); let actual = super::token_descr(&self.token); let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { @@ -270,6 +271,16 @@ impl<'a> Parser<'a> { }; self.last_unexpected_token_span = Some(self.token.span); let mut err = self.struct_span_err(self.token.span, &msg_exp); + + // Add suggestion for a missing closing angle bracket if '>' is included in expected_tokens + // there are unclosed angle brackets + if self.unmatched_angle_bracket_count > 0 + && self.token.kind == TokenKind::Eq + && expected.iter().any(|tok| matches!(tok, TokenType::Token(TokenKind::Gt))) + { + err.span_label(self.prev_token.span, "maybe try to close unmatched angle bracket"); + } + let sp = if self.token == token::Eof { // This is EOF; don't want to point at the following char, but rather the last token. self.prev_token.span diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 6db415ead41..cfd7ad48222 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -472,7 +472,11 @@ impl<'a> Parser<'a> { /// Parses a prefix-unary-operator expr. fn parse_prefix_expr(&mut self, attrs: Option<AttrVec>) -> PResult<'a, P<Expr>> { let attrs = self.parse_or_use_outer_attributes(attrs)?; - let needs_tokens = super::attr::maybe_needs_tokens(&attrs); + // FIXME: Use super::attr::maybe_needs_tokens(&attrs) once we come up + // with a good way of passing `force_tokens` through from `parse_nonterminal`. + // Checking !attrs.is_empty() is correct, but will cause us to unnecessarily + // capture tokens in some circumstances. + let needs_tokens = !attrs.is_empty(); let do_parse = |this: &mut Parser<'a>| { let lo = this.token.span; // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() @@ -581,7 +585,7 @@ impl<'a> Parser<'a> { lhs_span: Span, expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind, ) -> PResult<'a, P<Expr>> { - let mk_expr = |this: &mut Self, rhs: P<Ty>| { + let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| { this.mk_expr( this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs), @@ -593,13 +597,49 @@ impl<'a> Parser<'a> { // LessThan comparison after this cast. let parser_snapshot_before_type = self.clone(); let cast_expr = match self.parse_ty_no_plus() { - Ok(rhs) => mk_expr(self, rhs), + Ok(rhs) => mk_expr(self, lhs, rhs), Err(mut type_err) => { // Rewind to before attempting to parse the type with generics, to recover // from situations like `x as usize < y` in which we first tried to parse // `usize < y` as a type with generic arguments. let parser_snapshot_after_type = mem::replace(self, parser_snapshot_before_type); + // Check for typo of `'a: loop { break 'a }` with a missing `'`. + match (&lhs.kind, &self.token.kind) { + ( + // `foo: ` + ExprKind::Path(None, ast::Path { segments, .. }), + TokenKind::Ident(kw::For | kw::Loop | kw::While, false), + ) if segments.len() == 1 => { + let snapshot = self.clone(); + let label = Label { + ident: Ident::from_str_and_span( + &format!("'{}", segments[0].ident), + segments[0].ident.span, + ), + }; + match self.parse_labeled_expr(label, AttrVec::new(), false) { + Ok(expr) => { + type_err.cancel(); + self.struct_span_err(label.ident.span, "malformed loop label") + .span_suggestion( + label.ident.span, + "use the correct loop label format", + label.ident.to_string(), + Applicability::MachineApplicable, + ) + .emit(); + return Ok(expr); + } + Err(mut err) => { + err.cancel(); + *self = snapshot; + } + } + } + _ => {} + } + match self.parse_path(PathStyle::Expr) { Ok(path) => { let (op_noun, op_verb) = match self.token.kind { @@ -626,7 +666,8 @@ impl<'a> Parser<'a> { op_noun, ); let span_after_type = parser_snapshot_after_type.token.span; - let expr = mk_expr(self, self.mk_ty(path.span, TyKind::Path(None, path))); + let expr = + mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path))); let expr_str = self .span_to_snippet(expr.span) @@ -1063,7 +1104,7 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::While) { self.parse_while_expr(None, self.prev_token.span, attrs) } else if let Some(label) = self.eat_label() { - self.parse_labeled_expr(label, attrs) + self.parse_labeled_expr(label, attrs, true) } else if self.eat_keyword(kw::Loop) { self.parse_loop_expr(None, self.prev_token.span, attrs) } else if self.eat_keyword(kw::Continue) { @@ -1224,7 +1265,12 @@ impl<'a> Parser<'a> { } /// Parse `'label: $expr`. The label is already parsed. - fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_labeled_expr( + &mut self, + label: Label, + attrs: AttrVec, + consume_colon: bool, + ) -> PResult<'a, P<Expr>> { let lo = label.ident.span; let label = Some(label); let ate_colon = self.eat(&token::Colon); @@ -1243,7 +1289,7 @@ impl<'a> Parser<'a> { self.parse_expr() }?; - if !ate_colon { + if !ate_colon && consume_colon { self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span); } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 810ae61307c..1ed4d39cd05 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,8 +1,8 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim, Error}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; -use super::{FollowedByType, Parser, PathStyle}; +use super::{FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; -use crate::maybe_whole; +use crate::{maybe_collect_tokens, maybe_whole}; use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; @@ -69,7 +69,7 @@ impl<'a> Parser<'a> { unsafety: Unsafe, ) -> PResult<'a, Mod> { let mut items = vec![]; - while let Some(item) = self.parse_item()? { + while let Some(item) = self.parse_item(ForceCollect::No)? { items.push(item); self.maybe_consume_incorrect_semicolon(&items); } @@ -93,13 +93,17 @@ impl<'a> Parser<'a> { pub(super) type ItemInfo = (Ident, ItemKind); impl<'a> Parser<'a> { - pub fn parse_item(&mut self) -> PResult<'a, Option<P<Item>>> { - self.parse_item_(|_| true).map(|i| i.map(P)) + pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<P<Item>>> { + self.parse_item_(|_| true, force_collect).map(|i| i.map(P)) } - fn parse_item_(&mut self, req_name: ReqName) -> PResult<'a, Option<Item>> { + fn parse_item_( + &mut self, + req_name: ReqName, + force_collect: ForceCollect, + ) -> PResult<'a, Option<Item>> { let attrs = self.parse_outer_attributes()?; - self.parse_item_common(attrs, true, false, req_name) + self.parse_item_common(attrs, true, false, req_name, force_collect) } pub(super) fn parse_item_common( @@ -108,6 +112,7 @@ impl<'a> Parser<'a> { mac_allowed: bool, attrs_allowed: bool, req_name: ReqName, + force_collect: ForceCollect, ) -> PResult<'a, Option<Item>> { maybe_whole!(self, NtItem, |item| { let mut item = item; @@ -116,16 +121,12 @@ impl<'a> Parser<'a> { Some(item.into_inner()) }); - let needs_tokens = super::attr::maybe_needs_tokens(&attrs); - let mut unclosed_delims = vec![]; - let parse_item = |this: &mut Self| { + let item = maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Self| { let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name); unclosed_delims.append(&mut this.unclosed_delims); - item - }; - - let item = if needs_tokens { self.collect_tokens(parse_item) } else { parse_item(self) }?; + Ok((item?, TrailingToken::None)) + })?; self.unclosed_delims.append(&mut unclosed_delims); Ok(item) @@ -731,20 +732,22 @@ impl<'a> Parser<'a> { /// Parses associated items. fn parse_assoc_item(&mut self, req_name: ReqName) -> PResult<'a, Option<Option<P<AssocItem>>>> { - Ok(self.parse_item_(req_name)?.map(|Item { attrs, id, span, vis, ident, kind, tokens }| { - let kind = match AssocItemKind::try_from(kind) { - Ok(kind) => kind, - Err(kind) => match kind { - ItemKind::Static(a, _, b) => { - self.struct_span_err(span, "associated `static` items are not allowed") - .emit(); - AssocItemKind::Const(Defaultness::Final, a, b) - } - _ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"), - }, - }; - Some(P(Item { attrs, id, span, vis, ident, kind, tokens })) - })) + Ok(self.parse_item_(req_name, ForceCollect::No)?.map( + |Item { attrs, id, span, vis, ident, kind, tokens }| { + let kind = match AssocItemKind::try_from(kind) { + Ok(kind) => kind, + Err(kind) => match kind { + ItemKind::Static(a, _, b) => { + self.struct_span_err(span, "associated `static` items are not allowed") + .emit(); + AssocItemKind::Const(Defaultness::Final, a, b) + } + _ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"), + }, + }; + Some(P(Item { attrs, id, span, vis, ident, kind, tokens })) + }, + )) } /// Parses a `type` alias with the following grammar: @@ -921,19 +924,21 @@ impl<'a> Parser<'a> { /// Parses a foreign item (one in an `extern { ... }` block). pub fn parse_foreign_item(&mut self) -> PResult<'a, Option<Option<P<ForeignItem>>>> { - Ok(self.parse_item_(|_| true)?.map(|Item { attrs, id, span, vis, ident, kind, tokens }| { - let kind = match ForeignItemKind::try_from(kind) { - Ok(kind) => kind, - Err(kind) => match kind { - ItemKind::Const(_, a, b) => { - self.error_on_foreign_const(span, ident); - ForeignItemKind::Static(a, Mutability::Not, b) - } - _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), - }, - }; - Some(P(Item { attrs, id, span, vis, ident, kind, tokens })) - })) + Ok(self.parse_item_(|_| true, ForceCollect::No)?.map( + |Item { attrs, id, span, vis, ident, kind, tokens }| { + let kind = match ForeignItemKind::try_from(kind) { + Ok(kind) => kind, + Err(kind) => match kind { + ItemKind::Const(_, a, b) => { + self.error_on_foreign_const(span, ident); + ForeignItemKind::Static(a, Mutability::Not, b) + } + _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), + }, + }; + Some(P(Item { attrs, id, span, vis, ident, kind, tokens })) + }, + )) } fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &str) -> Option<T> { @@ -1515,7 +1520,7 @@ impl<'a> Parser<'a> { { let kw_token = self.token.clone(); let kw_str = pprust::token_to_string(&kw_token); - let item = self.parse_item()?; + let item = self.parse_item(ForceCollect::No)?; self.struct_span_err( kw_token.span, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 5d7ea5b8d57..e2af63d1744 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -54,6 +54,18 @@ enum BlockMode { Ignore, } +/// Whether or not we should force collection of tokens for an AST node, +/// regardless of whether or not it has attributes +pub enum ForceCollect { + Yes, + No, +} + +pub enum TrailingToken { + None, + Semi, +} + /// Like `maybe_whole_expr`, but for things other than expressions. #[macro_export] macro_rules! maybe_whole { @@ -265,7 +277,7 @@ impl TokenCursor { } } -#[derive(Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq)] enum TokenType { Token(TokenKind), Keyword(Symbol), @@ -968,12 +980,8 @@ impl<'a> Parser<'a> { } } - // The value here is never passed to macros as tokens by itself (not as a part - // of the whole attribute), so we don't collect tokens here. If this changes, - // then token will need to be collected. One catch here is that we are using - // a nonterminal for keeping the expression, but this nonterminal should not - // be wrapped into a group when converting to token stream. - let expr = self.parse_expr()?; + // Collect tokens because they are used during lowering to HIR. + let expr = self.collect_tokens(|this| this.parse_expr())?; let span = expr.span; match &expr.kind { @@ -1218,6 +1226,13 @@ impl<'a> Parser<'a> { } } + pub fn collect_tokens<R: HasTokens>( + &mut self, + f: impl FnOnce(&mut Self) -> PResult<'a, R>, + ) -> PResult<'a, R> { + self.collect_tokens_trailing_token(|this| Ok((f(this)?, TrailingToken::None))) + } + /// Records all tokens consumed by the provided callback, /// including the current token. These tokens are collected /// into a `LazyTokenStream`, and returned along with the result @@ -1234,22 +1249,14 @@ impl<'a> Parser<'a> { /// This restriction shouldn't be an issue in practice, /// since this function is used to record the tokens for /// a parsed AST item, which always has matching delimiters. - pub fn collect_tokens<R: HasTokens>( + pub fn collect_tokens_trailing_token<R: HasTokens>( &mut self, - f: impl FnOnce(&mut Self) -> PResult<'a, R>, + f: impl FnOnce(&mut Self) -> PResult<'a, (R, TrailingToken)>, ) -> PResult<'a, R> { let start_token = (self.token.clone(), self.token_spacing); - let cursor_snapshot = TokenCursor { - frame: self.token_cursor.frame.clone(), - // We only ever capture tokens within our current frame, - // so we can just use an empty frame stack - stack: vec![], - desugar_doc_comments: self.token_cursor.desugar_doc_comments, - num_next_calls: self.token_cursor.num_next_calls, - append_unglued_token: self.token_cursor.append_unglued_token.clone(), - }; + let cursor_snapshot = self.token_cursor.clone(); - let mut ret = f(self)?; + let (mut ret, trailing_token) = f(self)?; // Produces a `TokenStream` on-demand. Using `cursor_snapshot` // and `num_calls`, we can reconstruct the `TokenStream` seen @@ -1268,15 +1275,10 @@ impl<'a> Parser<'a> { cursor_snapshot: TokenCursor, num_calls: usize, desugar_doc_comments: bool, - trailing_semi: bool, append_unglued_token: Option<TreeAndSpacing>, } impl CreateTokenStream for LazyTokenStreamImpl { fn create_token_stream(&self) -> TokenStream { - let mut num_calls = self.num_calls; - if self.trailing_semi { - num_calls += 1; - } // The token produced by the final call to `next` or `next_desugared` // was not actually consumed by the callback. The combination // of chaining the initial token and using `take` produces the desired @@ -1284,39 +1286,33 @@ impl<'a> Parser<'a> { // and omit the final token otherwise. let mut cursor_snapshot = self.cursor_snapshot.clone(); let tokens = std::iter::once(self.start_token.clone()) - .chain((0..num_calls).map(|_| { + .chain((0..self.num_calls).map(|_| { if self.desugar_doc_comments { cursor_snapshot.next_desugared() } else { cursor_snapshot.next() } })) - .take(num_calls); + .take(self.num_calls); make_token_stream(tokens, self.append_unglued_token.clone()) } - fn add_trailing_semi(&self) -> Box<dyn CreateTokenStream> { - if self.trailing_semi { - panic!("Called `add_trailing_semi` twice!"); - } - if self.append_unglued_token.is_some() { - panic!( - "Cannot call `add_trailing_semi` when we have an unglued token {:?}", - self.append_unglued_token - ); - } - let mut new = self.clone(); - new.trailing_semi = true; - Box::new(new) + } + + let mut num_calls = self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls; + match trailing_token { + TrailingToken::None => {} + TrailingToken::Semi => { + assert_eq!(self.token.kind, token::Semi); + num_calls += 1; } } let lazy_impl = LazyTokenStreamImpl { start_token, - num_calls: self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls, + num_calls, cursor_snapshot, desugar_doc_comments: self.desugar_doc_comments, - trailing_semi: false, append_unglued_token: self.token_cursor.append_unglued_token.clone(), }; ret.finalize_tokens(LazyTokenStream::new(lazy_impl)); @@ -1413,3 +1409,16 @@ fn make_token_stream( assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack); TokenStream::new(final_buf.inner) } + +#[macro_export] +macro_rules! maybe_collect_tokens { + ($self:ident, $force_collect:expr, $attrs:expr, $f:expr) => { + if matches!($force_collect, ForceCollect::Yes) + || $crate::parser::attr::maybe_needs_tokens($attrs) + { + $self.collect_tokens_trailing_token($f) + } else { + Ok($f($self)?.0) + } + }; +} diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 97d0c0d8745..012b76d3d18 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -5,7 +5,7 @@ use rustc_errors::PResult; use rustc_span::symbol::{kw, Ident}; use crate::parser::pat::{GateOr, RecoverComma}; -use crate::parser::{FollowedByType, Parser, PathStyle}; +use crate::parser::{FollowedByType, ForceCollect, Parser, PathStyle}; impl<'a> Parser<'a> { /// Checks whether a non-terminal may begin with a particular token. @@ -98,7 +98,7 @@ impl<'a> Parser<'a> { // in advance whether or not a proc-macro will be (transitively) invoked, // we always capture tokens for any `Nonterminal` which needs them. Ok(match kind { - NonterminalKind::Item => match self.collect_tokens(|this| this.parse_item())? { + NonterminalKind::Item => match self.parse_item(ForceCollect::Yes)? { Some(item) => token::NtItem(item), None => { return Err(self.struct_span_err(self.token.span, "expected an item keyword")); @@ -107,7 +107,7 @@ impl<'a> Parser<'a> { NonterminalKind::Block => { token::NtBlock(self.collect_tokens(|this| this.parse_block())?) } - NonterminalKind::Stmt => match self.collect_tokens(|this| this.parse_stmt())? { + NonterminalKind::Stmt => match self.parse_stmt(ForceCollect::Yes)? { Some(s) => token::NtStmt(s), None => { return Err(self.struct_span_err(self.token.span, "expected a statement")); diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 456e32680fe..d888514cf56 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -240,7 +240,7 @@ impl<'a> Parser<'a> { Err(err) } - /// Parse and throw away a parentesized comma separated + /// Parse and throw away a parenthesized comma separated /// sequence of patterns until `)` is reached. fn skip_pat_list(&mut self) -> PResult<'a, ()> { while !self.check(&token::CloseDelim(token::Paren)) { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index dd36122f6a1..6b7059eecf4 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -133,7 +133,15 @@ impl<'a> Parser<'a> { maybe_whole!(self, NtPath, |path| { if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some()) { - self.struct_span_err(path.span, "unexpected generic arguments in path").emit(); + self.struct_span_err( + path.segments + .iter() + .filter_map(|segment| segment.args.as_ref()) + .map(|arg| arg.span()) + .collect::<Vec<_>>(), + "unexpected generic arguments in path", + ) + .emit(); } path }); @@ -185,7 +193,6 @@ impl<'a> Parser<'a> { pub(super) fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> { let ident = self.parse_path_segment_ident()?; - let is_args_start = |token: &Token| { matches!( token.kind, @@ -420,7 +427,10 @@ impl<'a> Parser<'a> { match arg { Some(arg) => { if self.check(&token::Colon) | self.check(&token::Eq) { - let (ident, gen_args) = self.get_ident_from_generic_arg(arg, lo)?; + let (ident, gen_args) = match self.get_ident_from_generic_arg(arg) { + Ok(ident_gen_args) => ident_gen_args, + Err(arg) => return Ok(Some(AngleBracketedArg::Arg(arg))), + }; let kind = if self.eat(&token::Colon) { // Parse associated type constraint bound. @@ -561,50 +571,15 @@ impl<'a> Parser<'a> { fn get_ident_from_generic_arg( &self, gen_arg: GenericArg, - lo: Span, - ) -> PResult<'a, (Ident, Option<GenericArgs>)> { - let gen_arg_span = gen_arg.span(); - match gen_arg { - GenericArg::Type(t) => match t.into_inner().kind { - ast::TyKind::Path(qself, mut path) => { - if let Some(qself) = qself { - let mut err = self.struct_span_err( - gen_arg_span, - "qualified paths cannot be used in associated type constraints", - ); - err.span_label( - qself.path_span, - "not allowed in associated type constraints", - ); - return Err(err); - } - if path.segments.len() == 1 { - let path_seg = path.segments.remove(0); - let ident = path_seg.ident; - let gen_args = path_seg.args.map(|args| args.into_inner()); - return Ok((ident, gen_args)); - } - let err = self.struct_span_err( - path.span, - "paths with multiple segments cannot be used in associated type constraints", - ); - return Err(err); + ) -> Result<(Ident, Option<GenericArgs>), GenericArg> { + if let GenericArg::Type(ty) = &gen_arg { + if let ast::TyKind::Path(qself, path) = &ty.kind { + if qself.is_none() && path.segments.len() == 1 { + let seg = &path.segments[0]; + return Ok((seg.ident, seg.args.as_deref().cloned())); } - _ => { - let span = lo.to(self.prev_token.span); - let err = self.struct_span_err( - span, - "only path types can be used in associated type constraints", - ); - return Err(err); - } - }, - _ => { - let span = lo.to(self.prev_token.span); - let err = self - .struct_span_err(span, "only types can be used in associated type constraints"); - return Err(err); } } + Err(gen_arg) } } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 641b29227db..8373f6acd7e 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -3,14 +3,13 @@ use super::diagnostics::{AttemptLocalParseRecovery, Error}; use super::expr::LhsExpr; use super::pat::{GateOr, RecoverComma}; use super::path::PathStyle; -use super::{BlockMode, Parser, Restrictions, SemiColonMode}; -use crate::maybe_whole; +use super::{BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode, TrailingToken}; +use crate::{maybe_collect_tokens, maybe_whole}; use rustc_ast as ast; use rustc_ast::attr::HasAttrs; use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; -use rustc_ast::tokenstream::LazyTokenStream; use rustc_ast::util::classify; use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt, StmtKind, DUMMY_NODE_ID}; @@ -24,17 +23,22 @@ impl<'a> Parser<'a> { /// Parses a statement. This stops just before trailing semicolons on everything but items. /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. // Public for rustfmt usage. - pub fn parse_stmt(&mut self) -> PResult<'a, Option<Stmt>> { - Ok(self.parse_stmt_without_recovery().unwrap_or_else(|mut e| { + pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> { + Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|mut e| { e.emit(); self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); None })) } - fn parse_stmt_without_recovery(&mut self) -> PResult<'a, Option<Stmt>> { + /// If `force_capture` is true, forces collection of tokens regardless of whether + /// or not we have attributes + fn parse_stmt_without_recovery( + &mut self, + capture_semi: bool, + force_collect: ForceCollect, + ) -> PResult<'a, Option<Stmt>> { let mut attrs = self.parse_outer_attributes()?; - let has_attrs = !attrs.is_empty(); let lo = self.token.span; maybe_whole!(self, NtStmt, |stmt| { @@ -46,75 +50,77 @@ impl<'a> Parser<'a> { Some(stmt) }); - let parse_stmt_inner = |this: &mut Self| { - let stmt = if this.eat_keyword(kw::Let) { - this.parse_local_mk(lo, attrs.into())? - } else if this.is_kw_followed_by_ident(kw::Mut) { - this.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut")? - } else if this.is_kw_followed_by_ident(kw::Auto) { - this.bump(); // `auto` - let msg = "write `let` instead of `auto` to introduce a new variable"; - this.recover_stmt_local(lo, attrs.into(), msg, "let")? - } else if this.is_kw_followed_by_ident(sym::var) { - this.bump(); // `var` - let msg = "write `let` instead of `var` to introduce a new variable"; - this.recover_stmt_local(lo, attrs.into(), msg, "let")? - } else if this.check_path() - && !this.token.is_qpath_start() - && !this.is_path_start_item() - { - // We have avoided contextual keywords like `union`, items with `crate` visibility, - // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something - // that starts like a path (1 token), but it fact not a path. - // Also, we avoid stealing syntax from `parse_item_`. - this.parse_stmt_path_start(lo, attrs)? - } else if let Some(item) = - this.parse_item_common(attrs.clone(), false, true, |_| true)? - { - // FIXME: Bad copy of attrs - this.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) - } else if this.eat(&token::Semi) { - // Do not attempt to parse an expression if we're done here. - this.error_outer_attrs(&attrs); - this.mk_stmt(lo, StmtKind::Empty) - } else if this.token != token::CloseDelim(token::Brace) { - // Remainder are line-expr stmts. - let e = this.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?; - this.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) - } else { - this.error_outer_attrs(&attrs); - return Ok(None); - }; - Ok(Some(stmt)) - }; - - let stmt = if has_attrs { - self.collect_tokens(parse_stmt_inner)? + Ok(Some(if self.token.is_keyword(kw::Let) { + self.parse_local_mk(lo, attrs.into(), capture_semi, force_collect)? + } else if self.is_kw_followed_by_ident(kw::Mut) { + self.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut")? + } else if self.is_kw_followed_by_ident(kw::Auto) { + self.bump(); // `auto` + let msg = "write `let` instead of `auto` to introduce a new variable"; + self.recover_stmt_local(lo, attrs.into(), msg, "let")? + } else if self.is_kw_followed_by_ident(sym::var) { + self.bump(); // `var` + let msg = "write `let` instead of `var` to introduce a new variable"; + self.recover_stmt_local(lo, attrs.into(), msg, "let")? + } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { + // We have avoided contextual keywords like `union`, items with `crate` visibility, + // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something + // that starts like a path (1 token), but it fact not a path. + // Also, we avoid stealing syntax from `parse_item_`. + self.parse_stmt_path_start(lo, attrs, force_collect)? + } else if let Some(item) = + self.parse_item_common(attrs.clone(), false, true, |_| true, force_collect)? + { + // FIXME: Bad copy of attrs + self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) + } else if self.eat(&token::Semi) { + // Do not attempt to parse an expression if we're done here. + self.error_outer_attrs(&attrs); + self.mk_stmt(lo, StmtKind::Empty) + } else if self.token != token::CloseDelim(token::Brace) { + // Remainder are line-expr stmts. + let e = self.parse_expr_res(Restrictions::STMT_EXPR, Some(attrs.into()))?; + self.mk_stmt(lo.to(e.span), StmtKind::Expr(e)) } else { - parse_stmt_inner(self)? - }; - Ok(stmt) + self.error_outer_attrs(&attrs); + return Ok(None); + })) } - fn parse_stmt_path_start(&mut self, lo: Span, attrs: Vec<Attribute>) -> PResult<'a, Stmt> { - let path = self.parse_path(PathStyle::Expr)?; + fn parse_stmt_path_start( + &mut self, + lo: Span, + attrs: Vec<Attribute>, + force_collect: ForceCollect, + ) -> PResult<'a, Stmt> { + maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Parser<'a>| { + let path = this.parse_path(PathStyle::Expr)?; - if self.eat(&token::Not) { - return self.parse_stmt_mac(lo, attrs.into(), path); - } + if this.eat(&token::Not) { + let stmt_mac = this.parse_stmt_mac(lo, attrs.into(), path)?; + if this.token == token::Semi { + return Ok((stmt_mac, TrailingToken::Semi)); + } else { + return Ok((stmt_mac, TrailingToken::None)); + } + } - let expr = if self.eat(&token::OpenDelim(token::Brace)) { - self.parse_struct_expr(path, AttrVec::new(), true)? - } else { - let hi = self.prev_token.span; - self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) - }; + let expr = if this.eat(&token::OpenDelim(token::Brace)) { + this.parse_struct_expr(path, AttrVec::new(), true)? + } else { + let hi = this.prev_token.span; + this.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) + }; - let expr = self.with_res(Restrictions::STMT_EXPR, |this| { - let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?; - this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) - })?; - Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) + let expr = this.with_res(Restrictions::STMT_EXPR, |this| { + let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs.into())?; + this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) + })?; + Ok(( + this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Expr(expr)), + TrailingToken::None, + )) + }) } /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`. @@ -162,15 +168,34 @@ impl<'a> Parser<'a> { msg: &str, sugg: &str, ) -> PResult<'a, Stmt> { - let stmt = self.parse_local_mk(lo, attrs)?; + let stmt = self.recover_local_after_let(lo, attrs)?; self.struct_span_err(lo, "invalid variable declaration") .span_suggestion(lo, msg, sugg.to_string(), Applicability::MachineApplicable) .emit(); Ok(stmt) } - fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { - let local = self.parse_local(attrs)?; + fn parse_local_mk( + &mut self, + lo: Span, + attrs: AttrVec, + capture_semi: bool, + force_collect: ForceCollect, + ) -> PResult<'a, Stmt> { + maybe_collect_tokens!(self, force_collect, &attrs, |this: &mut Parser<'a>| { + this.expect_keyword(kw::Let)?; + let local = this.parse_local(attrs.into())?; + let trailing = if capture_semi && this.token.kind == token::Semi { + TrailingToken::Semi + } else { + TrailingToken::None + }; + Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), trailing)) + }) + } + + fn recover_local_after_let(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { + let local = self.parse_local(attrs.into())?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Local(local))) } @@ -292,7 +317,7 @@ impl<'a> Parser<'a> { // bar; // // which is valid in other languages, but not Rust. - match self.parse_stmt_without_recovery() { + match self.parse_stmt_without_recovery(false, ForceCollect::No) { // If the next token is an open brace (e.g., `if a b {`), the place- // inside-a-block suggestion would be more likely wrong than right. Ok(Some(_)) @@ -395,17 +420,11 @@ impl<'a> Parser<'a> { // Skip looking for a trailing semicolon when we have an interpolated statement. maybe_whole!(self, NtStmt, |x| Some(x)); - let mut stmt = match self.parse_stmt_without_recovery()? { + let mut stmt = match self.parse_stmt_without_recovery(true, ForceCollect::No)? { Some(stmt) => stmt, None => return Ok(None), }; - let add_semi_token = |tokens: Option<&mut LazyTokenStream>| { - if let Some(tokens) = tokens { - *tokens = tokens.add_trailing_semi(); - } - }; - let mut eat_semi = true; match stmt.kind { // Expression without semicolon. @@ -461,18 +480,12 @@ impl<'a> Parser<'a> { } } eat_semi = false; - // We just checked that there's a semicolon in the tokenstream, - // so capture it - add_semi_token(local.tokens.as_mut()); } StmtKind::Empty | StmtKind::Item(_) | StmtKind::Semi(_) => eat_semi = false, } if eat_semi && self.eat(&token::Semi) { stmt = stmt.add_trailing_semicolon(); - // We just checked that we have a semicolon in the tokenstream, - // so capture it - add_semi_token(stmt.tokens_mut()); } stmt.span = stmt.span.to(self.prev_token.span); Ok(Some(stmt)) |
