diff --git a/crates/ra_syntax/src/grammar/mod.rs b/crates/ra_syntax/src/grammar/mod.rs index c875640730a..95c43798333 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 79f5036b415..735c728e5cc 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(); +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; + if p.at(EQ) { 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; - if p.at(EQ) { - p.bump(); - types::type_(p) - } - m.complete(p, TYPE_PARAM); + 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"); + } } - } else { - types::path_type(p); - if p.at(COLON) { - bounds(p); - } else { - p.error("expected colon") + // test where_pred_for + // fn test() + // 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 `<`"); + } + } + _ => { + types::path_type(p); + if p.at(COLON) { + bounds(p); + } else { + p.error("expected colon"); + } } } m.complete(p, WHERE_PRED); 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 00000000000..b448c617808 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/0113_where_pred_for.rs @@ -0,0 +1,4 @@ +fn test() +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 00000000000..08aacc77ab9 --- /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)