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