diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs index 42763fc87c..07d9b07921 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 4fddc00ea5..fd7e63f849 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, + ::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!("::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 9ea423b40e..c51b4caa4f 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 { + 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; +} + +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; + fn to_owned(&self) -> TreeArc { 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 18730a8941..1123c2e958 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 e04b61eb38..77a62ab4d8 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)