about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2017-12-17 01:53:11 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2017-12-17 19:00:50 +0300
commit70e5c3731961b5754bc5b155a75b2f7ff7fb997b (patch)
tree0ec785acdd6d1c3ffe94041151b4514424d15e43 /src/libsyntax
parentaf57acef1cd1651861be0bfe77b4f4dd3066ce02 (diff)
downloadrust-70e5c3731961b5754bc5b155a75b2f7ff7fb997b.tar.gz
rust-70e5c3731961b5754bc5b155a75b2f7ff7fb997b.zip
syntax: recovery for incorrect associated item paths like `[T; N]::clone`
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs94
-rw-r--r--src/libsyntax/lib.rs1
-rw-r--r--src/libsyntax/parse/parser.rs70
3 files changed, 145 insertions, 20 deletions
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<P<Ty>> {
+        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<QSelf>, 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<TyParamBound> {
+        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<P<Ty>> {
+        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<QSelf>, 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<P<Ty>> {
+        Some(P(self.clone()))
+    }
+    fn to_recovered(&self, qself: Option<QSelf>, 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<P<Ty>>;
+    fn to_recovered(&self, qself: Option<QSelf>, 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<T: RecoverQPath>(&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<Attribute>) -> P<Expr> {
-        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<Expr>) -> 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<Attribute>)
@@ -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