]> git.lizzy.rs Git - rust.git/blob - crates/syntax/src/ast/make.rs
0cf17062610996e5079b39d9c38e255215cd623e
[rust.git] / crates / syntax / src / ast / make.rs
1 //! This module contains free-standing functions for creating AST fragments out
2 //! of smaller pieces.
3 //!
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
6 //! smarter than that, it belongs to the `ext` submodule.
7 //!
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
10 //! `parse(format!())` we use internally is an implementation detail -- long
11 //! term, it will be replaced with direct tree manipulation.
12 use itertools::Itertools;
13 use stdx::{format_to, never};
14
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 }
48
49 pub fn name(text: &str) -> ast::Name {
50     ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text))
51 }
52 pub fn name_ref(text: &str) -> ast::NameRef {
53     ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text))
54 }
55 fn raw_ident_esc(ident: &str) -> &'static str {
56     let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some();
57     if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") {
58         "r#"
59     } else {
60         ""
61     }
62 }
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
74 // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
75 // `expr_xxx`.
76 pub fn ty(text: &str) -> ast::Type {
77     ty_from_text(text)
78 }
79 pub fn ty_unit() -> ast::Type {
80     ty_from_text("()")
81 }
82 pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
83     let mut count: usize = 0;
84     let mut contents = types.into_iter().inspect(|_| count += 1).join(", ");
85     if count == 1 {
86         contents.push(',');
87     }
88
89     ty_from_text(&format!("({})", contents))
90 }
91 pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type {
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))
99 }
100
101 pub fn assoc_item_list() -> ast::AssocItemList {
102     ast_from_text("impl C for D {}")
103 }
104
105 pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl {
106     ast_from_text(&format!("impl {} for {} {{}}", trait_, ty))
107 }
108
109 pub(crate) fn generic_arg_list() -> ast::GenericArgList {
110     ast_from_text("const S: T<> = ();")
111 }
112
113 pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
114     ast_from_text(&format!("use {};", name_ref))
115 }
116
117 pub fn path_segment_self() -> ast::PathSegment {
118     ast_from_text("use self;")
119 }
120
121 pub fn path_segment_super() -> ast::PathSegment {
122     ast_from_text("use super;")
123 }
124
125 pub fn path_segment_crate() -> ast::PathSegment {
126     ast_from_text("use crate;")
127 }
128
129 pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
130     ast_from_text(&format!("use {}", segment))
131 }
132
133 pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
134     ast_from_text(&format!("{}::{}", qual, segment))
135 }
136 // FIXME: path concatenation operation doesn't make sense as AST op.
137 pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path {
138     ast_from_text(&format!("{}::{}", first, second))
139 }
140
141 pub fn path_from_segments(
142     segments: impl IntoIterator<Item = ast::PathSegment>,
143     is_abs: bool,
144 ) -> ast::Path {
145     let segments = segments.into_iter().map(|it| it.syntax().clone()).join("::");
146     ast_from_text(&if is_abs {
147         format!("use ::{};", segments)
148     } else {
149         format!("use {};", segments)
150     })
151 }
152 // FIXME: should not be pub
153 pub fn path_from_text(text: &str) -> ast::Path {
154     ast_from_text(&format!("fn main() {{ let test = {}; }}", text))
155 }
156
157 pub fn use_tree_glob() -> ast::UseTree {
158     ast_from_text("use *;")
159 }
160 pub fn use_tree(
161     path: ast::Path,
162     use_tree_list: Option<ast::UseTreeList>,
163     alias: Option<ast::Rename>,
164     add_star: bool,
165 ) -> ast::UseTree {
166     let mut buf = "use ".to_string();
167     buf += &path.syntax().to_string();
168     if let Some(use_tree_list) = use_tree_list {
169         format_to!(buf, "::{}", use_tree_list);
170     }
171     if add_star {
172         buf += "::*";
173     }
174
175     if let Some(alias) = alias {
176         format_to!(buf, " {}", alias);
177     }
178     ast_from_text(&buf)
179 }
180
181 pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast::UseTreeList {
182     let use_trees = use_trees.into_iter().map(|it| it.syntax().clone()).join(", ");
183     ast_from_text(&format!("use {{{}}};", use_trees))
184 }
185
186 pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use {
187     let visibility = match visibility {
188         None => String::new(),
189         Some(it) => format!("{} ", it),
190     };
191     ast_from_text(&format!("{}use {};", visibility, use_tree))
192 }
193
194 pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr {
195     ast_from_text(&format!("fn f() {{ {} {} }}", path, fields))
196 }
197
198 pub fn record_expr_field_list(
199     fields: impl IntoIterator<Item = ast::RecordExprField>,
200 ) -> ast::RecordExprFieldList {
201     let fields = fields.into_iter().join(", ");
202     ast_from_text(&format!("fn f() {{ S {{ {} }} }}", fields))
203 }
204
205 pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField {
206     return match expr {
207         Some(expr) => from_text(&format!("{}: {}", name, expr)),
208         None => from_text(&name.to_string()),
209     };
210
211     fn from_text(text: &str) -> ast::RecordExprField {
212         ast_from_text(&format!("fn f() {{ S {{ {}, }} }}", text))
213     }
214 }
215
216 pub fn record_field(
217     visibility: Option<ast::Visibility>,
218     name: ast::Name,
219     ty: ast::Type,
220 ) -> ast::RecordField {
221     let visibility = match visibility {
222         None => String::new(),
223         Some(it) => format!("{} ", it),
224     };
225     ast_from_text(&format!("struct S {{ {}{}: {}, }}", visibility, name, ty))
226 }
227
228 pub fn block_expr(
229     stmts: impl IntoIterator<Item = ast::Stmt>,
230     tail_expr: Option<ast::Expr>,
231 ) -> ast::BlockExpr {
232     let mut buf = "{\n".to_string();
233     for stmt in stmts.into_iter() {
234         format_to!(buf, "    {}\n", stmt);
235     }
236     if let Some(tail_expr) = tail_expr {
237         format_to!(buf, "    {}\n", tail_expr)
238     }
239     buf += "}";
240     ast_from_text(&format!("fn f() {}", buf))
241 }
242
243 pub fn expr_unit() -> ast::Expr {
244     expr_from_text("()")
245 }
246 pub fn expr_literal(text: &str) -> ast::Literal {
247     assert_eq!(text.trim(), text);
248     ast_from_text(&format!("fn f() {{ let _ = {}; }}", text))
249 }
250
251 pub fn expr_empty_block() -> ast::Expr {
252     expr_from_text("{}")
253 }
254 pub fn expr_path(path: ast::Path) -> ast::Expr {
255     expr_from_text(&path.to_string())
256 }
257 pub fn expr_continue() -> ast::Expr {
258     expr_from_text("continue")
259 }
260 pub fn expr_break(expr: Option<ast::Expr>) -> ast::Expr {
261     match expr {
262         Some(expr) => expr_from_text(&format!("break {}", expr)),
263         None => expr_from_text("break"),
264     }
265 }
266 pub fn expr_return(expr: Option<ast::Expr>) -> ast::Expr {
267     match expr {
268         Some(expr) => expr_from_text(&format!("return {}", expr)),
269         None => expr_from_text("return"),
270     }
271 }
272 pub fn expr_try(expr: ast::Expr) -> ast::Expr {
273     expr_from_text(&format!("{}?", expr))
274 }
275 pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
276     expr_from_text(&format!("match {} {}", expr, match_arm_list))
277 }
278 pub fn expr_if(
279     condition: ast::Condition,
280     then_branch: ast::BlockExpr,
281     else_branch: Option<ast::ElseBranch>,
282 ) -> ast::Expr {
283     let else_branch = match else_branch {
284         Some(ast::ElseBranch::Block(block)) => format!("else {}", block),
285         Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {}", if_expr),
286         None => String::new(),
287     };
288     expr_from_text(&format!("if {} {} {}", condition, then_branch, else_branch))
289 }
290 pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::Expr {
291     expr_from_text(&format!("for {} in {} {}", pat, expr, block))
292 }
293 pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
294     let token = token(op);
295     expr_from_text(&format!("{}{}", token, expr))
296 }
297 pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr {
298     expr_from_text(&format!("{}{}", f, arg_list))
299 }
300 pub fn expr_method_call(receiver: ast::Expr, method: &str, arg_list: ast::ArgList) -> ast::Expr {
301     expr_from_text(&format!("{}.{}{}", receiver, method, arg_list))
302 }
303 pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
304     expr_from_text(&if exclusive { format!("&mut {}", expr) } else { format!("&{}", expr) })
305 }
306 pub fn expr_paren(expr: ast::Expr) -> ast::Expr {
307     expr_from_text(&format!("({})", expr))
308 }
309 pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr {
310     let expr = elements.into_iter().format(", ");
311     expr_from_text(&format!("({})", expr))
312 }
313 pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
314     expr_from_text(&format!("{} = {}", lhs, rhs))
315 }
316 fn expr_from_text(text: &str) -> ast::Expr {
317     ast_from_text(&format!("const C: () = {};", text))
318 }
319
320 pub fn condition(expr: ast::Expr, pattern: Option<ast::Pat>) -> ast::Condition {
321     match pattern {
322         None => ast_from_text(&format!("const _: () = while {} {{}};", expr)),
323         Some(pattern) => {
324             ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr))
325         }
326     }
327 }
328
329 pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
330     ast_from_text(&format!("fn main() {{ ()({}) }}", args.into_iter().format(", ")))
331 }
332
333 pub fn ident_pat(name: ast::Name) -> ast::IdentPat {
334     return from_text(&name.text());
335
336     fn from_text(text: &str) -> ast::IdentPat {
337         ast_from_text(&format!("fn f({}: ())", text))
338     }
339 }
340 pub fn ident_mut_pat(name: ast::Name) -> ast::IdentPat {
341     return from_text(&name.text());
342
343     fn from_text(text: &str) -> ast::IdentPat {
344         ast_from_text(&format!("fn f(mut {}: ())", text))
345     }
346 }
347
348 pub fn wildcard_pat() -> ast::WildcardPat {
349     return from_text("_");
350
351     fn from_text(text: &str) -> ast::WildcardPat {
352         ast_from_text(&format!("fn f({}: ())", text))
353     }
354 }
355
356 pub fn literal_pat(lit: &str) -> ast::LiteralPat {
357     return from_text(lit);
358
359     fn from_text(text: &str) -> ast::LiteralPat {
360         ast_from_text(&format!("fn f() {{ match x {{ {} => {{}} }} }}", text))
361     }
362 }
363
364 /// Creates a tuple of patterns from an iterator of patterns.
365 ///
366 /// Invariant: `pats` must be length > 0
367 pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat {
368     let mut count: usize = 0;
369     let mut pats_str = pats.into_iter().inspect(|_| count += 1).join(", ");
370     if count == 1 {
371         pats_str.push(',');
372     }
373     return from_text(&format!("({})", pats_str));
374
375     fn from_text(text: &str) -> ast::TuplePat {
376         ast_from_text(&format!("fn f({}: ())", text))
377     }
378 }
379
380 pub fn tuple_struct_pat(
381     path: ast::Path,
382     pats: impl IntoIterator<Item = ast::Pat>,
383 ) -> ast::TupleStructPat {
384     let pats_str = pats.into_iter().join(", ");
385     return from_text(&format!("{}({})", path, pats_str));
386
387     fn from_text(text: &str) -> ast::TupleStructPat {
388         ast_from_text(&format!("fn f({}: ())", text))
389     }
390 }
391
392 pub fn record_pat(path: ast::Path, pats: impl IntoIterator<Item = ast::Pat>) -> ast::RecordPat {
393     let pats_str = pats.into_iter().join(", ");
394     return from_text(&format!("{} {{ {} }}", path, pats_str));
395
396     fn from_text(text: &str) -> ast::RecordPat {
397         ast_from_text(&format!("fn f({}: ())", text))
398     }
399 }
400
401 pub fn record_pat_with_fields(path: ast::Path, fields: ast::RecordPatFieldList) -> ast::RecordPat {
402     ast_from_text(&format!("fn f({} {}: ()))", path, fields))
403 }
404
405 pub fn record_pat_field_list(
406     fields: impl IntoIterator<Item = ast::RecordPatField>,
407 ) -> ast::RecordPatFieldList {
408     let fields = fields.into_iter().join(", ");
409     ast_from_text(&format!("fn f(S {{ {} }}: ()))", fields))
410 }
411
412 pub fn record_pat_field(name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField {
413     ast_from_text(&format!("fn f(S {{ {}: {} }}: ()))", name_ref, pat))
414 }
415
416 /// Returns a `BindPat` if the path has just one segment, a `PathPat` otherwise.
417 pub fn path_pat(path: ast::Path) -> ast::Pat {
418     return from_text(&path.to_string());
419     fn from_text(text: &str) -> ast::Pat {
420         ast_from_text(&format!("fn f({}: ())", text))
421     }
422 }
423
424 pub fn match_arm(pats: impl IntoIterator<Item = ast::Pat>, expr: ast::Expr) -> ast::MatchArm {
425     let pats_str = pats.into_iter().join(" | ");
426     return from_text(&format!("{} => {}", pats_str, expr));
427
428     fn from_text(text: &str) -> ast::MatchArm {
429         ast_from_text(&format!("fn f() {{ match () {{{}}} }}", text))
430     }
431 }
432
433 pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
434     let arms_str = arms
435         .into_iter()
436         .map(|arm| {
437             let needs_comma = arm.expr().map_or(true, |it| !it.is_block_like());
438             let comma = if needs_comma { "," } else { "" };
439             format!("    {}{}\n", arm.syntax(), comma)
440         })
441         .collect::<String>();
442     return from_text(&arms_str);
443
444     fn from_text(text: &str) -> ast::MatchArmList {
445         ast_from_text(&format!("fn f() {{ match () {{\n{}}} }}", text))
446     }
447 }
448
449 pub fn where_pred(
450     path: ast::Path,
451     bounds: impl IntoIterator<Item = ast::TypeBound>,
452 ) -> ast::WherePred {
453     let bounds = bounds.into_iter().join(" + ");
454     return from_text(&format!("{}: {}", path, bounds));
455
456     fn from_text(text: &str) -> ast::WherePred {
457         ast_from_text(&format!("fn f() where {} {{ }}", text))
458     }
459 }
460
461 pub fn where_clause(preds: impl IntoIterator<Item = ast::WherePred>) -> ast::WhereClause {
462     let preds = preds.into_iter().join(", ");
463     return from_text(preds.as_str());
464
465     fn from_text(text: &str) -> ast::WhereClause {
466         ast_from_text(&format!("fn f() where {} {{ }}", text))
467     }
468 }
469
470 pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetStmt {
471     let text = match initializer {
472         Some(it) => format!("let {} = {};", pattern, it),
473         None => format!("let {};", pattern),
474     };
475     ast_from_text(&format!("fn f() {{ {} }}", text))
476 }
477 pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
478     let semi = if expr.is_block_like() { "" } else { ";" };
479     ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi))
480 }
481
482 pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
483     ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty))
484 }
485
486 pub fn ret_type(ty: ast::Type) -> ast::RetType {
487     ast_from_text(&format!("fn f() -> {} {{ }}", ty))
488 }
489
490 pub fn param_list(
491     self_param: Option<ast::SelfParam>,
492     pats: impl IntoIterator<Item = ast::Param>,
493 ) -> ast::ParamList {
494     let args = pats.into_iter().join(", ");
495     let list = match self_param {
496         Some(self_param) if args.is_empty() => format!("fn f({}) {{ }}", self_param),
497         Some(self_param) => format!("fn f({}, {}) {{ }}", self_param, args),
498         None => format!("fn f({}) {{ }}", args),
499     };
500     ast_from_text(&list)
501 }
502
503 pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam {
504     let bound = match ty {
505         Some(it) => format!(": {}", it),
506         None => String::new(),
507     };
508     ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound))
509 }
510
511 pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam {
512     ast_from_text(&format!("fn f<{}>() {{ }}", lifetime))
513 }
514
515 pub fn generic_param_list(
516     pats: impl IntoIterator<Item = ast::GenericParam>,
517 ) -> ast::GenericParamList {
518     let args = pats.into_iter().join(", ");
519     ast_from_text(&format!("fn f<{}>() {{ }}", args))
520 }
521
522 pub fn visibility_pub_crate() -> ast::Visibility {
523     ast_from_text("pub(crate) struct S")
524 }
525
526 pub fn visibility_pub() -> ast::Visibility {
527     ast_from_text("pub struct S")
528 }
529
530 pub fn tuple_field_list(fields: impl IntoIterator<Item = ast::TupleField>) -> ast::TupleFieldList {
531     let fields = fields.into_iter().join(", ");
532     ast_from_text(&format!("struct f({});", fields))
533 }
534
535 pub fn record_field_list(
536     fields: impl IntoIterator<Item = ast::RecordField>,
537 ) -> ast::RecordFieldList {
538     let fields = fields.into_iter().join(", ");
539     ast_from_text(&format!("struct f {{ {} }}", fields))
540 }
541
542 pub fn tuple_field(visibility: Option<ast::Visibility>, ty: ast::Type) -> ast::TupleField {
543     let visibility = match visibility {
544         None => String::new(),
545         Some(it) => format!("{} ", it),
546     };
547     ast_from_text(&format!("struct f({}{});", visibility, ty))
548 }
549
550 pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Variant {
551     let field_list = match field_list {
552         None => String::new(),
553         Some(it) => format!("{}", it),
554     };
555     ast_from_text(&format!("enum f {{ {}{} }}", name, field_list))
556 }
557
558 pub fn fn_(
559     visibility: Option<ast::Visibility>,
560     fn_name: ast::Name,
561     type_params: Option<ast::GenericParamList>,
562     params: ast::ParamList,
563     body: ast::BlockExpr,
564     ret_type: Option<ast::RetType>,
565 ) -> ast::Fn {
566     let type_params =
567         if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() };
568     let ret_type = if let Some(ret_type) = ret_type { format!("{} ", ret_type) } else { "".into() };
569     let visibility = match visibility {
570         None => String::new(),
571         Some(it) => format!("{} ", it),
572     };
573
574     ast_from_text(&format!(
575         "{}fn {}{}{} {}{}",
576         visibility, fn_name, type_params, params, ret_type, body
577     ))
578 }
579
580 pub fn struct_(
581     visibility: Option<ast::Visibility>,
582     strukt_name: ast::Name,
583     type_params: Option<ast::GenericParamList>,
584     field_list: ast::FieldList,
585 ) -> ast::Struct {
586     let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" };
587     let type_params =
588         if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() };
589     let visibility = match visibility {
590         None => String::new(),
591         Some(it) => format!("{} ", it),
592     };
593
594     ast_from_text(&format!(
595         "{}struct {}{}{}{}",
596         visibility, strukt_name, type_params, field_list, semicolon
597     ))
598 }
599
600 fn ast_from_text<N: AstNode>(text: &str) -> N {
601     let parse = SourceFile::parse(text);
602     let node = match parse.tree().syntax().descendants().find_map(N::cast) {
603         Some(it) => it,
604         None => {
605             panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text)
606         }
607     };
608     let node = node.clone_subtree();
609     assert_eq!(node.syntax().text_range().start(), 0.into());
610     node
611 }
612
613 pub fn token(kind: SyntaxKind) -> SyntaxToken {
614     tokens::SOURCE_FILE
615         .tree()
616         .syntax()
617         .clone_for_update()
618         .descendants_with_tokens()
619         .filter_map(|it| it.into_token())
620         .find(|it| it.kind() == kind)
621         .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
622 }
623
624 pub mod tokens {
625     use once_cell::sync::Lazy;
626
627     use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken};
628
629     pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
630         SourceFile::parse(
631             "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p)\n;\n\n",
632         )
633     });
634
635     pub fn single_space() -> SyntaxToken {
636         SOURCE_FILE
637             .tree()
638             .syntax()
639             .clone_for_update()
640             .descendants_with_tokens()
641             .filter_map(|it| it.into_token())
642             .find(|it| it.kind() == WHITESPACE && it.text() == " ")
643             .unwrap()
644     }
645
646     pub fn whitespace(text: &str) -> SyntaxToken {
647         assert!(text.trim().is_empty());
648         let sf = SourceFile::parse(text).ok().unwrap();
649         sf.syntax().clone_for_update().first_child_or_token().unwrap().into_token().unwrap()
650     }
651
652     pub fn doc_comment(text: &str) -> SyntaxToken {
653         assert!(!text.trim().is_empty());
654         let sf = SourceFile::parse(text).ok().unwrap();
655         sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
656     }
657
658     pub fn literal(text: &str) -> SyntaxToken {
659         assert_eq!(text.trim(), text);
660         let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {}; }}", text));
661         lit.syntax().first_child_or_token().unwrap().into_token().unwrap()
662     }
663
664     pub fn single_newline() -> SyntaxToken {
665         let res = SOURCE_FILE
666             .tree()
667             .syntax()
668             .clone_for_update()
669             .descendants_with_tokens()
670             .filter_map(|it| it.into_token())
671             .find(|it| it.kind() == WHITESPACE && it.text() == "\n")
672             .unwrap();
673         res.detach();
674         res
675     }
676
677     pub fn blank_line() -> SyntaxToken {
678         SOURCE_FILE
679             .tree()
680             .syntax()
681             .clone_for_update()
682             .descendants_with_tokens()
683             .filter_map(|it| it.into_token())
684             .find(|it| it.kind() == WHITESPACE && it.text() == "\n\n")
685             .unwrap()
686     }
687
688     pub struct WsBuilder(SourceFile);
689
690     impl WsBuilder {
691         pub fn new(text: &str) -> WsBuilder {
692             WsBuilder(SourceFile::parse(text).ok().unwrap())
693         }
694         pub fn ws(&self) -> SyntaxToken {
695             self.0.syntax().first_child_or_token().unwrap().into_token().unwrap()
696         }
697     }
698 }