From d334b5a1db9ec6a57f54077d422a3f4b3c8c1178 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 Feb 2019 13:27:45 +0300 Subject: move parser to a separate crate --- crates/ra_parser/src/grammar/items/consts.rs | 21 ++++ crates/ra_parser/src/grammar/items/nominal.rs | 168 +++++++++++++++++++++++++ crates/ra_parser/src/grammar/items/traits.rs | 137 ++++++++++++++++++++ crates/ra_parser/src/grammar/items/use_item.rs | 121 ++++++++++++++++++ 4 files changed, 447 insertions(+) create mode 100644 crates/ra_parser/src/grammar/items/consts.rs create mode 100644 crates/ra_parser/src/grammar/items/nominal.rs create mode 100644 crates/ra_parser/src/grammar/items/traits.rs create mode 100644 crates/ra_parser/src/grammar/items/use_item.rs (limited to 'crates/ra_parser/src/grammar/items') diff --git a/crates/ra_parser/src/grammar/items/consts.rs b/crates/ra_parser/src/grammar/items/consts.rs new file mode 100644 index 000000000..5a5852f83 --- /dev/null +++ b/crates/ra_parser/src/grammar/items/consts.rs @@ -0,0 +1,21 @@ +use super::*; + +pub(super) fn static_def(p: &mut Parser) { + const_or_static(p, STATIC_KW) +} + +pub(super) fn const_def(p: &mut Parser) { + const_or_static(p, CONST_KW) +} + +fn const_or_static(p: &mut Parser, kw: SyntaxKind) { + assert!(p.at(kw)); + p.bump(); + p.eat(MUT_KW); // TODO: validator to forbid const mut + name(p); + types::ascription(p); + if p.eat(EQ) { + expressions::expr(p); + } + p.expect(SEMI); +} diff --git a/crates/ra_parser/src/grammar/items/nominal.rs b/crates/ra_parser/src/grammar/items/nominal.rs new file mode 100644 index 000000000..ff9b38f9c --- /dev/null +++ b/crates/ra_parser/src/grammar/items/nominal.rs @@ -0,0 +1,168 @@ +use super::*; + +pub(super) fn struct_def(p: &mut Parser, kind: SyntaxKind) { + assert!(p.at(STRUCT_KW) || p.at_contextual_kw("union")); + p.bump_remap(kind); + + name_r(p, ITEM_RECOVERY_SET); + type_params::opt_type_param_list(p); + match p.current() { + WHERE_KW => { + type_params::opt_where_clause(p); + match p.current() { + SEMI => { + p.bump(); + return; + } + L_CURLY => named_field_def_list(p), + _ => { + //TODO: special case `(` error message + p.error("expected `;` or `{`"); + return; + } + } + } + SEMI if kind == STRUCT_KW => { + p.bump(); + return; + } + L_CURLY => named_field_def_list(p), + L_PAREN if kind == STRUCT_KW => { + pos_field_def_list(p); + // test tuple_struct_where + // struct Test(T) where T: Clone; + // struct Test(T); + type_params::opt_where_clause(p); + p.expect(SEMI); + } + _ if kind == STRUCT_KW => { + p.error("expected `;`, `{`, or `(`"); + return; + } + _ => { + p.error("expected `{`"); + return; + } + } +} + +pub(super) fn enum_def(p: &mut Parser) { + assert!(p.at(ENUM_KW)); + p.bump(); + name_r(p, ITEM_RECOVERY_SET); + type_params::opt_type_param_list(p); + type_params::opt_where_clause(p); + if p.at(L_CURLY) { + enum_variant_list(p); + } else { + p.error("expected `{`") + } +} + +pub(crate) fn enum_variant_list(p: &mut Parser) { + assert!(p.at(L_CURLY)); + let m = p.start(); + p.bump(); + while !p.at(EOF) && !p.at(R_CURLY) { + if p.at(L_CURLY) { + error_block(p, "expected enum variant"); + continue; + } + let var = p.start(); + attributes::outer_attributes(p); + if p.at(IDENT) { + name(p); + match p.current() { + L_CURLY => named_field_def_list(p), + L_PAREN => pos_field_def_list(p), + EQ => { + p.bump(); + expressions::expr(p); + } + _ => (), + } + var.complete(p, ENUM_VARIANT); + } else { + var.abandon(p); + p.err_and_bump("expected enum variant"); + } + if !p.at(R_CURLY) { + p.expect(COMMA); + } + } + p.expect(R_CURLY); + m.complete(p, ENUM_VARIANT_LIST); +} + +pub(crate) fn named_field_def_list(p: &mut Parser) { + assert!(p.at(L_CURLY)); + let m = p.start(); + p.bump(); + while !p.at(R_CURLY) && !p.at(EOF) { + if p.at(L_CURLY) { + error_block(p, "expected field"); + continue; + } + named_field_def(p); + if !p.at(R_CURLY) { + p.expect(COMMA); + } + } + p.expect(R_CURLY); + m.complete(p, NAMED_FIELD_DEF_LIST); + + fn named_field_def(p: &mut Parser) { + let m = p.start(); + // test field_attrs + // struct S { + // #[serde(with = "url_serde")] + // pub uri: Uri, + // } + attributes::outer_attributes(p); + opt_visibility(p); + if p.at(IDENT) { + name(p); + p.expect(COLON); + types::type_(p); + m.complete(p, NAMED_FIELD_DEF); + } else { + m.abandon(p); + p.err_and_bump("expected field declaration"); + } + } +} + +fn pos_field_def_list(p: &mut Parser) { + assert!(p.at(L_PAREN)); + let m = p.start(); + if !p.expect(L_PAREN) { + return; + } + while !p.at(R_PAREN) && !p.at(EOF) { + let m = p.start(); + // test pos_field_attrs + // struct S ( + // #[serde(with = "url_serde")] + // pub Uri, + // ); + // + // enum S { + // Uri(#[serde(with = "url_serde")] Uri), + // } + attributes::outer_attributes(p); + opt_visibility(p); + if !p.at_ts(types::TYPE_FIRST) { + p.error("expected a type"); + m.complete(p, ERROR); + break; + } + types::type_(p); + m.complete(p, POS_FIELD_DEF); + + if !p.at(R_PAREN) { + p.expect(COMMA); + } + } + p.expect(R_PAREN); + m.complete(p, POS_FIELD_DEF_LIST); +} diff --git a/crates/ra_parser/src/grammar/items/traits.rs b/crates/ra_parser/src/grammar/items/traits.rs new file mode 100644 index 000000000..d5a8ccd98 --- /dev/null +++ b/crates/ra_parser/src/grammar/items/traits.rs @@ -0,0 +1,137 @@ +use super::*; + +// test trait_item +// trait T: Hash + Clone where U: Copy {} +pub(super) fn trait_def(p: &mut Parser) { + assert!(p.at(TRAIT_KW)); + p.bump(); + name_r(p, ITEM_RECOVERY_SET); + type_params::opt_type_param_list(p); + if p.at(COLON) { + type_params::bounds(p); + } + type_params::opt_where_clause(p); + if p.at(L_CURLY) { + trait_item_list(p); + } else { + p.error("expected `{`"); + } +} + +// test trait_item_list +// impl F { +// type A: Clone; +// const B: i32; +// fn foo() {} +// fn bar(&self); +// } +pub(crate) fn trait_item_list(p: &mut Parser) { + assert!(p.at(L_CURLY)); + let m = p.start(); + p.bump(); + while !p.at(EOF) && !p.at(R_CURLY) { + if p.at(L_CURLY) { + error_block(p, "expected an item"); + continue; + } + item_or_macro(p, true, ItemFlavor::Trait); + } + p.expect(R_CURLY); + m.complete(p, ITEM_LIST); +} + +// test impl_block +// impl Foo {} +pub(super) fn impl_block(p: &mut Parser) { + assert!(p.at(IMPL_KW)); + p.bump(); + if choose_type_params_over_qpath(p) { + type_params::opt_type_param_list(p); + } + + // TODO: never type + // impl ! {} + + // test impl_block_neg + // impl !Send for X {} + p.eat(EXCL); + impl_type(p); + if p.eat(FOR_KW) { + impl_type(p); + } + type_params::opt_where_clause(p); + if p.at(L_CURLY) { + impl_item_list(p); + } else { + p.error("expected `{`"); + } +} + +// test impl_item_list +// impl F { +// type A = i32; +// const B: i32 = 92; +// fn foo() {} +// fn bar(&self) {} +// } +pub(crate) fn impl_item_list(p: &mut Parser) { + assert!(p.at(L_CURLY)); + let m = p.start(); + p.bump(); + // test impl_inner_attributes + // enum F{} + // impl F { + // //! This is a doc comment + // #![doc("This is also a doc comment")] + // } + attributes::inner_attributes(p); + + while !p.at(EOF) && !p.at(R_CURLY) { + if p.at(L_CURLY) { + error_block(p, "expected an item"); + continue; + } + item_or_macro(p, true, ItemFlavor::Mod); + } + p.expect(R_CURLY); + m.complete(p, ITEM_LIST); +} + +fn choose_type_params_over_qpath(p: &Parser) -> bool { + // There's an ambiguity between generic parameters and qualified paths in impls. + // If we see `<` it may start both, so we have to inspect some following tokens. + // The following combinations can only start generics, + // but not qualified paths (with one exception): + // `<` `>` - empty generic parameters + // `<` `#` - generic parameters with attributes + // `<` (LIFETIME|IDENT) `>` - single generic parameter + // `<` (LIFETIME|IDENT) `,` - first generic parameter in a list + // `<` (LIFETIME|IDENT) `:` - generic parameter with bounds + // `<` (LIFETIME|IDENT) `=` - generic parameter with a default + // The only truly ambiguous case is + // `<` IDENT `>` `::` IDENT ... + // we disambiguate it in favor of generics (`impl ::absolute::Path { ... }`) + // because this is what almost always expected in practice, qualified paths in impls + // (`impl ::AssocTy { ... }`) aren't even allowed by type checker at the moment. + if !p.at(L_ANGLE) { + return false; + } + if p.nth(1) == POUND || p.nth(1) == R_ANGLE { + return true; + } + (p.nth(1) == LIFETIME || p.nth(1) == IDENT) + && (p.nth(2) == R_ANGLE || p.nth(2) == COMMA || p.nth(2) == COLON || p.nth(2) == EQ) +} + +// test_err impl_type +// impl Type {} +// impl Trait1 for T {} +// impl impl NotType {} +// impl Trait2 for impl NotType {} +pub(crate) fn impl_type(p: &mut Parser) { + if p.at(IMPL_KW) { + p.error("expected trait or type"); + return; + } + types::type_(p); +} diff --git a/crates/ra_parser/src/grammar/items/use_item.rs b/crates/ra_parser/src/grammar/items/use_item.rs new file mode 100644 index 000000000..5111d37eb --- /dev/null +++ b/crates/ra_parser/src/grammar/items/use_item.rs @@ -0,0 +1,121 @@ +use super::*; + +pub(super) fn use_item(p: &mut Parser) { + assert!(p.at(USE_KW)); + p.bump(); + use_tree(p); + p.expect(SEMI); +} + +/// Parse a use 'tree', such as `some::path` in `use some::path;` +/// Note that this is called both by `use_item` and `use_tree_list`, +/// so handles both `some::path::{inner::path}` and `inner::path` in +/// `use some::path::{inner::path};` +fn use_tree(p: &mut Parser) { + let la = p.nth(1); + let m = p.start(); + match (p.current(), la) { + // Finish the use_tree for cases of e.g. + // `use some::path::{self, *};` or `use *;` + // This does not handle cases such as `use some::path::*` + // N.B. in Rust 2015 `use *;` imports all from crate root + // however in Rust 2018 `use *;` errors: ('cannot glob-import all possible crates') + // TODO: Add this error (if not out of scope) + + // test use_star + // use *; + // use ::*; + // use some::path::{*}; + // use some::path::{::*}; + (STAR, _) => p.bump(), + (COLONCOLON, STAR) => { + // Parse `use ::*;`, which imports all from the crate root in Rust 2015 + // This is invalid inside a use_tree_list, (e.g. `use some::path::{::*}`) + // but still parses and errors later: ('crate root in paths can only be used in start position') + // TODO: Add this error (if not out of scope) + // In Rust 2018, it is always invalid (see above) + p.bump(); + p.bump(); + } + // Open a use tree list + // Handles cases such as `use {some::path};` or `{inner::path}` in + // `use some::path::{{inner::path}, other::path}` + + // test use_tree_list + // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) + // use {path::from::root}; // Rust 2015 + // use ::{some::arbritrary::path}; // Rust 2015 + // use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig + (L_CURLY, _) | (COLONCOLON, L_CURLY) => { + if p.at(COLONCOLON) { + p.bump(); + } + use_tree_list(p); + } + // Parse a 'standard' path. + // Also handles aliases (e.g. `use something as something_else`) + + // test use_path + // use ::crate_name; // Rust 2018 - All flavours + // use crate_name; // Rust 2018 - Anchored paths + // use item_in_scope_or_crate_name; // Rust 2018 - Uniform Paths + // + // use self::module::Item; + // use crate::Item; + // use self::some::Struct; + // use crate_name::some_item; + _ if paths::is_path_start(p) => { + paths::use_path(p); + match p.current() { + AS_KW => { + // test use_alias + // use some::path as some_name; + // use some::{ + // other::path as some_other_name, + // different::path as different_name, + // yet::another::path, + // running::out::of::synonyms::for_::different::* + // }; + opt_alias(p); + } + COLONCOLON => { + p.bump(); + match p.current() { + STAR => { + p.bump(); + } + // test use_tree_list_after_path + // use crate::{Item}; + // use self::{Item}; + L_CURLY => use_tree_list(p), + _ => { + // is this unreachable? + p.error("expected `{` or `*`"); + } + } + } + _ => (), + } + } + _ => { + m.abandon(p); + p.err_and_bump("expected one of `*`, `::`, `{`, `self`, `super` or an indentifier"); + return; + } + } + m.complete(p, USE_TREE); +} + +pub(crate) fn use_tree_list(p: &mut Parser) { + assert!(p.at(L_CURLY)); + let m = p.start(); + p.bump(); + while !p.at(EOF) && !p.at(R_CURLY) { + use_tree(p); + if !p.at(R_CURLY) { + p.expect(COMMA); + } + } + p.expect(R_CURLY); + m.complete(p, USE_TREE_LIST); +} -- cgit v1.2.3