]> git.lizzy.rs Git - rust.git/commitdiff
syntax: Better recovery for `$ty::AssocItem` and `ty!()::AssocItem`
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sat, 9 Mar 2019 14:41:01 +0000 (17:41 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Tue, 12 Mar 2019 07:25:03 +0000 (10:25 +0300)
src/libsyntax/parse/parser.rs
src/test/ui/did_you_mean/bad-assoc-expr.rs
src/test/ui/did_you_mean/bad-assoc-expr.stderr
src/test/ui/did_you_mean/bad-assoc-pat.rs
src/test/ui/did_you_mean/bad-assoc-pat.stderr
src/test/ui/did_you_mean/bad-assoc-ty.rs
src/test/ui/did_you_mean/bad-assoc-ty.stderr

index fe31311094b89412624eded504cb630872c0854b..ba948d41c7ad7e6f42ad477a89b875aea5308fd2 100644 (file)
@@ -154,6 +154,21 @@ macro_rules! maybe_whole {
     };
 }
 
+/// If the next tokens are ill-formed `$ty::` recover them as `<$ty>::`.
+macro_rules! maybe_recover_from_interpolated_ty_qpath {
+    ($self: expr, $allow_qpath_recovery: expr) => {
+        if $allow_qpath_recovery && $self.look_ahead(1, |t| t == &token::ModSep) {
+            if let token::Interpolated(nt) = &$self.token {
+                if let token::NtTy(ty) = &**nt {
+                    let ty = ty.clone();
+                    $self.bump();
+                    return $self.maybe_recover_from_bad_qpath_stage_2($self.prev_span, ty);
+                }
+            }
+        }
+    }
+}
+
 fn maybe_append(mut lhs: Vec<Attribute>, mut rhs: Option<Vec<Attribute>>) -> Vec<Attribute> {
     if let Some(ref mut rhs) = rhs {
         lhs.append(rhs);
@@ -172,11 +187,10 @@ enum PrevTokenKind {
     Other,
 }
 
-trait RecoverQPath: Sized {
+trait RecoverQPath: Sized + 'static {
     const PATH_STYLE: PathStyle = PathStyle::Expr;
     fn to_ty(&self) -> Option<P<Ty>>;
-    fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self;
-    fn to_string(&self) -> String;
+    fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self;
 }
 
 impl RecoverQPath for Ty {
@@ -184,11 +198,8 @@ impl RecoverQPath for Ty {
     fn to_ty(&self) -> Option<P<Ty>> {
         Some(P(self.clone()))
     }
-    fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self {
-        Self { span: path.span, node: TyKind::Path(qself, path), id: self.id }
-    }
-    fn to_string(&self) -> String {
-        pprust::ty_to_string(self)
+    fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
+        Self { span: path.span, node: TyKind::Path(qself, path), id: ast::DUMMY_NODE_ID }
     }
 }
 
@@ -196,11 +207,8 @@ impl RecoverQPath for Pat {
     fn to_ty(&self) -> Option<P<Ty>> {
         self.to_ty()
     }
-    fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self {
-        Self { span: path.span, node: PatKind::Path(qself, path), id: self.id }
-    }
-    fn to_string(&self) -> String {
-        pprust::pat_to_string(self)
+    fn recovered(qself: Option<QSelf>, path: ast::Path) -> Self {
+        Self { span: path.span, node: PatKind::Path(qself, path), id: ast::DUMMY_NODE_ID }
     }
 }
 
@@ -208,12 +216,9 @@ impl RecoverQPath for Expr {
     fn to_ty(&self) -> Option<P<Ty>> {
         self.to_ty()
     }
-    fn to_recovered(&self, qself: Option<QSelf>, path: ast::Path) -> Self {
+    fn recovered(qself: Option<QSelf>, path: ast::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)
+               attrs: ThinVec::new(), id: ast::DUMMY_NODE_ID }
     }
 }
 
@@ -1651,6 +1656,7 @@ fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> {
 
     fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool,
                        allow_c_variadic: bool) -> PResult<'a, P<Ty>> {
+        maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
         maybe_whole!(self, NtTy, |x| x);
 
         let lo = self.span;
@@ -1802,14 +1808,12 @@ fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool,
         };
 
         let span = lo.to(self.prev_span);
-        let ty = Ty { node, span, id: ast::DUMMY_NODE_ID };
+        let ty = P(Ty { node, span, id: ast::DUMMY_NODE_ID });
 
         // Try to recover from use of `+` with incorrect priority.
         self.maybe_report_ambiguous_plus(allow_plus, impl_dyn_multi, &ty);
         self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
-        let ty = self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery)?;
-
-        Ok(P(ty))
+        self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery)
     }
 
     fn parse_remaining_bounds(&mut self, generic_params: Vec<GenericParam>, path: ast::Path,
@@ -1881,35 +1885,35 @@ fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PRe
     }
 
     // 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, allow_recovery: bool)
