diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs
index 42763fc87c1..07d9b079217 100644
--- a/crates/ra_parser/src/grammar/type_params.rs
+++ b/crates/ra_parser/src/grammar/type_params.rs
@@ -165,7 +165,7 @@ fn where_predicate(p: &mut Parser) {
         LIFETIME => {
             p.bump();
             if p.at(COLON) {
-                lifetime_bounds(p);
+                bounds(p);
             } else {
                 p.error("expected colon");
             }
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 4fddc00ea5f..fd7e63f849f 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -799,3 +799,70 @@ fn test_doc_comment_preserves_indents() {
     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
     assert_eq!("doc1\n```\nfn foo() {\n    // ...\n}\n```", module.doc_comment_text().unwrap());
 }
+
+#[test]
+fn test_where_predicates() {
+    fn assert_bound(text: &str, bound: Option<&TypeBound>) {
+        assert_eq!(text, bound.unwrap().syntax().text().to_string());
+    }
+
+    let file = SourceFile::parse(
+        r#"
+fn foo()
+where
+   T: Clone + Copy + Debug + 'static,
+   'a: 'b + 'c,
+   Iterator::Item: 'a + Debug,
+   Iterator::Item: Debug + 'a,
+   <T as Iterator>::Item: Debug + 'a,
+   for<'a> F: Fn(&'a str)
+{}
+        "#,
+    );
+    let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();
+
+    let mut predicates = where_clause.predicates();
+
+    let pred = predicates.next().unwrap();
+    let mut bounds = pred.type_bound_list().unwrap().bounds();
+
+    assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
+    assert_bound("Clone", bounds.next());
+    assert_bound("Copy", bounds.next());
+    assert_bound("Debug", bounds.next());
+    assert_bound("'static", bounds.next());
+
+    let pred = predicates.next().unwrap();
+    let mut bounds = pred.type_bound_list().unwrap().bounds();
+
+    assert_eq!("'a", pred.lifetime().unwrap().syntax().text().to_string());
+
+    assert_bound("'b", bounds.next());
+    assert_bound("'c", bounds.next());
+
+    let pred = predicates.next().unwrap();
+    let mut bounds = pred.type_bound_list().unwrap().bounds();
+
+    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
+    assert_bound("'a", bounds.next());
+
+    let pred = predicates.next().unwrap();
+    let mut bounds = pred.type_bound_list().unwrap().bounds();
+
+    assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
+    assert_bound("Debug", bounds.next());
+    assert_bound("'a", bounds.next());
+
+    let pred = predicates.next().unwrap();
+    let mut bounds = pred.type_bound_list().unwrap().bounds();
+
+    assert_eq!("<T as Iterator>::Item", pred.type_ref().unwrap().syntax().text().to_string());
+    assert_bound("Debug", bounds.next());
+    assert_bound("'a", bounds.next());
+
+    let pred = predicates.next().unwrap();
+    let mut bounds = pred.type_bound_list().unwrap().bounds();
+
+    assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string());
+    assert_bound("Fn(&'a str)", bounds.next());
+}
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 9ea423b40e2..c51b4caa4f1 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -4810,7 +4810,48 @@ impl ToOwned for WhereClause {
 }
 
 
-impl WhereClause {}
+impl WhereClause {
+    pub fn predicates(&self) -> impl Iterator<Item = &WherePred> {
+        super::children(self)
+    }
+}
+
+// WherePred
+#[derive(Debug, PartialEq, Eq, Hash)]
+#[repr(transparent)]
+pub struct WherePred {
+    pub(crate) syntax: SyntaxNode,
+}
+unsafe impl TransparentNewType for WherePred {
+    type Repr = rowan::SyntaxNode<RaTypes>;
+}
+
+impl AstNode for WherePred {
+    fn cast(syntax: &SyntaxNode) -> Option<&Self> {
+        match syntax.kind() {
+            WHERE_PRED => Some(WherePred::from_repr(syntax.into_repr())),
+            _ => None,
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+
+impl ToOwned for WherePred {
+    type Owned = TreeArc<WherePred>;
+    fn to_owned(&self) -> TreeArc<WherePred> { TreeArc::cast(self.syntax.to_owned()) }
+}
+
+
+impl ast::TypeBoundsOwner for WherePred {}
+impl WherePred {
+    pub fn type_ref(&self) -> Option<&TypeRef> {
+        super::child_opt(self)
+    }
+
+    pub fn lifetime(&self) -> Option<&Lifetime> {
+        super::child_opt(self)
+    }
+}
 
 // WhileExpr
 #[derive(Debug, PartialEq, Eq, Hash)]
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 18730a8941e..1123c2e9589 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -595,7 +595,20 @@ Grammar(
                 ["bounds", "TypeBound"],
             ]
         ),
-        "WhereClause": (),
+        "WherePred": (
+            options: [
+                "TypeRef",
+                "Lifetime",
+            ],
+            traits: [
+                "TypeBoundsOwner",
+            ],
+        ),
+        "WhereClause": (
+            collections: [
+                ["predicates", "WherePred"],
+            ],
+        ),
         "ExprStmt": (
             options: [ ["expr", "Expr"] ]
         ),
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0056_where_clause.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0056_where_clause.txt
index e04b61eb386..77a62ab4d86 100644
--- a/crates/ra_syntax/tests/data/parser/inline/ok/0056_where_clause.txt
+++ b/crates/ra_syntax/tests/data/parser/inline/ok/0056_where_clause.txt
@@ -15,11 +15,14 @@ SOURCE_FILE@[0; 116)
         LIFETIME@[18; 20) "'a"
         COLON@[20; 21)
         WHITESPACE@[21; 22)
-        LIFETIME@[22; 24) "'b"
-        WHITESPACE@[24; 25)
-        PLUS@[25; 26)
-        WHITESPACE@[26; 27)
-        LIFETIME@[27; 29) "'c"
+        TYPE_BOUND_LIST@[22; 29)
+          TYPE_BOUND@[22; 24)
+            LIFETIME@[22; 24) "'b"
+          WHITESPACE@[24; 25)
+          PLUS@[25; 26)
+          WHITESPACE@[26; 27)
+          TYPE_BOUND@[27; 29)
+            LIFETIME@[27; 29) "'c"
       COMMA@[29; 30)
       WHITESPACE@[30; 34)
       WHERE_PRED@[34; 59)