aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax
diff options
context:
space:
mode:
Diffstat (limited to 'crates/syntax')
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs19
-rw-r--r--crates/syntax/src/ast/make.rs111
2 files changed, 80 insertions, 50 deletions
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index 04f97f368..168355555 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -195,18 +195,13 @@ impl ast::GenericParamList {
195 pub fn add_generic_param(&self, generic_param: ast::GenericParam) { 195 pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
196 match self.generic_params().last() { 196 match self.generic_params().last() {
197 Some(last_param) => { 197 Some(last_param) => {
198 let mut elems = Vec::new(); 198 let position = Position::after(last_param.syntax());
199 if !last_param 199 let elements = vec![
200 .syntax() 200 make::token(T![,]).into(),
201 .siblings_with_tokens(Direction::Next) 201 make::tokens::single_space().into(),
202 .any(|it| it.kind() == T![,]) 202 generic_param.syntax().clone().into(),
203 { 203 ];
204 elems.push(make::token(T![,]).into()); 204 ted::insert_all(position, elements);
205 elems.push(make::tokens::single_space().into());
206 };
207 elems.push(generic_param.syntax().clone().into());
208 let after_last_param = Position::after(last_param.syntax());
209 ted::insert_all(after_last_param, elems);
210 } 205 }
211 None => { 206 None => {
212 let after_l_angle = Position::after(self.l_angle_token().unwrap()); 207 let after_l_angle = Position::after(self.l_angle_token().unwrap());
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 5a6687397..1998ad1f6 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.
12use itertools::Itertools; 12use itertools::Itertools;
13use stdx::format_to; 13use stdx::{format_to, never};
14 14
15use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; 15use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken};
16 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.
21pub 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}
48
17pub fn name(text: &str) -> ast::Name { 49pub 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
21pub fn name_ref(text: &str) -> ast::NameRef { 52pub 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
25fn raw_ident_esc(ident: &str) -> &'static str { 55fn 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
64pub 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`.
36pub fn ty(text: &str) -> ast::Type { 76pub fn ty(text: &str) -> ast::Type {
37 ast_from_text(&format!("fn f() -> {} {{}}", text)) 77 ty_from_text(text)
38} 78}
39pub fn ty_unit() -> ast::Type { 79pub fn ty_unit() -> ast::Type {
40 ty("()") 80 ty_from_text("()")
41} 81}
42pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { 82pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
43 let mut count: usize = 0; 83 let mut count: usize = 0;
@@ -46,15 +86,16 @@ 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
52pub 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}
56pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { 91pub 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}
94pub fn ty_path(path: ast::Path) -> ast::Type {
95 ty_from_text(&path.to_string())
96}
97fn ty_from_text(text: &str) -> ast::Type {
98 ast_from_text(&format!("type _T = {};", text))
58} 99}
59 100
60pub fn assoc_item_list() -> ast::AssocItemList { 101pub fn assoc_item_list() -> ast::AssocItemList {
@@ -88,7 +129,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
88pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { 129pub 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.
92pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { 133pub 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
108pub fn path_from_text(text: &str) -> ast::Path { 149pub 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
112pub fn glob_use_tree() -> ast::UseTree { 153pub fn use_tree_glob() -> ast::UseTree {
113 ast_from_text("use *;") 154 ast_from_text("use *;")
114} 155}
115
116pub fn use_tree( 156pub 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 {
207pub fn expr_empty_block() -> ast::Expr { 247pub fn expr_empty_block() -> ast::Expr {
208 expr_from_text("{}") 248 expr_from_text("{}")
209} 249}
210pub fn expr_unimplemented() -> ast::Expr {
211 expr_from_text("unimplemented!()")
212}
213pub fn expr_unreachable() -> ast::Expr {
214 expr_from_text("unreachable!()")
215}
216pub fn expr_todo() -> ast::Expr {
217 expr_from_text("todo!()")
218}
219pub fn expr_path(path: ast::Path) -> ast::Expr { 250pub fn expr_path(path: ast::Path) -> ast::Expr {
220 expr_from_text(&path.to_string()) 251 expr_from_text(&path.to_string())
221} 252}
@@ -444,17 +475,6 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
444 ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) 475 ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi))
445} 476}
446 477
447pub fn token(kind: SyntaxKind) -> SyntaxToken {
448 tokens::SOURCE_FILE
449 .tree()
450 .syntax()
451 .clone_for_update()
452 .descendants_with_tokens()
453 .filter_map(|it| it.into_token())
454 .find(|it| it.kind() == kind)
455 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
456}
457
458pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { 478pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
459 ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) 479 ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty))
460} 480}
@@ -476,7 +496,7 @@ pub fn param_list(
476 ast_from_text(&list) 496 ast_from_text(&list)
477} 497}
478 498
479pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { 499pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam {
480 let bound = match ty { 500 let bound = match ty {
481 Some(it) => format!(": {}", it), 501 Some(it) => format!(": {}", it),
482 None => String::new(), 502 None => String::new(),
@@ -484,6 +504,10 @@ pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::Gener
484 ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) 504 ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound))
485} 505}
486 506
507pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam {
508 ast_from_text(&format!("fn f<{}>() {{ }}", lifetime))
509}
510
487pub fn generic_param_list( 511pub fn generic_param_list(
488 pats: impl IntoIterator<Item = ast::GenericParam>, 512 pats: impl IntoIterator<Item = ast::GenericParam>,
489) -> ast::GenericParamList { 513) -> ast::GenericParamList {
@@ -588,6 +612,17 @@ fn unroot(n: SyntaxNode) -> SyntaxNode {
588 SyntaxNode::new_root(n.green().into()) 612 SyntaxNode::new_root(n.green().into())
589} 613}
590 614
615pub fn token(kind: SyntaxKind) -> SyntaxToken {
616 tokens::SOURCE_FILE
617 .tree()
618 .syntax()
619 .clone_for_update()
620 .descendants_with_tokens()
621 .filter_map(|it| it.into_token())
622 .find(|it| it.kind() == kind)
623 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
624}
625
591pub mod tokens { 626pub mod tokens {
592 use once_cell::sync::Lazy; 627 use once_cell::sync::Lazy;
593 628