diff options
Diffstat (limited to 'crates/syntax/src/ast/make.rs')
-rw-r--r-- | crates/syntax/src/ast/make.rs | 122 |
1 files changed, 77 insertions, 45 deletions
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index f8b508a90..d13926ded 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -3,25 +3,55 @@ | |||
3 | //! | 3 | //! |
4 | //! Note that all functions here intended to be stupid constructors, which just | 4 | //! Note that all functions here intended to be stupid constructors, which just |
5 | //! assemble a finish node from immediate children. If you want to do something | 5 | //! assemble a finish node from immediate children. If you want to do something |
6 | //! smarter than that, it probably doesn't belong in this module. | 6 | //! smarter than that, it belongs to the `ext` submodule. |
7 | //! | 7 | //! |
8 | //! Keep in mind that `from_text` functions should be kept private. The public | 8 | //! Keep in mind that `from_text` functions should be kept private. The public |
9 | //! API should require to assemble every node piecewise. The trick of | 9 | //! API should require to assemble every node piecewise. The trick of |
10 | //! `parse(format!())` we use internally is an implementation detail -- long | 10 | //! `parse(format!())` we use internally is an implementation detail -- long |
11 | //! term, it will be replaced with direct tree manipulation. | 11 | //! term, it will be replaced with direct tree manipulation. |
12 | use itertools::Itertools; | 12 | use itertools::Itertools; |
13 | use stdx::format_to; | 13 | use stdx::{format_to, never}; |
14 | 14 | ||
15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; | 15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxToken}; |
16 | |||
17 | /// While the parent module defines basic atomic "constructors", the `ext` | ||
18 | /// module defines shortcuts for common things. | ||
19 | /// | ||
20 | /// It's named `ext` rather than `shortcuts` just to keep it short. | ||
21 | pub mod ext { | ||
22 | use super::*; | ||
23 | |||
24 | pub fn ident_path(ident: &str) -> ast::Path { | ||
25 | path_unqualified(path_segment(name_ref(ident))) | ||
26 | } | ||
27 | |||
28 | pub fn expr_unreachable() -> ast::Expr { | ||
29 | expr_from_text("unreachable!()") | ||
30 | } | ||
31 | pub fn expr_todo() -> ast::Expr { | ||
32 | expr_from_text("todo!()") | ||
33 | } | ||
34 | pub fn empty_block_expr() -> ast::BlockExpr { | ||
35 | block_expr(None, None) | ||
36 | } | ||
37 | |||
38 | pub fn ty_bool() -> ast::Type { | ||
39 | ty_path(ident_path("bool")) | ||
40 | } | ||
41 | pub fn ty_option(t: ast::Type) -> ast::Type { | ||
42 | ty_from_text(&format!("Option<{}>", t)) | ||
43 | } | ||
44 | pub fn ty_result(t: ast::Type, e: ast::Type) -> ast::Type { | ||
45 | ty_from_text(&format!("Result<{}, {}>", t, e)) | ||
46 | } | ||
47 | } | ||
16 | 48 | ||
17 | pub fn name(text: &str) -> ast::Name { | 49 | pub fn name(text: &str) -> ast::Name { |
18 | ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) | 50 | ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) |
19 | } | 51 | } |
20 | |||
21 | pub fn name_ref(text: &str) -> ast::NameRef { | 52 | pub fn name_ref(text: &str) -> ast::NameRef { |
22 | ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) | 53 | ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) |
23 | } | 54 | } |
24 | |||
25 | fn raw_ident_esc(ident: &str) -> &'static str { | 55 | fn raw_ident_esc(ident: &str) -> &'static str { |
26 | let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); | 56 | let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); |
27 | if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { | 57 | if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { |
@@ -31,13 +61,23 @@ fn raw_ident_esc(ident: &str) -> &'static str { | |||
31 | } | 61 | } |
32 | } | 62 | } |
33 | 63 | ||
64 | pub fn lifetime(text: &str) -> ast::Lifetime { | ||
65 | let mut text = text; | ||
66 | let tmp; | ||
67 | if never!(!text.starts_with('\'')) { | ||
68 | tmp = format!("'{}", text); | ||
69 | text = &tmp; | ||
70 | } | ||
71 | ast_from_text(&format!("fn f<{}>() {{ }}", text)) | ||
72 | } | ||
73 | |||
34 | // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la | 74 | // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la |
35 | // `expr_xxx`. | 75 | // `expr_xxx`. |
36 | pub fn ty(text: &str) -> ast::Type { | 76 | pub fn ty(text: &str) -> ast::Type { |
37 | ast_from_text(&format!("fn f() -> {} {{}}", text)) | 77 | ty_from_text(text) |
38 | } | 78 | } |
39 | pub fn ty_unit() -> ast::Type { | 79 | pub fn ty_unit() -> ast::Type { |
40 | ty("()") | 80 | ty_from_text("()") |
41 | } | 81 | } |
42 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | 82 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { |
43 | let mut count: usize = 0; | 83 | let mut count: usize = 0; |
@@ -46,19 +86,20 @@ pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | |||
46 | contents.push(','); | 86 | contents.push(','); |
47 | } | 87 | } |
48 | 88 | ||
49 | ty(&format!("({})", contents)) | 89 | ty_from_text(&format!("({})", contents)) |
50 | } | ||
51 | // FIXME: handle path to type | ||
52 | pub fn ty_generic(name: ast::NameRef, types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | ||
53 | let contents = types.into_iter().join(", "); | ||
54 | ty(&format!("{}<{}>", name, contents)) | ||
55 | } | 90 | } |
56 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { | 91 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { |
57 | ty(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) | 92 | ty_from_text(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) |
93 | } | ||
94 | pub fn ty_path(path: ast::Path) -> ast::Type { | ||
95 | ty_from_text(&path.to_string()) | ||
96 | } | ||
97 | fn ty_from_text(text: &str) -> ast::Type { | ||
98 | ast_from_text(&format!("type _T = {};", text)) | ||
58 | } | 99 | } |
59 | 100 | ||
60 | pub fn assoc_item_list() -> ast::AssocItemList { | 101 | pub fn assoc_item_list() -> ast::AssocItemList { |
61 | ast_from_text("impl C for D {};") | 102 | ast_from_text("impl C for D {}") |
62 | } | 103 | } |
63 | 104 | ||
64 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { | 105 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { |
@@ -88,7 +129,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path { | |||
88 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { | 129 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { |
89 | ast_from_text(&format!("{}::{}", qual, segment)) | 130 | ast_from_text(&format!("{}::{}", qual, segment)) |
90 | } | 131 | } |
91 | 132 | // FIXME: path concatenation operation doesn't make sense as AST op. | |
92 | pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { | 133 | pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { |
93 | ast_from_text(&format!("{}::{}", first, second)) | 134 | ast_from_text(&format!("{}::{}", first, second)) |
94 | } | 135 | } |
@@ -104,15 +145,14 @@ pub fn path_from_segments( | |||
104 | format!("use {};", segments) | 145 | format!("use {};", segments) |
105 | }) | 146 | }) |
106 | } | 147 | } |
107 | 148 | // FIXME: should not be pub | |
108 | pub fn path_from_text(text: &str) -> ast::Path { | 149 | pub fn path_from_text(text: &str) -> ast::Path { |
109 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) | 150 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) |
110 | } | 151 | } |
111 | 152 | ||
112 | pub fn glob_use_tree() -> ast::UseTree { | 153 | pub fn use_tree_glob() -> ast::UseTree { |
113 | ast_from_text("use *;") | 154 | ast_from_text("use *;") |
114 | } | 155 | } |
115 | |||
116 | pub fn use_tree( | 156 | pub fn use_tree( |
117 | path: ast::Path, | 157 | path: ast::Path, |
118 | use_tree_list: Option<ast::UseTreeList>, | 158 | use_tree_list: Option<ast::UseTreeList>, |
@@ -207,15 +247,6 @@ pub fn expr_literal(text: &str) -> ast::Literal { | |||
207 | pub fn expr_empty_block() -> ast::Expr { | 247 | pub fn expr_empty_block() -> ast::Expr { |
208 | expr_from_text("{}") | 248 | expr_from_text("{}") |
209 | } | 249 | } |
210 | pub fn expr_unimplemented() -> ast::Expr { | ||
211 | expr_from_text("unimplemented!()") | ||
212 | } | ||
213 | pub fn expr_unreachable() -> ast::Expr { | ||
214 | expr_from_text("unreachable!()") | ||
215 | } | ||
216 | pub fn expr_todo() -> ast::Expr { | ||
217 | expr_from_text("todo!()") | ||
218 | } | ||
219 | pub fn expr_path(path: ast::Path) -> ast::Expr { | 250 | pub fn expr_path(path: ast::Path) -> ast::Expr { |
220 | expr_from_text(&path.to_string()) | 251 | expr_from_text(&path.to_string()) |
221 | } | 252 | } |
@@ -275,6 +306,9 @@ pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr { | |||
275 | let expr = elements.into_iter().format(", "); | 306 | let expr = elements.into_iter().format(", "); |
276 | expr_from_text(&format!("({})", expr)) | 307 | expr_from_text(&format!("({})", expr)) |
277 | } | 308 | } |
309 | pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { | ||
310 | expr_from_text(&format!("{} = {}", lhs, rhs)) | ||
311 | } | ||
278 | fn expr_from_text(text: &str) -> ast::Expr { | 312 | fn expr_from_text(text: &str) -> ast::Expr { |
279 | ast_from_text(&format!("const C: () = {};", text)) | 313 | ast_from_text(&format!("const C: () = {};", text)) |
280 | } | 314 | } |
@@ -441,17 +475,6 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { | |||
441 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) | 475 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) |
442 | } | 476 | } |
443 | 477 | ||
444 | pub fn token(kind: SyntaxKind) -> SyntaxToken { | ||
445 | tokens::SOURCE_FILE | ||
446 | .tree() | ||
447 | .syntax() | ||
448 | .clone_for_update() | ||
449 | .descendants_with_tokens() | ||
450 | .filter_map(|it| it.into_token()) | ||
451 | .find(|it| it.kind() == kind) | ||
452 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | ||
453 | } | ||
454 | |||
455 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { | 478 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { |
456 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) | 479 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) |
457 | } | 480 | } |
@@ -473,7 +496,7 @@ pub fn param_list( | |||
473 | ast_from_text(&list) | 496 | ast_from_text(&list) |
474 | } | 497 | } |
475 | 498 | ||
476 | pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { | 499 | pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam { |
477 | let bound = match ty { | 500 | let bound = match ty { |
478 | Some(it) => format!(": {}", it), | 501 | Some(it) => format!(": {}", it), |
479 | None => String::new(), | 502 | None => String::new(), |
@@ -481,6 +504,10 @@ pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::Gener | |||
481 | ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) | 504 | ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) |
482 | } | 505 | } |
483 | 506 | ||
507 | pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam { | ||
508 | ast_from_text(&format!("fn f<{}>() {{ }}", lifetime)) | ||
509 | } | ||
510 | |||
484 | pub fn generic_param_list( | 511 | pub fn generic_param_list( |
485 | pats: impl IntoIterator<Item = ast::GenericParam>, | 512 | pats: impl IntoIterator<Item = ast::GenericParam>, |
486 | ) -> ast::GenericParamList { | 513 | ) -> ast::GenericParamList { |
@@ -574,15 +601,20 @@ fn ast_from_text<N: AstNode>(text: &str) -> N { | |||
574 | panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text) | 601 | panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text) |
575 | } | 602 | } |
576 | }; | 603 | }; |
577 | let node = node.syntax().clone(); | 604 | let node = node.clone_subtree(); |
578 | let node = unroot(node); | ||
579 | let node = N::cast(node).unwrap(); | ||
580 | assert_eq!(node.syntax().text_range().start(), 0.into()); | 605 | assert_eq!(node.syntax().text_range().start(), 0.into()); |
581 | node | 606 | node |
582 | } | 607 | } |
583 | 608 | ||
584 | fn unroot(n: SyntaxNode) -> SyntaxNode { | 609 | pub fn token(kind: SyntaxKind) -> SyntaxToken { |
585 | SyntaxNode::new_root(n.green().into()) | 610 | tokens::SOURCE_FILE |
611 | .tree() | ||
612 | .syntax() | ||
613 | .clone_for_update() | ||
614 | .descendants_with_tokens() | ||
615 | .filter_map(|it| it.into_token()) | ||
616 | .find(|it| it.kind() == kind) | ||
617 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | ||
586 | } | 618 | } |
587 | 619 | ||
588 | pub mod tokens { | 620 | pub mod tokens { |