]> git.lizzy.rs Git - rust.git/blob - crates/ra_syntax/src/ast/make.rs
do refact and fix some issue
[rust.git] / crates / ra_syntax / src / ast / make.rs
1 //! This module contains free-standing functions for creating AST fragments out
2 //! of smaller pieces.
3 use itertools::Itertools;
4
5 use crate::{ast, AstNode, SourceFile};
6
7 pub fn name(text: &str) -> ast::Name {
8     ast_from_text(&format!("mod {};", text))
9 }
10
11 pub fn name_ref(text: &str) -> ast::NameRef {
12     ast_from_text(&format!("fn f() {{ {}; }}", text))
13 }
14
15 pub fn path_from_name_ref(name_ref: ast::NameRef) -> ast::Path {
16     path_from_text(&name_ref.syntax().to_string())
17 }
18 pub fn path_qualified(qual: ast::Path, name_ref: ast::NameRef) -> ast::Path {
19     path_from_text(&format!("{}::{}", qual.syntax(), name_ref.syntax()))
20 }
21 fn path_from_text(text: &str) -> ast::Path {
22     ast_from_text(text)
23 }
24
25 pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordField {
26     return match expr {
27         Some(expr) => from_text(&format!("{}: {}", name.syntax(), expr.syntax())),
28         None => from_text(&name.syntax().to_string()),
29     };
30
31     fn from_text(text: &str) -> ast::RecordField {
32         ast_from_text(&format!("fn f() {{ S {{ {}, }} }}", text))
33     }
34 }
35
36 pub fn block_from_expr(e: ast::Expr) -> ast::Block {
37     return from_text(&format!("{{ {} }}", e.syntax()));
38
39     fn from_text(text: &str) -> ast::Block {
40         ast_from_text(&format!("fn f() {}", text))
41     }
42 }
43
44 pub fn expr_unit() -> ast::Expr {
45     expr_from_text("()")
46 }
47 pub fn expr_unimplemented() -> ast::Expr {
48     expr_from_text("unimplemented!()")
49 }
50 pub fn expr_path(path: ast::Path) -> ast::Expr {
51     expr_from_text(&path.syntax().to_string())
52 }
53 pub fn expr_continue() -> ast::Expr {
54     expr_from_text("continue")
55 }
56 pub fn expr_break() -> ast::Expr {
57     expr_from_text("break")
58 }
59 pub fn expr_return() -> ast::Expr {
60     expr_from_text("return")
61 }
62 pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
63     expr_from_text(&format!("match {} {}", expr.syntax(), match_arm_list.syntax()))
64 }
65 fn expr_from_text(text: &str) -> ast::Expr {
66     ast_from_text(&format!("const C: () = {};", text))
67 }
68
69 pub fn bind_pat(name: ast::Name) -> ast::BindPat {
70     return from_text(name.text());
71
72     fn from_text(text: &str) -> ast::BindPat {
73         ast_from_text(&format!("fn f({}: ())", text))
74     }
75 }
76
77 pub fn placeholder_pat() -> ast::PlaceholderPat {
78     return from_text("_");
79
80     fn from_text(text: &str) -> ast::PlaceholderPat {
81         ast_from_text(&format!("fn f({}: ())", text))
82     }
83 }
84
85 pub fn tuple_struct_pat(
86     path: ast::Path,
87     pats: impl IntoIterator<Item = ast::Pat>,
88 ) -> ast::TupleStructPat {
89     let pats_str = pats.into_iter().map(|p| p.syntax().to_string()).join(", ");
90     return from_text(&format!("{}({})", path.syntax(), pats_str));
91
92     fn from_text(text: &str) -> ast::TupleStructPat {
93         ast_from_text(&format!("fn f({}: ())", text))
94     }
95 }
96
97 pub fn record_pat(path: ast::Path, pats: impl IntoIterator<Item = ast::Pat>) -> ast::RecordPat {
98     let pats_str = pats.into_iter().map(|p| p.syntax().to_string()).join(", ");
99     return from_text(&format!("{} {{ {} }}", path.syntax(), pats_str));
100
101     fn from_text(text: &str) -> ast::RecordPat {
102         ast_from_text(&format!("fn f({}: ())", text))
103     }
104 }
105
106 pub fn path_pat(path: ast::Path) -> ast::PathPat {
107     let path_str = path.syntax().text().to_string();
108     return from_text(path_str.as_str());
109     fn from_text(text: &str) -> ast::PathPat {
110         ast_from_text(&format!("fn f({}: ())", text))
111     }
112 }
113
114 pub fn match_arm(pats: impl IntoIterator<Item = ast::Pat>, expr: ast::Expr) -> ast::MatchArm {
115     let pats_str = pats.into_iter().map(|p| p.syntax().to_string()).join(" | ");
116     return from_text(&format!("{} => {}", pats_str, expr.syntax()));
117
118     fn from_text(text: &str) -> ast::MatchArm {
119         ast_from_text(&format!("fn f() {{ match () {{{}}} }}", text))
120     }
121 }
122
123 pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
124     let arms_str = arms.into_iter().map(|arm| format!("\n    {}", arm.syntax())).join(",");
125     return from_text(&format!("{},\n", arms_str));
126
127     fn from_text(text: &str) -> ast::MatchArmList {
128         ast_from_text(&format!("fn f() {{ match () {{{}}} }}", text))
129     }
130 }
131
132 pub fn where_pred(
133     path: ast::Path,
134     bounds: impl IntoIterator<Item = ast::TypeBound>,
135 ) -> ast::WherePred {
136     let bounds = bounds.into_iter().map(|b| b.syntax().to_string()).join(" + ");
137     return from_text(&format!("{}: {}", path.syntax(), bounds));
138
139     fn from_text(text: &str) -> ast::WherePred {
140         ast_from_text(&format!("fn f() where {} {{ }}", text))
141     }
142 }
143
144 pub fn where_clause(preds: impl IntoIterator<Item = ast::WherePred>) -> ast::WhereClause {
145     let preds = preds.into_iter().map(|p| p.syntax().to_string()).join(", ");
146     return from_text(preds.as_str());
147
148     fn from_text(text: &str) -> ast::WhereClause {
149         ast_from_text(&format!("fn f() where {} {{ }}", text))
150     }
151 }
152
153 pub fn if_expression(condition: &ast::Expr, statement: &str) -> ast::IfExpr {
154     ast_from_text(&format!(
155         "fn f() {{ if !{} {{\n    {}\n}}\n}}",
156         condition.syntax().text(),
157         statement
158     ))
159 }
160
161 pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetStmt {
162     let text = match initializer {
163         Some(it) => format!("let {} = {};", pattern.syntax(), it.syntax()),
164         None => format!("let {};", pattern.syntax()),
165     };
166     ast_from_text(&format!("fn f() {{ {} }}", text))
167 }
168
169 fn ast_from_text<N: AstNode>(text: &str) -> N {
170     let parse = SourceFile::parse(text);
171     let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap();
172     res
173 }
174
175 pub mod tokens {
176     use crate::{AstNode, Parse, SourceFile, SyntaxKind, SyntaxKind::*, SyntaxToken, T};
177     use once_cell::sync::Lazy;
178
179     static SOURCE_FILE: Lazy<Parse<SourceFile>> =
180         Lazy::new(|| SourceFile::parse("const C: () = (1 != 1, 2 == 2)\n;"));
181
182     pub fn op(op: SyntaxKind) -> SyntaxToken {
183         SOURCE_FILE
184             .tree()
185             .syntax()
186             .descendants_with_tokens()
187             .filter_map(|it| it.into_token())
188             .find(|it| it.kind() == op)
189             .unwrap()
190     }
191
192     pub fn comma() -> SyntaxToken {
193         SOURCE_FILE
194             .tree()
195             .syntax()
196             .descendants_with_tokens()
197             .filter_map(|it| it.into_token())
198             .find(|it| it.kind() == T![,])
199             .unwrap()
200     }
201
202     pub fn single_space() -> SyntaxToken {
203         SOURCE_FILE
204             .tree()
205             .syntax()
206             .descendants_with_tokens()
207             .filter_map(|it| it.into_token())
208             .find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ")
209             .unwrap()
210     }
211
212     pub fn whitespace(text: &str) -> SyntaxToken {
213         assert!(text.trim().is_empty());
214         let sf = SourceFile::parse(text).ok().unwrap();
215         sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
216     }
217
218     pub fn single_newline() -> SyntaxToken {
219         SOURCE_FILE
220             .tree()
221             .syntax()
222             .descendants_with_tokens()
223             .filter_map(|it| it.into_token())
224             .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n")
225             .unwrap()
226     }
227
228     pub struct WsBuilder(SourceFile);
229
230     impl WsBuilder {
231         pub fn new(text: &str) -> WsBuilder {
232             WsBuilder(SourceFile::parse(text).ok().unwrap())
233         }
234         pub fn ws(&self) -> SyntaxToken {
235             self.0.syntax().first_child_or_token().unwrap().into_token().unwrap()
236         }
237     }
238 }