syntax/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 `quote!`. Do not add more usages to `from_text` -
12//! use `quote!` instead.
13
14mod quote;
15
16use either::Either;
17use itertools::Itertools;
18use parser::{Edition, T};
19use rowan::NodeOrToken;
20use stdx::{format_to, format_to_acc, never};
21
22use crate::{
23    AstNode, SourceFile, SyntaxKind, SyntaxToken,
24    ast::{self, Param, make::quote::quote},
25    utils::is_raw_identifier,
26};
27
28/// While the parent module defines basic atomic "constructors", the `ext`
29/// module defines shortcuts for common things.
30///
31/// It's named `ext` rather than `shortcuts` just to keep it short.
32pub mod ext {
33    use super::*;
34
35    pub fn simple_ident_pat(name: ast::Name) -> ast::IdentPat {
36        ast_from_text(&format!("fn f({}: ())", name.text()))
37    }
38
39    pub fn ident_path(ident: &str) -> ast::Path {
40        path_unqualified(path_segment(name_ref(ident)))
41    }
42
43    pub fn path_from_idents<'a>(
44        parts: impl std::iter::IntoIterator<Item = &'a str>,
45    ) -> Option<ast::Path> {
46        let mut iter = parts.into_iter();
47        let base = ext::ident_path(iter.next()?);
48        let path = iter.fold(base, |base, s| {
49            let path = ext::ident_path(s);
50            path_concat(base, path)
51        });
52        Some(path)
53    }
54
55    pub fn field_from_idents<'a>(
56        parts: impl std::iter::IntoIterator<Item = &'a str>,
57    ) -> Option<ast::Expr> {
58        let mut iter = parts.into_iter();
59        let base = expr_path(ext::ident_path(iter.next()?));
60        let expr = iter.fold(base, expr_field);
61        Some(expr)
62    }
63
64    pub fn expr_unit() -> ast::Expr {
65        expr_tuple([]).into()
66    }
67    pub fn expr_unreachable() -> ast::Expr {
68        expr_from_text("unreachable!()")
69    }
70    pub fn expr_todo() -> ast::Expr {
71        expr_from_text("todo!()")
72    }
73    pub fn expr_underscore() -> ast::Expr {
74        expr_from_text("_")
75    }
76    pub fn expr_ty_default(ty: &ast::Type) -> ast::Expr {
77        expr_from_text(&format!("{ty}::default()"))
78    }
79    pub fn expr_ty_new(ty: &ast::Type) -> ast::Expr {
80        expr_from_text(&format!("{ty}::new()"))
81    }
82    pub fn expr_self() -> ast::Expr {
83        expr_from_text("self")
84    }
85    pub fn zero_number() -> ast::Expr {
86        expr_from_text("0")
87    }
88    pub fn zero_float() -> ast::Expr {
89        expr_from_text("0.0")
90    }
91    pub fn empty_str() -> ast::Expr {
92        expr_from_text(r#""""#)
93    }
94    pub fn empty_char() -> ast::Expr {
95        expr_from_text("'\x00'")
96    }
97    pub fn default_bool() -> ast::Expr {
98        expr_from_text("false")
99    }
100    pub fn option_none() -> ast::Expr {
101        expr_from_text("None")
102    }
103    pub fn empty_block_expr() -> ast::BlockExpr {
104        block_expr(None, None)
105    }
106
107    pub fn ty_name(name: ast::Name) -> ast::Type {
108        ty_path(ident_path(&name.to_string()))
109    }
110    pub fn ty_bool() -> ast::Type {
111        ty_path(ident_path("bool"))
112    }
113    pub fn ty_option(t: ast::Type) -> ast::Type {
114        ty_from_text(&format!("Option<{t}>"))
115    }
116    pub fn ty_result(t: ast::Type, e: ast::Type) -> ast::Type {
117        ty_from_text(&format!("Result<{t}, {e}>"))
118    }
119
120    pub fn token_tree_from_node(node: &ast::SyntaxNode) -> ast::TokenTree {
121        ast_from_text(&format!("todo!{node}"))
122    }
123}
124
125pub fn name(name: &str) -> ast::Name {
126    let raw_escape = raw_ident_esc(name);
127    ast_from_text(&format!("mod {raw_escape}{name};"))
128}
129pub fn name_ref(name_ref: &str) -> ast::NameRef {
130    let raw_escape = raw_ident_esc(name_ref);
131    quote! {
132        NameRef {
133            [IDENT format!("{raw_escape}{name_ref}")]
134        }
135    }
136}
137pub fn name_ref_self_ty() -> ast::NameRef {
138    quote! {
139        NameRef {
140            [Self]
141        }
142    }
143}
144fn raw_ident_esc(ident: &str) -> &'static str {
145    if is_raw_identifier(ident, Edition::CURRENT) { "r#" } else { "" }
146}
147
148pub fn lifetime(text: &str) -> ast::Lifetime {
149    let mut text = text;
150    let tmp;
151    if never!(!text.starts_with('\'')) {
152        tmp = format!("'{text}");
153        text = &tmp;
154    }
155    quote! {
156        Lifetime {
157            [LIFETIME_IDENT text]
158        }
159    }
160}
161
162// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
163// `expr_xxx`.
164pub fn ty(text: &str) -> ast::Type {
165    ty_from_text(text)
166}
167pub fn ty_placeholder() -> ast::Type {
168    ty_from_text("_")
169}
170pub fn ty_unit() -> ast::Type {
171    ty_from_text("()")
172}
173pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
174    let mut count: usize = 0;
175    let mut contents = types.into_iter().inspect(|_| count += 1).join(", ");
176    if count == 1 {
177        contents.push(',');
178    }
179
180    ty_from_text(&format!("({contents})"))
181}
182pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type {
183    ty_from_text(&if exclusive { format!("&mut {target}") } else { format!("&{target}") })
184}
185pub fn ty_path(path: ast::Path) -> ast::Type {
186    ty_from_text(&path.to_string())
187}
188fn ty_from_text(text: &str) -> ast::Type {
189    ast_from_text(&format!("type _T = {text};"))
190}
191
192pub fn ty_alias(
193    attrs: impl IntoIterator<Item = ast::Attr>,
194    ident: &str,
195    generic_param_list: Option<ast::GenericParamList>,
196    type_param_bounds: Option<ast::TypeParam>,
197    where_clause: Option<ast::WhereClause>,
198    assignment: Option<(ast::Type, Option<ast::WhereClause>)>,
199) -> ast::TypeAlias {
200    let (assignment_ty, assignment_where) = assignment.unzip();
201    let assignment_where = assignment_where.flatten();
202    quote! {
203        TypeAlias {
204            #(#attrs "\n")*
205            [type] " "
206                Name { [IDENT ident] }
207                #generic_param_list
208                #(" " [:] " " #type_param_bounds)*
209                #(" " #where_clause)*
210                #(" " [=] " " #assignment_ty)*
211                #(" " #assignment_where)*
212            [;]
213        }
214    }
215}
216
217pub fn ty_fn_ptr<I: Iterator<Item = Param>>(
218    is_unsafe: bool,
219    abi: Option<ast::Abi>,
220    mut params: I,
221    ret_type: Option<ast::RetType>,
222) -> ast::FnPtrType {
223    let is_unsafe = is_unsafe.then_some(());
224    let first_param = params.next();
225    quote! {
226        FnPtrType {
227            #(#is_unsafe [unsafe] " ")* #(#abi " ")* [fn]
228                ['('] #first_param #([,] " " #params)* [')']
229                #(" " #ret_type)*
230        }
231    }
232}
233
234pub fn item_list(body: Option<Vec<ast::Item>>) -> ast::ItemList {
235    let is_break_braces = body.is_some();
236    let body_newline = if is_break_braces { "\n" } else { "" };
237    let body_indent = if is_break_braces { "    " } else { "" };
238
239    let body = match body {
240        Some(bd) => bd.iter().map(|elem| elem.to_string()).join("\n\n    "),
241        None => String::new(),
242    };
243    ast_from_text(&format!("mod C {{{body_newline}{body_indent}{body}{body_newline}}}"))
244}
245
246pub fn mod_(name: ast::Name, body: Option<ast::ItemList>) -> ast::Module {
247    let body = body.map_or(";".to_owned(), |body| format!(" {body}"));
248    ast_from_text(&format!("mod {name}{body}"))
249}
250
251pub fn assoc_item_list(body: Option<Vec<ast::AssocItem>>) -> ast::AssocItemList {
252    let is_break_braces = body.is_some();
253    let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() };
254    let body_indent = if is_break_braces { "    ".to_owned() } else { String::new() };
255
256    let body = match body {
257        Some(bd) => bd.iter().map(|elem| elem.to_string()).join("\n\n    "),
258        None => String::new(),
259    };
260    ast_from_text(&format!("impl C for D {{{body_newline}{body_indent}{body}{body_newline}}}"))
261}
262
263fn merge_gen_params(
264    ps: Option<ast::GenericParamList>,
265    bs: Option<ast::GenericParamList>,
266) -> Option<ast::GenericParamList> {
267    match (ps, bs) {
268        (None, None) => None,
269        (None, Some(bs)) => Some(bs),
270        (Some(ps), None) => Some(ps),
271        (Some(ps), Some(bs)) => {
272            // make sure lifetime is placed before other generic params
273            let generic_params = ps.generic_params().merge_by(bs.generic_params(), |_, b| {
274                !matches!(b, ast::GenericParam::LifetimeParam(_))
275            });
276            Some(generic_param_list(generic_params))
277        }
278    }
279}
280
281fn merge_where_clause(
282    ps: Option<ast::WhereClause>,
283    bs: Option<ast::WhereClause>,
284) -> Option<ast::WhereClause> {
285    match (ps, bs) {
286        (None, None) => None,
287        (None, Some(bs)) => Some(bs),
288        (Some(ps), None) => Some(ps),
289        (Some(ps), Some(bs)) => {
290            let preds = where_clause(std::iter::empty()).clone_for_update();
291            ps.predicates().for_each(|p| preds.add_predicate(p));
292            bs.predicates().for_each(|p| preds.add_predicate(p));
293            Some(preds)
294        }
295    }
296}
297
298pub fn impl_(
299    attrs: impl IntoIterator<Item = ast::Attr>,
300    generic_params: Option<ast::GenericParamList>,
301    generic_args: Option<ast::GenericArgList>,
302    path_type: ast::Type,
303    where_clause: Option<ast::WhereClause>,
304    body: Option<ast::AssocItemList>,
305) -> ast::Impl {
306    let attrs =
307        attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
308
309    let gen_args = generic_args.map_or_else(String::new, |it| it.to_string());
310
311    let gen_params = generic_params.map_or_else(String::new, |it| it.to_string());
312
313    let body_newline =
314        if where_clause.is_some() && body.is_none() { "\n".to_owned() } else { String::new() };
315    let where_clause = match where_clause {
316        Some(pr) => format!("\n{pr}\n"),
317        None => " ".to_owned(),
318    };
319
320    let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string());
321    ast_from_text(&format!("{attrs}impl{gen_params} {path_type}{gen_args}{where_clause}{body}"))
322}
323
324pub fn impl_trait(
325    attrs: impl IntoIterator<Item = ast::Attr>,
326    is_unsafe: bool,
327    trait_gen_params: Option<ast::GenericParamList>,
328    trait_gen_args: Option<ast::GenericArgList>,
329    type_gen_params: Option<ast::GenericParamList>,
330    type_gen_args: Option<ast::GenericArgList>,
331    is_negative: bool,
332    path_type: ast::Type,
333    ty: ast::Type,
334    trait_where_clause: Option<ast::WhereClause>,
335    ty_where_clause: Option<ast::WhereClause>,
336    body: Option<ast::AssocItemList>,
337) -> ast::Impl {
338    let attrs =
339        attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
340    let is_unsafe = if is_unsafe { "unsafe " } else { "" };
341
342    let trait_gen_args = trait_gen_args.map(|args| args.to_string()).unwrap_or_default();
343    let type_gen_args = type_gen_args.map(|args| args.to_string()).unwrap_or_default();
344
345    let gen_params = merge_gen_params(trait_gen_params, type_gen_params)
346        .map_or_else(String::new, |it| it.to_string());
347
348    let is_negative = if is_negative { "! " } else { "" };
349
350    let body_newline =
351        if (ty_where_clause.is_some() || trait_where_clause.is_some()) && body.is_none() {
352            "\n".to_owned()
353        } else {
354            String::new()
355        };
356
357    let where_clause = merge_where_clause(ty_where_clause, trait_where_clause)
358        .map_or_else(|| " ".to_owned(), |wc| format!("\n{wc}\n"));
359
360    let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string());
361
362    ast_from_text(&format!(
363        "{attrs}{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}"
364    ))
365}
366
367pub fn impl_trait_type(bounds: ast::TypeBoundList) -> ast::ImplTraitType {
368    ast_from_text(&format!("fn f(x: impl {bounds}) {{}}"))
369}
370
371pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
372    ast_from_text(&format!("type __ = {name_ref};"))
373}
374
375/// Type and expressions/patterns path differ in whether they require `::` before generic arguments.
376/// Type paths allow them but they are often omitted, while expression/pattern paths require them.
377pub fn generic_ty_path_segment(
378    name_ref: ast::NameRef,
379    generic_args: impl IntoIterator<Item = ast::GenericArg>,
380) -> ast::PathSegment {
381    let mut generic_args = generic_args.into_iter();
382    let first_generic_arg = generic_args.next();
383    quote! {
384        PathSegment {
385            #name_ref
386            GenericArgList {
387                [<] #first_generic_arg #([,] " " #generic_args)* [>]
388            }
389        }
390    }
391}
392
393pub fn path_segment_ty(type_ref: ast::Type, trait_ref: Option<ast::PathType>) -> ast::PathSegment {
394    let text = match trait_ref {
395        Some(trait_ref) => format!("fn f(x: <{type_ref} as {trait_ref}>) {{}}"),
396        None => format!("fn f(x: <{type_ref}>) {{}}"),
397    };
398    ast_from_text(&text)
399}
400
401pub fn path_segment_self() -> ast::PathSegment {
402    ast_from_text("use self;")
403}
404
405pub fn path_segment_super() -> ast::PathSegment {
406    ast_from_text("use super;")
407}
408
409pub fn path_segment_crate() -> ast::PathSegment {
410    ast_from_text("use crate;")
411}
412
413pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
414    ast_from_text(&format!("type __ = {segment};"))
415}
416
417pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
418    ast_from_text(&format!("{qual}::{segment}"))
419}
420// FIXME: path concatenation operation doesn't make sense as AST op.
421pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path {
422    ast_from_text(&format!("type __ = {first}::{second};"))
423}
424
425pub fn path_from_segments(
426    segments: impl IntoIterator<Item = ast::PathSegment>,
427    is_abs: bool,
428) -> ast::Path {
429    let segments = segments.into_iter().map(|it| it.syntax().clone()).join("::");
430    ast_from_text(&if is_abs {
431        format!("fn f(x: ::{segments}) {{}}")
432    } else {
433        format!("fn f(x: {segments}) {{}}")
434    })
435}
436
437pub fn join_paths(paths: impl IntoIterator<Item = ast::Path>) -> ast::Path {
438    let paths = paths.into_iter().map(|it| it.syntax().clone()).join("::");
439    ast_from_text(&format!("type __ = {paths};"))
440}
441
442// FIXME: should not be pub
443pub fn path_from_text(text: &str) -> ast::Path {
444    ast_from_text(&format!("fn main() {{ let test: {text}; }}"))
445}
446
447// FIXME: should not be pub
448pub fn path_from_text_with_edition(text: &str, edition: Edition) -> ast::Path {
449    ast_from_text_with_edition(&format!("fn main() {{ let test: {text}; }}"), edition)
450}
451
452pub fn use_tree_glob() -> ast::UseTree {
453    ast_from_text("use *;")
454}
455pub fn use_tree(
456    path: ast::Path,
457    use_tree_list: Option<ast::UseTreeList>,
458    alias: Option<ast::Rename>,
459    add_star: bool,
460) -> ast::UseTree {
461    let mut buf = "use ".to_owned();
462    buf += &path.syntax().to_string();
463    if let Some(use_tree_list) = use_tree_list {
464        format_to!(buf, "::{use_tree_list}");
465    }
466    if add_star {
467        buf += "::*";
468    }
469
470    if let Some(alias) = alias {
471        format_to!(buf, " {alias}");
472    }
473    ast_from_text(&buf)
474}
475
476pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast::UseTreeList {
477    let use_trees = use_trees.into_iter().map(|it| it.syntax().clone()).join(", ");
478    ast_from_text(&format!("use {{{use_trees}}};"))
479}
480
481pub fn use_(
482    attrs: impl IntoIterator<Item = ast::Attr>,
483    visibility: Option<ast::Visibility>,
484    use_tree: ast::UseTree,
485) -> ast::Use {
486    let attrs =
487        attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
488    let visibility = match visibility {
489        None => String::new(),
490        Some(it) => format!("{it} "),
491    };
492    ast_from_text(&format!("{attrs}{visibility}use {use_tree};"))
493}
494
495pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr {
496    ast_from_text(&format!("fn f() {{ {path} {fields} }}"))
497}
498
499pub fn record_expr_field_list(
500    fields: impl IntoIterator<Item = ast::RecordExprField>,
501) -> ast::RecordExprFieldList {
502    let fields = fields.into_iter().join(", ");
503    ast_from_text(&format!("fn f() {{ S {{ {fields} }} }}"))
504}
505
506pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField {
507    return match expr {
508        Some(expr) => from_text(&format!("{name}: {expr}")),
509        None => from_text(&name.to_string()),
510    };
511
512    fn from_text(text: &str) -> ast::RecordExprField {
513        ast_from_text(&format!("fn f() {{ S {{ {text}, }} }}"))
514    }
515}
516
517pub fn record_field(
518    visibility: Option<ast::Visibility>,
519    name: ast::Name,
520    ty: ast::Type,
521) -> ast::RecordField {
522    let visibility = match visibility {
523        None => String::new(),
524        Some(it) => format!("{it} "),
525    };
526    ast_from_text(&format!("struct S {{ {visibility}{name}: {ty}, }}"))
527}
528
529pub fn block_expr(
530    stmts: impl IntoIterator<Item = ast::Stmt>,
531    tail_expr: Option<ast::Expr>,
532) -> ast::BlockExpr {
533    quote! {
534        BlockExpr {
535            StmtList {
536                ['{'] "\n"
537                #("    " #stmts "\n")*
538                #("    " #tail_expr "\n")*
539                ['}']
540            }
541        }
542    }
543}
544
545pub fn async_move_block_expr(
546    stmts: impl IntoIterator<Item = ast::Stmt>,
547    tail_expr: Option<ast::Expr>,
548) -> ast::BlockExpr {
549    let mut buf = "async move {\n".to_owned();
550    for stmt in stmts.into_iter() {
551        format_to!(buf, "    {stmt}\n");
552    }
553    if let Some(tail_expr) = tail_expr {
554        format_to!(buf, "    {tail_expr}\n");
555    }
556    buf += "}";
557    ast_from_text(&format!("const _: () = {buf};"))
558}
559
560pub fn tail_only_block_expr(tail_expr: ast::Expr) -> ast::BlockExpr {
561    ast_from_text(&format!("fn f() {{ {tail_expr} }}"))
562}
563
564/// Ideally this function wouldn't exist since it involves manual indenting.
565/// It differs from `make::block_expr` by also supporting comments and whitespace.
566///
567/// FIXME: replace usages of this with the mutable syntax tree API
568pub fn hacky_block_expr(
569    elements: impl IntoIterator<Item = crate::SyntaxElement>,
570    tail_expr: Option<ast::Expr>,
571) -> ast::BlockExpr {
572    let mut buf = "{\n".to_owned();
573    for node_or_token in elements.into_iter() {
574        match node_or_token {
575            rowan::NodeOrToken::Node(n) => format_to!(buf, "    {n}\n"),
576            rowan::NodeOrToken::Token(t) => {
577                let kind = t.kind();
578                if kind == SyntaxKind::COMMENT {
579                    format_to!(buf, "    {t}\n")
580                } else if kind == SyntaxKind::WHITESPACE {
581                    let content = t.text().trim_matches(|c| c != '\n');
582                    if !content.is_empty() {
583                        format_to!(buf, "{}", &content[1..])
584                    }
585                }
586            }
587        }
588    }
589    if let Some(tail_expr) = tail_expr {
590        format_to!(buf, "    {tail_expr}\n");
591    }
592    buf += "}";
593    ast_from_text(&format!("fn f() {buf}"))
594}
595
596pub fn expr_literal(text: &str) -> ast::Literal {
597    assert_eq!(text.trim(), text);
598    ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
599}
600
601pub fn expr_const_value(text: &str) -> ast::ConstArg {
602    ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}"))
603}
604
605pub fn expr_empty_block() -> ast::BlockExpr {
606    ast_from_text("const C: () = {};")
607}
608pub fn expr_path(path: ast::Path) -> ast::Expr {
609    expr_from_text(&path.to_string())
610}
611pub fn expr_continue(label: Option<ast::Lifetime>) -> ast::Expr {
612    match label {
613        Some(label) => expr_from_text(&format!("continue {label}")),
614        None => expr_from_text("continue"),
615    }
616}
617// Consider `op: SyntaxKind` instead for nicer syntax at the call-site?
618pub fn expr_bin_op(lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr {
619    expr_from_text(&format!("{lhs} {op} {rhs}"))
620}
621pub fn expr_break(label: Option<ast::Lifetime>, expr: Option<ast::Expr>) -> ast::Expr {
622    let mut s = String::from("break");
623
624    if let Some(label) = label {
625        format_to!(s, " {label}");
626    }
627
628    if let Some(expr) = expr {
629        format_to!(s, " {expr}");
630    }
631
632    expr_from_text(&s)
633}
634pub fn expr_return(expr: Option<ast::Expr>) -> ast::Expr {
635    match expr {
636        Some(expr) => expr_from_text(&format!("return {expr}")),
637        None => expr_from_text("return"),
638    }
639}
640pub fn expr_try(expr: ast::Expr) -> ast::Expr {
641    expr_from_text(&format!("{expr}?"))
642}
643pub fn expr_await(expr: ast::Expr) -> ast::Expr {
644    expr_from_text(&format!("{expr}.await"))
645}
646pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr {
647    expr_from_text(&format!("match {expr} {match_arm_list}"))
648}
649pub fn expr_if(
650    condition: ast::Expr,
651    then_branch: ast::BlockExpr,
652    else_branch: Option<ast::ElseBranch>,
653) -> ast::IfExpr {
654    let else_branch = match else_branch {
655        Some(ast::ElseBranch::Block(block)) => format!("else {block}"),
656        Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"),
657        None => String::new(),
658    };
659    expr_from_text(&format!("if {condition} {then_branch} {else_branch}"))
660}
661pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::ForExpr {
662    expr_from_text(&format!("for {pat} in {expr} {block}"))
663}
664
665pub fn expr_while_loop(condition: ast::Expr, block: ast::BlockExpr) -> ast::WhileExpr {
666    expr_from_text(&format!("while {condition} {block}"))
667}
668
669pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr {
670    expr_from_text(&format!("loop {block}"))
671}
672
673pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr {
674    let token = token(op);
675    expr_from_text(&format!("{token}{expr}"))
676}
677pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr {
678    expr_from_text(&format!("{f}{arg_list}"))
679}
680pub fn expr_method_call(
681    receiver: ast::Expr,
682    method: ast::NameRef,
683    arg_list: ast::ArgList,
684) -> ast::MethodCallExpr {
685    expr_from_text(&format!("{receiver}.{method}{arg_list}"))
686}
687pub fn expr_macro(path: ast::Path, tt: ast::TokenTree) -> ast::MacroExpr {
688    expr_from_text(&format!("{path}!{tt}"))
689}
690pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
691    expr_from_text(&if exclusive { format!("&mut {expr}") } else { format!("&{expr}") })
692}
693pub fn expr_reborrow(expr: ast::Expr) -> ast::Expr {
694    expr_from_text(&format!("&mut *{expr}"))
695}
696pub fn expr_closure(
697    pats: impl IntoIterator<Item = ast::Param>,
698    expr: ast::Expr,
699) -> ast::ClosureExpr {
700    let params = pats.into_iter().join(", ");
701    expr_from_text(&format!("|{params}| {expr}"))
702}
703pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr {
704    expr_from_text(&format!("{receiver}.{field}"))
705}
706pub fn expr_paren(expr: ast::Expr) -> ast::ParenExpr {
707    expr_from_text(&format!("({expr})"))
708}
709pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr {
710    let expr = elements.into_iter().format(", ");
711    expr_from_text(&format!("({expr})"))
712}
713pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr {
714    expr_from_text(&format!("{lhs} = {rhs}"))
715}
716fn expr_from_text<E: Into<ast::Expr> + AstNode>(text: &str) -> E {
717    ast_from_text(&format!("const C: () = {text};"))
718}
719pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
720    ast_from_text(&format!("const _: () = while let {pattern} = {expr} {{}};"))
721}
722
723pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
724    let args = args.into_iter().format(", ");
725    ast_from_text(&format!("fn main() {{ ()({args}) }}"))
726}
727
728pub fn ident_pat(ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
729    let mut s = String::from("fn f(");
730    if ref_ {
731        s.push_str("ref ");
732    }
733    if mut_ {
734        s.push_str("mut ");
735    }
736    format_to!(s, "{name}");
737    s.push_str(": ())");
738    ast_from_text(&s)
739}
740
741pub fn wildcard_pat() -> ast::WildcardPat {
742    return from_text("_");
743
744    fn from_text(text: &str) -> ast::WildcardPat {
745        ast_from_text(&format!("fn f({text}: ())"))
746    }
747}
748
749pub fn rest_pat() -> ast::RestPat {
750    ast_from_text("fn f() { let ..; }")
751}
752
753pub fn literal_pat(lit: &str) -> ast::LiteralPat {
754    return from_text(lit);
755
756    fn from_text(text: &str) -> ast::LiteralPat {
757        ast_from_text(&format!("fn f() {{ match x {{ {text} => {{}} }} }}"))
758    }
759}
760
761pub fn slice_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::SlicePat {
762    let pats_str = pats.into_iter().join(", ");
763    return from_text(&format!("[{pats_str}]"));
764
765    fn from_text(text: &str) -> ast::SlicePat {
766        ast_from_text(&format!("fn f() {{ match () {{{text} => ()}} }}"))
767    }
768}
769
770/// Creates a tuple of patterns from an iterator of patterns.
771///
772/// Invariant: `pats` must be length > 0
773pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat {
774    let mut count: usize = 0;
775    let mut pats_str = pats.into_iter().inspect(|_| count += 1).join(", ");
776    if count == 1 {
777        pats_str.push(',');
778    }
779    return from_text(&format!("({pats_str})"));
780
781    fn from_text(text: &str) -> ast::TuplePat {
782        ast_from_text(&format!("fn f({text}: ())"))
783    }
784}
785
786pub fn tuple_struct_pat(
787    path: ast::Path,
788    pats: impl IntoIterator<Item = ast::Pat>,
789) -> ast::TupleStructPat {
790    let pats_str = pats.into_iter().join(", ");
791    return from_text(&format!("{path}({pats_str})"));
792
793    fn from_text(text: &str) -> ast::TupleStructPat {
794        ast_from_text(&format!("fn f({text}: ())"))
795    }
796}
797
798pub fn record_pat(path: ast::Path, pats: impl IntoIterator<Item = ast::Pat>) -> ast::RecordPat {
799    let pats_str = pats.into_iter().join(", ");
800    return from_text(&format!("{path} {{ {pats_str} }}"));
801
802    fn from_text(text: &str) -> ast::RecordPat {
803        ast_from_text(&format!("fn f({text}: ())"))
804    }
805}
806
807pub fn record_pat_with_fields(path: ast::Path, fields: ast::RecordPatFieldList) -> ast::RecordPat {
808    ast_from_text(&format!("fn f({path} {fields}: ()))"))
809}
810
811pub fn record_pat_field_list(
812    fields: impl IntoIterator<Item = ast::RecordPatField>,
813    rest_pat: Option<ast::RestPat>,
814) -> ast::RecordPatFieldList {
815    let mut fields = fields.into_iter().join(", ");
816    if let Some(rest_pat) = rest_pat {
817        if !fields.is_empty() {
818            fields.push_str(", ");
819        }
820        format_to!(fields, "{rest_pat}");
821    }
822    ast_from_text(&format!("fn f(S {{ {fields} }}: ()))"))
823}
824
825pub fn record_pat_field(name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField {
826    ast_from_text(&format!("fn f(S {{ {name_ref}: {pat} }}: ()))"))
827}
828
829pub fn record_pat_field_shorthand(pat: ast::Pat) -> ast::RecordPatField {
830    ast_from_text(&format!("fn f(S {{ {pat} }}: ()))"))
831}
832
833/// Returns a `IdentPat` if the path has just one segment, a `PathPat` otherwise.
834pub fn path_pat(path: ast::Path) -> ast::Pat {
835    return from_text(&path.to_string());
836    fn from_text(text: &str) -> ast::Pat {
837        ast_from_text(&format!("fn f({text}: ())"))
838    }
839}
840
841/// Returns a `Pat` if the path has just one segment, an `OrPat` otherwise.
842///
843/// Invariant: `pats` must be length > 1.
844pub fn or_pat(pats: impl IntoIterator<Item = ast::Pat>, leading_pipe: bool) -> ast::OrPat {
845    let leading_pipe = if leading_pipe { "| " } else { "" };
846    let pats = pats.into_iter().join(" | ");
847
848    return from_text(&format!("{leading_pipe}{pats}"));
849    fn from_text(text: &str) -> ast::OrPat {
850        ast_from_text(&format!("fn f({text}: ())"))
851    }
852}
853
854pub fn box_pat(pat: ast::Pat) -> ast::BoxPat {
855    ast_from_text(&format!("fn f(box {pat}: ())"))
856}
857
858pub fn paren_pat(pat: ast::Pat) -> ast::ParenPat {
859    ast_from_text(&format!("fn f(({pat}): ())"))
860}
861
862pub fn range_pat(start: Option<ast::Pat>, end: Option<ast::Pat>) -> ast::RangePat {
863    ast_from_text(&format!(
864        "fn f({}..{}: ())",
865        start.map(|e| e.to_string()).unwrap_or_default(),
866        end.map(|e| e.to_string()).unwrap_or_default()
867    ))
868}
869
870pub fn ref_pat(pat: ast::Pat) -> ast::RefPat {
871    ast_from_text(&format!("fn f(&{pat}: ())"))
872}
873
874pub fn match_arm(pat: ast::Pat, guard: Option<ast::MatchGuard>, expr: ast::Expr) -> ast::MatchArm {
875    let comma_str = if expr.is_block_like() { "" } else { "," };
876    return match guard {
877        Some(guard) => from_text(&format!("{pat} {guard} => {expr}{comma_str}")),
878        None => from_text(&format!("{pat} => {expr}{comma_str}")),
879    };
880
881    fn from_text(text: &str) -> ast::MatchArm {
882        ast_from_text(&format!("fn f() {{ match () {{{text}}} }}"))
883    }
884}
885
886pub fn match_arm_with_guard(
887    pats: impl IntoIterator<Item = ast::Pat>,
888    guard: ast::Expr,
889    expr: ast::Expr,
890) -> ast::MatchArm {
891    let pats_str = pats.into_iter().join(" | ");
892    return from_text(&format!("{pats_str} if {guard} => {expr}"));
893
894    fn from_text(text: &str) -> ast::MatchArm {
895        ast_from_text(&format!("fn f() {{ match () {{{text}}} }}"))
896    }
897}
898
899pub fn match_guard(condition: ast::Expr) -> ast::MatchGuard {
900    return from_text(&format!("if {condition}"));
901
902    fn from_text(text: &str) -> ast::MatchGuard {
903        ast_from_text(&format!("fn f() {{ match () {{() {text} => () }}"))
904    }
905}
906
907pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
908    let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| {
909        let needs_comma =
910            arm.comma_token().is_none() && arm.expr().is_none_or(|it| !it.is_block_like());
911        let comma = if needs_comma && arm.comma_token().is_none() { "," } else { "" };
912        let arm = arm.syntax();
913        format_to_acc!(acc, "    {arm}{comma}\n")
914    });
915    return from_text(&arms_str);
916
917    fn from_text(text: &str) -> ast::MatchArmList {
918        ast_from_text(&format!("fn f() {{ match () {{\n{text}}} }}"))
919    }
920}
921
922pub fn where_pred(
923    path: Either<ast::Lifetime, ast::Type>,
924    bounds: impl IntoIterator<Item = ast::TypeBound>,
925) -> ast::WherePred {
926    let bounds = bounds.into_iter().join(" + ");
927    return from_text(&format!("{path}: {bounds}"));
928
929    fn from_text(text: &str) -> ast::WherePred {
930        ast_from_text(&format!("fn f() where {text} {{ }}"))
931    }
932}
933
934pub fn where_clause(preds: impl IntoIterator<Item = ast::WherePred>) -> ast::WhereClause {
935    let preds = preds.into_iter().join(", ");
936    return from_text(preds.as_str());
937
938    fn from_text(text: &str) -> ast::WhereClause {
939        ast_from_text(&format!("fn f() where {text} {{ }}"))
940    }
941}
942
943pub fn let_stmt(
944    pattern: ast::Pat,
945    ty: Option<ast::Type>,
946    initializer: Option<ast::Expr>,
947) -> ast::LetStmt {
948    let mut text = String::new();
949    format_to!(text, "let {pattern}");
950    if let Some(ty) = ty {
951        format_to!(text, ": {ty}");
952    }
953    match initializer {
954        Some(it) => format_to!(text, " = {it};"),
955        None => format_to!(text, ";"),
956    };
957    ast_from_text(&format!("fn f() {{ {text} }}"))
958}
959
960pub fn let_else_stmt(
961    pattern: ast::Pat,
962    ty: Option<ast::Type>,
963    expr: ast::Expr,
964    diverging: ast::BlockExpr,
965) -> ast::LetStmt {
966    let mut text = String::new();
967    format_to!(text, "let {pattern}");
968    if let Some(ty) = ty {
969        format_to!(text, ": {ty}");
970    }
971    format_to!(text, " = {expr} else {diverging};");
972    ast_from_text(&format!("fn f() {{ {text} }}"))
973}
974
975pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
976    let semi = if expr.is_block_like() { "" } else { ";" };
977    ast_from_text(&format!("fn f() {{ {expr}{semi} (); }}"))
978}
979
980pub fn item_const(
981    attrs: impl IntoIterator<Item = ast::Attr>,
982    visibility: Option<ast::Visibility>,
983    name: ast::Name,
984    ty: ast::Type,
985    expr: ast::Expr,
986) -> ast::Const {
987    let attrs =
988        attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
989    let visibility = match visibility {
990        None => String::new(),
991        Some(it) => format!("{it} "),
992    };
993    ast_from_text(&format!("{attrs}{visibility}const {name}: {ty} = {expr};"))
994}
995
996pub fn item_static(
997    visibility: Option<ast::Visibility>,
998    is_unsafe: bool,
999    is_mut: bool,
1000    name: ast::Name,
1001    ty: ast::Type,
1002    expr: Option<ast::Expr>,
1003) -> ast::Static {
1004    let visibility = match visibility {
1005        None => String::new(),
1006        Some(it) => format!("{it} "),
1007    };
1008    let is_unsafe = if is_unsafe { "unsafe " } else { "" };
1009    let is_mut = if is_mut { "mut " } else { "" };
1010    let expr = match expr {
1011        Some(it) => &format!(" = {it}"),
1012        None => "",
1013    };
1014
1015    ast_from_text(&format!("{visibility}{is_unsafe}static {is_mut}{name}: {ty}{expr};"))
1016}
1017
1018pub fn unnamed_param(ty: ast::Type) -> ast::Param {
1019    quote! {
1020        Param {
1021            #ty
1022        }
1023    }
1024}
1025
1026pub fn untyped_param(pat: ast::Pat) -> ast::Param {
1027    quote! {
1028        Param {
1029            #pat
1030        }
1031    }
1032}
1033
1034pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
1035    ast_from_text(&format!("fn f({pat}: {ty}) {{ }}"))
1036}
1037
1038pub fn self_param() -> ast::SelfParam {
1039    ast_from_text("fn f(&self) { }")
1040}
1041
1042pub fn mut_self_param() -> ast::SelfParam {
1043    ast_from_text("fn f(&mut self) { }")
1044}
1045
1046pub fn ret_type(ty: ast::Type) -> ast::RetType {
1047    ast_from_text(&format!("fn f() -> {ty} {{ }}"))
1048}
1049
1050pub fn param_list(
1051    self_param: Option<ast::SelfParam>,
1052    pats: impl IntoIterator<Item = ast::Param>,
1053) -> ast::ParamList {
1054    let args = pats.into_iter().join(", ");
1055    let list = match self_param {
1056        Some(self_param) if args.is_empty() => format!("fn f({self_param}) {{ }}"),
1057        Some(self_param) => format!("fn f({self_param}, {args}) {{ }}"),
1058        None => format!("fn f({args}) {{ }}"),
1059    };
1060    ast_from_text(&list)
1061}
1062
1063pub fn trait_(
1064    is_unsafe: bool,
1065    ident: &str,
1066    gen_params: Option<ast::GenericParamList>,
1067    where_clause: Option<ast::WhereClause>,
1068    assoc_items: ast::AssocItemList,
1069) -> ast::Trait {
1070    let mut text = String::new();
1071
1072    if is_unsafe {
1073        format_to!(text, "unsafe ");
1074    }
1075
1076    format_to!(text, "trait {ident}");
1077
1078    if let Some(gen_params) = gen_params {
1079        format_to!(text, "{} ", gen_params.to_string());
1080    } else {
1081        text.push(' ');
1082    }
1083
1084    if let Some(where_clause) = where_clause {
1085        format_to!(text, "{} ", where_clause.to_string());
1086    }
1087
1088    format_to!(text, "{}", assoc_items.to_string());
1089
1090    ast_from_text(&text)
1091}
1092
1093// FIXME: remove when no one depends on `generate_impl_text_inner`
1094pub fn type_bound_text(bound: &str) -> ast::TypeBound {
1095    ast_from_text(&format!("fn f<T: {bound}>() {{ }}"))
1096}
1097
1098pub fn type_bound(bound: ast::Type) -> ast::TypeBound {
1099    ast_from_text(&format!("fn f<T: {bound}>() {{ }}"))
1100}
1101
1102pub fn type_bound_list(
1103    bounds: impl IntoIterator<Item = ast::TypeBound>,
1104) -> Option<ast::TypeBoundList> {
1105    let bounds = bounds.into_iter().map(|it| it.to_string()).unique().join(" + ");
1106    if bounds.is_empty() {
1107        return None;
1108    }
1109    Some(ast_from_text(&format!("fn f<T: {bounds}>() {{ }}")))
1110}
1111
1112pub fn type_param(name: ast::Name, bounds: Option<ast::TypeBoundList>) -> ast::TypeParam {
1113    let bounds = bounds.map_or_else(String::new, |it| format!(": {it}"));
1114    ast_from_text(&format!("fn f<{name}{bounds}>() {{ }}"))
1115}
1116
1117pub fn const_param(name: ast::Name, ty: ast::Type) -> ast::ConstParam {
1118    ast_from_text(&format!("fn f<const {name}: {ty}>() {{ }}"))
1119}
1120
1121pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam {
1122    ast_from_text(&format!("fn f<{lifetime}>() {{ }}"))
1123}
1124
1125pub fn generic_param_list(
1126    pats: impl IntoIterator<Item = ast::GenericParam>,
1127) -> ast::GenericParamList {
1128    let args = pats.into_iter().join(", ");
1129    ast_from_text(&format!("fn f<{args}>() {{ }}"))
1130}
1131
1132pub fn type_arg(ty: ast::Type) -> ast::TypeArg {
1133    ast_from_text(&format!("const S: T<{ty}> = ();"))
1134}
1135
1136pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg {
1137    ast_from_text(&format!("const S: T<{lifetime}> = ();"))
1138}
1139
1140pub fn turbofish_generic_arg_list(
1141    args: impl IntoIterator<Item = ast::GenericArg>,
1142) -> ast::GenericArgList {
1143    let args = args.into_iter().join(", ");
1144    ast_from_text(&format!("const S: T::<{args}> = ();"))
1145}
1146
1147pub fn generic_arg_list(args: impl IntoIterator<Item = ast::GenericArg>) -> ast::GenericArgList {
1148    let args = args.into_iter().join(", ");
1149    ast_from_text(&format!("const S: T<{args}> = ();"))
1150}
1151
1152pub fn visibility_pub_crate() -> ast::Visibility {
1153    ast_from_text("pub(crate) struct S")
1154}
1155
1156pub fn visibility_pub() -> ast::Visibility {
1157    ast_from_text("pub struct S")
1158}
1159
1160pub fn tuple_field_list(fields: impl IntoIterator<Item = ast::TupleField>) -> ast::TupleFieldList {
1161    let fields = fields.into_iter().join(", ");
1162    ast_from_text(&format!("struct f({fields});"))
1163}
1164
1165pub fn record_field_list(
1166    fields: impl IntoIterator<Item = ast::RecordField>,
1167) -> ast::RecordFieldList {
1168    let fields = fields.into_iter().join(", ");
1169    ast_from_text(&format!("struct f {{ {fields} }}"))
1170}
1171
1172pub fn tuple_field(visibility: Option<ast::Visibility>, ty: ast::Type) -> ast::TupleField {
1173    let visibility = match visibility {
1174        None => String::new(),
1175        Some(it) => format!("{it} "),
1176    };
1177    ast_from_text(&format!("struct f({visibility}{ty});"))
1178}
1179
1180pub fn variant_list(variants: impl IntoIterator<Item = ast::Variant>) -> ast::VariantList {
1181    let variants = variants.into_iter().join(", ");
1182    ast_from_text(&format!("enum f {{ {variants} }}"))
1183}
1184
1185pub fn variant(
1186    visibility: Option<ast::Visibility>,
1187    name: ast::Name,
1188    field_list: Option<ast::FieldList>,
1189    discriminant: Option<ast::Expr>,
1190) -> ast::Variant {
1191    let visibility = match visibility {
1192        None => String::new(),
1193        Some(it) => format!("{it} "),
1194    };
1195
1196    let field_list = match field_list {
1197        None => String::new(),
1198        Some(it) => match it {
1199            ast::FieldList::RecordFieldList(record) => format!(" {record}"),
1200            ast::FieldList::TupleFieldList(tuple) => format!("{tuple}"),
1201        },
1202    };
1203
1204    let discriminant = match discriminant {
1205        Some(it) => format!(" = {it}"),
1206        None => String::new(),
1207    };
1208    ast_from_text(&format!("enum f {{ {visibility}{name}{field_list}{discriminant} }}"))
1209}
1210
1211pub fn fn_(
1212    attrs: impl IntoIterator<Item = ast::Attr>,
1213    visibility: Option<ast::Visibility>,
1214    fn_name: ast::Name,
1215    type_params: Option<ast::GenericParamList>,
1216    where_clause: Option<ast::WhereClause>,
1217    params: ast::ParamList,
1218    body: ast::BlockExpr,
1219    ret_type: Option<ast::RetType>,
1220    is_async: bool,
1221    is_const: bool,
1222    is_unsafe: bool,
1223    is_gen: bool,
1224) -> ast::Fn {
1225    let attrs =
1226        attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
1227    let type_params = match type_params {
1228        Some(type_params) => format!("{type_params}"),
1229        None => "".into(),
1230    };
1231    let where_clause = match where_clause {
1232        Some(it) => format!("{it} "),
1233        None => "".into(),
1234    };
1235    let ret_type = match ret_type {
1236        Some(ret_type) => format!("{ret_type} "),
1237        None => "".into(),
1238    };
1239    let visibility = match visibility {
1240        None => String::new(),
1241        Some(it) => format!("{it} "),
1242    };
1243
1244    let async_literal = if is_async { "async " } else { "" };
1245    let const_literal = if is_const { "const " } else { "" };
1246    let unsafe_literal = if is_unsafe { "unsafe " } else { "" };
1247    let gen_literal = if is_gen { "gen " } else { "" };
1248
1249    ast_from_text(&format!(
1250        "{attrs}{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
1251    ))
1252}
1253pub fn struct_(
1254    visibility: Option<ast::Visibility>,
1255    strukt_name: ast::Name,
1256    generic_param_list: Option<ast::GenericParamList>,
1257    field_list: ast::FieldList,
1258) -> ast::Struct {
1259    let (semicolon, ws) =
1260        if matches!(field_list, ast::FieldList::TupleFieldList(_)) { (";", "") } else { ("", " ") };
1261    let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string());
1262    let visibility = match visibility {
1263        None => String::new(),
1264        Some(it) => format!("{it} "),
1265    };
1266
1267    ast_from_text(&format!(
1268        "{visibility}struct {strukt_name}{type_params}{ws}{field_list}{semicolon}"
1269    ))
1270}
1271
1272pub fn enum_(
1273    attrs: impl IntoIterator<Item = ast::Attr>,
1274    visibility: Option<ast::Visibility>,
1275    enum_name: ast::Name,
1276    generic_param_list: Option<ast::GenericParamList>,
1277    where_clause: Option<ast::WhereClause>,
1278    variant_list: ast::VariantList,
1279) -> ast::Enum {
1280    let attrs =
1281        attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
1282    let visibility = match visibility {
1283        None => String::new(),
1284        Some(it) => format!("{it} "),
1285    };
1286
1287    let generic_params = generic_param_list.map(|it| it.to_string()).unwrap_or_default();
1288    let where_clause = where_clause.map(|it| format!(" {it}")).unwrap_or_default();
1289
1290    ast_from_text(&format!(
1291        "{attrs}{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}"
1292    ))
1293}
1294
1295pub fn attr_outer(meta: ast::Meta) -> ast::Attr {
1296    ast_from_text(&format!("#[{meta}]"))
1297}
1298
1299pub fn attr_inner(meta: ast::Meta) -> ast::Attr {
1300    ast_from_text(&format!("#![{meta}]"))
1301}
1302
1303pub fn meta_expr(path: ast::Path, expr: ast::Expr) -> ast::Meta {
1304    ast_from_text(&format!("#[{path} = {expr}]"))
1305}
1306
1307pub fn meta_token_tree(path: ast::Path, tt: ast::TokenTree) -> ast::Meta {
1308    ast_from_text(&format!("#[{path}{tt}]"))
1309}
1310
1311pub fn meta_path(path: ast::Path) -> ast::Meta {
1312    ast_from_text(&format!("#[{path}]"))
1313}
1314
1315pub fn token_tree(
1316    delimiter: SyntaxKind,
1317    tt: impl IntoIterator<Item = NodeOrToken<ast::TokenTree, SyntaxToken>>,
1318) -> ast::TokenTree {
1319    let (l_delimiter, r_delimiter) = match delimiter {
1320        T!['('] => ('(', ')'),
1321        T!['['] => ('[', ']'),
1322        T!['{'] => ('{', '}'),
1323        _ => panic!("invalid delimiter `{delimiter:?}`"),
1324    };
1325    let tt = tt.into_iter().join("");
1326
1327    ast_from_text(&format!("tt!{l_delimiter}{tt}{r_delimiter}"))
1328}
1329
1330#[track_caller]
1331fn ast_from_text<N: AstNode>(text: &str) -> N {
1332    ast_from_text_with_edition(text, Edition::CURRENT)
1333}
1334
1335#[track_caller]
1336fn ast_from_text_with_edition<N: AstNode>(text: &str, edition: Edition) -> N {
1337    let parse = SourceFile::parse(text, edition);
1338    let node = match parse.tree().syntax().descendants().find_map(N::cast) {
1339        Some(it) => it,
1340        None => {
1341            let node = std::any::type_name::<N>();
1342            panic!("Failed to make ast node `{node}` from text {text}")
1343        }
1344    };
1345    let node = node.clone_subtree();
1346    assert_eq!(node.syntax().text_range().start(), 0.into());
1347    node
1348}
1349
1350pub fn token(kind: SyntaxKind) -> SyntaxToken {
1351    tokens::SOURCE_FILE
1352        .tree()
1353        .syntax()
1354        .clone_for_update()
1355        .descendants_with_tokens()
1356        .filter_map(|it| it.into_token())
1357        .find(|it| it.kind() == kind)
1358        .unwrap_or_else(|| panic!("unhandled token: {kind:?}"))
1359}
1360
1361pub mod tokens {
1362    use std::sync::LazyLock;
1363
1364    use parser::Edition;
1365
1366    use crate::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, ast};
1367
1368    pub(super) static SOURCE_FILE: LazyLock<Parse<SourceFile>> = LazyLock::new(|| {
1369        SourceFile::parse(
1370            "use crate::foo; const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, async { let _ @ [] }, while loop {} {})\n;\n\nunsafe impl A for B where: {}",
1371            Edition::CURRENT,
1372        )
1373    });
1374
1375    pub fn semicolon() -> SyntaxToken {
1376        SOURCE_FILE
1377            .tree()
1378            .syntax()
1379            .clone_for_update()
1380            .descendants_with_tokens()
1381            .filter_map(|it| it.into_token())
1382            .find(|it| it.kind() == SEMICOLON)
1383            .unwrap()
1384    }
1385
1386    pub fn single_space() -> SyntaxToken {
1387        SOURCE_FILE
1388            .tree()
1389            .syntax()
1390            .clone_for_update()
1391            .descendants_with_tokens()
1392            .filter_map(|it| it.into_token())
1393            .find(|it| it.kind() == WHITESPACE && it.text() == " ")
1394            .unwrap()
1395    }
1396
1397    pub fn crate_kw() -> SyntaxToken {
1398        SOURCE_FILE
1399            .tree()
1400            .syntax()
1401            .clone_for_update()
1402            .descendants_with_tokens()
1403            .filter_map(|it| it.into_token())
1404            .find(|it| it.kind() == CRATE_KW)
1405            .unwrap()
1406    }
1407
1408    pub fn whitespace(text: &str) -> SyntaxToken {
1409        assert!(text.trim().is_empty());
1410        let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap();
1411        sf.syntax().clone_for_update().first_child_or_token().unwrap().into_token().unwrap()
1412    }
1413
1414    pub fn doc_comment(text: &str) -> SyntaxToken {
1415        assert!(!text.trim().is_empty());
1416        let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap();
1417        sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
1418    }
1419
1420    pub fn literal(text: &str) -> SyntaxToken {
1421        assert_eq!(text.trim(), text);
1422        let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {text}; }}"));
1423        lit.syntax().first_child_or_token().unwrap().into_token().unwrap()
1424    }
1425
1426    pub fn ident(text: &str) -> SyntaxToken {
1427        assert_eq!(text.trim(), text);
1428        let path: ast::Path = super::ext::ident_path(text);
1429        path.syntax()
1430            .descendants_with_tokens()
1431            .filter_map(|it| it.into_token())
1432            .find(|it| it.kind() == IDENT)
1433            .unwrap()
1434    }
1435
1436    pub fn single_newline() -> SyntaxToken {
1437        let res = SOURCE_FILE
1438            .tree()
1439            .syntax()
1440            .clone_for_update()
1441            .descendants_with_tokens()
1442            .filter_map(|it| it.into_token())
1443            .find(|it| it.kind() == WHITESPACE && it.text() == "\n")
1444            .unwrap();
1445        res.detach();
1446        res
1447    }
1448
1449    pub fn blank_line() -> SyntaxToken {
1450        SOURCE_FILE
1451            .tree()
1452            .syntax()
1453            .clone_for_update()
1454            .descendants_with_tokens()
1455            .filter_map(|it| it.into_token())
1456            .find(|it| it.kind() == WHITESPACE && it.text() == "\n\n")
1457            .unwrap()
1458    }
1459
1460    pub struct WsBuilder(SourceFile);
1461
1462    impl WsBuilder {
1463        pub fn new(text: &str) -> WsBuilder {
1464            WsBuilder(SourceFile::parse(text, Edition::CURRENT).ok().unwrap())
1465        }
1466        pub fn ws(&self) -> SyntaxToken {
1467            self.0.syntax().first_child_or_token().unwrap().into_token().unwrap()
1468        }
1469    }
1470}
1471
1472#[cfg(test)]
1473mod tests {
1474    use expect_test::expect;
1475
1476    use super::*;
1477
1478    #[track_caller]
1479    fn check(node: impl AstNode, expect: expect_test::Expect) {
1480        let node_debug = format!("{:#?}", node.syntax());
1481        expect.assert_eq(&node_debug);
1482    }
1483
1484    #[test]
1485    fn test_unnamed_param() {
1486        check(
1487            unnamed_param(ty("Vec")),
1488            expect![[r#"
1489                PARAM@0..3
1490                  PATH_TYPE@0..3
1491                    PATH@0..3
1492                      PATH_SEGMENT@0..3
1493                        NAME_REF@0..3
1494                          IDENT@0..3 "Vec"
1495            "#]],
1496        );
1497
1498        check(
1499            unnamed_param(ty("Vec<T>")),
1500            expect![[r#"
1501                PARAM@0..6
1502                  PATH_TYPE@0..6
1503                    PATH@0..6
1504                      PATH_SEGMENT@0..6
1505                        NAME_REF@0..3
1506                          IDENT@0..3 "Vec"
1507                        GENERIC_ARG_LIST@3..6
1508                          L_ANGLE@3..4 "<"
1509                          TYPE_ARG@4..5
1510                            PATH_TYPE@4..5
1511                              PATH@4..5
1512                                PATH_SEGMENT@4..5
1513                                  NAME_REF@4..5
1514                                    IDENT@4..5 "T"
1515                          R_ANGLE@5..6 ">"
1516            "#]],
1517        );
1518    }
1519
1520    #[test]
1521    fn test_untyped_param() {
1522        check(
1523            untyped_param(path_pat(ext::ident_path("name"))),
1524            expect![[r#"
1525                PARAM@0..4
1526                  IDENT_PAT@0..4
1527                    NAME@0..4
1528                      IDENT@0..4 "name"
1529            "#]],
1530        );
1531
1532        check(
1533            untyped_param(
1534                range_pat(
1535                    Some(path_pat(ext::ident_path("start"))),
1536                    Some(path_pat(ext::ident_path("end"))),
1537                )
1538                .into(),
1539            ),
1540            expect![[r#"
1541                PARAM@0..10
1542                  RANGE_PAT@0..10
1543                    IDENT_PAT@0..5
1544                      NAME@0..5
1545                        IDENT@0..5 "start"
1546                    DOT2@5..7 ".."
1547                    IDENT_PAT@7..10
1548                      NAME@7..10
1549                        IDENT@7..10 "end"
1550            "#]],
1551        );
1552    }
1553}