Skip to main content

libperl_macrogen/
unified_type.rs

1//! 統一型表現モジュール
2//!
3//! C型とRust型を統一的に表現し、変換・比較を行う。
4
5use std::fmt;
6
7use quote::ToTokens;
8
9/// 整数サイズ
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum IntSize {
12    /// char (8-bit)
13    Char,
14    /// short (16-bit)
15    Short,
16    /// int (32-bit)
17    Int,
18    /// long (platform-dependent)
19    Long,
20    /// long long (64-bit)
21    LongLong,
22    /// __int128 (128-bit)
23    Int128,
24}
25
26/// 型の出自情報
27#[derive(Debug, Clone)]
28pub enum TypeSource {
29    /// apidoc の C型
30    Apidoc { raw: String, entry_name: String },
31    /// bindings.rs の Rust型
32    Bindings { raw: String },
33    /// マクロ本体からの推論
34    Inferred,
35    /// AST からのパース
36    Parsed,
37}
38
39/// 統一型表現
40///
41/// C型とRust型の両方を表現できる構造化された型。
42/// パース時に正規化され、以降は構造的な操作が可能。
43#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44pub enum UnifiedType {
45    /// void / ()
46    Void,
47    /// bool / _Bool
48    Bool,
49    /// char (signed は None = plain char, Some(true) = signed, Some(false) = unsigned)
50    Char { signed: Option<bool> },
51    /// 整数型
52    Int { signed: bool, size: IntSize },
53    /// float
54    Float,
55    /// double
56    Double,
57    /// long double
58    LongDouble,
59
60    /// ポインタ型
61    Pointer {
62        inner: Box<UnifiedType>,
63        is_const: bool,
64    },
65
66    /// 配列型
67    Array {
68        inner: Box<UnifiedType>,
69        size: Option<usize>,
70    },
71
72    /// 名前付き型 (typedef, struct, union, enum)
73    Named(String),
74
75    /// 関数ポインタ型
76    ///
77    /// C の関数ポインタ (`void (*)(int)`) や Rust の
78    /// `extern "C" fn(...) -> ...` に対応する。bindgen は通常
79    /// `Option<unsafe extern "C" fn(...)>` 形式で出すため、
80    /// `is_optional: true` のケースが事実上の標準。
81    FnPtr {
82        params: Vec<UnifiedType>,
83        ret: Box<UnifiedType>,
84        /// ABI 名 (`extern "C"` の `"C"` 等)。`None` はデフォルト ABI。
85        abi: Option<String>,
86        /// `unsafe fn` なら true
87        is_unsafe: bool,
88        /// `Option<extern "C" fn(...)>` の Option ラッパを表現
89        is_optional: bool,
90    },
91
92    /// 構造化未対応の型を syn の正規トークンで保持する escape hatch
93    ///
94    /// **保持する文字列は必ず `proc_macro2::TokenStream::to_string()`
95    /// 出力 (= syn 正規形)** であること。手書き文字列を入れない。
96    /// 比較・Hash の安定性は syn の正規化に依存する。
97    /// 構造的検査 (`is_pointer` 等) は常に false を返す。emit 時のみ意味を持つ。
98    Verbatim(String),
99
100    /// 不明な型
101    Unknown,
102}
103
104/// 出自情報付き型
105#[derive(Debug, Clone)]
106pub struct SourcedType {
107    pub ty: UnifiedType,
108    pub source: TypeSource,
109}
110
111impl UnifiedType {
112    /// C型文字列からパース
113    ///
114    /// 例:
115    /// - `"SV *"` → `Pointer { inner: Named("SV"), is_const: false }`
116    /// - `"const char *"` → `Pointer { inner: Char { signed: None }, is_const: true }`
117    /// - `"int"` → `Int { signed: true, size: IntSize::Int }`
118    pub fn from_c_str(s: &str) -> Self {
119        let trimmed = s.trim();
120
121        if trimmed.is_empty() {
122            return Self::Unknown;
123        }
124
125        // void
126        if trimmed == "void" {
127            return Self::Void;
128        }
129
130        // bool
131        if trimmed == "bool" || trimmed == "_Bool" {
132            return Self::Bool;
133        }
134
135        // ポインタ型の処理
136        if let Some(ptr_type) = Self::parse_c_pointer(trimmed) {
137            return ptr_type;
138        }
139
140        // 基本型の変換
141        Self::parse_c_basic_type(trimmed)
142    }
143
144    /// C言語のポインタ型をパース
145    fn parse_c_pointer(s: &str) -> Option<Self> {
146        let s = s.trim();
147
148        // 末尾の * を探す
149        if let Some(star_pos) = s.rfind('*') {
150            let before_star = s[..star_pos].trim();
151            let after_star = s[star_pos + 1..].trim();
152
153            // after_star が "const" の場合は無視(ポインタ自体のconst)
154            let _ptr_const = after_star == "const";
155
156            // before_star から型を解析
157            // "const char" -> is_const=true, base="char"
158            // "SV" -> is_const=false, base="SV"
159            let (is_const, base_type) = if before_star.starts_with("const ") {
160                (true, before_star[6..].trim())
161            } else if before_star.ends_with(" const") {
162                (true, before_star[..before_star.len() - 6].trim())
163            } else {
164                (false, before_star)
165            };
166
167            // 再帰的にポインタをチェック(ダブルポインタなど)
168            let inner_type = if base_type.contains('*') {
169                Self::parse_c_pointer(base_type).unwrap_or_else(|| Self::parse_c_basic_type(base_type))
170            } else {
171                Self::parse_c_basic_type(base_type)
172            };
173
174            return Some(Self::Pointer {
175                inner: Box::new(inner_type),
176                is_const,
177            });
178        }
179
180        None
181    }
182
183    /// C言語の基本型をパース
184    fn parse_c_basic_type(s: &str) -> Self {
185        let s = s.trim();
186
187        // const を除去(値型では const は無意味)
188        let s = s.strip_prefix("const ").unwrap_or(s);
189        let s = if s.ends_with(" const") {
190            &s[..s.len() - 6]
191        } else {
192            s
193        };
194        let s = s.trim();
195
196        // struct/union/enum プレフィックスを除去
197        let s = s
198            .strip_prefix("struct ")
199            .or_else(|| s.strip_prefix("union "))
200            .or_else(|| s.strip_prefix("enum "))
201            .unwrap_or(s);
202
203        // unsigned/signed の処理
204        let (is_unsigned, base) = if s == "unsigned" {
205            // "unsigned" alone means "unsigned int"
206            (true, "")
207        } else if s == "signed" {
208            // "signed" alone means "signed int"
209            (false, "")
210        } else if s.starts_with("unsigned ") {
211            (true, s[9..].trim())
212        } else if s.starts_with("signed ") {
213            (false, s[7..].trim())
214        } else {
215            (false, s)
216        };
217
218        // 型名のマッピング
219        match base {
220            // char 系
221            "char" if is_unsigned => Self::Char { signed: Some(false) },
222            "char" => Self::Char { signed: None },
223
224            // short 系
225            "short" | "short int" => Self::Int {
226                signed: !is_unsigned,
227                size: IntSize::Short,
228            },
229
230            // int 系
231            "int" | "" => Self::Int {
232                signed: !is_unsigned,
233                size: IntSize::Int,
234            },
235
236            // long 系
237            "long" | "long int" => Self::Int {
238                signed: !is_unsigned,
239                size: IntSize::Long,
240            },
241
242            // long long 系
243            "long long" | "long long int" => Self::Int {
244                signed: !is_unsigned,
245                size: IntSize::LongLong,
246            },
247
248            // __int128
249            "__int128" | "__int128_t" => Self::Int {
250                signed: !is_unsigned,
251                size: IntSize::Int128,
252            },
253
254            // 浮動小数点
255            "float" => Self::Float,
256            "double" => Self::Double,
257            "long double" => Self::LongDouble,
258
259            // void
260            "void" => Self::Void,
261
262            // bool
263            "bool" | "_Bool" => Self::Bool,
264
265            // size_t, ssize_t などの標準型は Named として保持
266            // (to_rust_string で usize/isize に変換される)
267            "size_t" | "ssize_t" | "ptrdiff_t" => Self::Named(base.to_string()),
268
269            // その他は名前付き型
270            _ => {
271                if is_unsigned {
272                    // "unsigned SomeType" のような場合
273                    Self::Named(format!("unsigned {}", base))
274                } else {
275                    Self::Named(base.to_string())
276                }
277            }
278        }
279    }
280
281    /// Rust型文字列からパース
282    ///
283    /// 例:
284    /// - `"*mut SV"` → `Pointer { inner: Named("SV"), is_const: false }`
285    /// - `"*const c_char"` → `Pointer { inner: Char { signed: None }, is_const: true }`
286    /// - `"c_int"` → `Int { signed: true, size: IntSize::Int }`
287    pub fn from_rust_str(s: &str) -> Self {
288        // synのto_token_stream().to_string()は "* mut" や " :: " のようにスペースを入れるため正規化
289        let normalized = s
290            .replace("* mut", "*mut")
291            .replace("* const", "*const")
292            .replace(" :: ", "::");
293        let trimmed = normalized.trim();
294
295        if trimmed.is_empty() {
296            return Self::Unknown;
297        }
298
299        // ポインタ型
300        if let Some(rest) = trimmed.strip_prefix("*mut ") {
301            return Self::Pointer {
302                inner: Box::new(Self::from_rust_str(rest)),
303                is_const: false,
304            };
305        }
306        if let Some(rest) = trimmed.strip_prefix("*const ") {
307            return Self::Pointer {
308                inner: Box::new(Self::from_rust_str(rest)),
309                is_const: true,
310            };
311        }
312        // スペースなしのポインタ
313        if let Some(rest) = trimmed.strip_prefix("*mut") {
314            return Self::Pointer {
315                inner: Box::new(Self::from_rust_str(rest.trim())),
316                is_const: false,
317            };
318        }
319        if let Some(rest) = trimmed.strip_prefix("*const") {
320            return Self::Pointer {
321                inner: Box::new(Self::from_rust_str(rest.trim())),
322                is_const: true,
323            };
324        }
325        // 配列 `[T; N]` — 要素型を保持して inner_type() で取り出せるようにする。
326        if trimmed.starts_with('[') && trimmed.ends_with(']') {
327            let inner = &trimmed[1..trimmed.len() - 1];
328            // "[T; N]" の T と N を分ける (ネスト `[` を考慮)
329            let mut depth = 0i32;
330            let mut semi_pos: Option<usize> = None;
331            for (i, ch) in inner.char_indices() {
332                match ch {
333                    '[' | '(' | '<' => depth += 1,
334                    ']' | ')' | '>' => depth -= 1,
335                    ';' if depth == 0 => {
336                        semi_pos = Some(i);
337                        break;
338                    }
339                    _ => {}
340                }
341            }
342            if let Some(pos) = semi_pos {
343                let elem = inner[..pos].trim();
344                let size_str = inner[pos + 1..].trim();
345                let size = size_str
346                    .split_whitespace()
347                    .next()
348                    .and_then(|s| s.trim_end_matches("usize").parse::<usize>().ok());
349                return Self::Array {
350                    inner: Box::new(Self::from_rust_str(elem)),
351                    size,
352                };
353            }
354        }
355
356        // 基本型
357        Self::parse_rust_basic_type(trimmed)
358    }
359
360    /// **構造ベース第一推奨**: `syn::Type` を直接 decompose して構築する。
361    ///
362    /// `to_token_stream().to_string()` → `from_rust_str` という round-trip
363    /// では prefix 剥がしの累積で破綻する型 (`::std::option::Option<extern "C" fn>` 等)
364    /// が安全に扱える。bindings.rs (`syn::File`) 由来データはこちらを使うこと。
365    ///
366    /// 構造的に decompose できない型は `UnifiedType::Verbatim` に格納し、
367    /// emit 時に元のトークン列をそのまま吐く (構造的検査は諦める)。
368    pub fn from_syn_type(ty: &syn::Type) -> Self {
369        match ty {
370            // () = Void
371            syn::Type::Tuple(t) if t.elems.is_empty() => Self::Void,
372
373            // 透過的に剥がす構造
374            syn::Type::Group(g) => Self::from_syn_type(&g.elem),
375            syn::Type::Paren(p) => Self::from_syn_type(&p.elem),
376
377            // *mut T / *const T
378            syn::Type::Ptr(p) => Self::Pointer {
379                inner: Box::new(Self::from_syn_type(&p.elem)),
380                is_const: p.const_token.is_some(),
381            },
382
383            // [T; N]
384            syn::Type::Array(a) => Self::Array {
385                inner: Box::new(Self::from_syn_type(&a.elem)),
386                size: extract_array_size(&a.len),
387            },
388
389            // 裸関数ポインタ (まれ。bindgen は通常 Option ラップ)
390            syn::Type::BareFn(f) => from_bare_fn(f, /* is_optional */ false),
391
392            // Path: Option<T>, primitives (c_int 等), 名前付き型 (SV 等)
393            syn::Type::Path(tp) => from_type_path(tp),
394
395            // 上記以外は escape hatch
396            _ => verbatim_of(ty),
397        }
398    }
399
400    /// Rust の基本型をパース
401    fn parse_rust_basic_type(s: &str) -> Self {
402        // std:: プレフィックスを除去
403        // syn の to_token_stream() は ":: std" のようにスペースを入れるので trim が必要
404        let s = s
405            .strip_prefix("::")
406            .map(|s| s.trim_start())
407            .unwrap_or(s);
408        let s = s
409            .strip_prefix("std::")
410            .unwrap_or(s);
411        let s = s
412            .strip_prefix("ffi::")
413            .or_else(|| s.strip_prefix("os::raw::"))
414            .unwrap_or(s);
415
416        match s {
417            "()" => Self::Void,
418            "c_void" => Self::Void,
419            "bool" => Self::Bool,
420
421            // char 系
422            "c_char" => Self::Char { signed: None },
423            "c_schar" => Self::Char { signed: Some(true) },
424            "c_uchar" => Self::Char { signed: Some(false) },
425
426            // 整数型
427            "c_short" => Self::Int { signed: true, size: IntSize::Short },
428            "c_ushort" => Self::Int { signed: false, size: IntSize::Short },
429            "c_int" => Self::Int { signed: true, size: IntSize::Int },
430            "c_uint" => Self::Int { signed: false, size: IntSize::Int },
431            "c_long" => Self::Int { signed: true, size: IntSize::Long },
432            "c_ulong" => Self::Int { signed: false, size: IntSize::Long },
433            "c_longlong" => Self::Int { signed: true, size: IntSize::LongLong },
434            "c_ulonglong" => Self::Int { signed: false, size: IntSize::LongLong },
435
436            // Rust ネイティブ整数
437            "i8" => Self::Char { signed: Some(true) },
438            "u8" => Self::Char { signed: Some(false) },
439            "i16" => Self::Int { signed: true, size: IntSize::Short },
440            "u16" => Self::Int { signed: false, size: IntSize::Short },
441            "i32" => Self::Int { signed: true, size: IntSize::Int },
442            "u32" => Self::Int { signed: false, size: IntSize::Int },
443            "i64" => Self::Int { signed: true, size: IntSize::LongLong },
444            "u64" => Self::Int { signed: false, size: IntSize::LongLong },
445            "i128" => Self::Int { signed: true, size: IntSize::Int128 },
446            "u128" => Self::Int { signed: false, size: IntSize::Int128 },
447            // isize/usize は c_long/c_ulong と同じ 64bit だが Rust では別型。
448            // Int{Long} に詰めると to_rust_string で `c_long`/`c_ulong` に
449            // 戻ってしまい、比較・演算で「同じ型」と誤認される。
450            // Named にすることでラウンドトリップを保ち区別する。
451            "isize" => Self::Named("isize".to_string()),
452            "usize" => Self::Named("usize".to_string()),
453
454            // 浮動小数点
455            "c_float" | "f32" => Self::Float,
456            "c_double" | "f64" => Self::Double,
457
458            // その他は名前付き型
459            _ => Self::Named(s.to_string()),
460        }
461    }
462
463    /// Rust型文字列に変換
464    pub fn to_rust_string(&self) -> String {
465        match self {
466            Self::Void => "()".to_string(),
467            Self::Bool => "bool".to_string(),
468
469            Self::Char { signed: None } => "c_char".to_string(),
470            Self::Char { signed: Some(true) } => "c_schar".to_string(),
471            Self::Char { signed: Some(false) } => "c_uchar".to_string(),
472
473            Self::Int { signed, size } => {
474                match (signed, size) {
475                    (true, IntSize::Char) => "c_schar".to_string(),
476                    (false, IntSize::Char) => "c_uchar".to_string(),
477                    (true, IntSize::Short) => "c_short".to_string(),
478                    (false, IntSize::Short) => "c_ushort".to_string(),
479                    (true, IntSize::Int) => "c_int".to_string(),
480                    (false, IntSize::Int) => "c_uint".to_string(),
481                    (true, IntSize::Long) => "c_long".to_string(),
482                    (false, IntSize::Long) => "c_ulong".to_string(),
483                    (true, IntSize::LongLong) => "c_longlong".to_string(),
484                    (false, IntSize::LongLong) => "c_ulonglong".to_string(),
485                    (true, IntSize::Int128) => "i128".to_string(),
486                    (false, IntSize::Int128) => "u128".to_string(),
487                }
488            }
489
490            Self::Float => "c_float".to_string(),
491            Self::Double => "c_double".to_string(),
492            Self::LongDouble => "c_double".to_string(), // Rust には long double がない
493
494            Self::Pointer { inner, is_const } => {
495                // void pointer (`Pointer { inner: Void }`) は Rust では
496                // `*mut c_void` / `*const c_void` と書く必要がある。
497                // `Self::Void` の to_rust_string は `()` を返すので、そのまま
498                // ラップすると `*mut ()` という invalid な型になる。
499                let inner_str = if matches!(inner.as_ref(), Self::Void) {
500                    "c_void".to_string()
501                } else {
502                    inner.to_rust_string()
503                };
504                if *is_const {
505                    format!("*const {}", inner_str)
506                } else {
507                    format!("*mut {}", inner_str)
508                }
509            }
510
511            Self::Array { inner, size } => {
512                let inner_str = inner.to_rust_string();
513                match size {
514                    Some(n) => format!("[{}; {}]", inner_str, n),
515                    None => format!("[{}]", inner_str),
516                }
517            }
518
519            Self::Named(name) => {
520                // C標準型をRust型に変換
521                match name.as_str() {
522                    "size_t" => "usize".to_string(),
523                    "ssize_t" | "ptrdiff_t" => "isize".to_string(),
524                    "off_t" | "off64_t" => "i64".to_string(),
525                    _ => name.clone(),
526                }
527            }
528
529            Self::FnPtr { params, ret, abi, is_unsafe, is_optional } => {
530                let mut s = String::new();
531                if *is_unsafe {
532                    s.push_str("unsafe ");
533                }
534                if let Some(abi_name) = abi {
535                    // `{:?}` で `"C"` のような文字列リテラル形式になる
536                    s.push_str(&format!("extern {:?} ", abi_name));
537                }
538                s.push_str("fn(");
539                let param_strs: Vec<String> = params.iter().map(|p| p.to_rust_string()).collect();
540                s.push_str(&param_strs.join(", "));
541                s.push(')');
542                let ret_str = ret.to_rust_string();
543                if ret_str != "()" {
544                    s.push_str(" -> ");
545                    s.push_str(&ret_str);
546                }
547                if *is_optional {
548                    format!("Option<{}>", s)
549                } else {
550                    s
551                }
552            }
553
554            Self::Verbatim(s) => s.clone(),
555
556            Self::Unknown => "UnknownType".to_string(),
557        }
558    }
559
560    /// const/mut を無視して比較
561    pub fn equals_ignoring_const(&self, other: &Self) -> bool {
562        match (self, other) {
563            (Self::Void, Self::Void) => true,
564            (Self::Bool, Self::Bool) => true,
565            (Self::Char { signed: s1 }, Self::Char { signed: s2 }) => s1 == s2,
566            (
567                Self::Int { signed: s1, size: sz1 },
568                Self::Int { signed: s2, size: sz2 },
569            ) => s1 == s2 && sz1 == sz2,
570            (Self::Float, Self::Float) => true,
571            (Self::Double, Self::Double) => true,
572            (Self::LongDouble, Self::LongDouble) => true,
573
574            // ポインタは const/mut を無視して内部型を比較
575            (
576                Self::Pointer { inner: i1, .. },
577                Self::Pointer { inner: i2, .. },
578            ) => i1.equals_ignoring_const(i2),
579
580            (
581                Self::Array { inner: i1, size: s1 },
582                Self::Array { inner: i2, size: s2 },
583            ) => s1 == s2 && i1.equals_ignoring_const(i2),
584
585            (Self::Named(n1), Self::Named(n2)) => n1 == n2,
586
587            (Self::Unknown, Self::Unknown) => true,
588
589            _ => false,
590        }
591    }
592
593    /// 大文字小文字を無視して比較
594    pub fn equals_ignoring_case(&self, other: &Self) -> bool {
595        match (self, other) {
596            (Self::Named(n1), Self::Named(n2)) => n1.eq_ignore_ascii_case(n2),
597
598            (
599                Self::Pointer { inner: i1, is_const: c1 },
600                Self::Pointer { inner: i2, is_const: c2 },
601            ) => c1 == c2 && i1.equals_ignoring_case(i2),
602
603            (
604                Self::Array { inner: i1, size: s1 },
605                Self::Array { inner: i2, size: s2 },
606            ) => s1 == s2 && i1.equals_ignoring_case(i2),
607
608            // 他の型は通常の比較
609            _ => self == other,
610        }
611    }
612
613    /// ポインタ型かどうか
614    pub fn is_pointer(&self) -> bool {
615        matches!(self, Self::Pointer { .. })
616    }
617
618    /// void ポインタ (*mut c_void / *const c_void) かどうか
619    pub fn is_void_pointer(&self) -> bool {
620        match self {
621            Self::Pointer { inner, .. } => {
622                matches!(**inner, Self::Void) || matches!(**inner, Self::Named(ref n) if n == "c_void")
623            }
624            _ => false,
625        }
626    }
627
628    /// 具体的なポインタ(ポインタだが void ではない)かどうか
629    pub fn is_concrete_pointer(&self) -> bool {
630        self.is_pointer() && !self.is_void_pointer()
631    }
632
633    /// const ポインタ型かどうか
634    pub fn is_const_pointer(&self) -> bool {
635        matches!(self, Self::Pointer { is_const: true, .. })
636    }
637
638    /// 浮動小数点型かどうか
639    pub fn is_float(&self) -> bool {
640        matches!(self, Self::Float | Self::Double | Self::LongDouble)
641            || matches!(self, Self::Named(n) if n == "NV")
642    }
643
644    /// bool 型かどうか
645    pub fn is_bool(&self) -> bool {
646        matches!(self, Self::Bool)
647    }
648
649    /// void 型かどうか
650    pub fn is_void(&self) -> bool {
651        matches!(self, Self::Void)
652    }
653
654    /// 名前付き型かどうか
655    pub fn is_named(&self) -> bool {
656        matches!(self, Self::Named(_))
657    }
658
659    /// 名前付き型の名前を取得
660    pub fn as_named(&self) -> Option<&str> {
661        match self {
662            Self::Named(name) => Some(name),
663            _ => None,
664        }
665    }
666
667    /// ポインタの内部型を取得
668    pub fn inner_type(&self) -> Option<&UnifiedType> {
669        match self {
670            Self::Pointer { inner, .. } => Some(inner),
671            Self::Array { inner, .. } => Some(inner),
672            _ => None,
673        }
674    }
675
676    /// 関数ポインタ型かどうか (Option ラップを問わず)
677    pub fn is_fn_ptr(&self) -> bool {
678        matches!(self, Self::FnPtr { .. })
679    }
680
681    /// `Option<fn>` 形式の関数ポインタかどうか
682    pub fn is_optional_fn_ptr(&self) -> bool {
683        matches!(self, Self::FnPtr { is_optional: true, .. })
684    }
685
686    /// Verbatim ハッチ経由かどうか (構造化未対応の指標)
687    pub fn is_verbatim(&self) -> bool {
688        matches!(self, Self::Verbatim(_))
689    }
690}
691
692// ============================================================================
693// from_syn_type の補助関数 (構造ベースで decompose する内部実装)
694// ============================================================================
695
696/// 配列長の `syn::Expr` から `usize` を取り出す。整数リテラル以外は `None`。
697fn extract_array_size(expr: &syn::Expr) -> Option<usize> {
698    if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(li), .. }) = expr {
699        li.base10_parse::<usize>().ok()
700    } else {
701        None
702    }
703}
704
705/// `syn::TypeBareFn` から `UnifiedType::FnPtr` を構築する。
706/// `is_optional` は `Option<extern "C" fn(...)>` 形式かどうかの呼出側情報。
707fn from_bare_fn(f: &syn::TypeBareFn, is_optional: bool) -> UnifiedType {
708    let abi = f.abi.as_ref().map(|abi| {
709        abi.name
710            .as_ref()
711            .map(|n| n.value())
712            .unwrap_or_else(|| "C".to_string())
713    });
714    let params: Vec<UnifiedType> = f
715        .inputs
716        .iter()
717        .map(|arg| UnifiedType::from_syn_type(&arg.ty))
718        .collect();
719    let ret = match &f.output {
720        syn::ReturnType::Default => UnifiedType::Void,
721        syn::ReturnType::Type(_, t) => UnifiedType::from_syn_type(t),
722    };
723    UnifiedType::FnPtr {
724        params,
725        ret: Box::new(ret),
726        abi,
727        is_unsafe: f.unsafety.is_some(),
728        is_optional,
729    }
730}
731
732/// `syn::TypePath` を Option<fn> / 基本型 / 名前付き型に振り分ける。
733fn from_type_path(tp: &syn::TypePath) -> UnifiedType {
734    // qself 付き (`<T as Trait>::U`) は構造化を諦める
735    if tp.qself.is_some() {
736        return verbatim_of(&syn::Type::Path(tp.clone()));
737    }
738    let path = &tp.path;
739
740    // Option<T> の検出: 最終セグメントが `Option` で <T> が一つ
741    if let Some(last) = path.segments.last() {
742        if last.ident == "Option" {
743            if let syn::PathArguments::AngleBracketed(ab) = &last.arguments {
744                if let Some(syn::GenericArgument::Type(inner)) = ab.args.first() {
745                    return from_optional_inner(inner);
746                }
747            }
748        }
749    }
750
751    // 単一識別子へ畳めるパス (`SV`, `c_int`, `::std::os::raw::c_int` 等)
752    if let Some(ident) = single_ident_of(path) {
753        if let Some(prim) = primitive_from_ident(&ident) {
754            return prim;
755        }
756        return UnifiedType::Named(ident);
757    }
758
759    // それ以外 (ジェネリック等) は escape hatch
760    verbatim_of(&syn::Type::Path(tp.clone()))
761}
762
763/// `Option<T>` の中身 `T` を見て、関数ポインタなら `is_optional: true` の
764/// `FnPtr` に持ち上げる。それ以外は Verbatim にフォールバック。
765fn from_optional_inner(inner: &syn::Type) -> UnifiedType {
766    match inner {
767        syn::Type::BareFn(f) => from_bare_fn(f, true),
768        syn::Type::Group(g) => from_optional_inner(&g.elem),
769        syn::Type::Paren(p) => from_optional_inner(&p.elem),
770        // Option<*mut T> 等は perl bindings には現れないため Verbatim で残す
771        _ => {
772            let wrapped: syn::TypePath = syn::parse_quote!(::std::option::Option<#inner>);
773            verbatim_of(&syn::Type::Path(wrapped))
774        }
775    }
776}
777
778/// パスを単一識別子に畳めるか試みる:
779/// - 最終セグメントに generics が無い
780/// - 最終セグメントの ident をそのまま採用 (`::std::os::raw::c_int` → `"c_int"`)
781fn single_ident_of(path: &syn::Path) -> Option<String> {
782    let last = path.segments.last()?;
783    if !matches!(last.arguments, syn::PathArguments::None) {
784        return None;
785    }
786    Some(last.ident.to_string())
787}
788
789/// 識別子が C 互換 / Rust 基本型に対応するか調べる。
790/// 文字列 prefix の剥がしは行わず、**識別子そのもの** で判定する。
791fn primitive_from_ident(name: &str) -> Option<UnifiedType> {
792    Some(match name {
793        // C 互換 void / bool
794        "c_void" => UnifiedType::Void,
795        "bool" => UnifiedType::Bool,
796
797        // C 互換 char
798        "c_char" => UnifiedType::Char { signed: None },
799        "c_schar" => UnifiedType::Char { signed: Some(true) },
800        "c_uchar" => UnifiedType::Char { signed: Some(false) },
801
802        // C 互換整数
803        "c_short" => UnifiedType::Int { signed: true, size: IntSize::Short },
804        "c_ushort" => UnifiedType::Int { signed: false, size: IntSize::Short },
805        "c_int" => UnifiedType::Int { signed: true, size: IntSize::Int },
806        "c_uint" => UnifiedType::Int { signed: false, size: IntSize::Int },
807        "c_long" => UnifiedType::Int { signed: true, size: IntSize::Long },
808        "c_ulong" => UnifiedType::Int { signed: false, size: IntSize::Long },
809        "c_longlong" => UnifiedType::Int { signed: true, size: IntSize::LongLong },
810        "c_ulonglong" => UnifiedType::Int { signed: false, size: IntSize::LongLong },
811
812        // Rust ネイティブ整数 (8/16/32/64/128)
813        // i8/u8 は char 系として扱い (バイト列の意味を保つ)
814        "i8" => UnifiedType::Char { signed: Some(true) },
815        "u8" => UnifiedType::Char { signed: Some(false) },
816        "i16" => UnifiedType::Int { signed: true, size: IntSize::Short },
817        "u16" => UnifiedType::Int { signed: false, size: IntSize::Short },
818        "i32" => UnifiedType::Int { signed: true, size: IntSize::Int },
819        "u32" => UnifiedType::Int { signed: false, size: IntSize::Int },
820        "i64" => UnifiedType::Int { signed: true, size: IntSize::LongLong },
821        "u64" => UnifiedType::Int { signed: false, size: IntSize::LongLong },
822        "i128" => UnifiedType::Int { signed: true, size: IntSize::Int128 },
823        "u128" => UnifiedType::Int { signed: false, size: IntSize::Int128 },
824
825        // isize/usize は Named 維持 (c_long とサイズが同じでも区別)
826        "isize" | "usize" => UnifiedType::Named(name.to_string()),
827
828        // 浮動小数点
829        "c_float" | "f32" => UnifiedType::Float,
830        "c_double" | "f64" => UnifiedType::Double,
831
832        _ => return None,
833    })
834}
835
836/// 任意の `syn::Type` を `proc_macro2::TokenStream` の正規形文字列で
837/// `UnifiedType::Verbatim` にラップする。
838fn verbatim_of(ty: &syn::Type) -> UnifiedType {
839    UnifiedType::Verbatim(ty.to_token_stream().to_string())
840}
841
842impl fmt::Display for UnifiedType {
843    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
844        write!(f, "{}", self.to_rust_string())
845    }
846}
847
848impl SourcedType {
849    /// 新しい出自情報付き型を作成
850    pub fn new(ty: UnifiedType, source: TypeSource) -> Self {
851        Self { ty, source }
852    }
853
854    /// apidoc からの型を作成
855    pub fn from_apidoc(raw: &str, entry_name: &str) -> Self {
856        Self {
857            ty: UnifiedType::from_c_str(raw),
858            source: TypeSource::Apidoc {
859                raw: raw.to_string(),
860                entry_name: entry_name.to_string(),
861            },
862        }
863    }
864
865    /// bindings.rs からの型を作成
866    pub fn from_bindings(raw: &str) -> Self {
867        Self {
868            ty: UnifiedType::from_rust_str(raw),
869            source: TypeSource::Bindings {
870                raw: raw.to_string(),
871            },
872        }
873    }
874
875    /// 元の文字列表現を取得
876    pub fn raw_string(&self) -> Option<&str> {
877        match &self.source {
878            TypeSource::Apidoc { raw, .. } => Some(raw),
879            TypeSource::Bindings { raw } => Some(raw),
880            _ => None,
881        }
882    }
883}
884
885#[cfg(test)]
886mod tests {
887    use super::*;
888
889    #[test]
890    fn test_from_c_str_basic_types() {
891        assert_eq!(UnifiedType::from_c_str("void"), UnifiedType::Void);
892        assert_eq!(UnifiedType::from_c_str("bool"), UnifiedType::Bool);
893        assert_eq!(
894            UnifiedType::from_c_str("int"),
895            UnifiedType::Int { signed: true, size: IntSize::Int }
896        );
897        assert_eq!(
898            UnifiedType::from_c_str("unsigned int"),
899            UnifiedType::Int { signed: false, size: IntSize::Int }
900        );
901        assert_eq!(
902            UnifiedType::from_c_str("long long"),
903            UnifiedType::Int { signed: true, size: IntSize::LongLong }
904        );
905    }
906
907    #[test]
908    fn test_from_c_str_pointer() {
909        assert_eq!(
910            UnifiedType::from_c_str("SV *"),
911            UnifiedType::Pointer {
912                inner: Box::new(UnifiedType::Named("SV".to_string())),
913                is_const: false,
914            }
915        );
916        assert_eq!(
917            UnifiedType::from_c_str("const char *"),
918            UnifiedType::Pointer {
919                inner: Box::new(UnifiedType::Char { signed: None }),
920                is_const: true,
921            }
922        );
923    }
924
925    #[test]
926    fn test_from_c_str_double_pointer() {
927        let ty = UnifiedType::from_c_str("SV **");
928        assert_eq!(
929            ty,
930            UnifiedType::Pointer {
931                inner: Box::new(UnifiedType::Pointer {
932                    inner: Box::new(UnifiedType::Named("SV".to_string())),
933                    is_const: false,
934                }),
935                is_const: false,
936            }
937        );
938    }
939
940    #[test]
941    fn test_from_rust_str_basic_types() {
942        assert_eq!(UnifiedType::from_rust_str("()"), UnifiedType::Void);
943        assert_eq!(UnifiedType::from_rust_str("bool"), UnifiedType::Bool);
944        assert_eq!(
945            UnifiedType::from_rust_str("c_int"),
946            UnifiedType::Int { signed: true, size: IntSize::Int }
947        );
948        assert_eq!(
949            UnifiedType::from_rust_str("c_uint"),
950            UnifiedType::Int { signed: false, size: IntSize::Int }
951        );
952    }
953
954    #[test]
955    fn test_from_rust_str_pointer() {
956        assert_eq!(
957            UnifiedType::from_rust_str("*mut SV"),
958            UnifiedType::Pointer {
959                inner: Box::new(UnifiedType::Named("SV".to_string())),
960                is_const: false,
961            }
962        );
963        assert_eq!(
964            UnifiedType::from_rust_str("*const c_char"),
965            UnifiedType::Pointer {
966                inner: Box::new(UnifiedType::Char { signed: None }),
967                is_const: true,
968            }
969        );
970    }
971
972    #[test]
973    fn test_from_rust_str_double_pointer() {
974        let ty = UnifiedType::from_rust_str("*mut *mut SV");
975        assert_eq!(
976            ty,
977            UnifiedType::Pointer {
978                inner: Box::new(UnifiedType::Pointer {
979                    inner: Box::new(UnifiedType::Named("SV".to_string())),
980                    is_const: false,
981                }),
982                is_const: false,
983            }
984        );
985    }
986
987    #[test]
988    fn test_from_rust_str_with_spaces() {
989        // syn の to_token_stream() が生成する "* mut" 形式
990        let ty = UnifiedType::from_rust_str("* mut * mut SV");
991        assert_eq!(
992            ty,
993            UnifiedType::Pointer {
994                inner: Box::new(UnifiedType::Pointer {
995                    inner: Box::new(UnifiedType::Named("SV".to_string())),
996                    is_const: false,
997                }),
998                is_const: false,
999            }
1000        );
1001    }
1002
1003    #[test]
1004    fn test_to_rust_string() {
1005        assert_eq!(UnifiedType::Void.to_rust_string(), "()");
1006        assert_eq!(
1007            UnifiedType::Int { signed: true, size: IntSize::Int }.to_rust_string(),
1008            "c_int"
1009        );
1010        assert_eq!(
1011            UnifiedType::Pointer {
1012                inner: Box::new(UnifiedType::Named("SV".to_string())),
1013                is_const: false,
1014            }.to_rust_string(),
1015            "*mut SV"
1016        );
1017        assert_eq!(
1018            UnifiedType::Pointer {
1019                inner: Box::new(UnifiedType::Char { signed: None }),
1020                is_const: true,
1021            }.to_rust_string(),
1022            "*const c_char"
1023        );
1024    }
1025
1026    #[test]
1027    fn test_roundtrip_c_to_rust() {
1028        let cases = [
1029            ("void", "()"),
1030            ("int", "c_int"),
1031            ("unsigned int", "c_uint"),
1032            ("SV *", "*mut SV"),
1033            ("const char *", "*const c_char"),
1034            ("SV **", "*mut *mut SV"),
1035        ];
1036
1037        for (c_type, expected_rust) in cases {
1038            let ty = UnifiedType::from_c_str(c_type);
1039            assert_eq!(ty.to_rust_string(), expected_rust, "C type: {}", c_type);
1040        }
1041    }
1042
1043    #[test]
1044    fn test_roundtrip_rust() {
1045        let cases = [
1046            "()",
1047            "bool",
1048            "c_int",
1049            "c_uint",
1050            "*mut SV",
1051            "*const c_char",
1052            "*mut *mut SV",
1053        ];
1054
1055        for rust_type in cases {
1056            let ty = UnifiedType::from_rust_str(rust_type);
1057            assert_eq!(ty.to_rust_string(), rust_type, "Rust type: {}", rust_type);
1058        }
1059    }
1060
1061    #[test]
1062    fn test_equals_ignoring_const() {
1063        let mut_sv = UnifiedType::Pointer {
1064            inner: Box::new(UnifiedType::Named("SV".to_string())),
1065            is_const: false,
1066        };
1067        let const_sv = UnifiedType::Pointer {
1068            inner: Box::new(UnifiedType::Named("SV".to_string())),
1069            is_const: true,
1070        };
1071
1072        assert!(mut_sv.equals_ignoring_const(&const_sv));
1073        assert!(!mut_sv.eq(&const_sv)); // 通常の比較では異なる
1074    }
1075
1076    #[test]
1077    fn test_equals_ignoring_case() {
1078        let sv_upper = UnifiedType::Named("SV".to_string());
1079        let sv_lower = UnifiedType::Named("sv".to_string());
1080
1081        assert!(sv_upper.equals_ignoring_case(&sv_lower));
1082        assert!(!sv_upper.eq(&sv_lower)); // 通常の比較では異なる
1083
1084        // ポインタ内部でも動作
1085        let ptr_sv_upper = UnifiedType::Pointer {
1086            inner: Box::new(sv_upper),
1087            is_const: false,
1088        };
1089        let ptr_sv_lower = UnifiedType::Pointer {
1090            inner: Box::new(sv_lower),
1091            is_const: false,
1092        };
1093        assert!(ptr_sv_upper.equals_ignoring_case(&ptr_sv_lower));
1094    }
1095
1096    #[test]
1097    fn test_const_value_type() {
1098        // const U32 should be the same as U32
1099        assert_eq!(
1100            UnifiedType::from_c_str("const U32"),
1101            UnifiedType::Named("U32".to_string())
1102        );
1103        assert_eq!(
1104            UnifiedType::from_c_str("const STRLEN"),
1105            UnifiedType::Named("STRLEN".to_string())
1106        );
1107        assert_eq!(
1108            UnifiedType::from_c_str("const int"),
1109            UnifiedType::Int { signed: true, size: IntSize::Int }
1110        );
1111        assert_eq!(
1112            UnifiedType::from_c_str("const bool"),
1113            UnifiedType::Bool
1114        );
1115    }
1116
1117    #[test]
1118    fn test_struct_prefix() {
1119        // struct X should be the same as X
1120        assert_eq!(
1121            UnifiedType::from_c_str("struct refcounted_he *"),
1122            UnifiedType::Pointer {
1123                inner: Box::new(UnifiedType::Named("refcounted_he".to_string())),
1124                is_const: false,
1125            }
1126        );
1127        assert_eq!(
1128            UnifiedType::from_c_str("struct SV"),
1129            UnifiedType::Named("SV".to_string())
1130        );
1131    }
1132
1133    #[test]
1134    fn test_is_const_pointer() {
1135        let mut_ptr = UnifiedType::from_rust_str("*mut SV");
1136        let const_ptr = UnifiedType::from_rust_str("*const c_char");
1137        let non_ptr = UnifiedType::from_rust_str("c_int");
1138
1139        assert!(!mut_ptr.is_const_pointer());
1140        assert!(const_ptr.is_const_pointer());
1141        assert!(!non_ptr.is_const_pointer());
1142    }
1143
1144    #[test]
1145    fn test_is_float() {
1146        assert!(UnifiedType::Float.is_float());
1147        assert!(UnifiedType::Double.is_float());
1148        assert!(UnifiedType::LongDouble.is_float());
1149        assert!(UnifiedType::Named("NV".to_string()).is_float());
1150        assert!(!UnifiedType::Int { signed: true, size: IntSize::Int }.is_float());
1151        assert!(!UnifiedType::Named("SV".to_string()).is_float());
1152    }
1153
1154    #[test]
1155    fn test_is_bool() {
1156        assert!(UnifiedType::Bool.is_bool());
1157        assert!(!UnifiedType::Int { signed: true, size: IntSize::Int }.is_bool());
1158        assert!(!UnifiedType::Void.is_bool());
1159    }
1160
1161    #[test]
1162    fn test_is_void() {
1163        assert!(UnifiedType::Void.is_void());
1164        assert!(!UnifiedType::Bool.is_void());
1165        assert!(!UnifiedType::Int { signed: true, size: IntSize::Int }.is_void());
1166    }
1167
1168    // === Stage 1: FnPtr / Verbatim ===
1169
1170    #[test]
1171    fn test_fn_ptr_void_no_args() {
1172        let ty = UnifiedType::FnPtr {
1173            params: vec![],
1174            ret: Box::new(UnifiedType::Void),
1175            abi: None,
1176            is_unsafe: false,
1177            is_optional: false,
1178        };
1179        assert!(ty.is_fn_ptr());
1180        assert!(!ty.is_optional_fn_ptr());
1181        assert!(!ty.is_pointer());
1182        assert!(!ty.is_void());
1183        assert_eq!(ty.to_rust_string(), "fn()");
1184    }
1185
1186    #[test]
1187    fn test_fn_ptr_extern_c_with_args() {
1188        let ty = UnifiedType::FnPtr {
1189            params: vec![
1190                UnifiedType::Pointer {
1191                    inner: Box::new(UnifiedType::Named("CV".to_string())),
1192                    is_const: false,
1193                },
1194            ],
1195            ret: Box::new(UnifiedType::Void),
1196            abi: Some("C".to_string()),
1197            is_unsafe: true,
1198            is_optional: false,
1199        };
1200        assert_eq!(ty.to_rust_string(), "unsafe extern \"C\" fn(*mut CV)");
1201    }
1202
1203    #[test]
1204    fn test_fn_ptr_optional_with_return() {
1205        // bindgen が生成する典型形: `Option<unsafe extern "C" fn(arg1: *mut CV) -> *mut SV>`
1206        let ty = UnifiedType::FnPtr {
1207            params: vec![
1208                UnifiedType::Pointer {
1209                    inner: Box::new(UnifiedType::Named("CV".to_string())),
1210                    is_const: false,
1211                },
1212            ],
1213            ret: Box::new(UnifiedType::Pointer {
1214                inner: Box::new(UnifiedType::Named("SV".to_string())),
1215                is_const: false,
1216            }),
1217            abi: Some("C".to_string()),
1218            is_unsafe: true,
1219            is_optional: true,
1220        };
1221        assert!(ty.is_fn_ptr());
1222        assert!(ty.is_optional_fn_ptr());
1223        assert_eq!(
1224            ty.to_rust_string(),
1225            "Option<unsafe extern \"C\" fn(*mut CV) -> *mut SV>"
1226        );
1227    }
1228
1229    #[test]
1230    fn test_verbatim_emits_as_is() {
1231        let raw = ":: std :: option :: Option < unsafe extern \"C\" fn (arg1 : * mut CV) >";
1232        let ty = UnifiedType::Verbatim(raw.to_string());
1233        assert!(ty.is_verbatim());
1234        assert!(!ty.is_pointer());
1235        assert!(!ty.is_fn_ptr());
1236        assert_eq!(ty.to_rust_string(), raw);
1237    }
1238
1239    // === Stage 2: from_syn_type ===
1240
1241    fn parse_ty(s: &str) -> syn::Type {
1242        syn::parse_str::<syn::Type>(s).expect("syn parse failed")
1243    }
1244
1245    #[test]
1246    fn test_syn_void_tuple() {
1247        assert_eq!(UnifiedType::from_syn_type(&parse_ty("()")), UnifiedType::Void);
1248    }
1249
1250    #[test]
1251    fn test_syn_pointer_mut() {
1252        let ty = parse_ty("*mut SV");
1253        assert_eq!(
1254            UnifiedType::from_syn_type(&ty),
1255            UnifiedType::Pointer {
1256                inner: Box::new(UnifiedType::Named("SV".to_string())),
1257                is_const: false,
1258            }
1259        );
1260    }
1261
1262    #[test]
1263    fn test_syn_pointer_const_char() {
1264        let ty = parse_ty("*const c_char");
1265        assert_eq!(
1266            UnifiedType::from_syn_type(&ty),
1267            UnifiedType::Pointer {
1268                inner: Box::new(UnifiedType::Char { signed: None }),
1269                is_const: true,
1270            }
1271        );
1272    }
1273
1274    #[test]
1275    fn test_syn_double_pointer() {
1276        let ty = parse_ty("*mut *mut OP");
1277        assert_eq!(
1278            UnifiedType::from_syn_type(&ty),
1279            UnifiedType::Pointer {
1280                inner: Box::new(UnifiedType::Pointer {
1281                    inner: Box::new(UnifiedType::Named("OP".to_string())),
1282                    is_const: false,
1283                }),
1284                is_const: false,
1285            }
1286        );
1287    }
1288
1289    #[test]
1290    fn test_syn_array() {
1291        let ty = parse_ty("[U32; 8]");
1292        match UnifiedType::from_syn_type(&ty) {
1293            UnifiedType::Array { inner, size } => {
1294                assert_eq!(*inner, UnifiedType::Named("U32".to_string()));
1295                assert_eq!(size, Some(8));
1296            }
1297            other => panic!("expected Array, got {:?}", other),
1298        }
1299    }
1300
1301    #[test]
1302    fn test_syn_primitive_full_path() {
1303        // `::std::os::raw::c_int` の最終識別子で判定
1304        let ty = parse_ty(":: std :: os :: raw :: c_int");
1305        assert_eq!(
1306            UnifiedType::from_syn_type(&ty),
1307            UnifiedType::Int { signed: true, size: IntSize::Int }
1308        );
1309    }
1310
1311    #[test]
1312    fn test_syn_primitive_short_path() {
1313        let ty = parse_ty("c_uchar");
1314        assert_eq!(
1315            UnifiedType::from_syn_type(&ty),
1316            UnifiedType::Char { signed: Some(false) }
1317        );
1318    }
1319
1320    #[test]
1321    fn test_syn_named_type() {
1322        let ty = parse_ty("PerlInterpreter");
1323        assert_eq!(
1324            UnifiedType::from_syn_type(&ty),
1325            UnifiedType::Named("PerlInterpreter".to_string())
1326        );
1327    }
1328
1329    #[test]
1330    fn test_syn_option_extern_c_fn_ptr() {
1331        // bindgen 標準形: `xcv_xsub` の型表現
1332        let ty = parse_ty(
1333            ":: std :: option :: Option < unsafe extern \"C\" fn (arg1 : * mut CV) >",
1334        );
1335        let ut = UnifiedType::from_syn_type(&ty);
1336        let expected = UnifiedType::FnPtr {
1337            params: vec![UnifiedType::Pointer {
1338                inner: Box::new(UnifiedType::Named("CV".to_string())),
1339                is_const: false,
1340            }],
1341            ret: Box::new(UnifiedType::Void),
1342            abi: Some("C".to_string()),
1343            is_unsafe: true,
1344            is_optional: true,
1345        };
1346        assert_eq!(ut, expected);
1347        // emit が `option::Option<...>` ではなく正しい形に戻ること
1348        assert_eq!(
1349            ut.to_rust_string(),
1350            "Option<unsafe extern \"C\" fn(*mut CV)>"
1351        );
1352    }
1353
1354    #[test]
1355    fn test_syn_option_fn_with_return() {
1356        let ty = parse_ty(
1357            "Option<unsafe extern \"C\" fn(my_perl: *mut PerlInterpreter, rx: *mut REGEXP) -> *mut SV>",
1358        );
1359        let ut = UnifiedType::from_syn_type(&ty);
1360        assert!(ut.is_optional_fn_ptr());
1361        assert_eq!(
1362            ut.to_rust_string(),
1363            "Option<unsafe extern \"C\" fn(*mut PerlInterpreter, *mut REGEXP) -> *mut SV>"
1364        );
1365    }
1366
1367    #[test]
1368    fn test_syn_unsupported_falls_to_verbatim() {
1369        // ジェネリック型 (Option<fn> 以外) は Verbatim にフォールバックする
1370        let ty = parse_ty("Vec<u8>");
1371        let ut = UnifiedType::from_syn_type(&ty);
1372        assert!(ut.is_verbatim());
1373        // Verbatim の文字列は syn 正規形 (空白入り)
1374        assert_eq!(ut.to_rust_string(), "Vec < u8 >");
1375    }
1376
1377    #[test]
1378    fn test_syn_paren_and_group_unwrap() {
1379        // 括弧で囲まれた型はそのまま剥がれて中身に等しくなる
1380        let ty = parse_ty("(*mut SV)");
1381        assert_eq!(
1382            UnifiedType::from_syn_type(&ty),
1383            UnifiedType::Pointer {
1384                inner: Box::new(UnifiedType::Named("SV".to_string())),
1385                is_const: false,
1386            }
1387        );
1388    }
1389
1390    #[test]
1391    fn test_fn_ptr_eq_hash() {
1392        // PartialEq / Hash の derive が新 variant でも壊れていないこと
1393        let a = UnifiedType::FnPtr {
1394            params: vec![UnifiedType::Void],
1395            ret: Box::new(UnifiedType::Void),
1396            abi: Some("C".to_string()),
1397            is_unsafe: true,
1398            is_optional: true,
1399        };
1400        let b = a.clone();
1401        assert_eq!(a, b);
1402        let mut set = std::collections::HashSet::new();
1403        set.insert(a.clone());
1404        assert!(set.contains(&b));
1405    }
1406}