Repository: rust-lang/rust-analyzer (Rust)

Issue:
Bug: operator precedence around **as** keyword is wrong
Hi, I just tried the vscode plugin with the language server, and I had a great experience until now! Congrats for the great project!

**About the bug:**

I opened one of my projects, and immediately in my lib.rs I got a syntax error from the language server. I checked, if the error was valid by compiling the crate, but rustc does not issue any warnings or errors about this specific line of code. 

Here is an example I created: [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0ef147647f513864accc5ccfec141eb7).

You can try it on the playground, it compiles. If I run the following command with **ra_cli**, I get some syntax errors:

```
 > ra_cli parse < bug.rs | grep err
-------------------------------------
parsing: 237.974µs
                err: `expected COMMA`
                err: `expected SEMI`
          err: `expected SEMI`
          err: `expected expression`
  err: `expected an item`
  err: `expected an item`
```
The error seems to be only about line 7 in the example code. I think something about the + operator might mess up operator precedence, or something else in the parser. My second guess is, that the `as u32` syntax is interpreted as something like `as {TypeParamBounds}`, and the + operator is part of the type / lifetime bounds for some reason. Link to the relevant section in the reference: [Type expressions](https://doc.rust-lang.org/reference/types.html#type-expressions).

**Update:**
Found same parsing bug in one of the files in this repository: [mock_analysis.rs#L76](https://github.com/rust-analyzer/rust-analyzer/blob/8d42deeac354d1f8297d6c4d52b2707fb8d7a771/crates/ra_analysis/src/mock_analysis.rs#L76)



Discussion:
I think I found the bug.

the `cast_expr` function uses `types::_type` to get the type, into which the var should be casted.

This is suboptimal, because when type is an ident the function `path_type()` will be used to parse the type. This however calls `path_type_(_, allow_bounds: bool)` with `true`. It should directly call `path_type_(_, false)`, to prevent the parser from eating the plus sign. Bounds are not allowed in the `as` expression according to language spec.

 I will create a PR.
Please see prior discussion in #273. I was waiting for @matklad to respond.  



Patch:
diff --git a/crates/ra_syntax/src/grammar/expressions.rs b/crates/ra_syntax/src/grammar/expressions.rs
index 60c8602f9f01..a9449c7bf835 100644
--- a/crates/ra_syntax/src/grammar/expressions.rs
+++ b/crates/ra_syntax/src/grammar/expressions.rs
@@ -368,12 +368,16 @@ fn try_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
 // test cast_expr
 // fn foo() {
 //     82 as i32;
+//     81 as i8 + 1;
+//     79 as i16 - 1;
 // }
 fn cast_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
     assert!(p.at(AS_KW));
     let m = lhs.precede(p);
     p.bump();
-    types::type_(p);
+    // Use type_no_bounds(), because cast expressions are not
+    // allowed to have bounds.
+    types::type_no_bounds(p);
     m.complete(p, CAST_EXPR)
 }
 
diff --git a/crates/ra_syntax/src/grammar/types.rs b/crates/ra_syntax/src/grammar/types.rs
index ed2718e7391c..811d399d4ee1 100644
--- a/crates/ra_syntax/src/grammar/types.rs
+++ b/crates/ra_syntax/src/grammar/types.rs
@@ -11,6 +11,14 @@ pub(super) const TYPE_FIRST: TokenSet = token_set_union![
 const TYPE_RECOVERY_SET: TokenSet = token_set![R_PAREN, COMMA];
 
 pub(super) fn type_(p: &mut Parser) {
+    type_with_bounds_cond(p, true);
+}
+
+pub(super) fn type_no_bounds(p: &mut Parser) {
+    type_with_bounds_cond(p, false);
+}
+
+fn type_with_bounds_cond(p: &mut Parser, allow_bounds: bool) {
     match p.current() {
         L_PAREN => paren_or_tuple_type(p),
         EXCL => never_type(p),
@@ -22,8 +30,9 @@ pub(super) fn type_(p: &mut Parser) {
         FOR_KW => for_type(p),
         IMPL_KW => impl_trait_type(p),
         DYN_KW => dyn_trait_type(p),
-        L_ANGLE => path_type(p),
-        _ if paths::is_path_start(p) => path_type(p),
+        // Some path types are not allowed to have bounds (no plus)
+        L_ANGLE => path_type_(p, allow_bounds),
+        _ if paths::is_path_start(p) => path_type_(p, allow_bounds),
         _ => {
             p.err_recover("expected type", TYPE_RECOVERY_SET);
         }
@@ -35,10 +44,6 @@ pub(super) fn ascription(p: &mut Parser) {
     type_(p)
 }
 
-fn type_no_plus(p: &mut Parser) {
-    type_(p);
-}
-
 fn paren_or_tuple_type(p: &mut Parser) {
     assert!(p.at(L_PAREN));
     let m = p.start();
@@ -101,7 +106,7 @@ fn pointer_type(p: &mut Parser) {
         }
     };
 
-    type_no_plus(p);
+    type_no_bounds(p);
     m.complete(p, POINTER_TYPE);
 }
 
@@ -147,7 +152,7 @@ fn reference_type(p: &mut Parser) {
     p.bump();
     p.eat(LIFETIME);
     p.eat(MUT_KW);
-    type_no_plus(p);
+    type_no_bounds(p);
     m.complete(p, REFERENCE_TYPE);
 }
 


Test patch:
diff --git a/crates/ra_syntax/tests/data/parser/inline/0079_cast_expr.rs b/crates/ra_syntax/tests/data/parser/inline/0079_cast_expr.rs
index 3e53d56d6410..b571a58608f7 100644
--- a/crates/ra_syntax/tests/data/parser/inline/0079_cast_expr.rs
+++ b/crates/ra_syntax/tests/data/parser/inline/0079_cast_expr.rs
@@ -1,3 +1,5 @@
 fn foo() {
     82 as i32;
+    81 as i8 + 1;
+    79 as i16 - 1;
 }
diff --git a/crates/ra_syntax/tests/data/parser/inline/0079_cast_expr.txt b/crates/ra_syntax/tests/data/parser/inline/0079_cast_expr.txt
index a80439913412..cb56aef0ba30 100644
--- a/crates/ra_syntax/tests/data/parser/inline/0079_cast_expr.txt
+++ b/crates/ra_syntax/tests/data/parser/inline/0079_cast_expr.txt
@@ -1,5 +1,5 @@
-SOURCE_FILE@[0; 28)
-  FN_DEF@[0; 27)
+SOURCE_FILE@[0; 65)
+  FN_DEF@[0; 64)
     FN_KW@[0; 2)
     WHITESPACE@[2; 3)
     NAME@[3; 6)
@@ -8,7 +8,7 @@ SOURCE_FILE@[0; 28)
       L_PAREN@[6; 7)
       R_PAREN@[7; 8)
     WHITESPACE@[8; 9)
-    BLOCK@[9; 27)
+    BLOCK@[9; 64)
       L_CURLY@[9; 10)
       WHITESPACE@[10; 15)
       EXPR_STMT@[15; 25)
@@ -24,6 +24,46 @@ SOURCE_FILE@[0; 28)
                 NAME_REF@[21; 24)
                   IDENT@[21; 24) "i32"
         SEMI@[24; 25)
-      WHITESPACE@[25; 26)
-      R_CURLY@[26; 27)
-  WHITESPACE@[27; 28)
+      WHITESPACE@[25; 30)
+      EXPR_STMT@[30; 43)
+        BIN_EXPR@[30; 42)
+          CAST_EXPR@[30; 38)
+            LITERAL@[30; 32)
+              INT_NUMBER@[30; 32) "81"
+            WHITESPACE@[32; 33)
+            AS_KW@[33; 35)
+            WHITESPACE@[35; 36)
+            PATH_TYPE@[36; 38)
+              PATH@[36; 38)
+                PATH_SEGMENT@[36; 38)
+                  NAME_REF@[36; 38)
+                    IDENT@[36; 38) "i8"
+          WHITESPACE@[38; 39)
+          PLUS@[39; 40)
+          WHITESPACE@[40; 41)
+          LITERAL@[41; 42)
+            INT_NUMBER@[41; 42) "1"
+        SEMI@[42; 43)
+      WHITESPACE@[43; 48)
+      EXPR_STMT@[48; 62)
+        BIN_EXPR@[48; 61)
+          CAST_EXPR@[48; 57)
+            LITERAL@[48; 50)
+              INT_NUMBER@[48; 50) "79"
+            WHITESPACE@[50; 51)
+            AS_KW@[51; 53)
+            WHITESPACE@[53; 54)
+            PATH_TYPE@[54; 57)
+              PATH@[54; 57)
+                PATH_SEGMENT@[54; 57)
+                  NAME_REF@[54; 57)
+                    IDENT@[54; 57) "i16"
+          WHITESPACE@[57; 58)
+          MINUS@[58; 59)
+          WHITESPACE@[59; 60)
+          LITERAL@[60; 61)
+            INT_NUMBER@[60; 61) "1"
+        SEMI@[61; 62)
+      WHITESPACE@[62; 63)
+      R_CURLY@[63; 64)
+  WHITESPACE@[64; 65)