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

Issue:
Correctly parse higher-ranked trait bounds
The function below:

```rust
fn test<F>(f: F)
where
    for<'a> F: Fn(&'a str)
{
    f("hello");
}
```

Produces a bunch of errors:

![image](https://user-images.githubusercontent.com/5196584/47969486-b8725c80-e078-11e8-9e29-d89a4698f91e.png)

As you can see in the following AST:

```
ROOT@[0; 76)
  FN_DEF@[0; 23)
    FN_KW@[0; 2)
    WHITESPACE@[2; 3)
    NAME@[3; 7)
      IDENT@[3; 7) "test"
    TYPE_PARAM_LIST@[7; 10)
      L_ANGLE@[7; 8)
      TYPE_PARAM@[8; 9)
        NAME@[8; 9)
          IDENT@[8; 9) "F"
      R_ANGLE@[9; 10)
    PARAM_LIST@[10; 16)
      L_PAREN@[10; 11)
      PARAM@[11; 15)
        BIND_PAT@[11; 12)
          NAME@[11; 12)
            IDENT@[11; 12) "f"
        COLON@[12; 13)
        WHITESPACE@[13; 14)
        PATH_TYPE@[14; 15)
          PATH@[14; 15)
            PATH_SEGMENT@[14; 15)
              NAME_REF@[14; 15)
                IDENT@[14; 15) "F"
      R_PAREN@[15; 16)
    WHITESPACE@[16; 18)
    WHERE_CLAUSE@[18; 23)
      WHERE_KW@[18; 23)
      err: `expected a block`
  WHITESPACE@[23; 29)
  err: `expected an item`
  ERROR@[29; 32)
    FOR_KW@[29; 32)
    err: `expected an item`
  ERROR@[32; 33)
    L_ANGLE@[32; 33)
    err: `expected an item`
  ERROR@[33; 35)
    LIFETIME@[33; 35) "'a"
    err: `expected an item`
  ERROR@[35; 36)
    R_ANGLE@[35; 36)
  WHITESPACE@[36; 37)
  MACRO_CALL@[37; 38)
    PATH@[37; 38)
      PATH_SEGMENT@[37; 38)
        NAME_REF@[37; 38)
          IDENT@[37; 38) "F"
          err: `expected EXCL`
          err: `expected `{`, `[`, `(``
          err: `expected SEMI`
          err: `expected an item`
  ERROR@[38; 39)
    COLON@[38; 39)
  WHITESPACE@[39; 40)
  MACRO_CALL@[40; 51)
    PATH@[40; 42)
      PATH_SEGMENT@[40; 42)
        NAME_REF@[40; 42)
          IDENT@[40; 42) "Fn"
          err: `expected EXCL`
    TOKEN_TREE@[42; 51)
      L_PAREN@[42; 43)
      AMP@[43; 44)
      LIFETIME@[44; 46) "'a"
      WHITESPACE@[46; 47)
      IDENT@[47; 50) "str"
      R_PAREN@[50; 51)
      err: `expected SEMI`
  WHITESPACE@[51; 53)
  err: `expected an item`
  ERROR@[53; 74)
    L_CURLY@[53; 54)
    WHITESPACE@[54; 60)
    IDENT@[60; 61) "f"
    L_PAREN@[61; 62)
    STRING@[62; 69)
    R_PAREN@[69; 70)
    SEMI@[70; 71)
    WHITESPACE@[71; 73)
    R_CURLY@[73; 74)
  WHITESPACE@[74; 76)
```

Pull request:
grammar: for predicates in where
closes #191

Patch:
diff --git a/crates/ra_syntax/src/grammar/mod.rs b/crates/ra_syntax/src/grammar/mod.rs
index c875640730..95c4379833 100644
--- a/crates/ra_syntax/src/grammar/mod.rs
+++ b/crates/ra_syntax/src/grammar/mod.rs
@@ -21,6 +21,11 @@
 //! After adding a new inline-test, run `cargo collect-tests` to extract
 //! it as a standalone text-fixture into `tests/data/parser/inline`, and
 //! run `cargo test` once to create the "gold" value.
+//!
+//! Coding convention: rules like `where_clause` always produce either a
+//! node or an error, rules like `opt_where_clause` may produce nothing.
+//! Non-opt rules typically start with `assert!(p.at(FIRST_TOKEN))`, the
+//! caller is responsible for branching on the first token.
 mod attributes;
 mod expressions;
 mod items;
diff --git a/crates/ra_syntax/src/grammar/type_params.rs b/crates/ra_syntax/src/grammar/type_params.rs
index 79f5036b41..735c728e5c 100644
--- a/crates/ra_syntax/src/grammar/type_params.rs
+++ b/crates/ra_syntax/src/grammar/type_params.rs
@@ -4,6 +4,11 @@ pub(super) fn opt_type_param_list(p: &mut Parser) {
     if !p.at(L_ANGLE) {
         return;
     }
+    type_param_list(p);
+}
+
+fn type_param_list(p: &mut Parser) {
+    assert!(p.at(L_ANGLE));
     let m = p.start();
     p.bump();
 
@@ -19,32 +24,32 @@ pub(super) fn opt_type_param_list(p: &mut Parser) {
     }
     p.expect(R_ANGLE);
     m.complete(p, TYPE_PARAM_LIST);
+}
 
-    fn lifetime_param(p: &mut Parser) {
-        assert!(p.at(LIFETIME));
-        let m = p.start();
-        p.bump();
-        if p.at(COLON) {
-            lifetime_bounds(p);
-        }
-        m.complete(p, LIFETIME_PARAM);
+fn lifetime_param(p: &mut Parser) {
+    assert!(p.at(LIFETIME));
+    let m = p.start();
+    p.bump();
+    if p.at(COLON) {
+        lifetime_bounds(p);
     }
+    m.complete(p, LIFETIME_PARAM);
+}
 
-    fn type_param(p: &mut Parser) {
-        assert!(p.at(IDENT));
-        let m = p.start();
-        name(p);
-        if p.at(COLON) {
-            bounds(p);
-        }
-        // test type_param_default
-        // struct S<T = i32>;
-        if p.at(EQ) {
-            p.bump();
-            types::type_(p)
-        }
-        m.complete(p, TYPE_PARAM);
+fn type_param(p: &mut Parser) {
+    assert!(p.at(IDENT));
+    let m = p.start();
+    name(p);
+    if p.at(COLON) {
+        bounds(p);
+    }
+    // test type_param_default
+    // struct S<T = i32>;
+    if p.at(EQ) {
+        p.bump();
+        types::type_(p)
     }
+    m.complete(p, TYPE_PARAM);
 }
 
 // test type_param_bounds
@@ -99,7 +104,7 @@ pub(super) fn opt_where_clause(p: &mut Parser) {
     let m = p.start();
     p.bump();
     loop {
-        if !(paths::is_path_start(p) || p.current() == LIFETIME) {
+        if !(paths::is_path_start(p) || p.current() == LIFETIME || p.current() == FOR_KW) {
             break;
         }
         where_predicate(p);
@@ -112,19 +117,41 @@ pub(super) fn opt_where_clause(p: &mut Parser) {
 
 fn where_predicate(p: &mut Parser) {
     let m = p.start();
-    if p.at(LIFETIME) {
-        p.eat(LIFETIME);
-        if p.at(COLON) {
-            lifetime_bounds(p)
-        } else {
-            p.error("expected colon")
+    match p.current() {
+        LIFETIME => {
+            p.bump();
+            if p.at(COLON) {
+                lifetime_bounds(p);
+            } else {
+                p.error("expected colon");
+            }
+        }
+        // test where_pred_for
+        // fn test<F>()
+        // where
+        //    for<'a> F: Fn(&'a str)
+        // { }
+        FOR_KW => {
+            p.bump();
+            if p.at(L_ANGLE) {
+                type_param_list(p);
+                types::path_type(p);
+                if p.at(COLON) {
+                    bounds(p);
+                } else {
+                    p.error("expected colon");
+                }
+            } else {
+                p.error("expected `<`");
+            }
         }
-    } else {
-        types::path_type(p);
-        if p.at(COLON) {
-            bounds(p);
-        } else {
-            p.error("expected colon")
+        _ => {
+            types::path_type(p);
+            if p.at(COLON) {
+                bounds(p);
+            } else {
+                p.error("expected colon");
+            }
         }
     }
     m.complete(p, WHERE_PRED);


Test patch:
diff --git a/crates/ra_syntax/tests/data/parser/inline/0113_where_pred_for.rs b/crates/ra_syntax/tests/data/parser/inline/0113_where_pred_for.rs
new file mode 100644
index 0000000000..b448c61780
--- /dev/null
+++ b/crates/ra_syntax/tests/data/parser/inline/0113_where_pred_for.rs
@@ -0,0 +1,4 @@
+fn test<F>()
+where
+   for<'a> F: Fn(&'a str)
+{ }
diff --git a/crates/ra_syntax/tests/data/parser/inline/0113_where_pred_for.txt b/crates/ra_syntax/tests/data/parser/inline/0113_where_pred_for.txt
new file mode 100644
index 0000000000..08aacc77ab
--- /dev/null
+++ b/crates/ra_syntax/tests/data/parser/inline/0113_where_pred_for.txt
@@ -0,0 +1,58 @@
+ROOT@[0; 49)
+  FN_DEF@[0; 48)
+    FN_KW@[0; 2)
+    WHITESPACE@[2; 3)
+    NAME@[3; 7)
+      IDENT@[3; 7) "test"
+    TYPE_PARAM_LIST@[7; 10)
+      L_ANGLE@[7; 8)
+      TYPE_PARAM@[8; 9)
+        NAME@[8; 9)
+          IDENT@[8; 9) "F"
+      R_ANGLE@[9; 10)
+    PARAM_LIST@[10; 12)
+      L_PAREN@[10; 11)
+      R_PAREN@[11; 12)
+    WHITESPACE@[12; 13)
+    WHERE_CLAUSE@[13; 44)
+      WHERE_KW@[13; 18)
+      WHITESPACE@[18; 22)
+      WHERE_PRED@[22; 44)
+        FOR_KW@[22; 25)
+        TYPE_PARAM_LIST@[25; 29)
+          L_ANGLE@[25; 26)
+          LIFETIME_PARAM@[26; 28)
+            LIFETIME@[26; 28) "'a"
+          R_ANGLE@[28; 29)
+        WHITESPACE@[29; 30)
+        PATH_TYPE@[30; 31)
+          PATH@[30; 31)
+            PATH_SEGMENT@[30; 31)
+              NAME_REF@[30; 31)
+                IDENT@[30; 31) "F"
+        COLON@[31; 32)
+        WHITESPACE@[32; 33)
+        PATH_TYPE@[33; 44)
+          PATH@[33; 44)
+            PATH_SEGMENT@[33; 44)
+              NAME_REF@[33; 35)
+                IDENT@[33; 35) "Fn"
+              PARAM_LIST@[35; 44)
+                L_PAREN@[35; 36)
+                PARAM@[36; 43)
+                  REFERENCE_TYPE@[36; 43)
+                    AMP@[36; 37)
+                    LIFETIME@[37; 39) "'a"
+                    WHITESPACE@[39; 40)
+                    PATH_TYPE@[40; 43)
+                      PATH@[40; 43)
+                        PATH_SEGMENT@[40; 43)
+                          NAME_REF@[40; 43)
+                            IDENT@[40; 43) "str"
+                R_PAREN@[43; 44)
+    WHITESPACE@[44; 45)
+    BLOCK@[45; 48)
+      L_CURLY@[45; 46)
+      WHITESPACE@[46; 47)
+      R_CURLY@[47; 48)
+  WHITESPACE@[48; 49)