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