about summary refs log tree commit diff
path: root/src/librustc_parse/parser
diff options
context:
space:
mode:
authorDavid Ross <daboross@daboross.net>2020-02-15 16:12:59 -0800
committerDavid Ross <daboross@daboross.net>2020-02-15 19:51:02 -0800
commit5ce9b80c0f93e54bc83f6cb15942ecdce31c3e6a (patch)
treebc5182901350a3a65c6586aa9cd2e98e7524e77a /src/librustc_parse/parser
parent940f65782cc5df7fecad27b38cc25b6d1eeaf2e8 (diff)
downloadrust-5ce9b80c0f93e54bc83f6cb15942ecdce31c3e6a.tar.gz
rust-5ce9b80c0f93e54bc83f6cb15942ecdce31c3e6a.zip
Refactor out error case & apply suggestions.
This is almost entirely refactoring and message changing, with the
single behavioral change of panicking for unexpected output.
Diffstat (limited to 'src/librustc_parse/parser')
-rw-r--r--src/librustc_parse/parser/expr.rs62
1 files changed, 42 insertions, 20 deletions
diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs
index d7d3145770c..76e4a80878f 100644
--- a/src/librustc_parse/parser/expr.rs
+++ b/src/librustc_parse/parser/expr.rs
@@ -551,8 +551,8 @@ impl<'a> Parser<'a> {
         // Save the state of the parser before parsing type normally, in case there is a
         // LessThan comparison after this cast.
         let parser_snapshot_before_type = self.clone();
-        let type_result = match self.parse_ty_no_plus() {
-            Ok(rhs) => Ok(mk_expr(self, rhs)),
+        let cast_expr = match self.parse_ty_no_plus() {
+            Ok(rhs) => mk_expr(self, rhs),
             Err(mut type_err) => {
                 // Rewind to before attempting to parse the type with generics, to recover
                 // from situations like `x as usize < y` in which we first tried to parse
@@ -606,41 +606,63 @@ impl<'a> Parser<'a> {
                             )
                             .emit();
 
-                        Ok(expr)
+                        expr
                     }
                     Err(mut path_err) => {
                         // Couldn't parse as a path, return original error and parser state.
                         path_err.cancel();
                         mem::replace(self, parser_snapshot_after_type);
-                        Err(type_err)
+                        return Err(type_err);
                     }
                 }
             }
         };
 
-        // Disallow postfix operators such as `.`, `?` or index (`[]`) after casts.
-        // Parses the postfix operator and emits an error.
-        let expr = type_result?;
-        let span = expr.span;
+        self.parse_and_disallow_postfix_after_cast(cast_expr)
+    }
 
-        // The resulting parse tree for `&x as T[0]` has a precedence of `((&x) as T)[0]`.
-        let with_postfix = self.parse_dot_or_call_expr_with_(expr, span)?;
-        if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) {
+    /// Parses a postfix operators such as `.`, `?`, or index (`[]`) after a cast,
+    /// then emits an error and returns the newly parsed tree.
+    /// The resulting parse tree for `&x as T[0]` has a precedence of `((&x) as T)[0]`.
+    fn parse_and_disallow_postfix_after_cast(
+        &mut self,
+        cast_expr: P<Expr>,
+    ) -> PResult<'a, P<Expr>> {
+        use std::collections::hash_map::DefaultHasher;
+        use std::hash::Hasher;
+        // Hash the memory location of expr before parsing any following postfix operators.
+        // This will be compared with the hash of the output expression.
+        // If they different we can assume we parsed another expression because the existing expression is not reallocated.
+        let mut before_hasher = DefaultHasher::new();
+        std::ptr::hash(&*cast_expr, &mut before_hasher);
+        let before_hash = before_hasher.finish();
+        let span = cast_expr.span;
+        let with_postfix = self.parse_dot_or_call_expr_with_(cast_expr, span)?;
+
+        let mut after_hasher = DefaultHasher::new();
+        std::ptr::hash(&*with_postfix, &mut after_hasher);
+        let after_hash = after_hasher.finish();
+
+        // Check if an illegal postfix operator has been added after the cast.
+        // If the resulting expression is not a cast, or has a different memory location, it is an illegal postfix operator.
+        if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) || after_hash != before_hash {
             let expr_str = self.span_to_snippet(span);
 
             let msg = format!(
-                "casts followed by {} are not supported",
+                "casts cannot be followed by {}",
                 match with_postfix.kind {
-                    ExprKind::Index(_, _) => "index operators",
-                    ExprKind::Try(_) => "try operators",
-                    ExprKind::Field(_, _) => "field access expressions",
-                    ExprKind::MethodCall(_, _) => "method call expressions",
-                    ExprKind::Await(_) => "awaits",
-                    _ => "expressions",
+                    ExprKind::Index(_, _) => "indexing",
+                    ExprKind::Try(_) => "?",
+                    ExprKind::Field(_, _) => "a field access",
+                    ExprKind::MethodCall(_, _) => "a method call",
+                    ExprKind::Call(_, _) => "a function call",
+                    ExprKind::Await(_) => "`.await`",
+                    ref kind =>
+                        unreachable!("parse_dot_or_call_expr_with_ shouldn't produce a {:?}", kind),
                 }
             );
-            let mut err = self.struct_span_err(with_postfix.span, &msg);
-            let suggestion = "try surrounding the expression with parentheses";
+            let mut err = self.struct_span_err(span, &msg);
+            let suggestion = "try surrounding the expression in parentheses";
             if let Ok(expr_str) = expr_str {
                 err.span_suggestion(
                     span,