-                                                     -> PResult<'a, T> {
+    fn maybe_recover_from_bad_qpath<T: RecoverQPath>(&mut self, base: P<T>, allow_recovery: bool)
+                                                     -> PResult<'a, P<T>> {
         // Do not add `::` to expected tokens.
-        if !allow_recovery || self.token != token::ModSep {
-            return Ok(base);
+        if allow_recovery && self.token == token::ModSep {
+            if let Some(ty) = base.to_ty() {
+                return self.maybe_recover_from_bad_qpath_stage_2(ty.span, ty);
+            }
         }
-        let ty = match base.to_ty() {
-            Some(ty) => ty,
-            None => return Ok(base),
-        };
+        Ok(base)
+    }
 
-        self.bump(); // `::`
-        let mut segments = Vec::new();
-        self.parse_path_segments(&mut segments, T::PATH_STYLE, true)?;
+    fn maybe_recover_from_bad_qpath_stage_2<T: RecoverQPath>(&mut self, ty_span: Span, ty: P<Ty>)
+                                                             -> PResult<'a, P<T>> {
+        self.expect(&token::ModSep)?;
 
-        let span = ty.span.to(self.prev_span);
-        let path_span = span.to(span); // use an empty path since `position` == 0
-        let recovered = base.to_recovered(
-            Some(QSelf { ty, path_span, position: 0 }),
-            ast::Path { segments, span },
-        );
+        let mut path = ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP };
+        self.parse_path_segments(&mut path.segments, T::PATH_STYLE, true)?;
+        path.span = ty_span.to(self.prev_span);
 
+        let ty_str = self.sess.source_map().span_to_snippet(ty_span)
+            .unwrap_or_else(|_| pprust::ty_to_string(&ty));
         self.diagnostic()
-            .struct_span_err(span, "missing angle brackets in associated item path")
+            .struct_span_err(path.span, "missing angle brackets in associated item path")
             .span_suggestion( // this is a best-effort recovery
-                span, "try", recovered.to_string(), Applicability::MaybeIncorrect
+                path.span, "try", format!("<{}>::{}", ty_str, path), Applicability::MaybeIncorrect
             ).emit();
 
-        Ok(recovered)
+        let path_span = path.span.to(path.span); // use an empty path since `position` == 0
+        Ok(P(T::recovered(Some(QSelf { ty, path_span, position: 0 }), path)))
     }
 
     fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
@@ -2574,15 +2578,6 @@ fn mk_assign_op(&mut self, binop: ast::BinOp,
         ExprKind::AssignOp(binop, lhs, rhs)
     }
 
