From 70e5c3731961b5754bc5b155a75b2f7ff7fb997b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 17 Dec 2017 01:53:11 +0300 Subject: [PATCH] syntax: recovery for incorrect associated item paths like `[T; N]::clone` --- src/libsyntax/ast.rs | 94 +++++++++++++++++++ src/libsyntax/lib.rs | 1 + src/libsyntax/parse/parser.rs | 70 ++++++++++---- src/test/ui/did_you_mean/bad-assoc-expr.rs | 24 +++++ .../ui/did_you_mean/bad-assoc-expr.stderr | 26 +++++ src/test/ui/did_you_mean/bad-assoc-pat.rs | 23 +++++ src/test/ui/did_you_mean/bad-assoc-pat.stderr | 38 ++++++++ src/test/ui/did_you_mean/bad-assoc-ty.rs | 31 ++++++ src/test/ui/did_you_mean/bad-assoc-ty.stderr | 70 ++++++++++++++ 9 files changed, 357 insertions(+), 20 deletions(-) create mode 100644 src/test/ui/did_you_mean/bad-assoc-expr.rs create mode 100644 src/test/ui/did_you_mean/bad-assoc-expr.stderr create mode 100644 src/test/ui/did_you_mean/bad-assoc-pat.rs create mode 100644 src/test/ui/did_you_mean/bad-assoc-pat.stderr create mode 100644 src/test/ui/did_you_mean/bad-assoc-ty.rs create mode 100644 src/test/ui/did_you_mean/bad-assoc-ty.stderr diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0d289dbd46b..461cb0480d2 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -20,6 +20,7 @@ use syntax_pos::{Span, DUMMY_SP}; use codemap::{respan, Spanned}; use abi::Abi; use ext::hygiene::{Mark, SyntaxContext}; +use parse::parser::{RecoverQPath, PathStyle}; use print::pprust; use ptr::P; use rustc_data_structures::indexed_vec; @@ -519,6 +520,38 @@ impl Pat { } } +impl RecoverQPath for Pat { + fn to_ty(&self) -> Option> { + let node = match &self.node { + PatKind::Wild => TyKind::Infer, + PatKind::Ident(BindingMode::ByValue(Mutability::Immutable), ident, None) => + TyKind::Path(None, Path::from_ident(ident.span, ident.node)), + PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()), + PatKind::Mac(mac) => TyKind::Mac(mac.clone()), + PatKind::Ref(pat, mutbl) => + pat.to_ty().map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?, + PatKind::Slice(pats, None, _) if pats.len() == 1 => + pats[0].to_ty().map(TyKind::Slice)?, + PatKind::Tuple(pats, None) => { + let mut tys = Vec::new(); + for pat in pats { + tys.push(pat.to_ty()?); + } + TyKind::Tup(tys) + } + _ => return None, + }; + + Some(P(Ty { node, id: self.id, span: self.span })) + } + fn to_recovered(&self, qself: Option, path: Path) -> Self { + Self { span: path.span, node: PatKind::Path(qself, path), id: self.id } + } + fn to_string(&self) -> String { + pprust::pat_to_string(self) + } +} + /// A single field in a struct pattern /// /// Patterns like the fields of Foo `{ x, ref y, ref mut z }` @@ -877,6 +910,54 @@ impl Expr { true } } + + fn to_bound(&self) -> Option { + match &self.node { + ExprKind::Path(None, path) => + Some(TraitTyParamBound(PolyTraitRef::new(Vec::new(), path.clone(), self.span), + TraitBoundModifier::None)), + _ => None, + } + } +} + +impl RecoverQPath for Expr { + fn to_ty(&self) -> Option> { + let node = match &self.node { + ExprKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()), + ExprKind::Mac(mac) => TyKind::Mac(mac.clone()), + ExprKind::Paren(expr) => expr.to_ty().map(TyKind::Paren)?, + ExprKind::AddrOf(mutbl, expr) => + expr.to_ty().map(|ty| TyKind::Rptr(None, MutTy { ty, mutbl: *mutbl }))?, + ExprKind::Repeat(expr, expr_len) => + expr.to_ty().map(|ty| TyKind::Array(ty, expr_len.clone()))?, + ExprKind::Array(exprs) if exprs.len() == 1 => + exprs[0].to_ty().map(TyKind::Slice)?, + ExprKind::Tup(exprs) => { + let mut tys = Vec::new(); + for expr in exprs { + tys.push(expr.to_ty()?); + } + TyKind::Tup(tys) + } + ExprKind::Binary(binop, lhs, rhs) if binop.node == BinOpKind::Add => + if let (Some(lhs), Some(rhs)) = (lhs.to_bound(), rhs.to_bound()) { + TyKind::TraitObject(vec![lhs, rhs], TraitObjectSyntax::None) + } else { + return None; + } + _ => return None, + }; + + Some(P(Ty { node, id: self.id, span: self.span })) + } + fn to_recovered(&self, qself: Option, path: Path) -> Self { + Self { span: path.span, node: ExprKind::Path(qself, path), + id: self.id, attrs: self.attrs.clone() } + } + fn to_string(&self) -> String { + pprust::expr_to_string(self) + } } impl fmt::Debug for Expr { @@ -1388,6 +1469,19 @@ pub struct Ty { pub span: Span, } +impl RecoverQPath for Ty { + fn to_ty(&self) -> Option> { + Some(P(self.clone())) + } + fn to_recovered(&self, qself: Option, path: Path) -> Self { + Self { span: path.span, node: TyKind::Path(qself, path), id: self.id } + } + fn to_string(&self) -> String { + pprust::ty_to_string(self) + } + const PATH_STYLE: PathStyle = PathStyle::Type; +} + impl fmt::Debug for Ty { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "type({})", pprust::ty_to_string(self)) diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 9e4f134e2bd..44383233a8a 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -22,6 +22,7 @@ #![feature(unicode)] #![feature(rustc_diagnostic_macros)] +#![feature(match_default_bindings)] #![feature(i128_type)] // See librustc_cratesio_shim/Cargo.toml for a comment explaining this. diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 09a65046e20..c3dd17e8775 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -169,6 +169,13 @@ enum PrevTokenKind { Other, } +pub(crate) trait RecoverQPath: Sized { + fn to_ty(&self) -> Option>; + fn to_recovered(&self, qself: Option, path: ast::Path) -> Self; + fn to_string(&self) -> String; + const PATH_STYLE: PathStyle = PathStyle::Expr; +} + /* ident is handled by common.rs */ #[derive(Clone)] @@ -1567,6 +1574,7 @@ impl<'a> Parser<'a> { // Try to recover from use of `+` with incorrect priority. self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?; + let ty = self.maybe_recover_from_bad_qpath(ty)?; Ok(P(ty)) } @@ -1621,6 +1629,32 @@ impl<'a> Parser<'a> { Ok(()) } + // Try to recover from associated item paths like `[T]::AssocItem`/`(T, U)::AssocItem`. + fn maybe_recover_from_bad_qpath(&mut self, base: T) -> PResult<'a, T> { + // Do not add `::` to expected tokens. + if self.token != token::ModSep { + return Ok(base); + } + let ty = match base.to_ty() { + Some(ty) => ty, + None => return Ok(base), + }; + + self.bump(); // `::` + let mut segments = Vec::new(); + 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 }); + + self.diagnostic() + .struct_span_err(span, "missing angle brackets in associated item path") + .span_suggestion(span, "try", recovered.to_string()).emit(); + + Ok(recovered) + } + fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None }; let mutbl = self.parse_mutability(); @@ -2012,12 +2046,7 @@ impl<'a> Parser<'a> { } pub fn mk_expr(&mut self, span: Span, node: ExprKind, attrs: ThinVec) -> P { - P(Expr { - id: ast::DUMMY_NODE_ID, - node, - span, - attrs: attrs.into(), - }) + P(Expr { node, span, attrs, id: ast::DUMMY_NODE_ID }) } pub fn mk_unary(&mut self, unop: ast::UnOp, expr: P) -> ast::ExprKind { @@ -2139,12 +2168,11 @@ impl<'a> Parser<'a> { self.bump(); hi = self.prev_span; - let span = lo.to(hi); - return if es.len() == 1 && !trailing_comma { - Ok(self.mk_expr(span, ExprKind::Paren(es.into_iter().nth(0).unwrap()), attrs)) + ex = if es.len() == 1 && !trailing_comma { + ExprKind::Paren(es.into_iter().nth(0).unwrap()) } else { - Ok(self.mk_expr(span, ExprKind::Tup(es), attrs)) - } + ExprKind::Tup(es) + }; } token::OpenDelim(token::Brace) => { return self.parse_block_expr(lo, BlockCheckMode::Default, attrs); @@ -2344,7 +2372,10 @@ impl<'a> Parser<'a> { } } - return Ok(self.mk_expr(lo.to(hi), ex, attrs)); + let expr = Expr { node: ex, span: lo.to(hi), id: ast::DUMMY_NODE_ID, attrs }; + let expr = self.maybe_recover_from_bad_qpath(expr)?; + + return Ok(P(expr)); } fn parse_struct_expr(&mut self, lo: Span, pth: ast::Path, mut attrs: ThinVec) @@ -3405,7 +3436,7 @@ impl<'a> Parser<'a> { if self.check(&token::Comma) || self.check(&token::CloseDelim(token::Bracket)) { - slice = Some(P(ast::Pat { + slice = Some(P(Pat { id: ast::DUMMY_NODE_ID, node: PatKind::Wild, span: self.span, @@ -3492,14 +3523,14 @@ impl<'a> Parser<'a> { (false, false) => BindingMode::ByValue(Mutability::Immutable), }; let fieldpath = codemap::Spanned{span:self.prev_span, node:fieldname}; - let fieldpat = P(ast::Pat{ + let fieldpat = P(Pat { id: ast::DUMMY_NODE_ID, node: PatKind::Ident(bind_type, fieldpath, None), span: boxed_span.to(hi), }); let subpat = if is_box { - P(ast::Pat{ + P(Pat { id: ast::DUMMY_NODE_ID, node: PatKind::Box(fieldpat), span: lo.to(hi), @@ -3708,11 +3739,10 @@ impl<'a> Parser<'a> { } } - Ok(P(ast::Pat { - id: ast::DUMMY_NODE_ID, - node: pat, - span: lo.to(self.prev_span), - })) + let pat = Pat { node: pat, span: lo.to(self.prev_span), id: ast::DUMMY_NODE_ID }; + let pat = self.maybe_recover_from_bad_qpath(pat)?; + + Ok(P(pat)) } /// Parse ident or ident @ pat diff --git a/src/test/ui/did_you_mean/bad-assoc-expr.rs b/src/test/ui/did_you_mean/bad-assoc-expr.rs new file mode 100644 index 00000000000..72b616ddd69 --- /dev/null +++ b/src/test/ui/did_you_mean/bad-assoc-expr.rs @@ -0,0 +1,24 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let a = [1, 2, 3, 4]; + [i32; 4]::clone(&a); + //~^ ERROR missing angle brackets in associated item path + + [i32]::as_ref(&a); + //~^ ERROR missing angle brackets in associated item path + + (u8)::clone(&0); + //~^ ERROR missing angle brackets in associated item path + + (u8, u8)::clone(&(0, 0)); + //~^ ERROR missing angle brackets in associated item path +} diff --git a/src/test/ui/did_you_mean/bad-assoc-expr.stderr b/src/test/ui/did_you_mean/bad-assoc-expr.stderr new file mode 100644 index 00000000000..1f8fc118f78 --- /dev/null +++ b/src/test/ui/did_you_mean/bad-assoc-expr.stderr @@ -0,0 +1,26 @@ +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-expr.rs:13:5 + | +13 | [i32; 4]::clone(&a); + | ^^^^^^^^^^^^^^^ help: try: `<[i32; 4]>::clone` + +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-expr.rs:16:5 + | +16 | [i32]::as_ref(&a); + | ^^^^^^^^^^^^^ help: try: `<[i32]>::as_ref` + +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-expr.rs:19:5 + | +19 | (u8)::clone(&0); + | ^^^^^^^^^^^ help: try: `<(u8)>::clone` + +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-expr.rs:22:5 + | +22 | (u8, u8)::clone(&(0, 0)); + | ^^^^^^^^^^^^^^^ help: try: `<(u8, u8)>::clone` + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/did_you_mean/bad-assoc-pat.rs b/src/test/ui/did_you_mean/bad-assoc-pat.rs new file mode 100644 index 00000000000..e6b7127f100 --- /dev/null +++ b/src/test/ui/did_you_mean/bad-assoc-pat.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + match 0u8 { + [u8]::AssocItem => {} + //~^ ERROR missing angle brackets in associated item path + //~| ERROR no associated item named `AssocItem` found for type `[u8]` in the current scope + (u8, u8)::AssocItem => {} + //~^ ERROR missing angle brackets in associated item path + //~| ERROR no associated item named `AssocItem` found for type `(u8, u8)` in the current sco + _::AssocItem => {} + //~^ ERROR missing angle brackets in associated item path + //~| ERROR no associated item named `AssocItem` found for type `_` in the current scope + } +} diff --git a/src/test/ui/did_you_mean/bad-assoc-pat.stderr b/src/test/ui/did_you_mean/bad-assoc-pat.stderr new file mode 100644 index 00000000000..20f9b96dbaa --- /dev/null +++ b/src/test/ui/did_you_mean/bad-assoc-pat.stderr @@ -0,0 +1,38 @@ +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-pat.rs:13:9 + | +13 | [u8]::AssocItem => {} + | ^^^^^^^^^^^^^^^ help: try: `<[u8]>::AssocItem` + +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-pat.rs:16:9 + | +16 | (u8, u8)::AssocItem => {} + | ^^^^^^^^^^^^^^^^^^^ help: try: `<(u8, u8)>::AssocItem` + +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-pat.rs:19:9 + | +19 | _::AssocItem => {} + | ^^^^^^^^^^^^ help: try: `<_>::AssocItem` + +error[E0599]: no associated item named `AssocItem` found for type `[u8]` in the current scope + --> $DIR/bad-assoc-pat.rs:13:9 + | +13 | [u8]::AssocItem => {} + | ^^^^^^^^^^^^^^^ associated item not found in `[u8]` + +error[E0599]: no associated item named `AssocItem` found for type `(u8, u8)` in the current scope + --> $DIR/bad-assoc-pat.rs:16:9 + | +16 | (u8, u8)::AssocItem => {} + | ^^^^^^^^^^^^^^^^^^^ associated item not found in `(u8, u8)` + +error[E0599]: no associated item named `AssocItem` found for type `_` in the current scope + --> $DIR/bad-assoc-pat.rs:19:9 + | +19 | _::AssocItem => {} + | ^^^^^^^^^^^^ associated item not found in `_` + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/did_you_mean/bad-assoc-ty.rs b/src/test/ui/did_you_mean/bad-assoc-ty.rs new file mode 100644 index 00000000000..45a52936738 --- /dev/null +++ b/src/test/ui/did_you_mean/bad-assoc-ty.rs @@ -0,0 +1,31 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +type A = [u8; 4]::AssocTy; +//~^ ERROR missing angle brackets in associated item path +//~| ERROR ambiguous associated type + +type B = [u8]::AssocTy; +//~^ ERROR missing angle brackets in associated item path +//~| ERROR ambiguous associated type + +type C = (u8)::AssocTy; +//~^ ERROR missing angle brackets in associated item path +//~| ERROR ambiguous associated type + +type D = (u8, u8)::AssocTy; +//~^ ERROR missing angle brackets in associated item path +//~| ERROR ambiguous associated type + +type E = _::AssocTy; +//~^ ERROR missing angle brackets in associated item path +//~| ERROR the type placeholder `_` is not allowed within types on item signatures + +fn main() {} diff --git a/src/test/ui/did_you_mean/bad-assoc-ty.stderr b/src/test/ui/did_you_mean/bad-assoc-ty.stderr new file mode 100644 index 00000000000..617339a7d92 --- /dev/null +++ b/src/test/ui/did_you_mean/bad-assoc-ty.stderr @@ -0,0 +1,70 @@ +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-ty.rs:11:10 + | +11 | type A = [u8; 4]::AssocTy; + | ^^^^^^^^^^^^^^^^ help: try: `<[u8; 4]>::AssocTy` + +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-ty.rs:15:10 + | +15 | type B = [u8]::AssocTy; + | ^^^^^^^^^^^^^ help: try: `<[u8]>::AssocTy` + +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-ty.rs:19:10 + | +19 | type C = (u8)::AssocTy; + | ^^^^^^^^^^^^^ help: try: `<(u8)>::AssocTy` + +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-ty.rs:23:10 + | +23 | type D = (u8, u8)::AssocTy; + | ^^^^^^^^^^^^^^^^^ help: try: `<(u8, u8)>::AssocTy` + +error: missing angle brackets in associated item path + --> $DIR/bad-assoc-ty.rs:27:10 + | +27 | type E = _::AssocTy; + | ^^^^^^^^^^ help: try: `<_>::AssocTy` + +error[E0223]: ambiguous associated type + --> $DIR/bad-assoc-ty.rs:11:10 + | +11 | type A = [u8; 4]::AssocTy; + | ^^^^^^^^^^^^^^^^ ambiguous associated type + | + = note: specify the type using the syntax `<[u8; ] as Trait>::AssocTy` + +error[E0223]: ambiguous associated type + --> $DIR/bad-assoc-ty.rs:15:10 + | +15 | type B = [u8]::AssocTy; + | ^^^^^^^^^^^^^ ambiguous associated type + | + = note: specify the type using the syntax `<[u8] as Trait>::AssocTy` + +error[E0223]: ambiguous associated type + --> $DIR/bad-assoc-ty.rs:19:10 + | +19 | type C = (u8)::AssocTy; + | ^^^^^^^^^^^^^ ambiguous associated type + | + = note: specify the type using the syntax `::AssocTy` + +error[E0223]: ambiguous associated type + --> $DIR/bad-assoc-ty.rs:23:10 + | +23 | type D = (u8, u8)::AssocTy; + | ^^^^^^^^^^^^^^^^^ ambiguous associated type + | + = note: specify the type using the syntax `<(u8, u8) as Trait>::AssocTy` + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/bad-assoc-ty.rs:27:10 + | +27 | type E = _::AssocTy; + | ^ not allowed in type signatures + +error: aborting due to 10 previous errors +