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