-    pub fn mk_mac_expr(&mut self, span: Span, m: Mac_, attrs: ThinVec<Attribute>) -> P<Expr> {
-        P(Expr {
-            id: ast::DUMMY_NODE_ID,
-            node: ExprKind::Mac(source_map::Spanned {node: m, span: span}),
-            span,
-            attrs,
-        })
-    }
-
     fn expect_delimited_token_tree(&mut self) -> PResult<'a, (MacDelimiter, TokenStream)> {
         let delim = match self.token {
             token::OpenDelim(delim) => delim,
@@ -2612,6 +2607,7 @@ fn expect_delimited_token_tree(&mut self) -> PResult<'a, (MacDelimiter, TokenStr
     /// N.B., this does not parse outer attributes, and is private because it only works
     /// correctly if called from `parse_dot_or_call_expr()`.
     fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
+        maybe_recover_from_interpolated_ty_qpath!(self, true);
         maybe_whole_expr!(self);
 
         // Outer attributes are already parsed and will be
@@ -2826,29 +2822,23 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
                     db.note("variable declaration using `let` is a statement");
                     return Err(db);
                 } else if self.token.is_path_start() {
-                    let pth = self.parse_path(PathStyle::Expr)?;
+                    let path = self.parse_path(PathStyle::Expr)?;
 
                     // `!`, as an operator, is prefix, so we know this isn't that
                     if self.eat(&token::Not) {
                         // MACRO INVOCATION expression
                         let (delim, tts) = self.expect_delimited_token_tree()?;
-                        let hi = self.prev_span;
-                        let node = Mac_ { path: pth, tts, delim };
-                        return Ok(self.mk_mac_expr(lo.to(hi), node, attrs))
-                    }
-                    if self.check(&token::OpenDelim(token::Brace)) {
+                        hi = self.prev_span;
+                        ex = ExprKind::Mac(respan(lo.to(hi), Mac_ { path, tts, delim }));
+                    } else if self.check(&token::OpenDelim(token::Brace)) &&
+                              !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) {
                         // This is a struct literal, unless we're prohibited
                         // from parsing struct literals here.
-                        let prohibited = self.restrictions.contains(
-                            Restrictions::NO_STRUCT_LITERAL
-                        );
-                        if !prohibited {
-                            return self.parse_struct_expr(lo, pth, attrs);
-                        }
+                        return self.parse_struct_expr(lo, path, attrs);
+                    } else {
+                        hi = path.span;
+                        ex = ExprKind::Path(None, path);
                     }
-
-                    hi = pth.span;
-                    ex = ExprKind::Path(None, pth);
                 } else {
                     if !self.unclosed_delims.is_empty() && self.check(&token::Semi) {
                         // Don't complain about bare semicolons after unclosed braces
@@ -2883,10 +2873,8 @@ fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
             }
         }
 
-        let expr = Expr { node: ex, span: lo.to(hi), id: ast::DUMMY_NODE_ID, attrs };
-        let expr = self.maybe_recover_from_bad_qpath(expr, true)?;
-
-        return Ok(P(expr));
+        let expr = self.mk_expr(lo.to(hi), ex, attrs);
+        self.maybe_recover_from_bad_qpath(expr, true)
     }
 
     fn parse_struct_expr(&mut self, lo: Span, pth: ast::Path, mut attrs: ThinVec<Attribute>)
@@ -4581,6 +4569,7 @@ fn parse_pat_with_range_pat(
         allow_range_pat: bool,
         expected: Option<&'static str>,
     ) -> PResult<'a, P<Pat>> {
+        maybe_recover_from_interpolated_ty_qpath!(self, true);
         maybe_whole!(self, NtPat, |x| x);
 
         let lo = self.span;
@@ -4756,7 +4745,7 @@ fn parse_pat_with_range_pat(
             }
         }
 
-        let pat = Pat { node: pat, span: lo.to(self.prev_span), id: ast::DUMMY_NODE_ID };
+        let pat = P(Pat { node: pat, span: lo.to(self.prev_span), id: ast::DUMMY_NODE_ID });
         let pat = self.maybe_recover_from_bad_qpath(pat, true)?;
 
         if !allow_range_pat {
@@ -4782,7 +4771,7 @@ fn parse_pat_with_range_pat(
             }
         }
 
-        Ok(P(pat))
+        Ok(pat)
     }
 
     /// Parses `ident` or `ident @ pat`.
