aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-05-09 17:51:06 +0100
committerAleksey Kladov <[email protected]>2021-05-09 17:55:43 +0100
commit4f3c0adc5aafea465c71c85f36484da970df1ba2 (patch)
tree4cb502a6c345c3ecfb4090067ab1de2cfd69ed55 /crates/syntax
parent680a0d54e4d2d474ae41f4f4a95c749495a02883 (diff)
internal: introduce `ast::make::ext` module with common shortcuts
There's a tension between keeping a well-architectured minimal orthogonal set of constructs, and providing convenience functions. Relieve this pressure by introducing an dedicated module for non-orthogonal shortcuts. This is inspired by the django.shortcuts module which serves a similar purpose architecturally.
Diffstat (limited to 'crates/syntax')
-rw-r--r--crates/syntax/src/ast/make.rs95
1 files changed, 52 insertions, 43 deletions
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index c39e248ce..a378b1d37 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -3,7 +3,7 @@
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
@@ -14,13 +14,49 @@ use 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
35 pub fn ty_bool() -> ast::Type {
36 ty_path(ident_path("bool"))
37 }
38 pub fn ty_option(t: ast::Type) -> ast::Type {
39 ty_from_text(&format!("Option<{}>", t))
40 }
41 pub fn ty_result(t: ast::Type, e: ast::Type) -> ast::Type {
42 ty_from_text(&format!("Result<{}, {}>", t, e))
43 }
44}
45
17pub fn name(text: &str) -> ast::Name { 46pub fn name(text: &str) -> ast::Name {
18 ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) 47 ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text))
19} 48}
20
21pub fn name_ref(text: &str) -> ast::NameRef { 49pub fn name_ref(text: &str) -> ast::NameRef {
22 ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) 50 ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text))
23} 51}
52fn raw_ident_esc(ident: &str) -> &'static str {
53 let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some();
54 if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") {
55 "r#"
56 } else {
57 ""
58 }
59}
24 60
25pub fn lifetime(text: &str) -> ast::Lifetime { 61pub fn lifetime(text: &str) -> ast::Lifetime {
26 let mut text = text; 62 let mut text = text;
@@ -32,15 +68,6 @@ pub fn lifetime(text: &str) -> ast::Lifetime {
32 ast_from_text(&format!("fn f<{}>() {{ }}", text)) 68 ast_from_text(&format!("fn f<{}>() {{ }}", text))
33} 69}
34 70
35fn raw_ident_esc(ident: &str) -> &'static str {
36 let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some();
37 if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") {
38 "r#"
39 } else {
40 ""
41 }
42}
43
44// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la 71// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
45// `expr_xxx`. 72// `expr_xxx`.
46pub fn ty(text: &str) -> ast::Type { 73pub fn ty(text: &str) -> ast::Type {
@@ -49,9 +76,6 @@ pub fn ty(text: &str) -> ast::Type {
49pub fn ty_unit() -> ast::Type { 76pub fn ty_unit() -> ast::Type {
50 ty_from_text("()") 77 ty_from_text("()")
51} 78}
52pub fn ty_bool() -> ast::Type {
53 ty_path(path_unqualified(path_segment(name_ref("bool"))))
54}
55pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { 79pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
56 let mut count: usize = 0; 80 let mut count: usize = 0;
57 let mut contents = types.into_iter().inspect(|_| count += 1).join(", "); 81 let mut contents = types.into_iter().inspect(|_| count += 1).join(", ");
@@ -61,11 +85,6 @@ pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
61 85
62 ty_from_text(&format!("({})", contents)) 86 ty_from_text(&format!("({})", contents))
63} 87}
64// FIXME: handle path to type
65pub fn ty_generic(name: ast::NameRef, types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
66 let contents = types.into_iter().join(", ");
67 ty_from_text(&format!("{}<{}>", name, contents))
68}
69pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { 88pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type {
70 ty_from_text(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) 89 ty_from_text(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) })
71} 90}
@@ -107,7 +126,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
107pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { 126pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
108 ast_from_text(&format!("{}::{}", qual, segment)) 127 ast_from_text(&format!("{}::{}", qual, segment))
109} 128}
110 129// FIXME: path concatenation operation doesn't make sense as AST op.
111pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { 130pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path {
112 ast_from_text(&format!("{}::{}", first, second)) 131 ast_from_text(&format!("{}::{}", first, second))
113} 132}
@@ -123,15 +142,14 @@ pub fn path_from_segments(
123 format!("use {};", segments) 142 format!("use {};", segments)
124 }) 143 })
125} 144}
126 145// FIXME: should not be pub
127pub fn path_from_text(text: &str) -> ast::Path { 146pub fn path_from_text(text: &str) -> ast::Path {
128 ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) 147 ast_from_text(&format!("fn main() {{ let test = {}; }}", text))
129} 148}
130 149
131pub fn glob_use_tree() -> ast::UseTree { 150pub fn use_tree_glob() -> ast::UseTree {
132 ast_from_text("use *;") 151 ast_from_text("use *;")
133} 152}
134
135pub fn use_tree( 153pub fn use_tree(
136 path: ast::Path, 154 path: ast::Path,
137 use_tree_list: Option<ast::UseTreeList>, 155 use_tree_list: Option<ast::UseTreeList>,
@@ -226,15 +244,6 @@ pub fn expr_literal(text: &str) -> ast::Literal {
226pub fn expr_empty_block() -> ast::Expr { 244pub fn expr_empty_block() -> ast::Expr {
227 expr_from_text("{}") 245 expr_from_text("{}")
228} 246}
229pub fn expr_unimplemented() -> ast::Expr {
230 expr_from_text("unimplemented!()")
231}
232pub fn expr_unreachable() -> ast::Expr {
233 expr_from_text("unreachable!()")
234}
235pub fn expr_todo() -> ast::Expr {
236 expr_from_text("todo!()")
237}
238pub fn expr_path(path: ast::Path) -> ast::Expr { 247pub fn expr_path(path: ast::Path) -> ast::Expr {
239 expr_from_text(&path.to_string()) 248 expr_from_text(&path.to_string())
240} 249}
@@ -463,17 +472,6 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
463 ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) 472 ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi))
464} 473}
465 474
466pub fn token(kind: SyntaxKind) -> SyntaxToken {
467 tokens::SOURCE_FILE
468 .tree()
469 .syntax()
470 .clone_for_update()
471 .descendants_with_tokens()
472 .filter_map(|it| it.into_token())
473 .find(|it| it.kind() == kind)
474 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
475}
476
477pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { 475pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
478 ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) 476 ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty))
479} 477}
@@ -611,6 +609,17 @@ fn unroot(n: SyntaxNode) -> SyntaxNode {
611 SyntaxNode::new_root(n.green().into()) 609 SyntaxNode::new_root(n.green().into())
612} 610}
613 611
612pub fn token(kind: SyntaxKind) -> SyntaxToken {
613 tokens::SOURCE_FILE
614 .tree()
615 .syntax()
616 .clone_for_update()
617 .descendants_with_tokens()
618 .filter_map(|it| it.into_token())
619 .find(|it| it.kind() == kind)
620 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
621}
622
614pub mod tokens { 623pub mod tokens {
615 use once_cell::sync::Lazy; 624 use once_cell::sync::Lazy;
616 625