1use proc_macro2::Span;
7use quote::ToTokens;
8
9use crate::ast::BinOp;
10
11fn expr_precedence(expr: &syn::Expr) -> u8 {
18 match expr {
19 syn::Expr::Lit(_) | syn::Expr::Path(_) | syn::Expr::Paren(_) => 100,
20 syn::Expr::MethodCall(_) | syn::Expr::Field(_) | syn::Expr::Index(_) |
21 syn::Expr::Call(_) => 90,
22 syn::Expr::Try(_) => 85,
23 syn::Expr::Unary(_) => 80, syn::Expr::Cast(_) => 75, syn::Expr::Binary(b) => syn_binop_precedence(&b.op),
26 syn::Expr::Range(_) => 15,
27 syn::Expr::Assign(_) => 10,
28 syn::Expr::Return(_) | syn::Expr::Break(_) | syn::Expr::Closure(_) => 5,
29 syn::Expr::If(_) | syn::Expr::Match(_) => 1,
33 syn::Expr::Block(_) | syn::Expr::Unsafe(_) |
35 syn::Expr::Loop(_) | syn::Expr::While(_) | syn::Expr::ForLoop(_) => 100,
36 _ => 50, }
38}
39
40fn syn_binop_precedence(op: &syn::BinOp) -> u8 {
42 match op {
43 syn::BinOp::Mul(_) | syn::BinOp::Div(_) | syn::BinOp::Rem(_) => 70,
44 syn::BinOp::Add(_) | syn::BinOp::Sub(_) => 65,
45 syn::BinOp::Shl(_) | syn::BinOp::Shr(_) => 60,
46 syn::BinOp::BitAnd(_) => 55,
47 syn::BinOp::BitXor(_) => 50,
48 syn::BinOp::BitOr(_) => 45,
49 syn::BinOp::Lt(_) | syn::BinOp::Gt(_) | syn::BinOp::Le(_) | syn::BinOp::Ge(_) |
50 syn::BinOp::Eq(_) | syn::BinOp::Ne(_) => 40,
51 syn::BinOp::And(_) => 35,
52 syn::BinOp::Or(_) => 30,
53 _ => 50,
54 }
55}
56
57pub fn parenthesize(expr: syn::Expr) -> syn::Expr {
66 match expr {
67 syn::Expr::Binary(mut binary) => {
68 let parent_prec = syn_binop_precedence(&binary.op);
69 *binary.left = parenthesize_child(*binary.left, parent_prec, true);
70 *binary.right = parenthesize_child(*binary.right, parent_prec, false);
71 syn::Expr::Binary(binary)
72 }
73 syn::Expr::Cast(mut cast) => {
74 let child = parenthesize(*cast.expr);
77 let child_prec = expr_precedence(&child);
78 *cast.expr = if child_prec < 75 {
79 wrap_paren(child)
80 } else {
81 child
82 };
83 syn::Expr::Cast(cast)
84 }
85 syn::Expr::Unary(mut unary) => {
86 let child = parenthesize(*unary.expr);
89 let child_prec = expr_precedence(&child);
90 *unary.expr = if child_prec < 80 {
91 wrap_paren(child)
92 } else {
93 child
94 };
95 syn::Expr::Unary(unary)
96 }
97 syn::Expr::Field(mut field) => {
98 let child = parenthesize(*field.base);
100 let child_prec = expr_precedence(&child);
101 *field.base = if child_prec < 90 {
102 wrap_paren(child)
103 } else {
104 child
105 };
106 syn::Expr::Field(field)
107 }
108 syn::Expr::MethodCall(mut mc) => {
109 let child = parenthesize(*mc.receiver);
110 let child_prec = expr_precedence(&child);
111 *mc.receiver = if child_prec < 90 {
112 wrap_paren(child)
113 } else {
114 child
115 };
116 syn::Expr::MethodCall(mc)
117 }
118 syn::Expr::If(mut if_expr) => {
119 *if_expr.cond = parenthesize(*if_expr.cond);
120 parenthesize_block(&mut if_expr.then_branch);
122 if let Some((_, ref mut else_branch)) = if_expr.else_branch {
123 *else_branch = Box::new(parenthesize(*else_branch.clone()));
124 }
125 syn::Expr::If(if_expr)
126 }
127 syn::Expr::Paren(mut paren) => {
128 *paren.expr = parenthesize(*paren.expr);
129 syn::Expr::Paren(paren)
130 }
131 syn::Expr::Assign(mut assign) => {
132 *assign.left = parenthesize(*assign.left);
133 *assign.right = parenthesize(*assign.right);
134 syn::Expr::Assign(assign)
135 }
136 syn::Expr::Call(mut call) => {
137 *call.func = parenthesize(*call.func);
138 for arg in call.args.iter_mut() {
139 *arg = parenthesize(arg.clone());
140 }
141 syn::Expr::Call(call)
142 }
143 syn::Expr::Return(mut ret) => {
144 if let Some(ref mut expr) = ret.expr {
145 *expr = Box::new(parenthesize(*expr.clone()));
146 }
147 syn::Expr::Return(ret)
148 }
149 syn::Expr::Block(mut b) => {
150 parenthesize_block(&mut b.block);
151 syn::Expr::Block(b)
152 }
153 syn::Expr::Reference(mut r) => {
154 r.expr = Box::new(parenthesize(*r.expr));
155 syn::Expr::Reference(r)
156 }
157 syn::Expr::Index(mut i) => {
158 i.expr = Box::new(parenthesize(*i.expr));
159 i.index = Box::new(parenthesize(*i.index));
160 syn::Expr::Index(i)
161 }
162 other => other,
164 }
165}
166
167fn parenthesize_child(child: syn::Expr, parent_prec: u8, is_left: bool) -> syn::Expr {
169 let child = parenthesize(child);
170 let child_prec = expr_precedence(&child);
171 let mut needs_parens = child_prec < parent_prec
174 || (child_prec == parent_prec && !is_left);
175 if is_left && starts_with_block(&child) {
180 needs_parens = true;
181 }
182 if needs_parens {
183 wrap_paren(child)
184 } else {
185 child
186 }
187}
188
189fn starts_with_block(expr: &syn::Expr) -> bool {
191 matches!(expr,
192 syn::Expr::Block(_) | syn::Expr::Unsafe(_) | syn::Expr::If(_) |
193 syn::Expr::Match(_) | syn::Expr::Loop(_) | syn::Expr::While(_) |
194 syn::Expr::ForLoop(_)
195 )
196}
197
198fn wrap_paren(expr: syn::Expr) -> syn::Expr {
200 syn::Expr::Paren(syn::ExprParen {
201 attrs: vec![],
202 paren_token: syn::token::Paren::default(),
203 expr: Box::new(expr),
204 })
205}
206
207fn parenthesize_block(block: &mut syn::Block) {
209 for stmt in block.stmts.iter_mut() {
210 match stmt {
211 syn::Stmt::Expr(expr, _) => {
212 *expr = parenthesize(expr.clone());
213 }
214 syn::Stmt::Local(local) => {
215 if let Some(ref mut init) = local.init {
216 init.expr = Box::new(parenthesize(*init.expr.clone()));
217 }
218 }
219 _ => {}
220 }
221 }
222}
223
224pub fn to_syn_binop(op: BinOp) -> syn::BinOp {
230 match op {
231 BinOp::Add => syn::BinOp::Add(Default::default()),
232 BinOp::Sub => syn::BinOp::Sub(Default::default()),
233 BinOp::Mul => syn::BinOp::Mul(Default::default()),
234 BinOp::Div => syn::BinOp::Div(Default::default()),
235 BinOp::Mod => syn::BinOp::Rem(Default::default()),
236 BinOp::BitAnd => syn::BinOp::BitAnd(Default::default()),
237 BinOp::BitOr => syn::BinOp::BitOr(Default::default()),
238 BinOp::BitXor => syn::BinOp::BitXor(Default::default()),
239 BinOp::Shl => syn::BinOp::Shl(Default::default()),
240 BinOp::Shr => syn::BinOp::Shr(Default::default()),
241 BinOp::Eq => syn::BinOp::Eq(Default::default()),
242 BinOp::Ne => syn::BinOp::Ne(Default::default()),
243 BinOp::Lt => syn::BinOp::Lt(Default::default()),
244 BinOp::Gt => syn::BinOp::Gt(Default::default()),
245 BinOp::Le => syn::BinOp::Le(Default::default()),
246 BinOp::Ge => syn::BinOp::Ge(Default::default()),
247 BinOp::LogAnd => syn::BinOp::And(Default::default()),
248 BinOp::LogOr => syn::BinOp::Or(Default::default()),
249 }
250}
251
252pub fn expr_to_string(expr: &syn::Expr) -> String {
254 let parenthesized = parenthesize(expr.clone());
255 parenthesized.to_token_stream().to_string()
256}
257
258pub fn ident(name: &str) -> syn::Ident {
265 if let Some(raw_name) = name.strip_prefix("r#") {
267 return syn::Ident::new_raw(raw_name, Span::call_site());
268 }
269 if name.is_empty() || !name.chars().all(|c| c.is_alphanumeric() || c == '_') ||
271 name.starts_with(|c: char| c.is_ascii_digit()) {
272 return syn::Ident::new("__invalid_ident__", Span::call_site());
273 }
274 if is_rust_keyword(name) {
276 syn::Ident::new_raw(name, Span::call_site())
277 } else {
278 syn::Ident::new(name, Span::call_site())
279 }
280}
281
282fn is_rust_keyword(name: &str) -> bool {
283 matches!(name,
284 "as" | "break" | "const" | "continue" | "crate" | "else" | "enum" |
285 "extern" | "false" | "fn" | "for" | "if" | "impl" | "in" | "let" |
286 "loop" | "match" | "mod" | "move" | "mut" | "pub" | "ref" | "return" |
287 "self" | "Self" | "static" | "struct" | "super" | "trait" | "true" |
288 "type" | "unsafe" | "use" | "where" | "while" | "async" | "await" |
289 "dyn" | "abstract" | "become" | "box" | "do" | "final" | "macro" |
290 "override" | "priv" | "typeof" | "unsized" | "virtual" | "yield" | "gen" | "try"
291 )
292}
293
294pub fn normalize_parens(s: &str) -> String {
308 if let Some(parsed) = syn::parse_str::<syn::Expr>(s).ok() {
313 let stripped = strip_all_parens(parsed);
314 let paren_added = parenthesize(stripped);
315 let pretty = pretty_expr(&paren_added);
316 if !pretty.is_empty() && !pretty.contains('\n') {
317 return pretty;
318 }
319 let toks = quote::quote! { #paren_added }.to_string();
321 if !toks.is_empty() {
322 return toks;
323 }
324 }
325 fallback_strip_outer_parens(s)
326}
327
328fn fallback_strip_outer_parens(s: &str) -> String {
330 let s = s.trim();
331 if s.len() < 2 || !s.starts_with('(') || !s.ends_with(')') {
332 return s.to_string();
333 }
334 let inner = &s[1..s.len() - 1];
335 if inner.trim_start().starts_with('{') {
337 return s.to_string();
338 }
339 let mut depth = 0i32;
340 for ch in inner.chars() {
341 match ch {
342 '(' | '{' | '[' => depth += 1,
343 ')' | '}' | ']' => {
344 depth -= 1;
345 if depth < 0 {
346 return s.to_string();
347 }
348 }
349 _ => {}
350 }
351 }
352 if depth == 0 { inner.to_string() } else { s.to_string() }
353}
354
355pub fn strip_all_parens(expr: syn::Expr) -> syn::Expr {
360 match expr {
361 syn::Expr::Paren(p) => strip_all_parens(*p.expr),
362 syn::Expr::Binary(mut b) => {
363 *b.left = strip_all_parens(*b.left);
364 *b.right = strip_all_parens(*b.right);
365 syn::Expr::Binary(b)
366 }
367 syn::Expr::Unary(mut u) => {
368 *u.expr = strip_all_parens(*u.expr);
369 syn::Expr::Unary(u)
370 }
371 syn::Expr::Cast(mut c) => {
372 *c.expr = strip_all_parens(*c.expr);
373 syn::Expr::Cast(c)
374 }
375 syn::Expr::Field(mut f) => {
376 *f.base = strip_all_parens(*f.base);
377 syn::Expr::Field(f)
378 }
379 syn::Expr::MethodCall(mut m) => {
380 *m.receiver = strip_all_parens(*m.receiver);
381 for arg in m.args.iter_mut() {
382 *arg = strip_all_parens(arg.clone());
383 }
384 syn::Expr::MethodCall(m)
385 }
386 syn::Expr::Call(mut c) => {
387 *c.func = strip_all_parens(*c.func);
388 for arg in c.args.iter_mut() {
389 *arg = strip_all_parens(arg.clone());
390 }
391 syn::Expr::Call(c)
392 }
393 syn::Expr::If(mut i) => {
394 *i.cond = strip_all_parens(*i.cond);
395 strip_parens_in_block(&mut i.then_branch);
396 if let Some((_, ref mut else_branch)) = i.else_branch {
397 *else_branch = Box::new(strip_all_parens(*else_branch.clone()));
398 }
399 syn::Expr::If(i)
400 }
401 syn::Expr::Index(mut i) => {
402 *i.expr = strip_all_parens(*i.expr);
403 *i.index = strip_all_parens(*i.index);
404 syn::Expr::Index(i)
405 }
406 syn::Expr::Assign(mut a) => {
407 *a.left = strip_all_parens(*a.left);
408 *a.right = strip_all_parens(*a.right);
409 syn::Expr::Assign(a)
410 }
411 syn::Expr::Return(mut r) => {
412 if let Some(ref mut e) = r.expr {
413 *e = Box::new(strip_all_parens(*e.clone()));
414 }
415 syn::Expr::Return(r)
416 }
417 syn::Expr::Block(mut b) => {
418 strip_parens_in_block(&mut b.block);
419 syn::Expr::Block(b)
420 }
421 syn::Expr::Reference(mut r) => {
422 *r.expr = strip_all_parens(*r.expr);
423 syn::Expr::Reference(r)
424 }
425 syn::Expr::Unsafe(mut u) => {
426 strip_parens_in_block(&mut u.block);
427 syn::Expr::Unsafe(u)
428 }
429 other => other,
430 }
431}
432
433fn strip_parens_in_block(block: &mut syn::Block) {
434 for stmt in block.stmts.iter_mut() {
435 match stmt {
436 syn::Stmt::Expr(e, _) => *e = strip_all_parens(e.clone()),
437 syn::Stmt::Local(l) => {
438 if let Some(ref mut init) = l.init {
439 *init.expr = strip_all_parens(*init.expr.clone());
440 }
441 }
442 _ => {}
443 }
444 }
445}
446
447fn pretty_expr(expr: &syn::Expr) -> String {
449 let tokens = quote::quote! {
452 fn __() -> __T {
453 #expr
454 }
455 };
456 let file: syn::File = match syn::parse2(tokens) {
457 Ok(f) => f,
458 Err(_) => {
459 return expr.to_token_stream().to_string();
461 }
462 };
463 let formatted = prettyplease::unparse(&file);
464 extract_fn_body(&formatted)
465}
466
467fn extract_fn_body(formatted: &str) -> String {
469 let lines: Vec<&str> = formatted.lines().collect();
470 if lines.len() < 3 {
471 return formatted.to_string();
472 }
473 let body_lines: Vec<&str> = lines[1..lines.len() - 1]
475 .iter()
476 .map(|l| l.strip_prefix(" ").unwrap_or(l))
477 .collect();
478 body_lines.join("\n")
479}
480
481pub fn is_bool_syn_expr(expr: &syn::Expr) -> bool {
487 match expr {
488 syn::Expr::Binary(b) => matches!(b.op,
489 syn::BinOp::Eq(_) | syn::BinOp::Ne(_) |
490 syn::BinOp::Lt(_) | syn::BinOp::Gt(_) |
491 syn::BinOp::Le(_) | syn::BinOp::Ge(_) |
492 syn::BinOp::And(_) | syn::BinOp::Or(_)
493 ),
494 syn::Expr::Unary(u) => matches!(u.op, syn::UnOp::Not(_)) && is_bool_syn_expr(&u.expr),
495 syn::Expr::Lit(lit) => matches!(lit.lit, syn::Lit::Bool(_)),
496 syn::Expr::Paren(p) => is_bool_syn_expr(&p.expr),
497 syn::Expr::MethodCall(mc) => mc.method == "is_null",
498 _ => false,
499 }
500}
501
502pub fn looks_like_pointer(expr: &syn::Expr) -> bool {
505 match expr {
506 syn::Expr::Cast(cast) => {
507 let ty_str = cast.ty.to_token_stream().to_string();
508 ty_str.contains("* mut") || ty_str.contains("* const")
509 }
510 syn::Expr::MethodCall(mc) => {
511 let method = mc.method.to_string();
512 matches!(method.as_str(),
513 "offset" | "wrapping_add" | "wrapping_sub" | "as_ptr" | "as_mut_ptr")
514 }
515 syn::Expr::Call(call) => {
516 let func_str = call.func.to_token_stream().to_string();
517 func_str.contains("null_mut") || func_str.contains("null")
518 }
519 syn::Expr::Paren(p) => looks_like_pointer(&p.expr),
520 _ => false,
521 }
522}
523
524pub fn wrap_as_bool(expr: syn::Expr) -> syn::Expr {
530 if is_bool_syn_expr(&expr) {
531 return expr;
532 }
533 if looks_like_pointer(&expr) {
534 let is_null_call = syn::Expr::MethodCall(syn::ExprMethodCall {
536 attrs: vec![],
537 receiver: Box::new(expr),
538 dot_token: Default::default(),
539 method: ident("is_null"),
540 turbofish: None,
541 paren_token: Default::default(),
542 args: syn::punctuated::Punctuated::new(),
543 });
544 return syn::Expr::Unary(syn::ExprUnary {
545 attrs: vec![],
546 op: syn::UnOp::Not(Default::default()),
547 expr: Box::new(is_null_call),
548 });
549 }
550 syn::Expr::Binary(syn::ExprBinary {
552 attrs: vec![],
553 left: Box::new(expr),
554 op: syn::BinOp::Ne(Default::default()),
555 right: Box::new(int_lit(0)),
556 })
557}
558
559pub fn int_lit(n: i64) -> syn::Expr {
561 let lit = syn::LitInt::new(&n.to_string(), Span::call_site());
562 syn::Expr::Lit(syn::ExprLit {
563 attrs: vec![],
564 lit: syn::Lit::Int(lit),
565 })
566}
567
568pub fn cast_syn_expr(expr: syn::Expr, ty_str: &str) -> syn::Expr {
573 insert_cast(expr, parse_type(ty_str))
574}
575
576pub fn insert_cast(expr: syn::Expr, ty: syn::Type) -> syn::Expr {
580 syn::Expr::Cast(syn::ExprCast {
581 attrs: vec![],
582 expr: Box::new(expr),
583 as_token: Default::default(),
584 ty: Box::new(ty),
585 })
586}
587
588pub fn parse_type(ty_str: &str) -> syn::Type {
590 syn::parse_str(ty_str).unwrap_or_else(|_| {
591 syn::parse_str("c_int").unwrap()
593 })
594}
595
596pub fn null_for_type(ty_str: &str) -> syn::Expr {
602 if ty_str.contains("*const") {
603 syn::parse_str("std::ptr::null()").unwrap()
604 } else if ty_str.contains("*mut") || ty_str.contains("*") {
605 syn::parse_str("std::ptr::null_mut()").unwrap()
606 } else {
607 int_lit(0)
608 }
609}
610
611pub fn as_ptr(expr: syn::Expr) -> syn::Expr {
613 syn::Expr::MethodCall(syn::ExprMethodCall {
614 attrs: vec![],
615 receiver: Box::new(expr),
616 dot_token: Default::default(),
617 method: ident("as_ptr"),
618 turbofish: None,
619 paren_token: Default::default(),
620 args: syn::punctuated::Punctuated::new(),
621 })
622}
623
624pub fn field_access(expr: syn::Expr, field_name: &str) -> syn::Expr {
626 syn::Expr::Field(syn::ExprField {
627 attrs: vec![],
628 base: Box::new(expr),
629 dot_token: Default::default(),
630 member: syn::Member::Named(ident(field_name)),
631 })
632}
633
634pub fn deref(expr: syn::Expr) -> syn::Expr {
636 syn::Expr::Unary(syn::ExprUnary {
637 attrs: vec![],
638 op: syn::UnOp::Deref(Default::default()),
639 expr: Box::new(expr),
640 })
641}
642
643pub fn addr_of_mut(expr: syn::Expr) -> syn::Expr {
653 syn::Expr::RawAddr(syn::ExprRawAddr {
654 attrs: vec![],
655 and_token: Default::default(),
656 raw: Default::default(),
657 mutability: syn::PointerMutability::Mut(Default::default()),
658 expr: Box::new(expr),
659 })
660}
661
662pub fn call(func_name: &str, args: Vec<syn::Expr>) -> syn::Expr {
664 let func_ident = ident(func_name);
665 let mut punctuated = syn::punctuated::Punctuated::new();
666 for arg in args {
667 punctuated.push(arg);
668 }
669 syn::Expr::Call(syn::ExprCall {
670 attrs: vec![],
671 func: Box::new(syn::Expr::Path(syn::ExprPath {
672 attrs: vec![],
673 qself: None,
674 path: func_ident.into(),
675 })),
676 paren_token: Default::default(),
677 args: punctuated,
678 })
679}
680
681pub fn ident_expr(name: &str) -> syn::Expr {
683 syn::Expr::Path(syn::ExprPath {
684 attrs: vec![],
685 qself: None,
686 path: ident(name).into(),
687 })
688}
689
690pub fn method_call(receiver: syn::Expr, method: &str, args: Vec<syn::Expr>) -> syn::Expr {
692 let mut punctuated = syn::punctuated::Punctuated::new();
693 for arg in args {
694 punctuated.push(arg);
695 }
696 syn::Expr::MethodCall(syn::ExprMethodCall {
697 attrs: vec![],
698 receiver: Box::new(receiver),
699 dot_token: Default::default(),
700 method: ident(method),
701 turbofish: None,
702 paren_token: Default::default(),
703 args: punctuated,
704 })
705}
706
707pub fn assign_expr(lhs: syn::Expr, rhs: syn::Expr) -> syn::Expr {
709 syn::Expr::Assign(syn::ExprAssign {
710 attrs: vec![],
711 left: Box::new(lhs),
712 eq_token: Default::default(),
713 right: Box::new(rhs),
714 })
715}
716
717pub fn assign_op_expr(lhs: syn::Expr, op: syn::BinOp, rhs: syn::Expr) -> syn::Expr {
721 syn::Expr::Binary(syn::ExprBinary {
722 attrs: vec![],
723 left: Box::new(lhs),
724 op,
725 right: Box::new(rhs),
726 })
727}
728
729pub fn semi_stmt(expr: syn::Expr) -> syn::Stmt {
731 syn::Stmt::Expr(expr, Some(Default::default()))
732}
733
734pub fn let_stmt(name: &str, value: syn::Expr) -> syn::Stmt {
736 let pat = syn::Pat::Ident(syn::PatIdent {
737 attrs: vec![],
738 by_ref: None,
739 mutability: None,
740 ident: ident(name),
741 subpat: None,
742 });
743 syn::Stmt::Local(syn::Local {
744 attrs: vec![],
745 let_token: Default::default(),
746 pat,
747 init: Some(syn::LocalInit {
748 eq_token: Default::default(),
749 expr: Box::new(value),
750 diverge: None,
751 }),
752 semi_token: Default::default(),
753 })
754}
755
756pub fn block_with_value(stmts: Vec<syn::Stmt>, value: syn::Expr) -> syn::Expr {
758 let mut all_stmts = stmts;
759 all_stmts.push(syn::Stmt::Expr(value, None)); syn::Expr::Block(syn::ExprBlock {
761 attrs: vec![],
762 label: None,
763 block: syn::Block {
764 brace_token: Default::default(),
765 stmts: all_stmts,
766 },
767 })
768}
769
770pub fn c_assign_op_to_syn_compound(op: crate::ast::AssignOp) -> Option<syn::BinOp> {
774 use crate::ast::AssignOp;
775 Some(match op {
776 AssignOp::Assign => return None,
777 AssignOp::AddAssign => syn::BinOp::AddAssign(Default::default()),
778 AssignOp::SubAssign => syn::BinOp::SubAssign(Default::default()),
779 AssignOp::MulAssign => syn::BinOp::MulAssign(Default::default()),
780 AssignOp::DivAssign => syn::BinOp::DivAssign(Default::default()),
781 AssignOp::ModAssign => syn::BinOp::RemAssign(Default::default()),
782 AssignOp::AndAssign => syn::BinOp::BitAndAssign(Default::default()),
783 AssignOp::OrAssign => syn::BinOp::BitOrAssign(Default::default()),
784 AssignOp::XorAssign => syn::BinOp::BitXorAssign(Default::default()),
785 AssignOp::ShlAssign => syn::BinOp::ShlAssign(Default::default()),
786 AssignOp::ShrAssign => syn::BinOp::ShrAssign(Default::default()),
787 })
788}
789
790pub fn if_else(cond: syn::Expr, then_expr: syn::Expr, else_expr: syn::Expr) -> syn::Expr {
792 syn::Expr::If(syn::ExprIf {
793 attrs: vec![],
794 if_token: Default::default(),
795 cond: Box::new(cond),
796 then_branch: syn::Block {
797 brace_token: Default::default(),
798 stmts: vec![syn::Stmt::Expr(then_expr, None)],
799 },
800 else_branch: Some((
801 Default::default(),
802 Box::new(syn::Expr::Block(syn::ExprBlock {
803 attrs: vec![],
804 label: None,
805 block: syn::Block {
806 brace_token: Default::default(),
807 stmts: vec![syn::Stmt::Expr(else_expr, None)],
808 },
809 })),
810 )),
811 })
812}
813
814#[cfg(test)]
819mod tests {
820 use super::*;
821 use syn::parse_quote;
822
823 #[test]
824 fn test_parenthesize_binary_precedence() {
825 let expr: syn::Expr = parse_quote!(a + b * c);
827 let result = expr_to_string(&expr);
828 assert_eq!(result, "a + b * c");
829
830 let a: syn::Expr = parse_quote!(a);
832 let b: syn::Expr = parse_quote!(b);
833 let c: syn::Expr = parse_quote!(c);
834 let add: syn::Expr = parse_quote!(#a + #b);
835 let mul = syn::Expr::Binary(syn::ExprBinary {
836 attrs: vec![],
837 left: Box::new(add),
838 op: syn::BinOp::Mul(Default::default()),
839 right: Box::new(c),
840 });
841 let result = expr_to_string(&mul);
842 assert_eq!(result, "(a + b) * c");
843 }
844
845 #[test]
846 fn test_parenthesize_cast() {
847 let a: syn::Expr = parse_quote!(a);
849 let mask: syn::Expr = parse_quote!(MASK);
850 let bitand = syn::Expr::Binary(syn::ExprBinary {
851 attrs: vec![],
852 left: Box::new(a),
853 op: syn::BinOp::BitAnd(Default::default()),
854 right: Box::new(mask),
855 });
856 let cast = syn::Expr::Cast(syn::ExprCast {
857 attrs: vec![],
858 expr: Box::new(bitand),
859 as_token: Default::default(),
860 ty: Box::new(parse_quote!(u32)),
861 });
862 let result = expr_to_string(&cast);
863 assert_eq!(result, "(a & MASK) as u32");
864 }
865
866 #[test]
867 fn test_parenthesize_if_ne() {
868 let if_expr: syn::Expr = parse_quote!(if cond { A } else { B });
870 let ne = syn::Expr::Binary(syn::ExprBinary {
871 attrs: vec![],
872 left: Box::new(if_expr),
873 op: syn::BinOp::Ne(Default::default()),
874 right: Box::new(parse_quote!(0)),
875 });
876 let result = expr_to_string(&ne);
877 assert!(result.contains("if cond"));
881 }
882
883 #[test]
884 fn test_deref_field() {
885 let a: syn::Expr = parse_quote!(a);
887 let deref = syn::Expr::Unary(syn::ExprUnary {
888 attrs: vec![],
889 op: syn::UnOp::Deref(Default::default()),
890 expr: Box::new(a),
891 });
892 let field = syn::Expr::Field(syn::ExprField {
893 attrs: vec![],
894 base: Box::new(deref),
895 dot_token: Default::default(),
896 member: syn::Member::Named(ident("field")),
897 });
898 let result = expr_to_string(&field);
899 assert_eq!(result, "(* a) . field");
900 }
902
903 #[test]
904 fn test_ident_keyword() {
905 let i = ident("type");
906 assert_eq!(i.to_string(), "r#type");
907 }
908
909 #[test]
914 fn test_normalize_cast_removes_outer_parens() {
915 assert_eq!(normalize_parens("(x as i32)"), "x as i32");
917 }
918
919 #[test]
920 fn test_normalize_deref_removes_outer_parens() {
921 assert_eq!(normalize_parens("(*ptr)"), "*ptr");
923 }
924
925 #[test]
926 fn test_normalize_addr_of_removes_outer_parens() {
927 assert_eq!(normalize_parens("(&mut x)"), "&mut x");
929 }
930
931 #[test]
932 fn test_normalize_binary_removes_outer_parens() {
933 assert_eq!(normalize_parens("(a + b)"), "a + b");
935 }
936
937 #[test]
938 fn test_normalize_deref_field_preserves_needed_parens() {
939 assert_eq!(normalize_parens("(*a).field"), "(*a).field");
941 }
942
943 #[test]
944 fn test_normalize_cast_in_binary_preserves_needed_parens() {
945 assert_eq!(normalize_parens("(a & MASK) as u32"), "(a & MASK) as u32");
947 }
948
949 #[test]
950 fn test_normalize_nested_unnecessary_parens() {
951 assert_eq!(normalize_parens("((x as i32))"), "x as i32");
953 }
954
955 #[test]
956 fn test_normalize_preserves_precedence() {
957 assert_eq!(normalize_parens("(a + b) * c"), "(a + b) * c");
959 }
960
961 #[test]
962 fn test_normalize_no_change_needed() {
963 assert_eq!(normalize_parens("x"), "x");
964 assert_eq!(normalize_parens("42"), "42");
965 assert_eq!(normalize_parens("foo(a, b)"), "foo(a, b)");
966 }
967
968 #[test]
969 fn test_normalize_method_call() {
970 assert_eq!(normalize_parens("(ptr).is_null()"), "ptr.is_null()");
972 }
973
974 #[test]
975 fn test_normalize_logical_ops() {
976 assert_eq!(normalize_parens("(a && b)"), "a && b");
978 assert_eq!(normalize_parens("(a || b)"), "a || b");
980 }
981
982 #[test]
983 fn test_normalize_complex_nested() {
984 assert_eq!(
986 normalize_parens("((*sv).sv_flags as u32)"),
987 "(*sv).sv_flags as u32"
988 );
989 }
990
991 #[test]
992 fn test_normalize_unary_minus() {
993 assert_eq!(normalize_parens("(-x)"), "-x");
995 }
996
997 #[test]
998 fn test_normalize_not() {
999 assert_eq!(normalize_parens("(!cond)"), "!cond");
1001 }
1002
1003 #[test]
1004 fn test_normalize_block_expr_passthrough() {
1005 let s = "{ x += 1; x }";
1007 let result = normalize_parens(s);
1008 assert!(result == s || !result.contains('\n'));
1010 }
1011
1012 #[test]
1017 fn test_is_bool_syn_expr_comparison() {
1018 let expr: syn::Expr = parse_quote!(a == b);
1019 assert!(is_bool_syn_expr(&expr));
1020
1021 let expr: syn::Expr = parse_quote!(a != 0);
1022 assert!(is_bool_syn_expr(&expr));
1023
1024 let expr: syn::Expr = parse_quote!(a < b);
1025 assert!(is_bool_syn_expr(&expr));
1026 }
1027
1028 #[test]
1029 fn test_is_bool_syn_expr_logical() {
1030 let expr: syn::Expr = parse_quote!(a && b);
1031 assert!(is_bool_syn_expr(&expr));
1032
1033 let expr: syn::Expr = parse_quote!(a || b);
1034 assert!(is_bool_syn_expr(&expr));
1035 }
1036
1037 #[test]
1038 fn test_is_bool_syn_expr_not() {
1039 let expr: syn::Expr = parse_quote!(!(a == b));
1041 assert!(is_bool_syn_expr(&expr));
1042
1043 let expr: syn::Expr = parse_quote!(!x);
1045 assert!(!is_bool_syn_expr(&expr));
1046 }
1047
1048 #[test]
1049 fn test_is_bool_syn_expr_non_bool() {
1050 let expr: syn::Expr = parse_quote!(a + b);
1051 assert!(!is_bool_syn_expr(&expr));
1052
1053 let expr: syn::Expr = parse_quote!(42);
1054 assert!(!is_bool_syn_expr(&expr));
1055
1056 let expr: syn::Expr = parse_quote!(foo(x));
1057 assert!(!is_bool_syn_expr(&expr));
1058 }
1059
1060 #[test]
1061 fn test_is_bool_syn_expr_bool_lit() {
1062 let expr: syn::Expr = parse_quote!(true);
1063 assert!(is_bool_syn_expr(&expr));
1064
1065 let expr: syn::Expr = parse_quote!(false);
1066 assert!(is_bool_syn_expr(&expr));
1067 }
1068
1069 #[test]
1070 fn test_is_bool_syn_expr_is_null() {
1071 let expr: syn::Expr = parse_quote!(ptr.is_null());
1072 assert!(is_bool_syn_expr(&expr));
1073 }
1074
1075 #[test]
1076 fn test_is_bool_syn_expr_paren() {
1077 let expr: syn::Expr = parse_quote!((a == b));
1078 assert!(is_bool_syn_expr(&expr));
1079 }
1080
1081 #[test]
1082 fn test_looks_like_pointer_cast() {
1083 let expr: syn::Expr = parse_quote!(x as *mut i32);
1084 assert!(looks_like_pointer(&expr));
1085
1086 let expr: syn::Expr = parse_quote!(x as *const u8);
1087 assert!(looks_like_pointer(&expr));
1088
1089 let expr: syn::Expr = parse_quote!(x as i32);
1090 assert!(!looks_like_pointer(&expr));
1091 }
1092
1093 #[test]
1094 fn test_looks_like_pointer_method() {
1095 let expr: syn::Expr = parse_quote!(p.offset(1));
1096 assert!(looks_like_pointer(&expr));
1097
1098 let expr: syn::Expr = parse_quote!(p.wrapping_add(n));
1099 assert!(looks_like_pointer(&expr));
1100
1101 let expr: syn::Expr = parse_quote!(arr.as_ptr());
1102 assert!(looks_like_pointer(&expr));
1103 }
1104
1105 #[test]
1106 fn test_looks_like_pointer_null() {
1107 let expr: syn::Expr = parse_quote!(std::ptr::null_mut());
1108 assert!(looks_like_pointer(&expr));
1109 }
1110
1111 #[test]
1112 fn test_wrap_as_bool_already_bool() {
1113 let expr: syn::Expr = parse_quote!(a == b);
1114 let result = wrap_as_bool(expr);
1115 let s = expr_to_string(&result);
1116 assert_eq!(s, "a == b");
1117 }
1118
1119 #[test]
1120 fn test_wrap_as_bool_integer() {
1121 let expr: syn::Expr = parse_quote!(x);
1122 let result = wrap_as_bool(expr);
1123 let s = expr_to_string(&result);
1124 assert_eq!(s, "x != 0");
1125 }
1126
1127 #[test]
1128 fn test_wrap_as_bool_pointer() {
1129 let expr: syn::Expr = parse_quote!(p as *mut i32);
1130 let result = wrap_as_bool(expr);
1131 let s = expr_to_string(&result);
1132 assert!(s.contains("is_null"), "expected is_null in: {}", s);
1133 }
1134
1135 #[test]
1136 fn test_int_lit() {
1137 let expr = int_lit(42);
1138 let s = expr_to_string(&expr);
1139 assert_eq!(s, "42");
1140
1141 let expr = int_lit(0);
1142 let s = expr_to_string(&expr);
1143 assert_eq!(s, "0");
1144
1145 let expr = int_lit(-1);
1146 let s = expr_to_string(&expr);
1147 assert_eq!(s, "- 1"); }
1149
1150 #[test]
1151 fn test_insert_cast() {
1152 let expr: syn::Expr = parse_quote!(x);
1153 let ty = parse_type("u32");
1154 let result = insert_cast(expr, ty);
1155 let s = expr_to_string(&result);
1156 assert_eq!(s, "x as u32");
1157 }
1158
1159 #[test]
1160 fn test_insert_cast_complex_expr() {
1161 let expr: syn::Expr = parse_quote!(a + b);
1163 let ty = parse_type("i32");
1164 let result = insert_cast(expr, ty);
1165 let s = expr_to_string(&result);
1166 assert_eq!(s, "(a + b) as i32");
1167 }
1168
1169 #[test]
1170 fn test_parse_type_basic() {
1171 let ty = parse_type("i32");
1172 assert_eq!(ty.to_token_stream().to_string(), "i32");
1173 }
1174
1175 #[test]
1176 fn test_parse_type_pointer() {
1177 let ty = parse_type("*mut u8");
1178 assert_eq!(ty.to_token_stream().to_string(), "* mut u8");
1179 }
1180
1181 #[test]
1182 fn test_parse_type_fallback() {
1183 let ty = parse_type("not a valid type!!!");
1185 assert_eq!(ty.to_token_stream().to_string(), "c_int");
1186 }
1187
1188 #[test]
1189 fn test_null_for_type_mut() {
1190 let expr = null_for_type("*mut SV");
1191 let s = expr_to_string(&expr);
1192 assert!(s.contains("null_mut"), "expected null_mut in: {}", s);
1193 }
1194
1195 #[test]
1196 fn test_null_for_type_const() {
1197 let expr = null_for_type("*const c_char");
1198 let s = expr_to_string(&expr);
1199 assert!(s.contains("null"), "expected null in: {}", s);
1200 assert!(!s.contains("null_mut"), "should not contain null_mut in: {}", s);
1201 }
1202
1203 #[test]
1204 fn test_null_for_type_non_pointer() {
1205 let expr = null_for_type("i32");
1206 let s = expr_to_string(&expr);
1207 assert_eq!(s, "0");
1208 }
1209
1210 #[test]
1211 fn test_as_ptr() {
1212 let expr: syn::Expr = parse_quote!(PL_Yes);
1213 let result = as_ptr(expr);
1214 let s = expr_to_string(&result);
1215 assert!(s.contains("as_ptr"), "expected as_ptr in: {}", s);
1216 assert!(s.contains("PL_Yes"), "expected PL_Yes in: {}", s);
1217 }
1218
1219 #[test]
1220 fn test_field_access() {
1221 let expr: syn::Expr = parse_quote!(sv);
1222 let result = field_access(expr, "sv_flags");
1223 let s = expr_to_string(&result);
1224 assert_eq!(s, "sv . sv_flags");
1225 }
1226
1227 #[test]
1228 fn test_deref_simple() {
1229 let expr: syn::Expr = parse_quote!(ptr);
1230 let result = deref(expr);
1231 let s = expr_to_string(&result);
1232 assert_eq!(s, "* ptr");
1233 }
1234
1235 #[test]
1236 fn test_deref_field_parenthesized() {
1237 let ptr: syn::Expr = parse_quote!(ptr);
1239 let d = deref(ptr);
1240 let f = field_access(d, "field");
1241 let s = expr_to_string(&f);
1242 assert_eq!(s, "(* ptr) . field");
1243 }
1244
1245 #[test]
1246 fn test_addr_of_mut() {
1247 let expr: syn::Expr = parse_quote!(x);
1250 let result = addr_of_mut(expr);
1251 let s = expr_to_string(&result);
1252 assert_eq!(s, "& raw mut x");
1253 }
1254
1255 #[test]
1256 fn test_call_no_args() {
1257 let result = call("foo", vec![]);
1258 let s = expr_to_string(&result);
1259 assert!(s.contains("foo"), "expected foo in: {}", s);
1260 let normalized = s.replace(' ', "");
1262 assert_eq!(normalized, "foo()");
1263 }
1264
1265 #[test]
1266 fn test_call_with_args() {
1267 let a: syn::Expr = parse_quote!(x);
1268 let b: syn::Expr = parse_quote!(y);
1269 let result = call("bar", vec![a, b]);
1270 let s = expr_to_string(&result);
1271 let normalized = s.replace(' ', "");
1272 assert_eq!(normalized, "bar(x,y)");
1273 }
1274
1275 #[test]
1276 fn test_if_else() {
1277 let cond: syn::Expr = parse_quote!(x > 0);
1278 let then_expr: syn::Expr = parse_quote!(a);
1279 let else_expr: syn::Expr = parse_quote!(b);
1280 let result = if_else(cond, then_expr, else_expr);
1281 let s = expr_to_string(&result);
1282 assert!(s.contains("if"), "expected if in: {}", s);
1283 assert!(s.contains("else"), "expected else in: {}", s);
1284 }
1285
1286 #[test]
1287 fn test_wrap_as_bool_with_binary() {
1288 let expr: syn::Expr = parse_quote!(a + b);
1290 let result = wrap_as_bool(expr);
1291 let s = expr_to_string(&result);
1292 assert_eq!(s, "a + b != 0");
1293 }
1294
1295 #[test]
1296 fn test_combined_cast_and_bool() {
1297 let x: syn::Expr = parse_quote!(flags);
1300 let cast = insert_cast(x, parse_type("u32"));
1301 let bool_expr = wrap_as_bool(cast);
1302 let s = expr_to_string(&bool_expr);
1303 assert_eq!(s, "flags as u32 != 0");
1304 }
1305}