@@ -5250,7 +5239,8 @@ fn parse_stmt_without_recovery(&mut self,
                     self.warn_missing_semicolon();
                     StmtKind::Mac(P((mac, style, attrs.into())))
                 } else {
-                    let e = self.mk_mac_expr(lo.to(hi), mac.node, ThinVec::new());
+                    let e = self.mk_expr(mac.span, ExprKind::Mac(mac), ThinVec::new());
+                    let e = self.maybe_recover_from_bad_qpath(e, true)?;
                     let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
                     let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
                     StmtKind::Expr(e)
index 2e13db17d8271a6652aa9098c51bec4bc45476d7..1d584757f2faf5ecd3f5ffc1a9ffae9fe8a1655a 100644 (file)
@@ -18,3 +18,19 @@ fn main() {
     10 + (u8)::clone(&0);
     //~^ ERROR missing angle brackets in associated item path
 }
+
+macro_rules! expr {
+    ($ty: ty) => ($ty::clone(&0))
+    //~^ ERROR missing angle brackets in associated item path
+}
+macro_rules! ty {
+    () => (u8)
+}
+
+fn check_macros() {
+    expr!(u8);
+    let _ = ty!()::clone(&0);
+    //~^ ERROR missing angle brackets in associated item path
+    ty!()::clone(&0);
+    //~^ ERROR missing angle brackets in associated item path
+}
index e1eceabcc30afdd6ff6cf73c7d00fca339d0d7ad..2024564b911f635c9b8936e3ba7e5940b3e03ce0 100644 (file)
@@ -34,5 +34,26 @@ error: missing angle brackets in associated item path
 LL |     10 + (u8)::clone(&0);
    |          ^^^^^^^^^^^ help: try: `<(u8)>::clone`
 
-error: aborting due to 6 previous errors
+error: missing angle brackets in associated item path
+  --> $DIR/bad-assoc-expr.rs:32:13
+   |
+LL |     let _ = ty!()::clone(&0);
+   |             ^^^^^^^^^^^^ help: try: `<ty!()>::clone`
+
+error: missing angle brackets in associated item path
+  --> $DIR/bad-assoc-expr.rs:34:5
+   |
+LL |     ty!()::clone(&0);
+   |     ^^^^^^^^^^^^ help: try: `<ty!()>::clone`
+
+error: missing angle brackets in associated item path
+  --> $DIR/bad-assoc-expr.rs:23:19
+   |
+LL |     ($ty: ty) => ($ty::clone(&0))
+   |                   ^^^^^^^^^^ help: try: `<$ty>::clone`
+...
+LL |     expr!(u8);
+   |     ---------- in this macro invocation
+
+error: aborting due to 9 previous errors
 
index 5bd2f1a894e823478777aedae62fe769c33ba435..7e7ba59dca8169f73af3637f5467bb2c94bd53ca 100644 (file)
@@ -16,3 +16,21 @@ fn main() {
         //~| ERROR no associated item named `AssocItem` found for type `(u8,)` in the current scope
     }
 }
+
+macro_rules! pat {
+    ($ty: ty) => ($ty::AssocItem)
+    //~^ ERROR missing angle brackets in associated item path
+    //~| ERROR no associated item named `AssocItem` found for type `u8` in the current scope
+}
+macro_rules! ty {
+    () => (u8)
+}
+
+fn check_macros() {
+    match 0u8 {
+        pat!(u8) => {}
+        ty!()::AssocItem => {}
+        //~^ ERROR missing angle brackets in associated item path
+        //~| ERROR no associated item named `AssocItem` found for type `u8` in the current scope
+    }
+}
index 92fd9f26777f1c605be896acca8241756241806b..e620e7b43565f8d3896c37c5db3c41e6f33b12f8 100644 (file)
@@ -22,6 +22,21 @@ error: missing angle brackets in associated item path
 LL |         &(u8,)::AssocItem => {}
    |          ^^^^^^^^^^^^^^^^ help: try: `<(u8,)>::AssocItem`
 
+error: missing angle brackets in associated item path
+  --> $DIR/bad-assoc-pat.rs:32:9
+   |
+LL |         ty!()::AssocItem => {}
+   |         ^^^^^^^^^^^^^^^^ help: try: `<ty!()>::AssocItem`
+
+error: missing angle brackets in associated item path
+  --> $DIR/bad-assoc-pat.rs:21:19
+   |
+LL |     ($ty: ty) => ($ty::AssocItem)
+   |                   ^^^^^^^^^^^^^^ help: try: `<$ty>::AssocItem`
+...
+LL |         pat!(u8) => {}
+   |         -------- in this macro invocation
+
 error[E0599]: no associated item named `AssocItem` found for type `[u8]` in the current scope
   --> $DIR/bad-assoc-pat.rs:3:15
    |
