diff options
| author | bors <bors@rust-lang.org> | 2023-07-30 05:52:12 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-07-30 05:52:12 +0000 |
| commit | 2183cdac191e8e6c2663db055c8ed529ec60052c (patch) | |
| tree | 19ce6a78de546337bb7bc803452b52f6ef99090b /compiler/rustc_parse/src/parser | |
| parent | 70757fbdd76565563a2311db393459899a38b40f (diff) | |
| parent | a4bfcbe4d3f2a1080a70c15c1f681b9f341c882e (diff) | |
| download | rust-2183cdac191e8e6c2663db055c8ed529ec60052c.tar.gz rust-2183cdac191e8e6c2663db055c8ed529ec60052c.zip | |
Auto merge of #2996 - rust-lang:rustup-2023-07-30, r=oli-obk
Automatic sync from rustc
Diffstat (limited to 'compiler/rustc_parse/src/parser')
| -rw-r--r-- | compiler/rustc_parse/src/parser/attr_wrapper.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 53 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 148 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/mod.rs | 46 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/nonterminal.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/pat.rs | 68 |
7 files changed, 252 insertions, 71 deletions
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index b579da098d8..4cc03664b47 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -107,7 +107,7 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { let tokens = std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1)) .chain((0..self.num_calls).map(|_| { - let token = cursor_snapshot.next(cursor_snapshot.desugar_doc_comments); + let token = cursor_snapshot.next(); (FlatToken::Token(token.0), token.1) })) .take(self.num_calls); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index c3cf6437afa..00ffa7de2ff 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -14,7 +14,7 @@ use crate::errors::{ PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, - UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, + TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, }; @@ -500,6 +500,10 @@ impl<'a> Parser<'a> { // Special-case "expected `;`" errors if expected.contains(&TokenType::Token(token::Semi)) { + if self.prev_token == token::Question && self.maybe_recover_from_ternary_operator() { + return Ok(true); + } + if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { // Likely inside a macro, can't provide meaningful suggestions. } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { @@ -1330,6 +1334,45 @@ impl<'a> Parser<'a> { } } + /// Rust has no ternary operator (`cond ? then : else`). Parse it and try + /// to recover from it if `then` and `else` are valid expressions. Returns + /// whether it was a ternary operator. + pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> bool { + if self.prev_token != token::Question { + return false; + } + + let lo = self.prev_token.span.lo(); + let snapshot = self.create_snapshot_for_diagnostic(); + + if match self.parse_expr() { + Ok(_) => true, + Err(err) => { + err.cancel(); + // The colon can sometimes be mistaken for type + // ascription. Catch when this happens and continue. + self.token == token::Colon + } + } { + if self.eat_noexpect(&token::Colon) { + match self.parse_expr() { + Ok(_) => { + self.sess.emit_err(TernaryOperator { span: self.token.span.with_lo(lo) }); + return true; + } + Err(err) => { + err.cancel(); + self.restore_snapshot(snapshot); + } + }; + } + } else { + self.restore_snapshot(snapshot); + }; + + false + } + pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> { // Do not add `+` to expected tokens. if !self.token.is_like_plus() { @@ -2057,7 +2100,7 @@ impl<'a> Parser<'a> { } pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> { - let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName))?; + let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?; self.expect(&token::Colon)?; let ty = self.parse_ty()?; @@ -2111,7 +2154,7 @@ impl<'a> Parser<'a> { } _ => ( self.token.span, - format!("expected expression, found {}", super::token_descr(&self.token),), + format!("expected expression, found {}", super::token_descr(&self.token)), ), }; let mut err = self.struct_span_err(span, msg); @@ -2465,7 +2508,7 @@ impl<'a> Parser<'a> { // Skip the `:`. snapshot_pat.bump(); snapshot_type.bump(); - match snapshot_pat.parse_pat_no_top_alt(expected) { + match snapshot_pat.parse_pat_no_top_alt(expected, None) { Err(inner_err) => { inner_err.cancel(); } @@ -2729,7 +2772,7 @@ impl<'a> Parser<'a> { /// sequence of patterns until `)` is reached. fn skip_pat_list(&mut self) -> PResult<'a, ()> { while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) { - self.parse_pat_no_top_alt(None)?; + self.parse_pat_no_top_alt(None, None)?; if !self.eat(&token::Comma) { return Ok(()); } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b54cb8c5a0c..0e19a67a841 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2338,7 +2338,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { - let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName))?; + let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?; let ty = if this.eat(&token::Colon) { this.parse_ty()? } else { @@ -2781,7 +2781,7 @@ impl<'a> Parser<'a> { return None; } let pre_pat_snapshot = self.create_snapshot_for_diagnostic(); - match self.parse_pat_no_top_alt(None) { + match self.parse_pat_no_top_alt(None, None) { Ok(_pat) => { if self.token.kind == token::FatArrow { // Reached arm end. diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1470180dea7..1301ed3e388 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -226,9 +226,9 @@ impl<'a> Parser<'a> { } else if self.is_static_global() { // STATIC ITEM self.bump(); // `static` - let m = self.parse_mutability(); - let (ident, ty, expr) = self.parse_item_global(Some(m))?; - (ident, ItemKind::Static(Box::new(StaticItem { ty, mutability: m, expr }))) + let mutability = self.parse_mutability(); + let (ident, item) = self.parse_static_item(mutability)?; + (ident, ItemKind::Static(Box::new(item))) } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM if self.token.is_keyword(kw::Impl) { @@ -236,8 +236,16 @@ impl<'a> Parser<'a> { self.recover_const_impl(const_span, attrs, def_())? } else { self.recover_const_mut(const_span); - let (ident, ty, expr) = self.parse_item_global(None)?; - (ident, ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ty, expr }))) + let (ident, generics, ty, expr) = self.parse_const_item()?; + ( + ident, + ItemKind::Const(Box::new(ConstItem { + defaultness: def_(), + generics, + ty, + expr, + })), + ) } } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { // TRAIT ITEM @@ -878,6 +886,7 @@ impl<'a> Parser<'a> { self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Box::new(ConstItem { defaultness: Defaultness::Final, + generics: Generics::default(), ty, expr, })) @@ -892,7 +901,7 @@ impl<'a> Parser<'a> { /// Parses a `type` alias with the following grammar: /// ```ebnf - /// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ; + /// TypeAlias = "type" Ident Generics (":" GenericBounds)? WhereClause ("=" Ty)? WhereClause ";" ; /// ``` /// The `"type"` has already been eaten. fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> { @@ -1220,33 +1229,132 @@ impl<'a> Parser<'a> { Ok(impl_info) } - /// Parse `["const" | ("static" "mut"?)] $ident ":" $ty (= $expr)?` with - /// `["const" | ("static" "mut"?)]` already parsed and stored in `m`. + /// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in `mutability`. /// - /// When `m` is `"const"`, `$ident` may also be `"_"`. - fn parse_item_global( - &mut self, - m: Option<Mutability>, - ) -> PResult<'a, (Ident, P<Ty>, Option<P<ast::Expr>>)> { - let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?; + /// ```ebnf + /// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ; + /// ``` + fn parse_static_item(&mut self, mutability: Mutability) -> PResult<'a, (Ident, StaticItem)> { + let ident = self.parse_ident()?; + + if self.token.kind == TokenKind::Lt && self.may_recover() { + let generics = self.parse_generics()?; + self.sess.emit_err(errors::StaticWithGenerics { span: generics.span }); + } - // Parse the type of a `const` or `static mut?` item. - // That is, the `":" $ty` fragment. + // Parse the type of a static item. That is, the `":" $ty` fragment. + // FIXME: This could maybe benefit from `.may_recover()`? let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi)) { - // If there wasn't a `:` or the colon was followed by a `=` or `;` recover a missing type. (true, false) => self.parse_ty()?, - (colon, _) => self.recover_missing_const_type(colon, m), + // If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing type. + (colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)), + }; + + let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None }; + + self.expect_semi()?; + + Ok((ident, StaticItem { ty, mutability, expr })) + } + + /// Parse a constant item with the prefix `"const"` already parsed. + /// + /// ```ebnf + /// Const = "const" ($ident | "_") Generics ":" $ty (= $expr)? WhereClause ";" ; + /// ``` + fn parse_const_item(&mut self) -> PResult<'a, (Ident, Generics, P<Ty>, Option<P<ast::Expr>>)> { + let ident = self.parse_ident_or_underscore()?; + + let mut generics = self.parse_generics()?; + + // Check the span for emptiness instead of the list of parameters in order to correctly + // recognize and subsequently flag empty parameter lists (`<>`) as unstable. + if !generics.span.is_empty() { + self.sess.gated_spans.gate(sym::generic_const_items, generics.span); + } + + // Parse the type of a constant item. That is, the `":" $ty` fragment. + // FIXME: This could maybe benefit from `.may_recover()`? + let ty = match ( + self.eat(&token::Colon), + self.check(&token::Eq) | self.check(&token::Semi) | self.check_keyword(kw::Where), + ) { + (true, false) => self.parse_ty()?, + // If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type. + (colon, _) => self.recover_missing_global_item_type(colon, None), }; + // Proactively parse a where-clause to be able to provide a good error message in case we + // encounter the item body following it. + let before_where_clause = + if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() }; + let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None }; + + let after_where_clause = self.parse_where_clause()?; + + // Provide a nice error message if the user placed a where-clause before the item body. + // Users may be tempted to write such code if they are still used to the deprecated + // where-clause location on type aliases and associated types. See also #89122. + if before_where_clause.has_where_token && let Some(expr) = &expr { + self.sess.emit_err(errors::WhereClauseBeforeConstBody { + span: before_where_clause.span, + name: ident.span, + body: expr.span, + sugg: if !after_where_clause.has_where_token { + self.sess.source_map().span_to_snippet(expr.span).ok().map(|body| { + errors::WhereClauseBeforeConstBodySugg { + left: before_where_clause.span.shrink_to_lo(), + snippet: body, + right: before_where_clause.span.shrink_to_hi().to(expr.span), + } + }) + } else { + // FIXME(generic_const_items): Provide a structured suggestion to merge the first + // where-clause into the second one. + None + }, + }); + } + + // Merge the predicates of both where-clauses since either one can be relevant. + // If we didn't parse a body (which is valid for associated consts in traits) and we were + // allowed to recover, `before_where_clause` contains the predicates, otherwise they are + // in `after_where_clause`. Further, both of them might contain predicates iff two + // where-clauses were provided which is syntactically ill-formed but we want to recover from + // it and treat them as one large where-clause. + let mut predicates = before_where_clause.predicates; + predicates.extend(after_where_clause.predicates); + let where_clause = WhereClause { + has_where_token: before_where_clause.has_where_token + || after_where_clause.has_where_token, + predicates, + span: if after_where_clause.has_where_token { + after_where_clause.span + } else { + before_where_clause.span + }, + }; + + if where_clause.has_where_token { + self.sess.gated_spans.gate(sym::generic_const_items, where_clause.span); + } + + generics.where_clause = where_clause; + self.expect_semi()?; - Ok((id, ty, expr)) + + Ok((ident, generics, ty, expr)) } /// We were supposed to parse `":" $ty` but the `:` or the type was missing. /// This means that the type is missing. - fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutability>) -> P<Ty> { + fn recover_missing_global_item_type( + &mut self, + colon_present: bool, + m: Option<Mutability>, + ) -> P<Ty> { // Construct the error and stash it away with the hope // that typeck will later enrich the error with a type. let kind = match m { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 2e1a61e634e..37b4c371c94 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -138,7 +138,6 @@ pub struct Parser<'a> { // Important: This must only be advanced from `bump` to ensure that // `token_cursor.num_next_calls` is updated properly. token_cursor: TokenCursor, - desugar_doc_comments: bool, /// This field is used to keep track of how many left angle brackets we have seen. This is /// required in order to detect extra leading left angle brackets (`<` characters) and error /// appropriately. @@ -225,6 +224,9 @@ struct TokenCursor { // because it's the outermost token stream which never has delimiters. stack: Vec<(TokenTreeCursor, Delimiter, DelimSpan)>, + // We need to desugar doc comments from `/// foo` form into `#[doc = + // r"foo"]` form when parsing declarative macro inputs in `parse_tt`, + // because some declarative macros look for `doc` attributes. desugar_doc_comments: bool, // Counts the number of calls to `{,inlined_}next`. @@ -255,33 +257,38 @@ struct TokenCursor { } impl TokenCursor { - fn next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) { - self.inlined_next(desugar_doc_comments) + fn next(&mut self) -> (Token, Spacing) { + self.inlined_next() } /// This always-inlined version should only be used on hot code paths. #[inline(always)] - fn inlined_next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) { + fn inlined_next(&mut self) -> (Token, Spacing) { loop { // FIXME: we currently don't return `Delimiter` open/close delims. To fix #67062 we will // need to, whereupon the `delim != Delimiter::Invisible` conditions below can be // removed. if let Some(tree) = self.tree_cursor.next_ref() { match tree { - &TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) { - (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => { - let desugared = self.desugar(attr_style, data, span); - self.tree_cursor.replace_prev_and_rewind(desugared); - // Continue to get the first token of the desugared doc comment. - } - _ => { - debug_assert!(!matches!( - token.kind, - token::OpenDelim(_) | token::CloseDelim(_) - )); - return (token.clone(), spacing); + &TokenTree::Token(ref token, spacing) => { + match (self.desugar_doc_comments, token) { + ( + true, + &Token { kind: token::DocComment(_, attr_style, data), span }, + ) => { + let desugared = self.desugar(attr_style, data, span); + self.tree_cursor.replace_prev_and_rewind(desugared); + // Continue to get the first token of the desugared doc comment. + } + _ => { + debug_assert!(!matches!( + token.kind, + token::OpenDelim(_) | token::CloseDelim(_) + )); + return (token.clone(), spacing); + } } - }, + } &TokenTree::Delimited(sp, delim, ref tts) => { let trees = tts.clone().into_trees(); self.stack.push((mem::replace(&mut self.tree_cursor, trees), delim, sp)); @@ -463,7 +470,6 @@ impl<'a> Parser<'a> { desugar_doc_comments, break_last_token: false, }, - desugar_doc_comments, unmatched_angle_bracket_count: 0, max_angle_bracket_count: 0, last_unexpected_token_span: None, @@ -1107,7 +1113,7 @@ impl<'a> Parser<'a> { pub fn bump(&mut self) { // Note: destructuring here would give nicer code, but it was found in #96210 to be slower // than `.0`/`.1` access. - let mut next = self.token_cursor.inlined_next(self.desugar_doc_comments); + let mut next = self.token_cursor.inlined_next(); self.token_cursor.num_next_calls += 1; // We've retrieved an token from the underlying // cursor, so we no longer need to worry about @@ -1157,7 +1163,7 @@ impl<'a> Parser<'a> { let mut i = 0; let mut token = Token::dummy(); while i < dist { - token = cursor.next(/* desugar_doc_comments */ false).0; + token = cursor.next().0; if matches!( token.kind, token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index adb0d372a40..f5681532b3a 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -131,7 +131,7 @@ impl<'a> Parser<'a> { }, NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => { token::NtPat(self.collect_tokens_no_attrs(|this| match kind { - NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None), + NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None), NonterminalKind::PatWithOr { .. } => this.parse_pat_allow_top_alt( None, RecoverComma::No, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 14891c45d81..8d68a3a50ac 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -2,10 +2,11 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken}; use crate::errors::{ self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, - ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, - InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect, - RemoveLet, RepeatedMutInPattern, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, + ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax, + InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, + PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern, + TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, + UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, }; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -80,7 +81,8 @@ enum EatOrResult { } /// The syntax location of a given pattern. Used for diagnostics. -pub(super) enum PatternLocation { +#[derive(Clone, Copy)] +pub enum PatternLocation { LetBinding, FunctionParameter, } @@ -91,8 +93,12 @@ impl<'a> Parser<'a> { /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns /// at the top level. Used when parsing the parameters of lambda expressions, /// functions, function pointers, and `pat` macro fragments. - pub fn parse_pat_no_top_alt(&mut self, expected: Option<Expected>) -> PResult<'a, P<Pat>> { - self.parse_pat_with_range_pat(true, expected) + pub fn parse_pat_no_top_alt( + &mut self, + expected: Option<Expected>, + syntax_loc: Option<PatternLocation>, + ) -> PResult<'a, P<Pat>> { + self.parse_pat_with_range_pat(true, expected, syntax_loc) } /// Parses a pattern. @@ -110,7 +116,7 @@ impl<'a> Parser<'a> { ra: RecoverColon, rt: CommaRecoveryMode, ) -> PResult<'a, P<Pat>> { - self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt).map(|(pat, _)| pat) + self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = @@ -121,6 +127,7 @@ impl<'a> Parser<'a> { rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, + syntax_loc: Option<PatternLocation>, ) -> PResult<'a, (P<Pat>, bool)> { // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated // suggestions (which bothers rustfix). @@ -133,7 +140,7 @@ impl<'a> Parser<'a> { }; // Parse the first pattern (`p_0`). - let mut first_pat = self.parse_pat_no_top_alt(expected)?; + let mut first_pat = self.parse_pat_no_top_alt(expected, syntax_loc)?; if rc == RecoverComma::Yes { self.maybe_recover_unexpected_comma(first_pat.span, rt)?; } @@ -172,7 +179,7 @@ impl<'a> Parser<'a> { break; } } - let pat = self.parse_pat_no_top_alt(expected).map_err(|mut err| { + let pat = self.parse_pat_no_top_alt(expected, syntax_loc).map_err(|mut err| { err.span_label(lo, WHILE_PARSING_OR_MSG); err })?; @@ -208,6 +215,7 @@ impl<'a> Parser<'a> { rc, RecoverColon::No, CommaRecoveryMode::LikelyTuple, + Some(syntax_loc), )?; let colon = self.eat(&token::Colon); @@ -319,6 +327,7 @@ impl<'a> Parser<'a> { &mut self, allow_range_pat: bool, expected: Option<Expected>, + syntax_loc: Option<PatternLocation>, ) -> PResult<'a, P<Pat>> { maybe_recover_from_interpolated_ty_qpath!(self, true); maybe_whole!(self, NtPat, |x| x); @@ -358,11 +367,11 @@ impl<'a> Parser<'a> { // Parse _ PatKind::Wild } else if self.eat_keyword(kw::Mut) { - self.parse_pat_ident_mut()? + self.parse_pat_ident_mut(syntax_loc)? } else if self.eat_keyword(kw::Ref) { // Parse ref ident @ pat / ref mut ident @ pat let mutbl = self.parse_mutability(); - self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl))? + self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl), syntax_loc)? } else if self.eat_keyword(kw::Box) { self.parse_pat_box()? } else if self.check_inline_const(0) { @@ -384,7 +393,7 @@ impl<'a> Parser<'a> { // Parse `ident @ pat` // This can give false positives and parse nullary enums, // they are dealt with later in resolve. - self.parse_pat_ident(BindingAnnotation::NONE)? + self.parse_pat_ident(BindingAnnotation::NONE, syntax_loc)? } else if self.is_start_of_pat_with_path() { // Parse pattern starting with a path let (qself, path) = if self.eat_lt() { @@ -485,7 +494,7 @@ impl<'a> Parser<'a> { // At this point we attempt to parse `@ $pat_rhs` and emit an error. self.bump(); // `@` - let mut rhs = self.parse_pat_no_top_alt(None)?; + let mut rhs = self.parse_pat_no_top_alt(None, None)?; let whole_span = lhs.span.to(rhs.span); if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind { @@ -541,7 +550,7 @@ impl<'a> Parser<'a> { } let mutbl = self.parse_mutability(); - let subpat = self.parse_pat_with_range_pat(false, expected)?; + let subpat = self.parse_pat_with_range_pat(false, expected, None)?; Ok(PatKind::Ref(subpat, mutbl)) } @@ -566,12 +575,12 @@ impl<'a> Parser<'a> { } /// Parse a mutable binding with the `mut` token already eaten. - fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> { + fn parse_pat_ident_mut(&mut self, syntax_loc: Option<PatternLocation>) -> PResult<'a, PatKind> { let mut_span = self.prev_token.span; if self.eat_keyword(kw::Ref) { self.sess.emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) }); - return self.parse_pat_ident(BindingAnnotation::REF_MUT); + return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc); } self.recover_additional_muts(); @@ -584,7 +593,7 @@ impl<'a> Parser<'a> { } // Parse the pattern we hope to be an identifier. - let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier))?; + let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?; // If we don't have `mut $ident (@ pat)?`, error. if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind @@ -810,10 +819,25 @@ impl<'a> Parser<'a> { /// Parses `ident` or `ident @ pat`. /// Used by the copy foo and ref foo patterns to give a good /// error message when parsing mistakes like `ref foo(a, b)`. - fn parse_pat_ident(&mut self, binding_annotation: BindingAnnotation) -> PResult<'a, PatKind> { + fn parse_pat_ident( + &mut self, + binding_annotation: BindingAnnotation, + syntax_loc: Option<PatternLocation>, + ) -> PResult<'a, PatKind> { let ident = self.parse_ident()?; + + if !matches!(syntax_loc, Some(PatternLocation::FunctionParameter)) + && self.check_noexpect(&token::Lt) + && self.look_ahead(1, |t| t.can_begin_type()) + { + return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax { + span: self.token.span, + suggest_turbofish: self.token.span.shrink_to_lo(), + })); + } + let sub = if self.eat(&token::At) { - Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) + Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { None }; @@ -902,14 +926,14 @@ impl<'a> Parser<'a> { // We cannot use `parse_pat_ident()` since it will complain `box` // is not an identifier. let sub = if self.eat(&token::At) { - Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) + Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { None }; Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub)) } else { - let pat = self.parse_pat_with_range_pat(false, None)?; + let pat = self.parse_pat_with_range_pat(false, None, None)?; self.sess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span)); Ok(PatKind::Box(pat)) } |
