X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_parse%2Fsrc%2Fparser%2Fpath.rs;h=c15ef5cae674c823ce510b8fef96030d44c3b0d1;hb=21fdd549f63499a6f15160c22175cc9c3bbeb473;hp=207ecd00e0c0a3a7ae5b39e63c4a891a8f82b8e4;hpb=d12b8578163ac67e5d088550920f7cafd435f52b;p=rust.git diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 207ecd00e0c..c15ef5cae67 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -2,7 +2,7 @@ use super::{Parser, Restrictions, TokenType}; use crate::maybe_whole; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Token}; +use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocConstraint, AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, @@ -96,7 +96,7 @@ pub(super) fn parse_qpath(&mut self, style: PathStyle) -> PResult<'a, (QSelf, Pa /// ^ help: use double colon /// ``` fn recover_colon_before_qpath_proj(&mut self) -> bool { - if self.token.kind != token::Colon + if !self.check_noexpect(&TokenKind::Colon) || self.look_ahead(1, |t| !t.is_ident() || t.is_reserved_ident()) { return false; @@ -236,14 +236,14 @@ pub(super) fn parse_path_segment( token.kind, token::Lt | token::BinOp(token::Shl) - | token::OpenDelim(token::Paren) + | token::OpenDelim(Delimiter::Parenthesis) | token::LArrow ) }; let check_args_start = |this: &mut Self| { this.expected_tokens.extend_from_slice(&[ TokenType::Token(token::Lt), - TokenType::Token(token::OpenDelim(token::Paren)), + TokenType::Token(token::OpenDelim(Delimiter::Parenthesis)), ]); is_args_start(&this.token) }; @@ -478,7 +478,7 @@ pub(super) fn parse_angle_args( while let Some(arg) = self.parse_angle_arg(ty_generics)? { args.push(arg); if !self.eat(&token::Comma) { - if self.token.kind == token::Semi + if self.check_noexpect(&TokenKind::Semi) && self.look_ahead(1, |t| t.is_ident() || t.is_lifetime()) { // Add `>` to the list of expected tokens. @@ -517,11 +517,25 @@ fn parse_angle_arg( let arg = self.parse_generic_arg(ty_generics)?; match arg { Some(arg) => { - if self.check(&token::Colon) | self.check(&token::Eq) { - let (ident, gen_args) = match self.get_ident_from_generic_arg(arg) { + // we are using noexpect here because we first want to find out if either `=` or `:` + // is present and then use that info to push the other token onto the tokens list + let separated = + self.check_noexpect(&token::Colon) || self.check_noexpect(&token::Eq); + if separated && (self.check(&token::Colon) | self.check(&token::Eq)) { + let arg_span = arg.span(); + let (binder, 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))), + Err(()) => return Ok(Some(AngleBracketedArg::Arg(arg))), }; + if binder.is_some() { + // FIXME(compiler-errors): this could be improved by suggesting lifting + // this up to the trait, at least before this becomes real syntax. + // e.g. `Trait Assoc = Ty>` -> `for<'a> Trait` + return Err(self.struct_span_err( + arg_span, + "`for<...>` is not allowed on associated type bounds", + )); + } let kind = if self.eat(&token::Colon) { // Parse associated type constraint bound. @@ -543,6 +557,14 @@ fn parse_angle_arg( AssocConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span }; Ok(Some(AngleBracketedArg::Constraint(constraint))) } else { + // we only want to suggest `:` and `=` in contexts where the previous token + // is an ident and the current token or the next token is an ident + if self.prev_token.is_ident() + && (self.token.is_ident() || self.look_ahead(1, |token| token.is_ident())) + { + self.check(&token::Colon); + self.check(&token::Eq); + } Ok(Some(AngleBracketedArg::Arg(arg))) } } @@ -629,7 +651,7 @@ pub(super) fn expr_is_valid_const_arg(&self, expr: &P) -> bool /// the caller. pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> { // Parse const argument. - let value = if let token::OpenDelim(token::Brace) = self.token.kind { + let value = if let token::OpenDelim(Delimiter::Brace) = self.token.kind { self.parse_block_expr( None, self.token.span, @@ -657,7 +679,8 @@ pub(super) fn parse_generic_arg( GenericArg::Const(self.parse_const_arg()?) } else if self.check_type() { // Parse type argument. - let is_const_fn = self.look_ahead(1, |t| t.kind == token::OpenDelim(token::Paren)); + let is_const_fn = + self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)); let mut snapshot = self.create_snapshot_for_diagnostic(); match self.parse_ty() { Ok(ty) => GenericArg::Type(ty), @@ -700,18 +723,32 @@ pub(super) fn parse_generic_arg( Ok(Some(arg)) } + /// Given a arg inside of generics, we try to destructure it as if it were the LHS in + /// `LHS = ...`, i.e. an associated type binding. + /// This returns (optionally, if they are present) any `for<'a, 'b>` binder args, the + /// identifier, and any GAT arguments. fn get_ident_from_generic_arg( &self, - gen_arg: GenericArg, - ) -> Result<(Ident, Option), GenericArg> { - if let GenericArg::Type(ty) = &gen_arg - && let ast::TyKind::Path(qself, path) = &ty.kind - && qself.is_none() - && path.segments.len() == 1 - { - let seg = &path.segments[0]; - return Ok((seg.ident, seg.args.as_deref().cloned())); + gen_arg: &GenericArg, + ) -> Result<(Option>, Ident, Option), ()> { + if let GenericArg::Type(ty) = gen_arg { + if let ast::TyKind::Path(qself, path) = &ty.kind + && qself.is_none() + && let [seg] = path.segments.as_slice() + { + return Ok((None, seg.ident, seg.args.as_deref().cloned())); + } else if let ast::TyKind::TraitObject(bounds, ast::TraitObjectSyntax::None) = &ty.kind + && let [ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None)] = + bounds.as_slice() + && let [seg] = trait_ref.trait_ref.path.segments.as_slice() + { + return Ok(( + Some(trait_ref.bound_generic_params.clone()), + seg.ident, + seg.args.as_deref().cloned(), + )); + } } - Err(gen_arg) + Err(()) } }