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    let ws = block_whitespace(&expr);
648    expr_from_text(&format!("match {expr}{ws}{match_arm_list}"))
649}
650pub fn expr_if(
651    condition: ast::Expr,
652    then_branch: ast::BlockExpr,
653    else_branch: Option<ast::ElseBranch>,
654) -> ast::IfExpr {
655    let else_branch = match else_branch {
656        Some(ast::ElseBranch::Block(block)) => format!("else {block}"),
657        Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"),
658        None => String::new(),
659    };
660    let ws = block_whitespace(&condition);
661    expr_from_text(&format!("if {condition}{ws}{then_branch} {else_branch}"))
662}
663pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::ForExpr {
664    let ws = block_whitespace(&expr);
665    expr_from_text(&format!("for {pat} in {expr}{ws}{block}"))
666}
667
668pub fn expr_while_loop(condition: ast::Expr, block: ast::BlockExpr) -> ast::WhileExpr {
669    let ws = block_whitespace(&condition);
670    expr_from_text(&format!("while {condition}{ws}{block}"))
671}
672
673pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr {
674    expr_from_text(&format!("loop {block}"))
675}
676
677pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr {
678    let token = token(op);
679    expr_from_text(&format!("{token}{expr}"))
680}
681pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr {
682    expr_from_text(&format!("{f}{arg_list}"))
683}
684pub fn expr_method_call(
685    receiver: ast::Expr,
686    method: ast::NameRef,
687    arg_list: ast::ArgList,
688) -> ast::MethodCallExpr {
689    expr_from_text(&format!("{receiver}.{method}{arg_list}"))
690}
691pub fn expr_macro(path: ast::Path, tt: ast::TokenTree) -> ast::MacroExpr {
692    expr_from_text(&format!("{path}!{tt}"))
693}
694pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
695    expr_from_text(&if exclusive { format!("&mut {expr}") } else { format!("&{expr}") })
696}
697pub fn expr_raw_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
698    expr_from_text(&if exclusive {
699        format!("&raw mut {expr}")
700    } else {
701        format!("&raw const {expr}")
702    })
703}
704pub fn expr_reborrow(expr: ast::Expr) -> ast::Expr {
705    expr_from_text(&format!("&mut *{expr}"))
706}
707pub fn expr_closure(
708    pats: impl IntoIterator<Item = ast::Param>,
709    expr: ast::Expr,
710) -> ast::ClosureExpr {
711    let params = pats.into_iter().join(", ");
712    expr_from_text(&format!("|{params}| {expr}"))
713}
714pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr {
715    expr_from_text(&format!("{receiver}.{field}"))
716}
717pub fn expr_paren(expr: ast::Expr) -> ast::ParenExpr {
718    expr_from_text(&format!("({expr})"))
719}
720pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr {
721    let expr = elements.into_iter().format(", ");
722    expr_from_text(&format!("({expr})"))
723}
724pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr {
725    expr_from_text(&format!("{lhs} = {rhs}"))
726}
727fn expr_from_text<E: Into<ast::Expr> + AstNode>(text: &str) -> E {
728    ast_from_text(&format!("const C: () = {text};"))
729}
730fn block_whitespace(after: &impl AstNode) -> &'static str {
731    if after.syntax().text().contains_char('\n') { "\n" } else { " " }
732}
733pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
734    ast_from_text(&format!("const _: () = while let {pattern} = {expr} {{}};"))
735}
736
737pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
738    let args = args.into_iter().format(", ");
739    ast_from_text(&format!("fn main() {{ ()({args}) }}"))
740}
741
742pub fn ident_pat(ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
743    let mut s = String::from("fn f(");
744    if ref_ {
745        s.push_str("ref ");
746    }
747    if mut_ {
748        s.push_str("mut ");
749    }
750    format_to!(s, "{name}");
751    s.push_str(": ())");
752    ast_from_text(&s)
753}
754
755pub fn wildcard_pat() -> ast::WildcardPat {
756    return from_text("_");
757
758    fn from_text(text: &str) -> ast::WildcardPat {
759        ast_from_text(&format!("fn f({text}: ())"))
760    }
761}
762
763pub fn rest_pat() -> ast::RestPat {
764    ast_from_text("fn f() { let ..; }")
765}
766
767pub fn literal_pat(lit: &str) -> ast::LiteralPat {
768    return from_text(lit);
769
770    fn from_text(text: &str) -> ast::LiteralPat {
771        ast_from_text(&format!("fn f() {{ match x {{ {text} => {{}} }} }}"))
772    }
773}
774
775pub fn slice_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::SlicePat {
776    let pats_str = pats.into_iter().join(", ");
777    return from_text(&format!("[{pats_str}]"));
778
779    fn from_text(text: &str) -> ast::SlicePat {
780        ast_from_text(&format!("fn f() {{ match () {{{text} => ()}} }}"))
781    }
782}
783
784/// Creates a tuple of patterns from an iterator of patterns.
785///
786/// Invariant: `pats` must be length > 0
787pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat {
788    let mut count: usize = 0;
789    let mut pats_str = pats.into_iter().inspect(|_| count += 1).join(", ");
790    if count == 1 {
791        pats_str.push(',');
792    }
793    return from_text(&format!("({pats_str})"));
794
795    fn from_text(text: &str) -> ast::TuplePat {
796        ast_from_text(&format!("fn f({text}: ())"))
797    }
798}
799
800pub fn tuple_struct_pat(
801    path: ast::Path,
802    pats: impl IntoIterator<Item = ast::Pat>,
803) -> ast::TupleStructPat {
804    let pats_str = pats.into_iter().join(", ");
805    return from_text(&format!("{path}({pats_str})"));
806
807    fn from_text(text: &str) -> ast::TupleStructPat {
808        ast_from_text(&format!("fn f({text}: ())"))
809    }
810}
811
812pub fn record_pat(path: ast::Path, pats: impl IntoIterator<Item = ast::Pat>) -> ast::RecordPat {
813    let pats_str = pats.into_iter().join(", ");
814    return from_text(&format!("{path} {{ {pats_str} }}"));
815
816    fn from_text(text: &str) -> ast::RecordPat {
817        ast_from_text(&format!("fn f({text}: ())"))
818    }
819}
820
821pub fn record_pat_with_fields(path: ast::Path, fields: ast::RecordPatFieldList) -> ast::RecordPat {
822    ast_from_text(&format!("fn f({path} {fields}: ()))"))
823}
824
825pub fn record_pat_field_list(
826    fields: impl IntoIterator<Item = ast::RecordPatField>,
827    rest_pat: Option<ast::RestPat>,
828) -> ast::RecordPatFieldList {
829    let mut fields = fields.into_iter().join(", ");
830    if let Some(rest_pat) = rest_pat {
831        if !fields.is_empty() {
832            fields.push_str(", ");
833        }
834        format_to!(fields, "{rest_pat}");
835    }
836    ast_from_text(&format!("fn f(S {{ {fields} }}: ()))"))
837}
838
839pub fn record_pat_field(name_ref: ast::NameRef, pat: ast::Pat) -> ast::RecordPatField {
840    ast_from_text(&format!("fn f(S {{ {name_ref}: {pat} }}: ()))"))
841}
842
843pub fn record_pat_field_shorthand(pat: ast::Pat) -> ast::RecordPatField {
844    ast_from_text(&format!("fn f(S {{ {pat} }}: ()))"))
845}
846
847/// Returns a `IdentPat` if the path has just one segment, a `PathPat` otherwise.
848pub fn path_pat(path: ast::Path) -> ast::Pat {
849    return from_text(&path.to_string());
850    fn from_text(text: &str) -> ast::Pat {
851        ast_from_text(&format!("fn f({text}: ())"))
852    }
853}
854
855/// Returns a `Pat` if the path has just one segment, an `OrPat` otherwise.
856///
857/// Invariant: `pats` must be length > 1.
858pub fn or_pat(pats: impl IntoIterator<Item = ast::Pat>, leading_pipe: bool) -> ast::OrPat {
859    let leading_pipe = if leading_pipe { "| " } else { "" };
860    let pats = pats.into_iter().join(" | ");
861
862    return from_text(&format!("{leading_pipe}{pats}"));
863    fn from_text(text: &str) -> ast::OrPat {
864        ast_from_text(&format!("fn f({text}: ())"))
865    }
866}
867
868pub fn box_pat(pat: ast::Pat) -> ast::BoxPat {
869    ast_from_text(&format!("fn f(box {pat}: ())"))
870}
871
872pub fn paren_pat(pat: ast::Pat) -> ast::ParenPat {
873    ast_from_text(&format!("fn f(({pat}): ())"))
874}
875
876pub fn range_pat(start: Option<ast::Pat>, end: Option<ast::Pat>) -> ast::RangePat {
877    ast_from_text(&format!(
878        "fn f({}..{}: ())",
879        start.map(|e| e.to_string()).unwrap_or_default(),
880        end.map(|e| e.to_string()).unwrap_or_default()
881    ))
882}
883
884pub fn ref_pat(pat: ast::Pat) -> ast::RefPat {
885    ast_from_text(&format!("fn f(&{pat}: ())"))
886}
887
888pub fn match_arm(pat: ast::Pat, guard: Option<ast::MatchGuard>, expr: ast::Expr) -> ast::MatchArm {
889    let comma_str = if expr.is_block_like() { "" } else { "," };
890    let ws = guard.as_ref().filter(|_| expr.is_block_like()).map_or(" ", block_whitespace);
891    return match guard {
892        Some(guard) => from_text(&format!("{pat} {guard} =>{ws}{expr}{comma_str}")),
893        None => from_text(&format!("{pat} => {expr}{comma_str}")),
894    };
895
896    fn from_text(text: &str) -> ast::MatchArm {
897        ast_from_text(&format!("fn f() {{ match () {{{text}}} }}"))
898    }
899}
900
901pub fn match_guard(condition: ast::Expr) -> ast::MatchGuard {
902    return from_text(&format!("if {condition}"));
903
904    fn from_text(text: &str) -> ast::MatchGuard {
905        ast_from_text(&format!("fn f() {{ match () {{() {text} => () }}"))
906    }
907}
908
909pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
910    let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| {
911        let needs_comma =
912            arm.comma_token().is_none() && arm.expr().is_none_or(|it| !it.is_block_like());
913        let comma = if needs_comma && arm.comma_token().is_none() { "," } else { "" };
914        let arm = arm.syntax();
915        format_to_acc!(acc, "    {arm}{comma}\n")
916    });
917    return from_text(&arms_str);
918
919    fn from_text(text: &str) -> ast::MatchArmList {
920        ast_from_text(&format!("fn f() {{ match () {{\n{text}}} }}"))
921    }
922}
923
924pub fn where_pred(
925    path: Either<ast::Lifetime, ast::Type>,
926    bounds: impl IntoIterator<Item = ast::TypeBound>,
927) -> ast::WherePred {
928    let bounds = bounds.into_iter().join(" + ");
929    return from_text(&format!("{path}: {bounds}"));
930
931    fn from_text(text: &str) -> ast::WherePred {
932        ast_from_text(&format!("fn f() where {text} {{ }}"))
933    }
934}
935
936pub fn where_clause(preds: impl IntoIterator<Item = ast::WherePred>) -> ast::WhereClause {
937    let preds = preds.into_iter().join(", ");
938    return from_text(preds.as_str());
939
940    fn from_text(text: &str) -> ast::WhereClause {
941        ast_from_text(&format!("fn f() where {text} {{ }}"))
942    }
943}
944
945pub fn let_stmt(
946    pattern: ast::Pat,
947    ty: Option<ast::Type>,
948    initializer: Option<ast::Expr>,
949) -> ast::LetStmt {
950    let mut text = String::new();
951    format_to!(text, "let {pattern}");
952    if let Some(ty) = ty {
953        format_to!(text, ": {ty}");
954    }
955    match initializer {
956        Some(it) => format_to!(text, " = {it};"),
957        None => format_to!(text, ";"),
958    };
959    ast_from_text(&format!("fn f() {{ {text} }}"))
960}
961
962pub fn let_else_stmt(
963    pattern: ast::Pat,
964    ty: Option<ast::Type>,
965    expr: ast::Expr,
966    diverging: ast::BlockExpr,
967) -> ast::LetStmt {
968    let mut text = String::new();
969    format_to!(text, "let {pattern}");
970    if let Some(ty) = ty {
971        format_to!(text, ": {ty}");
972    }
973    format_to!(text, " = {expr} else {diverging};");
974    ast_from_text(&format!("fn f() {{ {text} }}"))
975}
976
977pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
978    let semi = if expr.is_block_like() { "" } else { ";" };
979    ast_from_text(&format!("fn f() {{ {expr}{semi} (); }}"))
980}
981
982pub fn item_const(
983    attrs: impl IntoIterator<Item = ast::Attr>,
984    visibility: Option<ast::Visibility>,
985    name: ast::Name,
986    ty: ast::Type,
987    expr: ast::Expr,
988) -> ast::Const {
989    let attrs =
990        attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
991    let visibility = match visibility {
992        None => String::new(),
993        Some(it) => format!("{it} "),
994    };
995    ast_from_text(&format!("{attrs}{visibility}const {name}: {ty} = {expr};"))
996}
997
998pub fn item_static(
999    visibility: Option<ast::Visibility>,
1000    is_unsafe: bool,
1001    is_mut: bool,
1002    name: ast::Name,
1003    ty: ast::Type,
1004    expr: Option<ast::Expr>,
1005) -> ast::Static {
1006    let visibility = match visibility {
1007        None => String::new(),
1008        Some(it) => format!("{it} "),
1009    };
1010    let is_unsafe = if is_unsafe { "unsafe " } else { "" };
1011    let is_mut = if is_mut { "mut " } else { "" };
1012    let expr = match expr {
1013        Some(it) => &format!(" = {it}"),
1014        None => "",
1015    };
1016
1017    ast_from_text(&format!("{visibility}{is_unsafe}static {is_mut}{name}: {ty}{expr};"))
1018}
1019
1020pub fn unnamed_param(ty: ast::Type) -> ast::Param {
1021    quote! {
1022        Param {
1023            #ty
1024        }
1025    }
1026}
1027
1028pub fn untyped_param(pat: ast::Pat) -> ast::Param {
1029    quote! {
1030        Param {
1031            #pat
1032        }
1033    }
1034}
1035
1036pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
1037    ast_from_text(&format!("fn f({pat}: {ty}) {{ }}"))
1038}
1039
1040pub fn self_param() -> ast::SelfParam {
1041    ast_from_text("fn f(&self) { }")
1042}
1043
1044pub fn mut_self_param() -> ast::SelfParam {
1045    ast_from_text("fn f(&mut self) { }")
1046}
1047
1048pub fn ret_type(ty: ast::Type) -> ast::RetType {
1049    ast_from_text(&format!("fn f() -> {ty} {{ }}"))
1050}
1051
1052pub fn param_list(
1053    self_param: Option<ast::SelfParam>,
1054    pats: impl IntoIterator<Item = ast::Param>,
1055) -> ast::ParamList {
1056    let args = pats.into_iter().join(", ");
1057    let list = match self_param {
1058        Some(self_param) if args.is_empty() => format!("fn f({self_param}) {{ }}"),
1059        Some(self_param) => format!("fn f({self_param}, {args}) {{ }}"),
1060        None => format!("fn f({args}) {{ }}"),
1061    };
1062    ast_from_text(&list)
1063}
1064
1065pub fn trait_(
1066    is_unsafe: bool,
1067    ident: &str,
1068    gen_params: Option<ast::GenericParamList>,
1069    where_clause: Option<ast::WhereClause>,
1070    assoc_items: ast::AssocItemList,
1071) -> ast::Trait {
1072    let mut text = String::new();
1073
1074    if is_unsafe {
1075        format_to!(text, "unsafe ");
1076    }
1077
1078    format_to!(text, "trait {ident}");
1079
1080    if let Some(gen_params) = gen_params {
1081        format_to!(text, "{} ", gen_params.to_string());
1082    } else {
1083        text.push(' ');
1084    }
1085
1086    if let Some(where_clause) = where_clause {
1087        format_to!(text, "{} ", where_clause.to_string());
1088    }
1089
1090    format_to!(text, "{}", assoc_items.to_string());
1091
1092    ast_from_text(&text)
1093}
1094
1095// FIXME: remove when no one depends on `generate_impl_text_inner`
1096pub fn type_bound_text(bound: &str) -> ast::TypeBound {
1097    ast_from_text(&format!("fn f<T: {bound}>() {{ }}"))
1098}
1099
1100pub fn type_bound(bound: ast::Type) -> ast::TypeBound {
1101    ast_from_text(&format!("fn f<T: {bound}>() {{ }}"))
1102}
1103
1104pub fn type_bound_list(
1105    bounds: impl IntoIterator<Item = ast::TypeBound>,
1106) -> Option<ast::TypeBoundList> {
1107    let bounds = bounds.into_iter().map(|it| it.to_string()).unique().join(" + ");
1108    if bounds.is_empty() {
1109        return None;
1110    }
1111    Some(ast_from_text(&format!("fn f<T: {bounds}>() {{ }}")))
1112}
1113
1114pub fn type_param(name: ast::Name, bounds: Option<ast::TypeBoundList>) -> ast::TypeParam {
1115    let bounds = bounds.map_or_else(String::new, |it| format!(": {it}"));
1116    ast_from_text(&format!("fn f<{name}{bounds}>() {{ }}"))
1117}
1118
1119pub fn const_param(name: ast::Name, ty: ast::Type) -> ast::ConstParam {
1120    ast_from_text(&format!("fn f<const {name}: {ty}>() {{ }}"))
1121}
1122
1123pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam {
1124    ast_from_text(&format!("fn f<{lifetime}>() {{ }}"))
1125}
1126
1127pub fn generic_param_list(
1128    pats: impl IntoIterator<Item = ast::GenericParam>,
1129) -> ast::GenericParamList {
1130    let args = pats.into_iter().join(", ");
1131    ast_from_text(&format!("fn f<{args}>() {{ }}"))
1132}
1133
1134pub fn type_arg(ty: ast::Type) -> ast::TypeArg {
1135    ast_from_text(&format!("const S: T<{ty}> = ();"))
1136}
1137
1138pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg {
1139    ast_from_text(&format!("const S: T<{lifetime}> = ();"))
1140}
1141
1142pub fn turbofish_generic_arg_list(
1143    args: impl IntoIterator<Item = ast::GenericArg>,
1144) -> ast::GenericArgList {
1145    let args = args.into_iter().join(", ");
1146    ast_from_text(&format!("const S: T::<{args}> = ();"))
1147}
1148
1149pub fn generic_arg_list(args: impl IntoIterator<Item = ast::GenericArg>) -> ast::GenericArgList {
1150    let args = args.into_iter().join(", ");
1151    ast_from_text(&format!("const S: T<{args}> = ();"))
1152}
1153
1154pub fn visibility_pub_crate() -> ast::Visibility {
1155    ast_from_text("pub(crate) struct S")
1156}
1157
1158pub fn visibility_pub() -> ast::Visibility {
1159    ast_from_text("pub struct S")
1160}
1161
1162pub fn tuple_field_list(fields: impl IntoIterator<Item = ast::TupleField>) -> ast::TupleFieldList {
1163    let fields = fields.into_iter().join(", ");
1164    ast_from_text(&format!("struct f({fields});"))
1165}
1166
1167pub fn record_field_list(
1168    fields: impl IntoIterator<Item = ast::RecordField>,
1169) -> ast::RecordFieldList {
1170    let fields = fields.into_iter().join(", ");
1171    ast_from_text(&format!("struct f {{ {fields} }}"))
1172}
1173
1174pub fn tuple_field(visibility: Option<ast::Visibility>, ty: ast::Type) -> ast::TupleField {
1175    let visibility = match visibility {
1176        None => String::new(),
1177        Some(it) => format!("{it} "),
1178    };
1179    ast_from_text(&format!("struct f({visibility}{ty});"))
1180}
1181
1182pub fn variant_list(variants: impl IntoIterator<Item = ast::Variant>) -> ast::VariantList {
1183    let variants = variants.into_iter().join(", ");
1184    ast_from_text(&format!("enum f {{ {variants} }}"))
1185}
1186
1187pub fn variant(
1188    visibility: Option<ast::Visibility>,
1189    name: ast::Name,
1190    field_list: Option<ast::FieldList>,
1191    discriminant: Option<ast::Expr>,
1192) -> ast::Variant {
1193    let visibility = match visibility {
1194        None => String::new(),
1195        Some(it) => format!("{it} "),
1196    };
1197
1198    let field_list = match field_list {
1199        None => String::new(),
1200        Some(it) => match it {
1201            ast::FieldList::RecordFieldList(record) => format!(" {record}"),
1202            ast::FieldList::TupleFieldList(tuple) => format!("{tuple}"),
1203        },
1204    };
1205
1206    let discriminant = match discriminant {
1207        Some(it) => format!(" = {it}"),
1208        None => String::new(),
1209    };
1210    ast_from_text(&format!("enum f {{ {visibility}{name}{field_list}{discriminant} }}"))
1211}
1212
1213pub fn fn_(
1214    attrs: impl IntoIterator<Item = ast::Attr>,
1215    visibility: Option<ast::Visibility>,
1216    fn_name: ast::Name,
1217    type_params: Option<ast::GenericParamList>,
1218    where_clause: Option<ast::WhereClause>,
1219    params: ast::ParamList,
1220    body: ast::BlockExpr,
1221    ret_type: Option<ast::RetType>,
1222    is_async: bool,
1223    is_const: bool,
1224    is_unsafe: bool,
1225    is_gen: bool,
1226) -> ast::Fn {
1227    let attrs =
1228        attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
1229    let type_params = match type_params {
1230        Some(type_params) => format!("{type_params}"),
1231        None => "".into(),
1232    };
1233    let where_clause = match where_clause {
1234        Some(it) => format!("{it} "),
1235        None => "".into(),
1236    };
1237    let ret_type = match ret_type {
1238        Some(ret_type) => format!("{ret_type} "),
1239        None => "".into(),
1240    };
1241    let visibility = match visibility {
1242        None => String::new(),
1243        Some(it) => format!("{it} "),
1244    };
1245
1246    let async_literal = if is_async { "async " } else { "" };
1247    let const_literal = if is_const { "const " } else { "" };
1248    let unsafe_literal = if is_unsafe { "unsafe " } else { "" };
1249    let gen_literal = if is_gen { "gen " } else { "" };
1250
1251    ast_from_text(&format!(
1252        "{attrs}{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
1253    ))
1254}
1255pub fn struct_(
1256    visibility: Option<ast::Visibility>,
1257    strukt_name: ast::Name,
1258    generic_param_list: Option<ast::GenericParamList>,
1259    field_list: ast::FieldList,
1260) -> ast::Struct {
1261    let (semicolon, ws) =
1262        if matches!(field_list, ast::FieldList::TupleFieldList(_)) { (";", "") } else { ("", " ") };
1263    let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string());
1264    let visibility = match visibility {
1265        None => String::new(),
1266        Some(it) => format!("{it} "),
1267    };
1268
1269    ast_from_text(&format!(
1270        "{visibility}struct {strukt_name}{type_params}{ws}{field_list}{semicolon}"
1271    ))
1272}
1273
1274pub fn enum_(
1275    attrs: impl IntoIterator<Item = ast::Attr>,
1276    visibility: Option<ast::Visibility>,
1277    enum_name: ast::Name,
1278    generic_param_list: Option<ast::GenericParamList>,
1279    where_clause: Option<ast::WhereClause>,
1280    variant_list: ast::VariantList,
1281) -> ast::Enum {
1282    let attrs =
1283        attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr));
1284    let visibility = match visibility {
1285        None => String::new(),
1286        Some(it) => format!("{it} "),
1287    };
1288
1289    let generic_params = generic_param_list.map(|it| it.to_string()).unwrap_or_default();
1290    let where_clause = where_clause.map(|it| format!(" {it}")).unwrap_or_default();
1291
1292    ast_from_text(&format!(
1293        "{attrs}{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}"
1294    ))
1295}
1296
1297pub fn attr_outer(meta: ast::Meta) -> ast::Attr {
1298    ast_from_text(&format!("#[{meta}]"))
1299}
1300
1301pub fn attr_inner(meta: ast::Meta) -> ast::Attr {
1302    ast_from_text(&format!("#![{meta}]"))
1303}
1304
1305pub fn meta_expr(path: ast::Path, expr: ast::Expr) -> ast::Meta {
1306    ast_from_text(&format!("#[{path} = {expr}]"))
1307}
1308
1309pub fn meta_token_tree(path: ast::Path, tt: ast::TokenTree) -> ast::Meta {
1310    ast_from_text(&format!("#[{path}{tt}]"))
1311}
1312
1313pub fn meta_path(path: ast::Path) -> ast::Meta {
1314    ast_from_text(&format!("#[{path}]"))
1315}
1316
1317pub fn token_tree(
1318    delimiter: SyntaxKind,
1319    tt: impl IntoIterator<Item = NodeOrToken<ast::TokenTree, SyntaxToken>>,
1320) -> ast::TokenTree {
1321    let (l_delimiter, r_delimiter) = match delimiter {
1322        T!['('] => ('(', ')'),
1323        T!['['] => ('[', ']'),
1324        T!['{'] => ('{', '}'),
1325        _ => panic!("invalid delimiter `{delimiter:?}`"),
1326    };
1327    let tt = tt.into_iter().join("");
1328
1329    ast_from_text(&format!("tt!{l_delimiter}{tt}{r_delimiter}"))
1330}
1331
1332#[track_caller]
1333fn ast_from_text<N: AstNode>(text: &str) -> N {
1334    ast_from_text_with_edition(text, Edition::CURRENT)
1335}
1336
1337#[track_caller]
1338fn ast_from_text_with_edition<N: AstNode>(text: &str, edition: Edition) -> N {
1339    let parse = SourceFile::parse(text, edition);
1340    let node = match parse.tree().syntax().descendants().find_map(N::cast) {
1341        Some(it) => it,
1342        None => {
1343            let node = std::any::type_name::<N>();
1344            panic!("Failed to make ast node `{node}` from text {text}")
1345        }
1346    };
1347    let node = node.clone_subtree();
1348    assert_eq!(node.syntax().text_range().start(), 0.into());
1349    node
1350}
1351
1352pub fn token(kind: SyntaxKind) -> SyntaxToken {
1353    tokens::SOURCE_FILE
1354        .tree()
1355        .syntax()
1356        .clone_for_update()
1357        .descendants_with_tokens()
1358        .filter_map(|it| it.into_token())
1359        .find(|it| it.kind() == kind)
1360        .unwrap_or_else(|| panic!("unhandled token: {kind:?}"))
1361}
1362
1363pub mod tokens {
1364    use std::sync::LazyLock;
1365
1366    use parser::Edition;
1367
1368    use crate::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, ast};
1369
1370    pub(super) static SOURCE_FILE: LazyLock<Parse<SourceFile>> = LazyLock::new(|| {
1371        SourceFile::parse(
1372            "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: {}",
1373            Edition::CURRENT,
1374        )
1375    });
1376
1377    pub fn semicolon() -> SyntaxToken {
1378        SOURCE_FILE
1379            .tree()
1380            .syntax()
1381            .clone_for_update()
1382            .descendants_with_tokens()
1383            .filter_map(|it| it.into_token())
1384            .find(|it| it.kind() == SEMICOLON)
1385            .unwrap()
1386    }
1387
1388    pub fn single_space() -> SyntaxToken {
1389        SOURCE_FILE
1390            .tree()
1391            .syntax()
1392            .clone_for_update()
1393            .descendants_with_tokens()
1394            .filter_map(|it| it.into_token())
1395            .find(|it| it.kind() == WHITESPACE && it.text() == " ")
1396            .unwrap()
1397    }
1398
1399    pub fn crate_kw() -> SyntaxToken {
1400        SOURCE_FILE
1401            .tree()
1402            .syntax()
1403            .clone_for_update()
1404            .descendants_with_tokens()
1405            .filter_map(|it| it.into_token())
1406            .find(|it| it.kind() == CRATE_KW)
1407            .unwrap()
1408    }
1409
1410    pub fn whitespace(text: &str) -> SyntaxToken {
1411        assert!(text.trim().is_empty());
1412        let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap();
1413        sf.syntax().clone_for_update().first_child_or_token().unwrap().into_token().unwrap()
1414    }
1415
1416    pub fn doc_comment(text: &str) -> SyntaxToken {
1417        assert!(!text.trim().is_empty());
1418        let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap();
1419        sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
1420    }
1421
1422    pub fn literal(text: &str) -> SyntaxToken {
1423        assert_eq!(text.trim(), text);
1424        let lit: ast::Literal = super::ast_from_text(&format!("fn f() {{ let _ = {text}; }}"));
1425        lit.syntax().first_child_or_token().unwrap().into_token().unwrap()
1426    }
1427
1428    pub fn ident(text: &str) -> SyntaxToken {
1429        assert_eq!(text.trim(), text);
1430        let path: ast::Path = super::ext::ident_path(text);
1431        path.syntax()
1432            .descendants_with_tokens()
1433            .filter_map(|it| it.into_token())
1434            .find(|it| it.kind() == IDENT)
1435            .unwrap()
1436    }
1437
1438    pub fn single_newline() -> SyntaxToken {
1439        let res = SOURCE_FILE
1440            .tree()
1441            .syntax()
1442            .clone_for_update()
1443            .descendants_with_tokens()
1444            .filter_map(|it| it.into_token())
1445            .find(|it| it.kind() == WHITESPACE && it.text() == "\n")
1446            .unwrap();
1447        res.detach();
1448        res
1449    }
1450
1451    pub fn blank_line() -> SyntaxToken {
1452        SOURCE_FILE
1453            .tree()
1454            .syntax()
1455            .clone_for_update()
1456            .descendants_with_tokens()
1457            .filter_map(|it| it.into_token())
1458            .find(|it| it.kind() == WHITESPACE && it.text() == "\n\n")
1459            .unwrap()
1460    }
1461
1462    pub struct WsBuilder(SourceFile);
1463
1464    impl WsBuilder {
1465        pub fn new(text: &str) -> WsBuilder {
1466            WsBuilder(SourceFile::parse(text, Edition::CURRENT).ok().unwrap())
1467        }
1468        pub fn ws(&self) -> SyntaxToken {
1469            self.0.syntax().first_child_or_token().unwrap().into_token().unwrap()
1470        }
1471    }
1472}
1473
1474#[cfg(test)]
1475mod tests {
1476    use expect_test::expect;
1477
1478    use super::*;
1479
1480    #[track_caller]
1481    fn check(node: impl AstNode, expect: expect_test::Expect) {
1482        let node_debug = format!("{:#?}", node.syntax());
1483        expect.assert_eq(&node_debug);
1484    }
1485
1486    #[test]
1487    fn test_unnamed_param() {
1488        check(
1489            unnamed_param(ty("Vec")),
1490            expect![[r#"
1491                PARAM@0..3
1492                  PATH_TYPE@0..3
1493                    PATH@0..3
1494                      PATH_SEGMENT@0..3
1495                        NAME_REF@0..3
1496                          IDENT@0..3 "Vec"
1497            "#]],
1498        );
1499
1500        check(
1501            unnamed_param(ty("Vec<T>")),
1502            expect![[r#"
1503                PARAM@0..6
1504                  PATH_TYPE@0..6
1505                    PATH@0..6
1506                      PATH_SEGMENT@0..6
1507                        NAME_REF@0..3
1508                          IDENT@0..3 "Vec"
1509                        GENERIC_ARG_LIST@3..6
1510                          L_ANGLE@3..4 "<"
1511                          TYPE_ARG@4..5
1512                            PATH_TYPE@4..5
1513                              PATH@4..5
1514                                PATH_SEGMENT@4..5
1515                                  NAME_REF@4..5
1516                                    IDENT@4..5 "T"
1517                          R_ANGLE@5..6 ">"
1518            "#]],
1519        );
1520    }
1521
1522    #[test]
1523    fn test_untyped_param() {
1524        check(
1525            untyped_param(path_pat(ext::ident_path("name"))),
1526            expect![[r#"
1527                PARAM@0..4
1528                  IDENT_PAT@0..4
1529                    NAME@0..4
1530                      IDENT@0..4 "name"
1531            "#]],
1532        );
1533
1534        check(
1535            untyped_param(
1536                range_pat(
1537                    Some(path_pat(ext::ident_path("start"))),
1538                    Some(path_pat(ext::ident_path("end"))),
1539                )
1540                .into(),
1541            ),
1542            expect![[r#"
1543                PARAM@0..10
1544                  RANGE_PAT@0..10
1545                    IDENT_PAT@0..5
1546                      NAME@0..5
1547                        IDENT@0..5 "start"
1548                    DOT2@5..7 ".."
1549                    IDENT_PAT@7..10
1550                      NAME@7..10
1551                        IDENT@7..10 "end"
1552            "#]],
1553        );
1554    }
1555}