@@ -54,6 +69,25 @@ LL |         &(u8,)::AssocItem => {}
    |          |
    |          associated item not found in `(u8,)`
 
-error: aborting due to 8 previous errors
+error[E0599]: no associated item named `AssocItem` found for type `u8` in the current scope
+  --> $DIR/bad-assoc-pat.rs:21:24
+   |
+LL |     ($ty: ty) => ($ty::AssocItem)
+   |                   -----^^^^^^^^^
+   |                   |
+   |                   associated item not found in `u8`
+...
+LL |         pat!(u8) => {}
+   |         -------- in this macro invocation
+
+error[E0599]: no associated item named `AssocItem` found for type `u8` in the current scope
+  --> $DIR/bad-assoc-pat.rs:32:16
+   |
+LL |         ty!()::AssocItem => {}
+   |         -------^^^^^^^^^
+   |         |
+   |         associated item not found in `u8`
+
+error: aborting due to 12 previous errors
 
 For more information about this error, try `rustc --explain E0599`.
index 436bda1547be9e6f47746979b076bc252ec13940..85e36f887be087d94c293a9f6831617fe4d672b6 100644 (file)
 type H = Fn(u8) -> (u8)::Output;
 //~^ ERROR ambiguous associated type
 
+macro_rules! ty {
+    ($ty: ty) => ($ty::AssocTy);
+    //~^ ERROR missing angle brackets in associated item path
+    //~| ERROR ambiguous associated type
+    () => (u8);
+}
+
+type J = ty!(u8);
+type I = ty!()::AssocTy;
+//~^ ERROR missing angle brackets in associated item path
+//~| ERROR ambiguous associated type
+
 fn main() {}
index a9dcc831ba7d5ea3db2f0a1ab3f02136c75eebea..7e7f18f2d6e43bd489ad7109930393f121b78eb1 100644 (file)
@@ -38,7 +38,22 @@ error: missing angle brackets in associated item path
   --> $DIR/bad-assoc-ty.rs:27:10
    |
 LL | type G = 'static + (Send)::AssocTy;
-   |          ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<'static + Send>::AssocTy`
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<'static + (Send)>::AssocTy`
+
+error: missing angle brackets in associated item path
+  --> $DIR/bad-assoc-ty.rs:44:10
+   |
+LL | type I = ty!()::AssocTy;
+   |          ^^^^^^^^^^^^^^ help: try: `<ty!()>::AssocTy`
+
+error: missing angle brackets in associated item path
+  --> $DIR/bad-assoc-ty.rs:37:19
+   |
+LL |     ($ty: ty) => ($ty::AssocTy);
+   |                   ^^^^^^^^^^^^ help: try: `<$ty>::AssocTy`
+...
+LL | type J = ty!(u8);
+   |          ------- in this macro invocation
 
 error[E0223]: ambiguous associated type
   --> $DIR/bad-assoc-ty.rs:1:10
@@ -88,7 +103,22 @@ error[E0223]: ambiguous associated type
 LL | type H = Fn(u8) -> (u8)::Output;
    |          ^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn std::ops::Fn(u8) -> u8 + 'static) as Trait>::Output`
 
-error: aborting due to 15 previous errors
+error[E0223]: ambiguous associated type
+  --> $DIR/bad-assoc-ty.rs:37:19
+   |
+LL |     ($ty: ty) => ($ty::AssocTy);
+   |                   ^^^^^^^^^^^^ help: use fully-qualified syntax: `<u8 as Trait>::AssocTy`
+...
+LL | type J = ty!(u8);
+   |          ------- in this macro invocation
+
+error[E0223]: ambiguous associated type
+  --> $DIR/bad-assoc-ty.rs:44:10
+   |
+LL | type I = ty!()::AssocTy;
+   |          ^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<u8 as Trait>::AssocTy`
+
+error: aborting due to 19 previous errors
 
 Some errors occurred: E0121, E0223.
 For more information about an error, try `rustc --explain E0121`.