Skip to main content

libperl_macrogen/
syn_codegen.rs

1//! syn::Expr ベースの Rust コード生成モジュール
2//!
3//! C AST (crate::ast::Expr) から syn::Expr を構築し、
4//! 優先順位に基づく括弧挿入パスを経て正確な Rust コードを生成する。
5
6use proc_macro2::Span;
7use quote::ToTokens;
8
9use crate::ast::BinOp;
10
11// ============================================================================
12// Rust 演算子の優先順位
13// ============================================================================
14
15/// Rust の式の優先順位(数値が大きいほど高い)
16/// 参考: https://doc.rust-lang.org/reference/expressions.html#expression-precedence
17fn 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,                    // -, !, *, &
24        syn::Expr::Cast(_) => 75,                      // as
25        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        // If/Match は内部が独立だが、演算子の子として使う場合は括弧が必要:
30        //   (if cond { A } else { B }) as u8  — 括弧必須
31        //   if cond { A } else { B } as u8    — else ブランチ内の cast と誤解析
32        syn::Expr::If(_) | syn::Expr::Match(_) => 1,
33        // Block/Unsafe 等の { } で囲まれた式は自己完結するため最高優先
34        syn::Expr::Block(_) | syn::Expr::Unsafe(_) |
35        syn::Expr::Loop(_) | syn::Expr::While(_) | syn::Expr::ForLoop(_) => 100,
36        _ => 50,  // デフォルト
37    }
38}
39
40/// syn の二項演算子の優先順位
41fn 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        // 複合代入は Expr::Assign (10) と同格。ここに arm が無いと _ => 50 に
54        // 落ち、RHS の BitOr (45) 等が括弧で包まれて unused_parens になる
55        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
63// ============================================================================
64// 括弧挿入パス
65// ============================================================================
66
67/// syn::Expr 木に必要な括弧 (Expr::Paren) を挿入する。
68///
69/// syn::Expr の ToTokens は括弧を自動挿入しないため、
70/// 親子の優先順位を比較して必要な箇所に Expr::Paren を挿入する。
71pub 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            // as (prec 75) は全ての二項演算子 (prec ≤ 70) より高い
81            // → 子が Binary/If/Block なら括弧必要
82            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            // 単項 (prec 80) は as (prec 75) より高い
93            // → 子が Cast/Binary/If なら括弧必要
94            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            // フィールドアクセス (prec 90) → 子が Cast/Unary/Binary なら括弧必要
105            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            // then/else ブロック内は再帰的に処理
127            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        // その他はそのまま返す
169        other => other,
170    }
171}
172
173/// 子式を親の優先順位に基づいて括弧で囲むか判定
174fn 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    // 子の優先順位が親より低い → 括弧必要
178    // 同じ優先順位で右辺 → 括弧必要(左結合のため)
179    let mut needs_parens = child_prec < parent_prec
180        || (child_prec == parent_prec && !is_left);
181    // Block/Unsafe/Loop/If/Match 等の `{}` で始まる式は、
182    // Binary や Cast の左辺に置くと Rust パーサが "expression statement"
183    // として解釈してしまう(例: `{ x } != 0` は構文エラー)。
184    // 子の優先順位に関わらず括弧で囲む。
185    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
195/// 式が `{` で始まるか(block / if / match / unsafe / loop / while / for)
196fn 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
204/// Expr::Paren で囲む
205fn 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
213/// ブロック内の文を再帰的に括弧処理
214fn 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
230// ============================================================================
231// C AST → syn::Expr 変換ヘルパー
232// ============================================================================
233
234/// C の BinOp を syn::BinOp に変換
235pub 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
258/// syn::Expr を文字列に変換
259pub fn expr_to_string(expr: &syn::Expr) -> String {
260    let parenthesized = parenthesize(expr.clone());
261    parenthesized.to_token_stream().to_string()
262}
263
264/// syn::Ident を作成するヘルパー
265///
266/// 通常の識別子を syn::Ident に変換する。
267/// `r#` プレフィックス付きや Rust キーワードも正しく処理する。
268/// `name!()` のようなマクロ呼び出し形式や不正な識別子は
269/// `__invalid_ident__` にフォールバックする。
270pub fn ident(name: &str) -> syn::Ident {
271    // 既に r# プレフィックスが付いている場合は除去して raw ident として作成
272    if let Some(raw_name) = name.strip_prefix("r#") {
273        return syn::Ident::new_raw(raw_name, Span::call_site());
274    }
275    // 有効な識別子かチェック(マクロ呼び出し形式等を拒否)
276    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    // Rust のキーワードは r# プレフィックスが必要
281    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
300// ============================================================================
301// 括弧正規化 (Phase 4): 文字列 → parse → strip → parenthesize → format
302// ============================================================================
303
304/// 式文字列の括弧を正規化する。
305///
306/// 1. syn::parse_str でパース
307/// 2. すべての syn::Expr::Paren を除去
308/// 3. parenthesize() で必要な括弧のみ再挿入
309/// 4. prettyplease で整形
310///
311/// 結果が多行になる場合やパース失敗時は、
312/// 単純な外側括弧除去(strip_outer_parens 相当)にフォールバックする。
313pub fn normalize_parens(s: &str) -> String {
314    // syn ベースの正規化を試行
315    // - prettyplease で整形された単行結果を優先
316    // - 多行の場合は parenthesize 後の token-stream (単行) を使う
317    // - パース失敗時は外側括弧のみ除去にフォールバック
318    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        // 多行 → token-stream の単行化で代用
326        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
334/// strip_outer_parens と同等のフォールバックロジック
335fn 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    // ブロック式 ({...}) は strip しない
342    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
361/// syn::Expr ツリーからすべての Paren ラッパーノードを除去する。
362///
363/// ツリー構造(Binary, Unary, Cast 等)は保持される。
364/// 除去後に parenthesize() を適用すると、必要な括弧のみが再挿入される。
365pub 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
453/// syn::Expr を prettyplease で整形して文字列化
454fn pretty_expr(expr: &syn::Expr) -> String {
455    // prettyplease は syn::File 単位で動作するため、
456    // ダミー関数でラップして整形し、本体を抽出する
457    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            // パース失敗時は ToTokens でフォールバック
466            return expr.to_token_stream().to_string();
467        }
468    };
469    let formatted = prettyplease::unparse(&file);
470    extract_fn_body(&formatted)
471}
472
473/// prettyplease 出力から関数本体の式を抽出
474fn 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    // "fn __() -> __T {" と "}" を除去し、4スペースインデントを除去
480    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
487// ============================================================================
488// AST 変換パス (Phase 3)
489// ============================================================================
490
491/// syn::Expr が bool を返すかどうかを判定
492pub 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
508/// syn::Expr がポインタっぽいかを文字列ヒントで判定
509/// (syn::Expr には型情報がないため、メソッド名やキャストの型名で推定)
510pub 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
530/// 式を bool に変換する
531///
532/// - 既に bool 式 → そのまま
533/// - ポインタっぽい → `!expr.is_null()`
534/// - その他 → `expr != 0`
535pub 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        // !expr.is_null()
541        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    // expr != 0
557    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
565/// 整数リテラルを作成
566pub 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
574/// 型名文字列から `as T` キャストを挿入(parse_type + insert_cast の統合版)
575///
576/// `syn::parse_str(&format!("{} as {}", ...))` の代替。パース失敗時もフォールバック型で
577/// キャストノードを確実に構築するため、キャストが消えるリスクがない。
578pub fn cast_syn_expr(expr: syn::Expr, ty_str: &str) -> syn::Expr {
579    insert_cast(expr, parse_type(ty_str))
580}
581
582/// `as T` キャストを挿入
583///
584/// 括弧は `parenthesize()` パスで自動挿入されるため、ここでは不要。
585pub 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
594/// 型名文字列から syn::Type をパース
595pub fn parse_type(ty_str: &str) -> syn::Type {
596    syn::parse_str(ty_str).unwrap_or_else(|_| {
597        // パース失敗時はフォールバック
598        syn::parse_str("c_int").unwrap()
599    })
600}
601
602/// null ポインタ式を型に合わせて生成
603///
604/// - const ポインタ → `std::ptr::null()`
605/// - mut ポインタ → `std::ptr::null_mut()`
606/// - 非ポインタ → `0`
607pub 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
617/// `.as_ptr()` メソッド呼び出しを付加
618pub 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
630/// フィールドアクセス `expr.field` を構築
631pub 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
640/// Deref `*expr` を構築
641pub 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
649/// `&raw mut expr` を構築(C の `&` 演算子に対応する生ポインタ取得)。
650///
651/// C では `&x` は「アドレスを取る」= 生ポインタを得る操作で、
652/// Rust の `&mut T` 参照とは意味論が異なる。FFI コードでは生ポインタが正解。
653/// `&raw mut expr` は Rust 2024 で安定化された構文で、`*mut T` を直接生成する。
654///
655/// これにより `(SV**)&sv` のような C コードが
656/// `&raw mut sv as *mut *mut SV` に翻訳され、E0606(参照→生ポインタの直接 cast)
657/// が回避される。
658pub 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
668/// 関数呼び出し `func(args...)` を構築
669pub 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
687/// 識別子を `syn::Expr::Path` として構築(`name` を式コンテキストで参照する)
688pub 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
696/// メソッド呼び出し `receiver.method(args...)` を構築
697pub 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
713/// `lhs = rhs` を式として構築(plain assign)
714pub 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
723/// `lhs op= rhs` を式として構築(複合代入)
724///
725/// `op` には `syn::BinOp::AddAssign(_)` 等の `*Assign` 系バリアントを渡す。
726pub 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
735/// 式を `Stmt::Expr(_, Some(Semi))` の文として構築
736pub fn semi_stmt(expr: syn::Expr) -> syn::Stmt {
737    syn::Stmt::Expr(expr, Some(Default::default()))
738}
739
740/// `let name = value;` 文を構築
741pub 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
762/// `{ stmts...; value }` ブロック式を構築
763pub 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)); // 末尾値(セミコロンなし)
766    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
776/// `AssignOp` (C AST) → 複合代入用の `syn::BinOp` を返す。
777///
778/// `AssignOp::Assign` は plain `=` であり `BinOp` を持たないため `None`。
779pub 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
796/// if-else 式を構築
797pub 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// ============================================================================
821// テスト
822// ============================================================================
823
824#[cfg(test)]
825mod tests {
826    use super::*;
827    use syn::parse_quote;
828
829    #[test]
830    fn test_parenthesize_binary_precedence() {
831        // (a + b) * c → 括弧が必要
832        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        // a + b を * の左辺に → 括弧必要
837        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        // a & MASK as u32 → (a & MASK) as u32 が必要
854        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        // (if cond { A } else { B }) != 0 → 括弧が必要
875        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        // if 式は prec 100 だが、Binary(Ne) の子としては括弧不要(中身が独立)
884        // → 実際には Rust では if {} != 0 は if {} else {} != 0 にパースされるため括弧必要
885        // この挙動を正しくハンドルするにはIf式の特別扱いが必要
886        assert!(result.contains("if cond"));
887    }
888
889    #[test]
890    fn test_deref_field() {
891        // (*a).field — Deref + Field
892        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        // Note: ToTokens ではスペースが入る。prettyplease で整形すると (*a).field になる
907    }
908
909    #[test]
910    fn test_ident_keyword() {
911        let i = ident("type");
912        assert_eq!(i.to_string(), "r#type");
913    }
914
915    // ================================================================
916    // Phase 4: normalize_parens のテスト
917    // ================================================================
918
919    #[test]
920    fn test_normalize_cast_removes_outer_parens() {
921        // (x as i32) → x as i32
922        assert_eq!(normalize_parens("(x as i32)"), "x as i32");
923    }
924
925    #[test]
926    fn test_normalize_deref_removes_outer_parens() {
927        // (*ptr) → *ptr
928        assert_eq!(normalize_parens("(*ptr)"), "*ptr");
929    }
930
931    #[test]
932    fn test_normalize_addr_of_removes_outer_parens() {
933        // (&mut x) → &mut x
934        assert_eq!(normalize_parens("(&mut x)"), "&mut x");
935    }
936
937    #[test]
938    fn test_normalize_binary_removes_outer_parens() {
939        // (a + b) → a + b
940        assert_eq!(normalize_parens("(a + b)"), "a + b");
941    }
942
943    #[test]
944    fn test_normalize_deref_field_preserves_needed_parens() {
945        // (*a).field → (*a).field (parens needed!)
946        assert_eq!(normalize_parens("(*a).field"), "(*a).field");
947    }
948
949    #[test]
950    fn test_normalize_cast_in_binary_preserves_needed_parens() {
951        // (a & MASK) as u32 → (a & MASK) as u32 (parens needed!)
952        assert_eq!(normalize_parens("(a & MASK) as u32"), "(a & MASK) as u32");
953    }
954
955    #[test]
956    fn test_normalize_nested_unnecessary_parens() {
957        // ((x as i32)) → x as i32
958        assert_eq!(normalize_parens("((x as i32))"), "x as i32");
959    }
960
961    #[test]
962    fn test_normalize_preserves_precedence() {
963        // (a + b) * c → (a + b) * c (parens needed!)
964        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        // (ptr).is_null() → ptr.is_null()
977        assert_eq!(normalize_parens("(ptr).is_null()"), "ptr.is_null()");
978    }
979
980    #[test]
981    fn test_normalize_logical_ops() {
982        // (a && b) → a && b
983        assert_eq!(normalize_parens("(a && b)"), "a && b");
984        // (a || b) → a || b
985        assert_eq!(normalize_parens("(a || b)"), "a || b");
986    }
987
988    #[test]
989    fn test_normalize_complex_nested() {
990        // ((*sv).sv_flags as u32) → (*sv).sv_flags as u32
991        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        // (-x) → -x
1000        assert_eq!(normalize_parens("(-x)"), "-x");
1001    }
1002
1003    #[test]
1004    fn test_normalize_not() {
1005        // (!cond) → !cond
1006        assert_eq!(normalize_parens("(!cond)"), "!cond");
1007    }
1008
1009    #[test]
1010    fn test_normalize_block_expr_passthrough() {
1011        // Block expressions should pass through (parse may fail or be multi-line)
1012        let s = "{ x += 1; x }";
1013        let result = normalize_parens(s);
1014        // Should either normalize or return original
1015        assert!(result == s || !result.contains('\n'));
1016    }
1017
1018    // ================================================================
1019    // Phase 3: AST 変換パスのテスト
1020    // ================================================================
1021
1022    #[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        // !bool_expr → bool
1046        let expr: syn::Expr = parse_quote!(!(a == b));
1047        assert!(is_bool_syn_expr(&expr));
1048
1049        // !non_bool → not bool (bitwise not)
1050        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");  // ToTokens adds space after unary minus
1154    }
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        // (a + b) as i32 — parenthesize should add parens
1168        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        // Invalid type string → fallback to c_int
1190        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        // (*ptr).field — deref should get parens when used with field access
1244        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        // C の `&x` は生ポインタを得る操作なので `&raw mut x` を生成する
1254        // (Rust 2024 で安定化)。詳細は addr_of_mut のドキュメント参照。
1255        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        // ToTokens may add spaces: "foo ()"
1267        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        // Binary expr like (a + b) should become (a + b) != 0
1295        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        // A typical pattern: cast result to bool
1304        // (x as u32) != 0
1305        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}