Repository: rust-analyzer/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)


Pull request description:
Fixed cast expression parsing in ra_syntax.
Fixes #279 
Related to https://github.com/rust-analyzer/rust-analyzer/pull/273

The cast expression expected any type via types::type_() function,
but the language spec does only allow TypeNoBounds (types without direct extra bounds
via `+`).

**Example:**

```rust
fn test() {
	6i8 as i32 + 5;
}
```

This fails, because the types::type_() function which should parse the type after the
as keyword is greedy, and takes the plus sign after path types as extra type bounds.

My proposed fix is to replace the not implemented `type_no_plus()` just calls (`type_()`)
function, which is used at several places. The replacement is `type_with_bounds_cond(p: &mut Parser, allow_bounds: bool)`, which passes the condition to relevant sub-parsers.

This function is then called by `type_()` and the new public `type_no_bounds()`.

Patch:
diff --git a/crates/ra_syntax/src/grammar/expressions.rs b/crates/ra_syntax/src/grammar/expressions.rs
index 60c8602f9f..a9449c7bf8 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 ed2718e739..811d399d4e 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 3e53d56d64..b571a58608 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 a804399134..cb56aef0ba 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)