1use quote::ToTokens;
4
5use super::ast::*;
6use crate::SourceResult;
7
8pub trait ToPure {
10 type Output;
12 fn to_pure(&self) -> Self::Output;
14}
15
16fn abi_to_string(abi: &syn::Abi) -> String {
18 abi.name
19 .as_ref()
20 .map(|lit| lit.value())
21 .unwrap_or_else(|| "C".to_string()) }
23
24fn label_to_string(label: &syn::Label) -> String {
26 label.name.ident.to_string()
27}
28
29fn infer_async_from_return_type(ret: &syn::ReturnType) -> bool {
38 let ty = match ret {
39 syn::ReturnType::Default => return false,
40 syn::ReturnType::Type(_, ty) => ty,
41 };
42
43 let ty_str = ty.to_token_stream().to_string();
45
46 is_pinned_boxed_future(&ty_str)
50}
51
52fn is_pinned_boxed_future(ty_str: &str) -> bool {
54 let normalized: String = ty_str.chars().filter(|c| !c.is_whitespace()).collect();
56
57 normalized.starts_with("Pin<Box<dynFuture<")
63 || normalized.starts_with("::core::pin::Pin<Box<dynFuture<")
64 || normalized.starts_with("core::pin::Pin<Box<dynFuture<")
65 || normalized.starts_with("std::pin::Pin<Box<dynFuture<")
66 || normalized.starts_with("::std::pin::Pin<Box<dynFuture<")
67}
68
69impl PureFile {
70 pub fn from_source(source: &str) -> SourceResult<Self> {
72 let file = syn::parse_file(source)?;
73 Ok(file.to_pure())
74 }
75}
76
77impl ToPure for syn::File {
78 type Output = PureFile;
79
80 fn to_pure(&self) -> PureFile {
81 PureFile {
82 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
83 items: self.items.iter().map(|i| i.to_pure()).collect(),
84 }
85 }
86}
87
88impl ToPure for syn::Attribute {
89 type Output = PureAttribute;
90
91 fn to_pure(&self) -> PureAttribute {
92 let meta = match &self.meta {
93 syn::Meta::Path(_) => PureAttrMeta::Path,
94 syn::Meta::List(list) => {
95 PureAttrMeta::List(list.tokens.to_string())
97 }
98 syn::Meta::NameValue(nv) => {
99 PureAttrMeta::NameValue(nv.value.to_token_stream().to_string())
101 }
102 };
103
104 PureAttribute {
105 path: path_to_string(self.path()),
106 meta,
107 is_inner: matches!(self.style, syn::AttrStyle::Inner(_)),
108 }
109 }
110}
111
112impl ToPure for syn::Item {
113 type Output = PureItem;
114
115 fn to_pure(&self) -> PureItem {
116 match self {
117 syn::Item::Use(u) => PureItem::Use(u.to_pure()),
118 syn::Item::Fn(f) => PureItem::Fn(f.to_pure()),
119 syn::Item::Struct(s) => PureItem::Struct(s.to_pure()),
120 syn::Item::Enum(e) => PureItem::Enum(e.to_pure()),
121 syn::Item::Impl(i) => PureItem::Impl(i.to_pure()),
122 syn::Item::Const(c) => PureItem::Const(c.to_pure()),
123 syn::Item::Static(s) => PureItem::Static(s.to_pure()),
124 syn::Item::Type(t) => PureItem::Type(t.to_pure()),
125 syn::Item::Mod(m) => PureItem::Mod(m.to_pure()),
126 syn::Item::Trait(t) => PureItem::Trait(t.to_pure()),
127 syn::Item::Macro(m) => PureItem::Macro(m.to_pure()),
128 other => PureItem::Other(other.to_token_stream().to_string()),
129 }
130 }
131}
132
133impl ToPure for syn::ItemUse {
134 type Output = PureUse;
135
136 fn to_pure(&self) -> PureUse {
137 PureUse {
138 vis: self.vis.to_pure(),
139 tree: self.tree.to_pure(),
140 }
141 }
142}
143
144impl ToPure for syn::UseTree {
145 type Output = PureUseTree;
146
147 fn to_pure(&self) -> PureUseTree {
148 match self {
149 syn::UseTree::Path(p) => PureUseTree::Path {
150 path: p.ident.to_string(),
151 tree: Box::new(p.tree.to_pure()),
152 },
153 syn::UseTree::Name(n) => PureUseTree::Name(n.ident.to_string()),
154 syn::UseTree::Rename(r) => PureUseTree::Rename {
155 name: r.ident.to_string(),
156 rename: r.rename.to_string(),
157 },
158 syn::UseTree::Glob(_) => PureUseTree::Glob,
159 syn::UseTree::Group(g) => {
160 PureUseTree::Group(g.items.iter().map(|t| t.to_pure()).collect())
161 }
162 }
163 }
164}
165
166impl ToPure for syn::Visibility {
167 type Output = PureVis;
168
169 fn to_pure(&self) -> PureVis {
170 match self {
171 syn::Visibility::Public(_) => PureVis::Public,
172 syn::Visibility::Restricted(r) => {
173 let path = path_to_string(&r.path);
174 match path.as_str() {
175 "crate" => PureVis::Crate,
176 "super" => PureVis::Super,
177 _ => PureVis::In(path),
178 }
179 }
180 syn::Visibility::Inherited => PureVis::Private,
181 }
182 }
183}
184
185impl ToPure for syn::ItemFn {
186 type Output = PureFn;
187
188 fn to_pure(&self) -> PureFn {
189 let is_async = self.sig.asyncness.is_some();
190 let is_async_inferred = !is_async && infer_async_from_return_type(&self.sig.output);
191
192 PureFn {
193 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
194 vis: self.vis.to_pure(),
195 is_async,
196 is_async_inferred,
197 is_const: self.sig.constness.is_some(),
198 is_unsafe: self.sig.unsafety.is_some(),
199 abi: self.sig.abi.as_ref().map(abi_to_string),
200 name: self.sig.ident.to_string(),
201 generics: self.sig.generics.to_pure(),
202 params: self.sig.inputs.iter().map(|p| p.to_pure()).collect(),
203 ret: match &self.sig.output {
204 syn::ReturnType::Default => None,
205 syn::ReturnType::Type(_, ty) => Some(ty.to_pure()),
206 },
207 body: self.block.to_pure(),
208 }
209 }
210}
211
212impl ToPure for syn::FnArg {
213 type Output = PureParam;
214
215 fn to_pure(&self) -> PureParam {
216 match self {
217 syn::FnArg::Receiver(r) => PureParam::SelfValue {
218 is_ref: r.reference.is_some(),
219 is_mut: r.mutability.is_some(),
220 },
221 syn::FnArg::Typed(t) => PureParam::Typed {
222 name: pat_to_name(&t.pat),
223 ty: t.ty.to_pure(),
224 },
225 }
226 }
227}
228
229impl ToPure for syn::Generics {
230 type Output = PureGenerics;
231
232 fn to_pure(&self) -> PureGenerics {
233 PureGenerics {
234 params: self.params.iter().map(|p| p.to_pure()).collect(),
235 where_clause: self
236 .where_clause
237 .as_ref()
238 .map(|w| {
239 w.predicates
240 .iter()
241 .map(|p| p.to_token_stream().to_string())
242 .collect()
243 })
244 .unwrap_or_default(),
245 }
246 }
247}
248
249impl ToPure for syn::GenericParam {
250 type Output = PureGenericParam;
251
252 fn to_pure(&self) -> PureGenericParam {
253 match self {
254 syn::GenericParam::Type(t) => PureGenericParam::Type {
255 name: t.ident.to_string(),
256 bounds: t
257 .bounds
258 .iter()
259 .map(|b| b.to_token_stream().to_string())
260 .collect(),
261 },
262 syn::GenericParam::Lifetime(l) => PureGenericParam::Lifetime {
263 name: l.lifetime.to_string(),
264 bounds: l.bounds.iter().map(|b| b.to_string()).collect(),
265 },
266 syn::GenericParam::Const(c) => PureGenericParam::Const {
267 name: c.ident.to_string(),
268 ty: c.ty.to_token_stream().to_string(),
269 },
270 }
271 }
272}
273
274impl ToPure for syn::Block {
275 type Output = PureBlock;
276
277 fn to_pure(&self) -> PureBlock {
278 PureBlock {
279 stmts: self.stmts.iter().map(|s| s.to_pure()).collect(),
280 }
281 }
282}
283
284impl ToPure for syn::Stmt {
285 type Output = PureStmt;
286
287 fn to_pure(&self) -> PureStmt {
288 match self {
289 syn::Stmt::Local(l) => {
290 let (pattern, ty) = match &l.pat {
292 syn::Pat::Type(pt) => (pt.pat.to_pure(), Some(pt.ty.to_pure())),
293 other => (other.to_pure(), None),
294 };
295 PureStmt::Local {
296 pattern,
297 ty,
298 init: l.init.as_ref().map(|i| i.expr.to_pure()),
299 }
300 }
301 syn::Stmt::Item(i) => PureStmt::Item(Box::new(i.to_pure())),
302 syn::Stmt::Expr(e, semi) => {
303 if semi.is_some() {
304 PureStmt::Semi(e.to_pure())
305 } else {
306 PureStmt::Expr(e.to_pure())
307 }
308 }
309 syn::Stmt::Macro(m) => PureStmt::Semi(PureExpr::Macro {
310 name: path_to_string(&m.mac.path),
311 delimiter: match m.mac.delimiter {
312 syn::MacroDelimiter::Paren(_) => MacroDelimiter::Paren,
313 syn::MacroDelimiter::Brace(_) => MacroDelimiter::Brace,
314 syn::MacroDelimiter::Bracket(_) => MacroDelimiter::Bracket,
315 },
316 tokens: m.mac.tokens.to_string(),
317 }),
318 }
319 }
320}
321
322impl ToPure for syn::Pat {
323 type Output = PurePattern;
324
325 fn to_pure(&self) -> PurePattern {
326 match self {
327 syn::Pat::Ident(i) => PurePattern::Ident {
328 name: i.ident.to_string(),
329 is_mut: i.mutability.is_some(),
330 },
331 syn::Pat::Wild(_) => PurePattern::Wild,
332 syn::Pat::Tuple(t) => PurePattern::Tuple(t.elems.iter().map(|p| p.to_pure()).collect()),
333 syn::Pat::TupleStruct(ts) => PurePattern::Struct {
334 path: path_to_string(&ts.path),
335 fields: ts
336 .elems
337 .iter()
338 .enumerate()
339 .map(|(i, p)| (i.to_string(), p.to_pure()))
340 .collect(),
341 rest: false,
342 },
343 syn::Pat::Struct(s) => PurePattern::Struct {
344 path: path_to_string(&s.path),
345 fields: s
346 .fields
347 .iter()
348 .map(|f| {
349 let name = f.member.to_token_stream().to_string();
350 (name, f.pat.to_pure())
351 })
352 .collect(),
353 rest: s.rest.is_some(),
354 },
355 syn::Pat::Reference(r) => PurePattern::Ref {
356 is_mut: r.mutability.is_some(),
357 pattern: Box::new(r.pat.to_pure()),
358 },
359 syn::Pat::Lit(l) => PurePattern::Lit(l.lit.to_token_stream().to_string()),
360 syn::Pat::Or(o) => PurePattern::Or(o.cases.iter().map(|p| p.to_pure()).collect()),
361 syn::Pat::Type(t) => t.pat.to_pure(),
362 syn::Pat::Path(p) => PurePattern::Path(path_to_string(&p.path)),
363 syn::Pat::Range(r) => PurePattern::Range {
364 start: r.start.as_ref().map(|e| e.to_token_stream().to_string()),
365 end: r.end.as_ref().map(|e| e.to_token_stream().to_string()),
366 inclusive: matches!(r.limits, syn::RangeLimits::Closed(_)),
367 },
368 syn::Pat::Slice(s) => PurePattern::Slice(s.elems.iter().map(|p| p.to_pure()).collect()),
369 syn::Pat::Rest(_) => PurePattern::Rest,
370 syn::Pat::Paren(p) => p.pat.to_pure(),
371 other => PurePattern::Other(other.to_token_stream().to_string()),
372 }
373 }
374}
375
376impl ToPure for syn::Expr {
377 type Output = PureExpr;
378
379 fn to_pure(&self) -> PureExpr {
380 match self {
381 syn::Expr::Lit(l) => PureExpr::Lit(l.lit.to_token_stream().to_string()),
382 syn::Expr::Path(p) => PureExpr::Path(path_to_string(&p.path)),
383 syn::Expr::Binary(b) => PureExpr::Binary {
384 op: b.op.to_token_stream().to_string(),
385 left: Box::new(b.left.to_pure()),
386 right: Box::new(b.right.to_pure()),
387 },
388 syn::Expr::Unary(u) => PureExpr::Unary {
389 op: u.op.to_token_stream().to_string(),
390 expr: Box::new(u.expr.to_pure()),
391 },
392 syn::Expr::Call(c) => PureExpr::Call {
393 func: Box::new(c.func.to_pure()),
394 args: c.args.iter().map(|a| a.to_pure()).collect(),
395 },
396 syn::Expr::MethodCall(m) => PureExpr::MethodCall {
397 receiver: Box::new(m.receiver.to_pure()),
398 method: m.method.to_string(),
399 turbofish: m
400 .turbofish
401 .as_ref()
402 .map(|t| t.args.to_token_stream().to_string()),
403 args: m.args.iter().map(|a| a.to_pure()).collect(),
404 },
405 syn::Expr::Field(f) => PureExpr::Field {
406 expr: Box::new(f.base.to_pure()),
407 field: f.member.to_token_stream().to_string(),
408 },
409 syn::Expr::Index(i) => PureExpr::Index {
410 expr: Box::new(i.expr.to_pure()),
411 index: Box::new(i.index.to_pure()),
412 },
413 syn::Expr::Block(b) => PureExpr::Block {
414 label: b.label.as_ref().map(label_to_string),
415 block: b.block.to_pure(),
416 },
417 syn::Expr::If(i) => PureExpr::If {
418 cond: Box::new(i.cond.to_pure()),
419 then_branch: i.then_branch.to_pure(),
420 else_branch: i.else_branch.as_ref().map(|(_, e)| Box::new(e.to_pure())),
421 },
422 syn::Expr::Match(m) => PureExpr::Match {
423 expr: Box::new(m.expr.to_pure()),
424 arms: m.arms.iter().map(|a| a.to_pure()).collect(),
425 },
426 syn::Expr::Loop(l) => PureExpr::Loop {
427 label: l.label.as_ref().map(label_to_string),
428 body: l.body.to_pure(),
429 },
430 syn::Expr::While(w) => PureExpr::While {
431 label: w.label.as_ref().map(label_to_string),
432 cond: Box::new(w.cond.to_pure()),
433 body: w.body.to_pure(),
434 },
435 syn::Expr::ForLoop(f) => PureExpr::For {
436 label: f.label.as_ref().map(label_to_string),
437 pat: f.pat.to_pure(),
438 expr: Box::new(f.expr.to_pure()),
439 body: f.body.to_pure(),
440 },
441 syn::Expr::Return(r) => {
442 PureExpr::Return(r.expr.as_ref().map(|e| Box::new(e.to_pure())))
443 }
444 syn::Expr::Break(b) => PureExpr::Break {
445 label: b.label.as_ref().map(|l| l.ident.to_string()),
446 expr: b.expr.as_ref().map(|e| Box::new(e.to_pure())),
447 },
448 syn::Expr::Continue(c) => PureExpr::Continue {
449 label: c.label.as_ref().map(|l| l.ident.to_string()),
450 },
451 syn::Expr::Closure(c) => PureExpr::Closure {
452 is_async: c.asyncness.is_some(),
453 is_move: c.capture.is_some(),
454 params: c
455 .inputs
456 .iter()
457 .map(|p| match p {
458 syn::Pat::Type(pt) => {
459 PureClosureParam::typed(pt.pat.to_pure(), pt.ty.to_pure())
460 }
461 other => PureClosureParam::untyped(other.to_pure()),
462 })
463 .collect(),
464 ret: match &c.output {
465 syn::ReturnType::Default => None,
466 syn::ReturnType::Type(_, ty) => Some(ty.to_pure()),
467 },
468 body: Box::new(c.body.to_pure()),
469 },
470 syn::Expr::Struct(s) => PureExpr::Struct {
471 path: path_to_string(&s.path),
472 fields: s
473 .fields
474 .iter()
475 .map(|f| {
476 let name = f.member.to_token_stream().to_string();
477 (name, f.expr.to_pure())
478 })
479 .collect(),
480 },
481 syn::Expr::Tuple(t) => PureExpr::Tuple(t.elems.iter().map(|e| e.to_pure()).collect()),
482 syn::Expr::Array(a) => PureExpr::Array(a.elems.iter().map(|e| e.to_pure()).collect()),
483 syn::Expr::Reference(r) => PureExpr::Ref {
484 is_mut: r.mutability.is_some(),
485 expr: Box::new(r.expr.to_pure()),
486 },
487 syn::Expr::Macro(m) => PureExpr::Macro {
488 name: path_to_string(&m.mac.path),
489 delimiter: match m.mac.delimiter {
490 syn::MacroDelimiter::Paren(_) => MacroDelimiter::Paren,
491 syn::MacroDelimiter::Brace(_) => MacroDelimiter::Brace,
492 syn::MacroDelimiter::Bracket(_) => MacroDelimiter::Bracket,
493 },
494 tokens: m.mac.tokens.to_string(),
495 },
496 syn::Expr::Await(a) => PureExpr::Await(Box::new(a.base.to_pure())),
497 syn::Expr::Try(t) => PureExpr::Try(Box::new(t.expr.to_pure())),
498 syn::Expr::Paren(p) => p.expr.to_pure(),
499 syn::Expr::Assign(a) => PureExpr::Binary {
501 op: "=".to_string(),
502 left: Box::new(a.left.to_pure()),
503 right: Box::new(a.right.to_pure()),
504 },
505 syn::Expr::Range(r) => PureExpr::Range {
507 start: r.start.as_ref().map(|e| Box::new(e.to_pure())),
508 end: r.end.as_ref().map(|e| Box::new(e.to_pure())),
509 inclusive: matches!(r.limits, syn::RangeLimits::Closed(_)),
510 },
511 syn::Expr::Cast(c) => PureExpr::Cast {
513 expr: Box::new(c.expr.to_pure()),
514 ty: c.ty.to_pure(),
515 },
516 syn::Expr::Let(l) => PureExpr::Let {
518 pattern: l.pat.to_pure(),
519 expr: Box::new(l.expr.to_pure()),
520 },
521 syn::Expr::Async(a) => PureExpr::Async {
523 is_move: a.capture.is_some(),
524 body: a.block.to_pure(),
525 },
526 syn::Expr::Unsafe(u) => PureExpr::Unsafe(u.block.to_pure()),
528 syn::Expr::Repeat(r) => PureExpr::Repeat {
530 expr: Box::new(r.expr.to_pure()),
531 len: Box::new(r.len.to_pure()),
532 },
533 other => PureExpr::Other(other.to_token_stream().to_string()),
534 }
535 }
536}
537
538impl ToPure for syn::Arm {
539 type Output = PureMatchArm;
540
541 fn to_pure(&self) -> PureMatchArm {
542 PureMatchArm {
543 pattern: self.pat.to_pure(),
544 guard: self.guard.as_ref().map(|(_, e)| e.to_pure()),
545 body: self.body.to_pure(),
546 }
547 }
548}
549
550impl ToPure for syn::Type {
551 type Output = PureType;
552
553 fn to_pure(&self) -> PureType {
554 match self {
555 syn::Type::Path(p) => PureType::Path(path_to_string(&p.path)),
556 syn::Type::Reference(r) => PureType::Ref {
557 lifetime: r.lifetime.as_ref().map(|l| l.to_string()),
558 is_mut: r.mutability.is_some(),
559 ty: Box::new(r.elem.to_pure()),
560 },
561 syn::Type::Tuple(t) => PureType::Tuple(t.elems.iter().map(|t| t.to_pure()).collect()),
562 syn::Type::Array(a) => PureType::Array {
563 ty: Box::new(a.elem.to_pure()),
564 len: a.len.to_token_stream().to_string(),
565 },
566 syn::Type::Slice(s) => PureType::Slice(Box::new(s.elem.to_pure())),
567 syn::Type::BareFn(f) => PureType::Fn {
568 params: f.inputs.iter().map(|i| i.ty.to_pure()).collect(),
569 ret: match &f.output {
570 syn::ReturnType::Default => None,
571 syn::ReturnType::Type(_, ty) => Some(Box::new(ty.to_pure())),
572 },
573 },
574 syn::Type::ImplTrait(i) => PureType::ImplTrait(
575 i.bounds
576 .iter()
577 .map(|b| b.to_token_stream().to_string())
578 .collect(),
579 ),
580 syn::Type::TraitObject(t) => PureType::TraitObject(
581 t.bounds
582 .iter()
583 .map(|b| b.to_token_stream().to_string())
584 .collect(),
585 ),
586 syn::Type::Infer(_) => PureType::Infer,
587 syn::Type::Never(_) => PureType::Never,
588 syn::Type::Paren(p) => p.elem.to_pure(),
589 other => PureType::Other(other.to_token_stream().to_string()),
590 }
591 }
592}
593
594impl ToPure for syn::ItemStruct {
595 type Output = PureStruct;
596
597 fn to_pure(&self) -> PureStruct {
598 PureStruct {
599 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
600 vis: self.vis.to_pure(),
601 name: self.ident.to_string(),
602 generics: self.generics.to_pure(),
603 fields: self.fields.to_pure(),
604 }
605 }
606}
607
608impl ToPure for syn::Fields {
609 type Output = PureFields;
610
611 fn to_pure(&self) -> PureFields {
612 match self {
613 syn::Fields::Named(n) => {
614 PureFields::Named(n.named.iter().map(|f| f.to_pure()).collect())
615 }
616 syn::Fields::Unnamed(u) => {
617 PureFields::Tuple(u.unnamed.iter().map(|f| f.ty.to_pure()).collect())
618 }
619 syn::Fields::Unit => PureFields::Unit,
620 }
621 }
622}
623
624impl ToPure for syn::Field {
625 type Output = PureField;
626
627 fn to_pure(&self) -> PureField {
628 PureField {
629 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
630 vis: self.vis.to_pure(),
631 name: self
632 .ident
633 .as_ref()
634 .map(|i| i.to_string())
635 .unwrap_or_default(),
636 ty: self.ty.to_pure(),
637 }
638 }
639}
640
641impl ToPure for syn::ItemEnum {
642 type Output = PureEnum;
643
644 fn to_pure(&self) -> PureEnum {
645 PureEnum {
646 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
647 vis: self.vis.to_pure(),
648 name: self.ident.to_string(),
649 generics: self.generics.to_pure(),
650 variants: self.variants.iter().map(|v| v.to_pure()).collect(),
651 }
652 }
653}
654
655impl ToPure for syn::Variant {
656 type Output = PureVariant;
657
658 fn to_pure(&self) -> PureVariant {
659 PureVariant {
660 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
661 name: self.ident.to_string(),
662 fields: self.fields.to_pure(),
663 discriminant: self
664 .discriminant
665 .as_ref()
666 .map(|(_, e)| e.to_token_stream().to_string()),
667 }
668 }
669}
670
671impl ToPure for syn::ItemImpl {
672 type Output = PureImpl;
673
674 fn to_pure(&self) -> PureImpl {
675 PureImpl {
676 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
677 generics: self.generics.to_pure(),
678 is_unsafe: self.unsafety.is_some(),
679 trait_: self.trait_.as_ref().map(|(_, p, _)| path_to_string(p)),
680 self_ty: self.self_ty.to_token_stream().to_string(),
681 items: self.items.iter().map(|i| i.to_pure()).collect(),
682 }
683 }
684}
685
686impl ToPure for syn::ImplItem {
687 type Output = PureImplItem;
688
689 fn to_pure(&self) -> PureImplItem {
690 match self {
691 syn::ImplItem::Fn(f) => PureImplItem::Fn(f.to_pure()),
692 syn::ImplItem::Const(c) => PureImplItem::Const(c.to_pure()),
693 syn::ImplItem::Type(t) => PureImplItem::Type(t.to_pure()),
694 other => PureImplItem::Other(other.to_token_stream().to_string()),
695 }
696 }
697}
698
699impl ToPure for syn::ImplItemFn {
700 type Output = PureFn;
701
702 fn to_pure(&self) -> PureFn {
703 let is_async = self.sig.asyncness.is_some();
704 let is_async_inferred = !is_async && infer_async_from_return_type(&self.sig.output);
705
706 PureFn {
707 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
708 vis: self.vis.to_pure(),
709 is_async,
710 is_async_inferred,
711 is_const: self.sig.constness.is_some(),
712 is_unsafe: self.sig.unsafety.is_some(),
713 abi: self.sig.abi.as_ref().map(abi_to_string),
714 name: self.sig.ident.to_string(),
715 generics: self.sig.generics.to_pure(),
716 params: self.sig.inputs.iter().map(|p| p.to_pure()).collect(),
717 ret: match &self.sig.output {
718 syn::ReturnType::Default => None,
719 syn::ReturnType::Type(_, ty) => Some(ty.to_pure()),
720 },
721 body: self.block.to_pure(),
722 }
723 }
724}
725
726impl ToPure for syn::ImplItemConst {
727 type Output = PureConst;
728
729 fn to_pure(&self) -> PureConst {
730 PureConst {
731 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
732 vis: self.vis.to_pure(),
733 name: self.ident.to_string(),
734 ty: self.ty.to_pure(),
735 value: Some(self.expr.to_pure()),
736 }
737 }
738}
739
740impl ToPure for syn::ImplItemType {
741 type Output = PureTypeAlias;
742
743 fn to_pure(&self) -> PureTypeAlias {
744 PureTypeAlias {
745 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
746 vis: self.vis.to_pure(),
747 name: self.ident.to_string(),
748 generics: self.generics.to_pure(),
749 ty: self.ty.to_pure(),
750 }
751 }
752}
753
754impl ToPure for syn::ItemConst {
755 type Output = PureConst;
756
757 fn to_pure(&self) -> PureConst {
758 PureConst {
759 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
760 vis: self.vis.to_pure(),
761 name: self.ident.to_string(),
762 ty: self.ty.to_pure(),
763 value: Some(self.expr.to_pure()),
764 }
765 }
766}
767
768impl ToPure for syn::ItemStatic {
769 type Output = PureStatic;
770
771 fn to_pure(&self) -> PureStatic {
772 PureStatic {
773 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
774 vis: self.vis.to_pure(),
775 is_mut: matches!(self.mutability, syn::StaticMutability::Mut(_)),
776 name: self.ident.to_string(),
777 ty: self.ty.to_pure(),
778 value: self.expr.to_pure(),
779 }
780 }
781}
782
783impl ToPure for syn::ItemType {
784 type Output = PureTypeAlias;
785
786 fn to_pure(&self) -> PureTypeAlias {
787 PureTypeAlias {
788 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
789 vis: self.vis.to_pure(),
790 name: self.ident.to_string(),
791 generics: self.generics.to_pure(),
792 ty: self.ty.to_pure(),
793 }
794 }
795}
796
797impl ToPure for syn::ItemMod {
798 type Output = PureMod;
799
800 fn to_pure(&self) -> PureMod {
801 let items = if let Some((_, items)) = &self.content {
803 items.iter().map(|item| item.to_pure()).collect()
804 } else {
805 Vec::new()
807 };
808
809 PureMod {
810 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
811 vis: self.vis.to_pure(),
812 name: self.ident.to_string(),
813 items,
814 }
815 }
816}
817
818impl ToPure for syn::ItemTrait {
819 type Output = PureTrait;
820
821 fn to_pure(&self) -> PureTrait {
822 PureTrait {
823 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
824 vis: self.vis.to_pure(),
825 is_unsafe: self.unsafety.is_some(),
826 is_auto: self.auto_token.is_some(),
827 name: self.ident.to_string(),
828 generics: self.generics.to_pure(),
829 supertraits: self
830 .supertraits
831 .iter()
832 .map(|b| b.to_token_stream().to_string())
833 .collect(),
834 items: self.items.iter().map(|i| i.to_pure()).collect(),
835 }
836 }
837}
838
839impl ToPure for syn::TraitItem {
840 type Output = PureTraitItem;
841
842 fn to_pure(&self) -> PureTraitItem {
843 match self {
844 syn::TraitItem::Fn(f) => PureTraitItem::Fn(f.to_pure()),
845 syn::TraitItem::Const(c) => PureTraitItem::Const(c.to_pure()),
846 syn::TraitItem::Type(t) => PureTraitItem::Type {
847 name: t.ident.to_string(),
848 bounds: t
849 .bounds
850 .iter()
851 .map(|b| b.to_token_stream().to_string())
852 .collect(),
853 default: t.default.as_ref().map(|(_, ty)| ty.to_pure()),
854 },
855 other => PureTraitItem::Other(other.to_token_stream().to_string()),
856 }
857 }
858}
859
860impl ToPure for syn::TraitItemFn {
861 type Output = PureFn;
862
863 fn to_pure(&self) -> PureFn {
864 let is_async = self.sig.asyncness.is_some();
865 let is_async_inferred = !is_async && infer_async_from_return_type(&self.sig.output);
866
867 PureFn {
868 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
869 vis: PureVis::Private, is_async,
871 is_async_inferred,
872 is_const: self.sig.constness.is_some(),
873 is_unsafe: self.sig.unsafety.is_some(),
874 abi: self.sig.abi.as_ref().map(abi_to_string),
875 name: self.sig.ident.to_string(),
876 generics: self.sig.generics.to_pure(),
877 params: self.sig.inputs.iter().map(|p| p.to_pure()).collect(),
878 ret: match &self.sig.output {
879 syn::ReturnType::Default => None,
880 syn::ReturnType::Type(_, ty) => Some(ty.to_pure()),
881 },
882 body: self
883 .default
884 .as_ref()
885 .map(|b| b.to_pure())
886 .unwrap_or_default(),
887 }
888 }
889}
890
891impl ToPure for syn::TraitItemConst {
892 type Output = PureConst;
893
894 fn to_pure(&self) -> PureConst {
895 PureConst {
896 attrs: self.attrs.iter().map(|a| a.to_pure()).collect(),
897 vis: PureVis::Private,
898 name: self.ident.to_string(),
899 ty: self.ty.to_pure(),
900 value: self.default.as_ref().map(|(_, e)| e.to_pure()),
901 }
902 }
903}
904
905impl ToPure for syn::ItemMacro {
906 type Output = PureMacro;
907
908 fn to_pure(&self) -> PureMacro {
909 PureMacro {
910 path: path_to_string(&self.mac.path),
911 delimiter: match self.mac.delimiter {
912 syn::MacroDelimiter::Paren(_) => MacroDelimiter::Paren,
913 syn::MacroDelimiter::Brace(_) => MacroDelimiter::Brace,
914 syn::MacroDelimiter::Bracket(_) => MacroDelimiter::Bracket,
915 },
916 tokens: self.mac.tokens.to_string(),
917 }
918 }
919}
920
921fn path_to_string(path: &syn::Path) -> String {
924 path.segments
925 .iter()
926 .map(|s| {
927 let ident = s.ident.to_string();
928 match &s.arguments {
929 syn::PathArguments::None => ident,
930 syn::PathArguments::AngleBracketed(args) => {
931 format!("{}{}", ident, args.to_token_stream())
932 }
933 syn::PathArguments::Parenthesized(args) => {
934 format!("{}{}", ident, args.to_token_stream())
935 }
936 }
937 })
938 .collect::<Vec<_>>()
939 .join("::")
940}
941
942fn pat_to_name(pat: &syn::Pat) -> String {
943 match pat {
944 syn::Pat::Ident(i) => i.ident.to_string(),
945 syn::Pat::Type(t) => pat_to_name(&t.pat),
946 syn::Pat::Tuple(t) => {
947 format!("_tuple{}", t.elems.len())
949 }
950 syn::Pat::TupleStruct(ts) => {
951 ts.path
953 .segments
954 .last()
955 .map(|s| s.ident.to_string())
956 .unwrap_or_else(|| "_pattern".to_string())
957 }
958 syn::Pat::Struct(s) => {
959 s.path
961 .segments
962 .last()
963 .map(|seg| seg.ident.to_string())
964 .unwrap_or_else(|| "_pattern".to_string())
965 }
966 syn::Pat::Wild(_) => "_".to_string(),
967 syn::Pat::Rest(_) => "_rest".to_string(),
968 syn::Pat::Slice(_) => "_slice".to_string(),
969 syn::Pat::Reference(r) => pat_to_name(&r.pat),
970 syn::Pat::Or(o) => {
971 o.cases
973 .first()
974 .map(pat_to_name)
975 .unwrap_or_else(|| "_or".to_string())
976 }
977 _other => "_param".to_string(),
978 }
979}
980
981#[cfg(test)]
982mod tests {
983 use super::*;
984
985 #[test]
986 fn test_parse_simple_fn() {
987 let pure = PureFile::from_source("fn main() {}").unwrap();
988 assert_eq!(pure.functions().len(), 1);
989 assert_eq!(pure.functions()[0].name, "main");
990 }
991
992 #[test]
993 fn test_parse_struct() {
994 let pure = PureFile::from_source(
995 r#"
996 struct Point {
997 x: i32,
998 y: i32,
999 }
1000 "#,
1001 )
1002 .unwrap();
1003
1004 assert_eq!(pure.structs().len(), 1);
1005 let s = &pure.structs()[0];
1006 assert_eq!(s.name, "Point");
1007
1008 if let PureFields::Named(fields) = &s.fields {
1009 assert_eq!(fields.len(), 2);
1010 assert_eq!(fields[0].name, "x");
1011 assert_eq!(fields[1].name, "y");
1012 } else {
1013 panic!("Expected named fields");
1014 }
1015 }
1016
1017 #[test]
1018 fn test_parse_use() {
1019 let pure = PureFile::from_source("use std::io;").unwrap();
1020 assert_eq!(pure.uses().len(), 1);
1021 }
1022
1023 #[test]
1024 fn test_thread_safe() {
1025 use std::sync::Arc;
1026 use std::thread;
1027
1028 let pure = PureFile::from_source(
1029 r#"
1030 fn foo() {}
1031 fn bar() {}
1032 "#,
1033 )
1034 .unwrap();
1035
1036 let shared = Arc::new(pure);
1037
1038 let s1 = Arc::clone(&shared);
1039 let s2 = Arc::clone(&shared);
1040
1041 let h1 = thread::spawn(move || s1.functions().len());
1042 let h2 = thread::spawn(move || s2.find_fn("foo").map(|f| f.name.clone()));
1043
1044 assert_eq!(h1.join().unwrap(), 2);
1045 assert_eq!(h2.join().unwrap(), Some("foo".to_string()));
1046 }
1047
1048 #[test]
1051 fn test_expr_binary() {
1052 let pure = PureFile::from_source("fn f() { let _ = 1 + 2; }").unwrap();
1053 let f = &pure.functions()[0];
1054 if let PureStmt::Local {
1055 init: Some(expr), ..
1056 } = &f.body.stmts[0]
1057 {
1058 if let PureExpr::Binary { op, .. } = expr {
1059 assert_eq!(op, "+");
1060 } else {
1061 panic!("Expected Binary expr");
1062 }
1063 } else {
1064 panic!("Expected Local stmt");
1065 }
1066 }
1067
1068 #[test]
1069 fn test_expr_unary() {
1070 let pure = PureFile::from_source("fn f() { let _ = -x; }").unwrap();
1071 let f = &pure.functions()[0];
1072 if let PureStmt::Local {
1073 init: Some(expr), ..
1074 } = &f.body.stmts[0]
1075 {
1076 if let PureExpr::Unary { op, .. } = expr {
1077 assert_eq!(op, "-");
1078 } else {
1079 panic!("Expected Unary expr");
1080 }
1081 } else {
1082 panic!("Expected Local stmt");
1083 }
1084 }
1085
1086 #[test]
1087 fn test_expr_method_call() {
1088 let pure = PureFile::from_source("fn f() { x.foo(1, 2); }").unwrap();
1089 let f = &pure.functions()[0];
1090 if let PureStmt::Semi(PureExpr::MethodCall { method, args, .. }) = &f.body.stmts[0] {
1091 assert_eq!(method, "foo");
1092 assert_eq!(args.len(), 2);
1093 } else {
1094 panic!("Expected MethodCall expr");
1095 }
1096 }
1097
1098 #[test]
1099 fn test_expr_method_call_with_turbofish() {
1100 let pure = PureFile::from_source("fn f() { x.collect::<Vec<_>>(); }").unwrap();
1101 let f = &pure.functions()[0];
1102 if let PureStmt::Semi(PureExpr::MethodCall {
1103 method, turbofish, ..
1104 }) = &f.body.stmts[0]
1105 {
1106 assert_eq!(method, "collect");
1107 assert!(turbofish.is_some());
1108 assert!(turbofish.as_ref().unwrap().contains("Vec"));
1109 } else {
1110 panic!("Expected MethodCall expr with turbofish");
1111 }
1112 }
1113
1114 #[test]
1115 fn test_expr_closure() {
1116 let pure = PureFile::from_source("fn f() { let _ = |x| x + 1; }").unwrap();
1117 let f = &pure.functions()[0];
1118 if let PureStmt::Local {
1119 init: Some(expr), ..
1120 } = &f.body.stmts[0]
1121 {
1122 if let PureExpr::Closure {
1123 params, is_move, ..
1124 } = expr
1125 {
1126 assert!(!is_move);
1127 assert_eq!(params.len(), 1);
1128 } else {
1129 panic!("Expected Closure expr");
1130 }
1131 } else {
1132 panic!("Expected Local stmt");
1133 }
1134 }
1135
1136 #[test]
1137 fn test_expr_closure_move() {
1138 let pure = PureFile::from_source("fn f() { let _ = move |x| x; }").unwrap();
1139 let f = &pure.functions()[0];
1140 if let PureStmt::Local {
1141 init: Some(PureExpr::Closure { is_move, .. }),
1142 ..
1143 } = &f.body.stmts[0]
1144 {
1145 assert!(is_move);
1146 } else {
1147 panic!("Expected move Closure");
1148 }
1149 }
1150
1151 #[test]
1152 fn test_expr_async_closure() {
1153 let pure = PureFile::from_source("fn f() { let _ = async || {}; }").unwrap();
1154 let f = &pure.functions()[0];
1155 if let PureStmt::Local {
1156 init: Some(PureExpr::Closure { is_async, .. }),
1157 ..
1158 } = &f.body.stmts[0]
1159 {
1160 assert!(is_async);
1161 } else {
1162 panic!("Expected async Closure");
1163 }
1164 }
1165
1166 #[test]
1167 fn test_expr_closure_typed_params() {
1168 let pure =
1169 PureFile::from_source("fn f() { let _ = |x: i32, y: String| -> bool { true }; }")
1170 .unwrap();
1171 let f = &pure.functions()[0];
1172 if let PureStmt::Local {
1173 init: Some(expr), ..
1174 } = &f.body.stmts[0]
1175 {
1176 if let PureExpr::Closure { params, ret, .. } = expr {
1177 assert_eq!(params.len(), 2);
1178 assert!(params[0].ty.is_some());
1180 assert_eq!(
1181 params[0].ty.as_ref().unwrap(),
1182 &PureType::Path("i32".to_string())
1183 );
1184 assert!(params[1].ty.is_some());
1186 assert_eq!(
1187 params[1].ty.as_ref().unwrap(),
1188 &PureType::Path("String".to_string())
1189 );
1190 assert!(ret.is_some());
1192 assert_eq!(ret.as_ref().unwrap(), &PureType::Path("bool".to_string()));
1193 } else {
1194 panic!("Expected Closure expr");
1195 }
1196 } else {
1197 panic!("Expected Local stmt");
1198 }
1199 }
1200
1201 #[test]
1202 fn test_expr_closure_untyped_params_no_ret() {
1203 let pure = PureFile::from_source("fn f() { let _ = |x| x + 1; }").unwrap();
1204 let f = &pure.functions()[0];
1205 if let PureStmt::Local {
1206 init: Some(PureExpr::Closure { params, ret, .. }),
1207 ..
1208 } = &f.body.stmts[0]
1209 {
1210 assert_eq!(params.len(), 1);
1211 assert!(params[0].ty.is_none());
1212 assert!(ret.is_none());
1213 } else {
1214 panic!("Expected Closure");
1215 }
1216 }
1217
1218 #[test]
1219 fn test_expr_if() {
1220 let pure = PureFile::from_source("fn f() { if true { 1 } else { 2 } }").unwrap();
1221 let f = &pure.functions()[0];
1222 if let PureStmt::Expr(PureExpr::If { else_branch, .. }) = &f.body.stmts[0] {
1223 assert!(else_branch.is_some());
1224 } else {
1225 panic!("Expected If expr");
1226 }
1227 }
1228
1229 #[test]
1230 fn test_expr_match() {
1231 let pure =
1232 PureFile::from_source(r#"fn f() { match x { 1 => "one", _ => "other" } }"#).unwrap();
1233 let f = &pure.functions()[0];
1234 if let PureStmt::Expr(PureExpr::Match { arms, .. }) = &f.body.stmts[0] {
1235 assert_eq!(arms.len(), 2);
1236 } else {
1237 panic!("Expected Match expr");
1238 }
1239 }
1240
1241 #[test]
1242 fn test_expr_for_loop() {
1243 let pure = PureFile::from_source("fn f() { for i in 0..10 {} }").unwrap();
1244 let f = &pure.functions()[0];
1245 match &f.body.stmts[0] {
1247 PureStmt::Expr(PureExpr::For { pat, .. })
1248 | PureStmt::Semi(PureExpr::For { pat, .. }) => {
1249 if let PurePattern::Ident { name, .. } = pat {
1250 assert_eq!(name, "i");
1251 } else {
1252 panic!("Expected Ident pattern");
1253 }
1254 }
1255 _ => panic!("Expected For expr"),
1256 }
1257 }
1258
1259 #[test]
1260 fn test_expr_while() {
1261 let pure = PureFile::from_source("fn f() { while x > 0 { x -= 1; } }").unwrap();
1262 let f = &pure.functions()[0];
1263 match &f.body.stmts[0] {
1264 PureStmt::Expr(PureExpr::While { .. }) | PureStmt::Semi(PureExpr::While { .. }) => {}
1265 _ => panic!("Expected While expr"),
1266 }
1267 }
1268
1269 #[test]
1270 fn test_expr_loop() {
1271 let pure = PureFile::from_source("fn f() { loop { break; } }").unwrap();
1272 let f = &pure.functions()[0];
1273 match &f.body.stmts[0] {
1274 PureStmt::Expr(PureExpr::Loop { .. }) | PureStmt::Semi(PureExpr::Loop { .. }) => {}
1275 _ => panic!("Expected Loop expr"),
1276 }
1277 }
1278
1279 #[test]
1280 fn test_expr_return() {
1281 let pure = PureFile::from_source("fn f() -> i32 { return 42; }").unwrap();
1282 let f = &pure.functions()[0];
1283 if let PureStmt::Semi(PureExpr::Return(Some(_))) = &f.body.stmts[0] {
1284 } else {
1286 panic!("Expected Return expr");
1287 }
1288 }
1289
1290 #[test]
1291 fn test_expr_struct() {
1292 let pure = PureFile::from_source("fn f() { Point { x: 1, y: 2 } }").unwrap();
1293 let f = &pure.functions()[0];
1294 if let PureStmt::Expr(PureExpr::Struct { path, fields }) = &f.body.stmts[0] {
1295 assert_eq!(path, "Point");
1296 assert_eq!(fields.len(), 2);
1297 } else {
1298 panic!("Expected Struct expr");
1299 }
1300 }
1301
1302 #[test]
1303 fn test_expr_tuple() {
1304 let pure = PureFile::from_source("fn f() { (1, 2, 3) }").unwrap();
1305 let f = &pure.functions()[0];
1306 if let PureStmt::Expr(PureExpr::Tuple(elems)) = &f.body.stmts[0] {
1307 assert_eq!(elems.len(), 3);
1308 } else {
1309 panic!("Expected Tuple expr");
1310 }
1311 }
1312
1313 #[test]
1314 fn test_expr_array() {
1315 let pure = PureFile::from_source("fn f() { [1, 2, 3] }").unwrap();
1316 let f = &pure.functions()[0];
1317 if let PureStmt::Expr(PureExpr::Array(elems)) = &f.body.stmts[0] {
1318 assert_eq!(elems.len(), 3);
1319 } else {
1320 panic!("Expected Array expr");
1321 }
1322 }
1323
1324 #[test]
1325 fn test_expr_repeat() {
1326 let pure = PureFile::from_source("fn f() { [0; 10] }").unwrap();
1327 let f = &pure.functions()[0];
1328 if let PureStmt::Expr(PureExpr::Repeat { .. }) = &f.body.stmts[0] {
1329 } else {
1331 panic!("Expected Repeat expr");
1332 }
1333 }
1334
1335 #[test]
1336 fn test_expr_reference() {
1337 let pure = PureFile::from_source("fn f() { let _ = &x; }").unwrap();
1338 let f = &pure.functions()[0];
1339 if let PureStmt::Local {
1340 init: Some(PureExpr::Ref { is_mut, .. }),
1341 ..
1342 } = &f.body.stmts[0]
1343 {
1344 assert!(!is_mut);
1345 } else {
1346 panic!("Expected Ref expr");
1347 }
1348 }
1349
1350 #[test]
1351 fn test_expr_reference_mut() {
1352 let pure = PureFile::from_source("fn f() { let _ = &mut x; }").unwrap();
1353 let f = &pure.functions()[0];
1354 if let PureStmt::Local {
1355 init: Some(PureExpr::Ref { is_mut, .. }),
1356 ..
1357 } = &f.body.stmts[0]
1358 {
1359 assert!(is_mut);
1360 } else {
1361 panic!("Expected mut Ref expr");
1362 }
1363 }
1364
1365 #[test]
1366 fn test_expr_await() {
1367 let pure = PureFile::from_source("async fn f() { x.await }").unwrap();
1368 let f = &pure.functions()[0];
1369 if let PureStmt::Expr(PureExpr::Await(_)) = &f.body.stmts[0] {
1370 } else {
1372 panic!("Expected Await expr");
1373 }
1374 }
1375
1376 #[test]
1377 fn test_expr_try() {
1378 let pure = PureFile::from_source("fn f() -> Result<(), ()> { x?; Ok(()) }").unwrap();
1379 let f = &pure.functions()[0];
1380 if let PureStmt::Semi(PureExpr::Try(_)) = &f.body.stmts[0] {
1381 } else {
1383 panic!("Expected Try expr");
1384 }
1385 }
1386
1387 #[test]
1388 fn test_expr_range() {
1389 let pure = PureFile::from_source("fn f() { let _ = 0..10; }").unwrap();
1390 let f = &pure.functions()[0];
1391 if let PureStmt::Local {
1392 init:
1393 Some(PureExpr::Range {
1394 start,
1395 end,
1396 inclusive,
1397 }),
1398 ..
1399 } = &f.body.stmts[0]
1400 {
1401 assert!(start.is_some());
1402 assert!(end.is_some());
1403 assert!(!inclusive);
1404 } else {
1405 panic!("Expected Range expr");
1406 }
1407 }
1408
1409 #[test]
1410 fn test_expr_range_inclusive() {
1411 let pure = PureFile::from_source("fn f() { let _ = 0..=10; }").unwrap();
1412 let f = &pure.functions()[0];
1413 if let PureStmt::Local {
1414 init: Some(PureExpr::Range { inclusive, .. }),
1415 ..
1416 } = &f.body.stmts[0]
1417 {
1418 assert!(inclusive);
1419 } else {
1420 panic!("Expected inclusive Range expr");
1421 }
1422 }
1423
1424 #[test]
1425 fn test_expr_cast() {
1426 let pure = PureFile::from_source("fn f() { let _ = x as u32; }").unwrap();
1427 let f = &pure.functions()[0];
1428 if let PureStmt::Local {
1429 init: Some(PureExpr::Cast { ty, .. }),
1430 ..
1431 } = &f.body.stmts[0]
1432 {
1433 if let PureType::Path(p) = ty {
1434 assert_eq!(p, "u32");
1435 } else {
1436 panic!("Expected Path type");
1437 }
1438 } else {
1439 panic!("Expected Cast expr");
1440 }
1441 }
1442
1443 #[test]
1444 fn test_expr_let() {
1445 let pure = PureFile::from_source("fn f() { if let Some(x) = y { } }").unwrap();
1446 let f = &pure.functions()[0];
1447 let if_expr = match &f.body.stmts[0] {
1448 PureStmt::Expr(e) | PureStmt::Semi(e) => e,
1449 _ => panic!("Expected expression statement"),
1450 };
1451 if let PureExpr::If { cond, .. } = if_expr {
1452 if let PureExpr::Let { pattern, .. } = cond.as_ref() {
1453 if let PurePattern::Struct { path, .. } = pattern {
1454 assert_eq!(path, "Some");
1455 } else {
1456 panic!("Expected Struct pattern");
1457 }
1458 } else {
1459 panic!("Expected Let expr in condition");
1460 }
1461 } else {
1462 panic!("Expected If expr");
1463 }
1464 }
1465
1466 #[test]
1467 fn test_expr_async_block() {
1468 let pure = PureFile::from_source("fn f() { async { 42 } }").unwrap();
1469 let f = &pure.functions()[0];
1470 if let PureStmt::Expr(PureExpr::Async { is_move, .. }) = &f.body.stmts[0] {
1471 assert!(!is_move);
1472 } else {
1473 panic!("Expected Async block");
1474 }
1475 }
1476
1477 #[test]
1478 fn test_expr_unsafe_block() {
1479 let pure = PureFile::from_source("fn f() { unsafe { dangerous() } }").unwrap();
1480 let f = &pure.functions()[0];
1481 if let PureStmt::Expr(PureExpr::Unsafe(_)) = &f.body.stmts[0] {
1482 } else {
1484 panic!("Expected Unsafe block");
1485 }
1486 }
1487
1488 #[test]
1489 fn test_expr_macro() {
1490 let pure = PureFile::from_source("fn f() { println!(\"hello\"); }").unwrap();
1491 let f = &pure.functions()[0];
1492 if let PureStmt::Semi(PureExpr::Macro { name, .. }) = &f.body.stmts[0] {
1493 assert_eq!(name, "println");
1494 } else {
1495 panic!("Expected Macro expr");
1496 }
1497 }
1498
1499 #[test]
1502 fn test_pattern_ident() {
1503 let pure = PureFile::from_source("fn f() { let x = 1; }").unwrap();
1504 let f = &pure.functions()[0];
1505 if let PureStmt::Local {
1506 pattern: PurePattern::Ident { name, is_mut },
1507 ..
1508 } = &f.body.stmts[0]
1509 {
1510 assert_eq!(name, "x");
1511 assert!(!is_mut);
1512 } else {
1513 panic!("Expected Ident pattern");
1514 }
1515 }
1516
1517 #[test]
1518 fn test_pattern_ident_mut() {
1519 let pure = PureFile::from_source("fn f() { let mut x = 1; }").unwrap();
1520 let f = &pure.functions()[0];
1521 if let PureStmt::Local {
1522 pattern: PurePattern::Ident { is_mut, .. },
1523 ..
1524 } = &f.body.stmts[0]
1525 {
1526 assert!(is_mut);
1527 } else {
1528 panic!("Expected mut Ident pattern");
1529 }
1530 }
1531
1532 #[test]
1533 fn test_pattern_wild() {
1534 let pure = PureFile::from_source("fn f() { let _ = 1; }").unwrap();
1535 let f = &pure.functions()[0];
1536 if let PureStmt::Local {
1537 pattern: PurePattern::Wild,
1538 ..
1539 } = &f.body.stmts[0]
1540 {
1541 } else {
1543 panic!("Expected Wild pattern");
1544 }
1545 }
1546
1547 #[test]
1548 fn test_pattern_tuple() {
1549 let pure = PureFile::from_source("fn f() { let (a, b) = (1, 2); }").unwrap();
1550 let f = &pure.functions()[0];
1551 if let PureStmt::Local {
1552 pattern: PurePattern::Tuple(elems),
1553 ..
1554 } = &f.body.stmts[0]
1555 {
1556 assert_eq!(elems.len(), 2);
1557 } else {
1558 panic!("Expected Tuple pattern");
1559 }
1560 }
1561
1562 #[test]
1563 fn test_pattern_struct() {
1564 let pure = PureFile::from_source("fn f() { let Point { x, y } = p; }").unwrap();
1565 let f = &pure.functions()[0];
1566 if let PureStmt::Local {
1567 pattern: PurePattern::Struct { path, fields, rest },
1568 ..
1569 } = &f.body.stmts[0]
1570 {
1571 assert_eq!(path, "Point");
1572 assert_eq!(fields.len(), 2);
1573 assert!(!rest);
1574 } else {
1575 panic!("Expected Struct pattern");
1576 }
1577 }
1578
1579 #[test]
1580 fn test_pattern_struct_with_rest() {
1581 let pure = PureFile::from_source("fn f() { let Point { x, .. } = p; }").unwrap();
1582 let f = &pure.functions()[0];
1583 if let PureStmt::Local {
1584 pattern: PurePattern::Struct { rest, .. },
1585 ..
1586 } = &f.body.stmts[0]
1587 {
1588 assert!(rest);
1589 } else {
1590 panic!("Expected Struct pattern with rest");
1591 }
1592 }
1593
1594 #[test]
1595 fn test_pattern_tuple_struct() {
1596 let pure = PureFile::from_source("fn f() { let Some(x) = opt; }").unwrap();
1597 let f = &pure.functions()[0];
1598 if let PureStmt::Local {
1599 pattern: PurePattern::Struct { path, .. },
1600 ..
1601 } = &f.body.stmts[0]
1602 {
1603 assert_eq!(path, "Some");
1604 } else {
1605 panic!("Expected TupleStruct pattern");
1606 }
1607 }
1608
1609 #[test]
1610 fn test_pattern_reference() {
1611 let pure = PureFile::from_source("fn f() { let &x = r; }").unwrap();
1612 let f = &pure.functions()[0];
1613 if let PureStmt::Local {
1614 pattern: PurePattern::Ref { is_mut, .. },
1615 ..
1616 } = &f.body.stmts[0]
1617 {
1618 assert!(!is_mut);
1619 } else {
1620 panic!("Expected Ref pattern");
1621 }
1622 }
1623
1624 #[test]
1625 fn test_pattern_or() {
1626 let pure =
1628 PureFile::from_source("fn f(x: i32) { match x { 1 | 2 | 3 => {} _ => {} } }").unwrap();
1629 let f = &pure.functions()[0];
1630 if let PureStmt::Expr(PureExpr::Match { arms, .. }) = &f.body.stmts[0] {
1631 if let PurePattern::Or(cases) = &arms[0].pattern {
1632 assert_eq!(cases.len(), 3);
1633 } else {
1634 panic!("Expected Or pattern");
1635 }
1636 } else {
1637 panic!("Expected Match expr");
1638 }
1639 }
1640
1641 #[test]
1642 fn test_pattern_range() {
1643 let pure =
1644 PureFile::from_source("fn f(x: i32) { match x { 0..=10 => {} _ => {} } }").unwrap();
1645 let f = &pure.functions()[0];
1646 if let PureStmt::Expr(PureExpr::Match { arms, .. }) = &f.body.stmts[0] {
1647 if let PurePattern::Range { inclusive, .. } = &arms[0].pattern {
1648 assert!(inclusive);
1649 } else {
1650 panic!("Expected Range pattern");
1651 }
1652 } else {
1653 panic!("Expected Match expr");
1654 }
1655 }
1656
1657 #[test]
1658 fn test_pattern_slice() {
1659 let pure = PureFile::from_source("fn f() { let [a, b, c] = arr; }").unwrap();
1660 let f = &pure.functions()[0];
1661 if let PureStmt::Local {
1662 pattern: PurePattern::Slice(elems),
1663 ..
1664 } = &f.body.stmts[0]
1665 {
1666 assert_eq!(elems.len(), 3);
1667 } else {
1668 panic!("Expected Slice pattern");
1669 }
1670 }
1671
1672 #[test]
1673 fn test_pattern_path() {
1674 let pure = PureFile::from_source(
1676 "fn f(x: std::cmp::Ordering) { match x { std::cmp::Ordering::Less => {} _ => {} } }",
1677 )
1678 .unwrap();
1679 let f = &pure.functions()[0];
1680 if let PureStmt::Expr(PureExpr::Match { arms, .. }) = &f.body.stmts[0] {
1681 if let PurePattern::Path(path) = &arms[0].pattern {
1682 assert!(path.contains("Ordering"));
1683 assert!(path.contains("Less"));
1684 } else {
1685 panic!("Expected Path pattern, got {:?}", arms[0].pattern);
1686 }
1687 } else {
1688 panic!("Expected Match expr");
1689 }
1690 }
1691
1692 #[test]
1695 fn test_type_path() {
1696 let pure = PureFile::from_source("fn f(x: i32) {}").unwrap();
1697 let f = &pure.functions()[0];
1698 if let PureParam::Typed {
1699 ty: PureType::Path(p),
1700 ..
1701 } = &f.params[0]
1702 {
1703 assert_eq!(p, "i32");
1704 } else {
1705 panic!("Expected Path type");
1706 }
1707 }
1708
1709 #[test]
1710 fn test_type_reference() {
1711 let pure = PureFile::from_source("fn f(x: &str) {}").unwrap();
1712 let f = &pure.functions()[0];
1713 if let PureParam::Typed {
1714 ty: PureType::Ref { is_mut, .. },
1715 ..
1716 } = &f.params[0]
1717 {
1718 assert!(!is_mut);
1719 } else {
1720 panic!("Expected Ref type");
1721 }
1722 }
1723
1724 #[test]
1725 fn test_type_reference_mut() {
1726 let pure = PureFile::from_source("fn f(x: &mut String) {}").unwrap();
1727 let f = &pure.functions()[0];
1728 if let PureParam::Typed {
1729 ty: PureType::Ref { is_mut, .. },
1730 ..
1731 } = &f.params[0]
1732 {
1733 assert!(is_mut);
1734 } else {
1735 panic!("Expected mut Ref type");
1736 }
1737 }
1738
1739 #[test]
1740 fn test_type_reference_with_lifetime() {
1741 let pure = PureFile::from_source("fn f<'a>(x: &'a str) {}").unwrap();
1742 let f = &pure.functions()[0];
1743 if let PureParam::Typed {
1744 ty: PureType::Ref { lifetime, .. },
1745 ..
1746 } = &f.params[0]
1747 {
1748 assert!(lifetime.is_some());
1749 assert!(lifetime.as_ref().unwrap().contains("'a"));
1750 } else {
1751 panic!("Expected Ref type with lifetime");
1752 }
1753 }
1754
1755 #[test]
1756 fn test_type_tuple() {
1757 let pure = PureFile::from_source("fn f(x: (i32, i32)) {}").unwrap();
1758 let f = &pure.functions()[0];
1759 if let PureParam::Typed {
1760 ty: PureType::Tuple(elems),
1761 ..
1762 } = &f.params[0]
1763 {
1764 assert_eq!(elems.len(), 2);
1765 } else {
1766 panic!("Expected Tuple type");
1767 }
1768 }
1769
1770 #[test]
1771 fn test_type_array() {
1772 let pure = PureFile::from_source("fn f(x: [i32; 10]) {}").unwrap();
1773 let f = &pure.functions()[0];
1774 if let PureParam::Typed {
1775 ty: PureType::Array { len, .. },
1776 ..
1777 } = &f.params[0]
1778 {
1779 assert_eq!(len, "10");
1780 } else {
1781 panic!("Expected Array type");
1782 }
1783 }
1784
1785 #[test]
1786 fn test_type_slice() {
1787 let pure = PureFile::from_source("fn f(x: &[i32]) {}").unwrap();
1788 let f = &pure.functions()[0];
1789 if let PureParam::Typed {
1790 ty: PureType::Ref { ty, .. },
1791 ..
1792 } = &f.params[0]
1793 {
1794 if let PureType::Slice(_) = ty.as_ref() {
1795 } else {
1797 panic!("Expected Slice type");
1798 }
1799 } else {
1800 panic!("Expected Ref to Slice type");
1801 }
1802 }
1803
1804 #[test]
1805 fn test_type_fn() {
1806 let pure = PureFile::from_source("fn f(x: fn(i32) -> i32) {}").unwrap();
1807 let f = &pure.functions()[0];
1808 if let PureParam::Typed {
1809 ty: PureType::Fn { params, ret },
1810 ..
1811 } = &f.params[0]
1812 {
1813 assert_eq!(params.len(), 1);
1814 assert!(ret.is_some());
1815 } else {
1816 panic!("Expected Fn type");
1817 }
1818 }
1819
1820 #[test]
1821 fn test_type_impl_trait() {
1822 let pure =
1823 PureFile::from_source("fn f() -> impl Iterator<Item = i32> { todo!() }").unwrap();
1824 let f = &pure.functions()[0];
1825 if let Some(PureType::ImplTrait(bounds)) = &f.ret {
1826 assert!(!bounds.is_empty());
1827 } else {
1828 panic!("Expected ImplTrait type");
1829 }
1830 }
1831
1832 #[test]
1833 fn test_type_never() {
1834 let pure = PureFile::from_source("fn f() -> ! { panic!() }").unwrap();
1835 let f = &pure.functions()[0];
1836 if let Some(PureType::Never) = &f.ret {
1837 } else {
1839 panic!("Expected Never type");
1840 }
1841 }
1842
1843 #[test]
1846 fn test_generics_type_param() {
1847 let pure = PureFile::from_source("fn f<T>(x: T) {}").unwrap();
1848 let f = &pure.functions()[0];
1849 assert_eq!(f.generics.params.len(), 1);
1850 if let PureGenericParam::Type { name, .. } = &f.generics.params[0] {
1851 assert_eq!(name, "T");
1852 } else {
1853 panic!("Expected Type param");
1854 }
1855 }
1856
1857 #[test]
1858 fn test_generics_type_param_with_bound() {
1859 let pure = PureFile::from_source("fn f<T: Clone>(x: T) {}").unwrap();
1860 let f = &pure.functions()[0];
1861 if let PureGenericParam::Type { bounds, .. } = &f.generics.params[0] {
1862 assert!(!bounds.is_empty());
1863 assert!(bounds[0].contains("Clone"));
1864 } else {
1865 panic!("Expected Type param with bound");
1866 }
1867 }
1868
1869 #[test]
1870 fn test_generics_lifetime_param() {
1871 let pure = PureFile::from_source("fn f<'a>(x: &'a str) {}").unwrap();
1872 let f = &pure.functions()[0];
1873 if let PureGenericParam::Lifetime { name, .. } = &f.generics.params[0] {
1874 assert!(name.contains("'a"));
1875 } else {
1876 panic!("Expected Lifetime param");
1877 }
1878 }
1879
1880 #[test]
1881 fn test_generics_const_param() {
1882 let pure = PureFile::from_source("fn f<const N: usize>() {}").unwrap();
1883 let f = &pure.functions()[0];
1884 if let PureGenericParam::Const { name, ty } = &f.generics.params[0] {
1885 assert_eq!(name, "N");
1886 assert!(ty.contains("usize"));
1887 } else {
1888 panic!("Expected Const param");
1889 }
1890 }
1891
1892 #[test]
1893 fn test_generics_where_clause() {
1894 let pure = PureFile::from_source("fn f<T>(x: T) where T: Clone {}").unwrap();
1895 let f = &pure.functions()[0];
1896 assert!(!f.generics.where_clause.is_empty());
1897 assert!(f.generics.where_clause[0].contains("Clone"));
1898 }
1899
1900 #[test]
1903 fn test_fn_async() {
1904 let pure = PureFile::from_source("async fn f() {}").unwrap();
1905 assert!(pure.functions()[0].is_async);
1906 }
1907
1908 #[test]
1909 fn test_fn_const() {
1910 let pure = PureFile::from_source("const fn f() {}").unwrap();
1911 assert!(pure.functions()[0].is_const);
1912 }
1913
1914 #[test]
1915 fn test_fn_unsafe() {
1916 let pure = PureFile::from_source("unsafe fn f() {}").unwrap();
1917 assert!(pure.functions()[0].is_unsafe);
1918 }
1919
1920 #[test]
1921 fn test_fn_self_param() {
1922 let pure = PureFile::from_source("impl Foo { fn f(&self) {} }").unwrap();
1923 let impls = pure.impls();
1924 if let PureImplItem::Fn(f) = &impls[0].items[0] {
1925 if let PureParam::SelfValue { is_ref, is_mut } = &f.params[0] {
1926 assert!(is_ref);
1927 assert!(!is_mut);
1928 } else {
1929 panic!("Expected SelfValue param");
1930 }
1931 } else {
1932 panic!("Expected Fn item");
1933 }
1934 }
1935
1936 #[test]
1937 fn test_fn_self_mut_param() {
1938 let pure = PureFile::from_source("impl Foo { fn f(&mut self) {} }").unwrap();
1939 let impls = pure.impls();
1940 if let PureImplItem::Fn(f) = &impls[0].items[0] {
1941 if let PureParam::SelfValue { is_ref, is_mut } = &f.params[0] {
1942 assert!(is_ref);
1943 assert!(is_mut);
1944 } else {
1945 panic!("Expected mut SelfValue param");
1946 }
1947 } else {
1948 panic!("Expected Fn item");
1949 }
1950 }
1951
1952 #[test]
1955 fn test_impl_inherent() {
1956 let pure = PureFile::from_source("impl Foo { fn new() -> Self { Foo } }").unwrap();
1957 let impls = pure.impls();
1958 assert_eq!(impls.len(), 1);
1959 assert!(impls[0].trait_.is_none());
1960 assert_eq!(impls[0].self_ty, "Foo");
1961 }
1962
1963 #[test]
1964 fn test_impl_trait() {
1965 let pure = PureFile::from_source("impl Clone for Foo { fn clone(&self) -> Self { Foo } }")
1966 .unwrap();
1967 let impls = pure.impls();
1968 assert_eq!(impls[0].trait_.as_ref().unwrap(), "Clone");
1969 }
1970
1971 #[test]
1972 fn test_impl_unsafe() {
1973 let pure = PureFile::from_source("unsafe impl Send for Foo {}").unwrap();
1974 let impls = pure.impls();
1975 assert!(impls[0].is_unsafe);
1976 }
1977
1978 #[test]
1979 fn test_impl_const() {
1980 let pure = PureFile::from_source("impl Foo { const X: i32 = 42; }").unwrap();
1981 let impls = pure.impls();
1982 if let PureImplItem::Const(c) = &impls[0].items[0] {
1983 assert_eq!(c.name, "X");
1984 } else {
1985 panic!("Expected Const item");
1986 }
1987 }
1988
1989 #[test]
1990 fn test_impl_type() {
1991 let pure = PureFile::from_source(
1992 "impl Iterator for Foo { type Item = i32; fn next(&mut self) -> Option<i32> { None } }",
1993 )
1994 .unwrap();
1995 let impls = pure.impls();
1996 if let PureImplItem::Type(t) = &impls[0].items[0] {
1997 assert_eq!(t.name, "Item");
1998 } else {
1999 panic!("Expected Type item");
2000 }
2001 }
2002
2003 #[test]
2006 fn test_trait_simple() {
2007 let pure = PureFile::from_source("trait Foo {}").unwrap();
2008 let traits = pure.traits();
2009 assert_eq!(traits.len(), 1);
2010 assert_eq!(traits[0].name, "Foo");
2011 }
2012
2013 #[test]
2014 fn test_trait_unsafe() {
2015 let pure = PureFile::from_source("unsafe trait Foo {}").unwrap();
2016 assert!(pure.traits()[0].is_unsafe);
2017 }
2018
2019 #[test]
2020 fn test_trait_with_supertraits() {
2021 let pure = PureFile::from_source("trait Foo: Clone + Send {}").unwrap();
2022 let traits = pure.traits();
2023 assert_eq!(traits[0].supertraits.len(), 2);
2024 }
2025
2026 #[test]
2027 fn test_trait_with_method() {
2028 let pure = PureFile::from_source("trait Foo { fn bar(&self); }").unwrap();
2029 let traits = pure.traits();
2030 if let PureTraitItem::Fn(f) = &traits[0].items[0] {
2031 assert_eq!(f.name, "bar");
2032 } else {
2033 panic!("Expected Fn item");
2034 }
2035 }
2036
2037 #[test]
2038 fn test_trait_with_default_method() {
2039 let pure = PureFile::from_source("trait Foo { fn bar(&self) { } }").unwrap();
2040 let traits = pure.traits();
2041 if let PureTraitItem::Fn(f) = &traits[0].items[0] {
2042 assert!(!f.body.stmts.is_empty() || f.body.stmts.is_empty()); } else {
2044 panic!("Expected Fn item");
2045 }
2046 }
2047
2048 #[test]
2049 fn test_trait_with_associated_type() {
2050 let pure = PureFile::from_source("trait Foo { type Item; }").unwrap();
2051 let traits = pure.traits();
2052 if let PureTraitItem::Type { name, .. } = &traits[0].items[0] {
2053 assert_eq!(name, "Item");
2054 } else {
2055 panic!("Expected Type item");
2056 }
2057 }
2058
2059 #[test]
2060 fn test_trait_with_associated_const() {
2061 let pure = PureFile::from_source("trait Foo { const X: i32; }").unwrap();
2062 let traits = pure.traits();
2063 if let PureTraitItem::Const(c) = &traits[0].items[0] {
2064 assert_eq!(c.name, "X");
2065 } else {
2066 panic!("Expected Const item");
2067 }
2068 }
2069
2070 #[test]
2073 fn test_enum_simple() {
2074 let pure = PureFile::from_source("enum Color { Red, Green, Blue }").unwrap();
2075 let enums: Vec<_> = pure
2076 .items
2077 .iter()
2078 .filter_map(|i| {
2079 if let PureItem::Enum(e) = i {
2080 Some(e)
2081 } else {
2082 None
2083 }
2084 })
2085 .collect();
2086 assert_eq!(enums.len(), 1);
2087 assert_eq!(enums[0].variants.len(), 3);
2088 }
2089
2090 #[test]
2091 fn test_enum_with_discriminant() {
2092 let pure = PureFile::from_source("enum Num { One = 1, Two = 2 }").unwrap();
2093 if let PureItem::Enum(e) = &pure.items[0] {
2094 assert!(e.variants[0].discriminant.is_some());
2095 } else {
2096 panic!("Expected Enum item");
2097 }
2098 }
2099
2100 #[test]
2101 fn test_enum_tuple_variant() {
2102 let pure = PureFile::from_source("enum Opt { Some(i32), None }").unwrap();
2103 if let PureItem::Enum(e) = &pure.items[0] {
2104 if let PureFields::Tuple(types) = &e.variants[0].fields {
2105 assert_eq!(types.len(), 1);
2106 } else {
2107 panic!("Expected Tuple fields");
2108 }
2109 } else {
2110 panic!("Expected Enum item");
2111 }
2112 }
2113
2114 #[test]
2115 fn test_enum_struct_variant() {
2116 let pure = PureFile::from_source("enum Msg { Move { x: i32, y: i32 } }").unwrap();
2117 if let PureItem::Enum(e) = &pure.items[0] {
2118 if let PureFields::Named(fields) = &e.variants[0].fields {
2119 assert_eq!(fields.len(), 2);
2120 } else {
2121 panic!("Expected Named fields");
2122 }
2123 } else {
2124 panic!("Expected Enum item");
2125 }
2126 }
2127
2128 #[test]
2131 fn test_struct_unit() {
2132 let pure = PureFile::from_source("struct Unit;").unwrap();
2133 let structs = pure.structs();
2134 assert!(matches!(structs[0].fields, PureFields::Unit));
2135 }
2136
2137 #[test]
2138 fn test_struct_tuple() {
2139 let pure = PureFile::from_source("struct Point(i32, i32);").unwrap();
2140 let structs = pure.structs();
2141 if let PureFields::Tuple(types) = &structs[0].fields {
2142 assert_eq!(types.len(), 2);
2143 } else {
2144 panic!("Expected Tuple fields");
2145 }
2146 }
2147
2148 #[test]
2151 fn test_vis_public() {
2152 let pure = PureFile::from_source("pub fn f() {}").unwrap();
2153 assert!(matches!(pure.functions()[0].vis, PureVis::Public));
2154 }
2155
2156 #[test]
2157 fn test_vis_private() {
2158 let pure = PureFile::from_source("fn f() {}").unwrap();
2159 assert!(matches!(pure.functions()[0].vis, PureVis::Private));
2160 }
2161
2162 #[test]
2163 fn test_vis_crate() {
2164 let pure = PureFile::from_source("pub(crate) fn f() {}").unwrap();
2165 assert!(matches!(pure.functions()[0].vis, PureVis::Crate));
2166 }
2167
2168 #[test]
2169 fn test_vis_super() {
2170 let pure = PureFile::from_source("pub(super) fn f() {}").unwrap();
2171 assert!(matches!(pure.functions()[0].vis, PureVis::Super));
2172 }
2173
2174 #[test]
2175 fn test_vis_in_path() {
2176 let pure = PureFile::from_source("pub(in crate::foo) fn f() {}").unwrap();
2177 if let PureVis::In(path) = &pure.functions()[0].vis {
2178 assert!(path.contains("crate"));
2179 } else {
2180 panic!("Expected In visibility");
2181 }
2182 }
2183
2184 #[test]
2187 fn test_attr_derive() {
2188 let pure = PureFile::from_source("#[derive(Clone, Debug)] struct Foo;").unwrap();
2189 let attrs = &pure.structs()[0].attrs;
2190 assert_eq!(attrs.len(), 1);
2191 assert_eq!(attrs[0].path, "derive");
2192 }
2193
2194 #[test]
2195 fn test_attr_simple() {
2196 let pure = PureFile::from_source("#[test] fn f() {}").unwrap();
2197 let attrs = &pure.functions()[0].attrs;
2198 assert_eq!(attrs.len(), 1);
2199 assert_eq!(attrs[0].path, "test");
2200 }
2201
2202 #[test]
2203 fn test_attr_inner() {
2204 let pure = PureFile::from_source("#![allow(unused)]").unwrap();
2205 assert!(pure.attrs[0].is_inner);
2206 }
2207
2208 #[test]
2209 fn test_attr_outer() {
2210 let pure = PureFile::from_source("#[allow(unused)] fn f() {}").unwrap();
2211 assert!(!pure.functions()[0].attrs[0].is_inner);
2212 }
2213
2214 #[test]
2217 fn test_use_path() {
2218 let pure = PureFile::from_source("use std::io::Read;").unwrap();
2219 let uses = pure.uses();
2220 if let PureUseTree::Path { path, tree } = &uses[0].tree {
2222 assert_eq!(path, "std");
2223 if let PureUseTree::Path { path, tree } = tree.as_ref() {
2224 assert_eq!(path, "io");
2225 if let PureUseTree::Name(name) = tree.as_ref() {
2226 assert_eq!(name, "Read");
2227 } else {
2228 panic!("Expected Name");
2229 }
2230 } else {
2231 panic!("Expected Path");
2232 }
2233 } else {
2234 panic!("Expected Path");
2235 }
2236 }
2237
2238 #[test]
2239 fn test_use_glob() {
2240 let pure = PureFile::from_source("use std::io::*;").unwrap();
2241 let uses = pure.uses();
2242 if let PureUseTree::Path { tree, .. } = &uses[0].tree {
2243 if let PureUseTree::Path { tree, .. } = tree.as_ref() {
2244 assert!(matches!(tree.as_ref(), PureUseTree::Glob));
2245 } else {
2246 panic!("Expected Path");
2247 }
2248 } else {
2249 panic!("Expected Path");
2250 }
2251 }
2252
2253 #[test]
2254 fn test_use_group() {
2255 let pure = PureFile::from_source("use std::{io, fs};").unwrap();
2256 let uses = pure.uses();
2257 if let PureUseTree::Path { tree, .. } = &uses[0].tree {
2258 if let PureUseTree::Group(items) = tree.as_ref() {
2259 assert_eq!(items.len(), 2);
2260 } else {
2261 panic!("Expected Group");
2262 }
2263 } else {
2264 panic!("Expected Path");
2265 }
2266 }
2267
2268 #[test]
2269 fn test_use_rename() {
2270 let pure = PureFile::from_source("use std::io::Result as IoResult;").unwrap();
2271 let uses = pure.uses();
2272 if let PureUseTree::Path { tree, .. } = &uses[0].tree {
2274 if let PureUseTree::Path { tree, .. } = tree.as_ref() {
2275 if let PureUseTree::Rename { name, rename } = tree.as_ref() {
2276 assert_eq!(name, "Result");
2277 assert_eq!(rename, "IoResult");
2278 } else {
2279 panic!("Expected Rename");
2280 }
2281 } else {
2282 panic!("Expected Path");
2283 }
2284 } else {
2285 panic!("Expected Path");
2286 }
2287 }
2288
2289 #[test]
2292 fn test_const_item() {
2293 let pure = PureFile::from_source("const MAX: i32 = 100;").unwrap();
2294 if let PureItem::Const(c) = &pure.items[0] {
2295 assert_eq!(c.name, "MAX");
2296 } else {
2297 panic!("Expected Const item");
2298 }
2299 }
2300
2301 #[test]
2302 fn test_static_item() {
2303 let pure = PureFile::from_source("static mut COUNTER: i32 = 0;").unwrap();
2304 if let PureItem::Static(s) = &pure.items[0] {
2305 assert!(s.is_mut);
2306 } else {
2307 panic!("Expected Static item");
2308 }
2309 }
2310
2311 #[test]
2314 fn test_type_alias() {
2315 let pure =
2316 PureFile::from_source("type Result<T> = std::result::Result<T, Error>;").unwrap();
2317 if let PureItem::Type(t) = &pure.items[0] {
2318 assert_eq!(t.name, "Result");
2319 } else {
2320 panic!("Expected Type item");
2321 }
2322 }
2323
2324 #[test]
2327 fn test_mod_declaration() {
2328 let pure = PureFile::from_source("mod foo;").unwrap();
2329 if let PureItem::Mod(m) = &pure.items[0] {
2330 assert!(m.items.is_empty());
2331 } else {
2332 panic!("Expected Mod item");
2333 }
2334 }
2335
2336 #[test]
2337 fn test_mod_inline() {
2338 let pure = PureFile::from_source("mod foo { fn bar() {} }").unwrap();
2339 if let PureItem::Mod(m) = &pure.items[0] {
2340 assert!(!m.items.is_empty());
2341 assert_eq!(m.items.len(), 1);
2342 } else {
2343 panic!("Expected Mod item");
2344 }
2345 }
2346
2347 #[test]
2350 fn test_macro_invocation() {
2351 let pure = PureFile::from_source("include!(\"foo.rs\");").unwrap();
2352 if let PureItem::Macro(m) = &pure.items[0] {
2353 assert_eq!(m.path, "include");
2354 } else {
2355 panic!("Expected Macro item");
2356 }
2357 }
2358
2359 #[test]
2362 fn test_match_arm_guard() {
2363 let pure =
2364 PureFile::from_source("fn f(x: i32) { match x { n if n > 0 => {} _ => {} } }").unwrap();
2365 let f = &pure.functions()[0];
2366 if let PureStmt::Expr(PureExpr::Match { arms, .. }) = &f.body.stmts[0] {
2367 assert!(arms[0].guard.is_some());
2368 } else {
2369 panic!("Expected Match expr");
2370 }
2371 }
2372
2373 #[test]
2376 fn test_path_to_string_simple() {
2377 assert_eq!(
2378 path_to_string(&syn::parse_str::<syn::Path>("std").unwrap()),
2379 "std"
2380 );
2381 }
2382
2383 #[test]
2384 fn test_path_to_string_nested() {
2385 assert_eq!(
2386 path_to_string(&syn::parse_str::<syn::Path>("std::io::Read").unwrap()),
2387 "std::io::Read"
2388 );
2389 }
2390
2391 #[test]
2392 fn test_path_to_string_with_generics() {
2393 let path = syn::parse_str::<syn::Path>("Vec<i32>").unwrap();
2394 let result = path_to_string(&path);
2395 assert!(result.contains("Vec"));
2396 assert!(result.contains("i32"));
2397 }
2398
2399 #[test]
2402 fn test_is_pinned_boxed_future_basic() {
2403 assert!(is_pinned_boxed_future("Pin<Box<dyn Future<Output = ()>>>"));
2404 assert!(is_pinned_boxed_future(
2405 "Pin<Box<dyn Future<Output = Result<T, E>>>>"
2406 ));
2407 assert!(is_pinned_boxed_future(
2408 "Pin<Box<dyn Future<Output = ()> + Send>>"
2409 ));
2410 assert!(is_pinned_boxed_future(
2411 "Pin<Box<dyn Future<Output = ()> + Send + 'static>>"
2412 ));
2413 }
2414
2415 #[test]
2416 fn test_is_pinned_boxed_future_with_spaces() {
2417 assert!(is_pinned_boxed_future(
2419 "Pin < Box < dyn Future < Output = () > > >"
2420 ));
2421 assert!(is_pinned_boxed_future(
2422 "Pin < Box < dyn Future < Output = Result < T , E > > + Send > >"
2423 ));
2424 }
2425
2426 #[test]
2427 fn test_is_pinned_boxed_future_with_path_prefix() {
2428 assert!(is_pinned_boxed_future(
2429 "::core::pin::Pin<Box<dyn Future<Output = ()>>>"
2430 ));
2431 assert!(is_pinned_boxed_future(
2432 "core::pin::Pin<Box<dyn Future<Output = ()>>>"
2433 ));
2434 assert!(is_pinned_boxed_future(
2435 "std::pin::Pin<Box<dyn Future<Output = ()>>>"
2436 ));
2437 assert!(is_pinned_boxed_future(
2438 "::std::pin::Pin<Box<dyn Future<Output = ()>>>"
2439 ));
2440 }
2441
2442 #[test]
2443 fn test_is_pinned_boxed_future_negative() {
2444 assert!(!is_pinned_boxed_future("Result<T, E>"));
2445 assert!(!is_pinned_boxed_future("Option<T>"));
2446 assert!(!is_pinned_boxed_future("Box<dyn Future<Output = ()>>"));
2447 assert!(!is_pinned_boxed_future("Pin<Box<T>>"));
2448 assert!(!is_pinned_boxed_future("Future<Output = ()>"));
2449 }
2450
2451 #[test]
2452 fn test_async_fn_is_async() {
2453 let pure = PureFile::from_source("async fn foo() {}").unwrap();
2454 let f = &pure.functions()[0];
2455 assert!(f.is_async);
2456 assert!(!f.is_async_inferred);
2457 assert!(f.is_effectively_async());
2458 }
2459
2460 #[test]
2461 fn test_sync_fn_not_async() {
2462 let pure = PureFile::from_source("fn foo() {}").unwrap();
2463 let f = &pure.functions()[0];
2464 assert!(!f.is_async);
2465 assert!(!f.is_async_inferred);
2466 assert!(!f.is_effectively_async());
2467 }
2468
2469 #[test]
2470 fn test_async_trait_style_fn_inferred() {
2471 let code = r#"
2473 fn foo(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
2474 Box::pin(async { })
2475 }
2476 "#;
2477 let pure = PureFile::from_source(code).unwrap();
2478 let f = &pure.functions()[0];
2479 assert!(!f.is_async, "Should not be explicitly async");
2480 assert!(
2481 f.is_async_inferred,
2482 "Should be inferred as async from return type"
2483 );
2484 assert!(f.is_effectively_async());
2485 }
2486
2487 #[test]
2488 fn test_async_trait_in_impl() {
2489 let code = r#"
2490 struct Foo;
2491 impl Foo {
2492 fn bar(&self) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send>> {
2493 Box::pin(async { Ok(()) })
2494 }
2495 }
2496 "#;
2497 let pure = PureFile::from_source(code).unwrap();
2498 let impls = pure.impls();
2499 assert_eq!(impls.len(), 1);
2500
2501 if let PureImplItem::Fn(method) = &impls[0].items[0] {
2502 assert!(!method.is_async);
2503 assert!(method.is_async_inferred);
2504 assert!(method.is_effectively_async());
2505 } else {
2506 panic!("Expected Fn in impl");
2507 }
2508 }
2509}