Skip to main content

swift_demangler/
constructor.rs

1//! Constructor and destructor symbol representation.
2//!
3//! This module provides types for representing Swift initializers and deinitializers.
4
5use crate::context::{SymbolContext, extract_context};
6use crate::helpers::{
7    HasExtensionContext, HasFunctionSignature, HasGenericSignature, HasModule, NodeExt,
8};
9use crate::raw::{Node, NodeKind};
10use crate::types::{FunctionType, GenericSignature, TypeRef};
11
12/// A Swift constructor (initializer) symbol.
13#[derive(Clone, Copy)]
14pub struct Constructor<'ctx> {
15    raw: Node<'ctx>,
16}
17
18impl<'ctx> Constructor<'ctx> {
19    /// Create a Constructor from a raw node.
20    pub fn new(raw: Node<'ctx>) -> Self {
21        Self { raw }
22    }
23
24    /// Get the underlying raw node.
25    pub fn raw(&self) -> Node<'ctx> {
26        self.raw
27    }
28
29    /// Get the context (location) where this constructor is defined.
30    pub fn context(&self) -> SymbolContext<'ctx> {
31        extract_context(self.raw)
32    }
33
34    /// Get the kind of constructor.
35    pub fn kind(&self) -> ConstructorKind {
36        match self.raw.kind() {
37            NodeKind::Allocator => ConstructorKind::Allocating,
38            NodeKind::Constructor => ConstructorKind::Regular,
39            _ => ConstructorKind::Regular,
40        }
41    }
42
43    /// Get the type containing this constructor.
44    pub fn containing_type(&self) -> Option<&'ctx str> {
45        self.raw.find_containing_type()
46    }
47
48    /// Check if the containing type is a class (reference type).
49    pub fn containing_type_is_class(&self) -> bool {
50        self.raw.containing_type_is_class()
51    }
52
53    /// Check if the containing type is a protocol.
54    pub fn containing_type_is_protocol(&self) -> bool {
55        self.raw.containing_type_is_protocol()
56    }
57
58    /// Get the argument labels for this constructor.
59    pub fn labels(&self) -> Vec<Option<&'ctx str>> {
60        self.raw.extract_labels()
61    }
62
63    /// Check if this is a failable initializer (returns optional).
64    pub fn is_failable(&self) -> bool {
65        if let Some(sig) = self.signature() {
66            if let Some(ret) = sig.return_type() {
67                // Check if return type is optional
68                matches!(ret.kind(), crate::types::TypeKind::Optional(_))
69            } else {
70                false
71            }
72        } else {
73            false
74        }
75    }
76}
77
78impl<'ctx> HasGenericSignature<'ctx> for Constructor<'ctx> {
79    fn generic_signature(&self) -> Option<GenericSignature<'ctx>> {
80        self.raw.find_generic_signature()
81    }
82}
83
84impl<'ctx> HasFunctionSignature<'ctx> for Constructor<'ctx> {
85    fn signature(&self) -> Option<FunctionType<'ctx>> {
86        self.raw.find_function_type()
87    }
88}
89
90impl<'ctx> HasExtensionContext<'ctx> for Constructor<'ctx> {
91    fn raw(&self) -> Node<'ctx> {
92        self.raw
93    }
94}
95
96impl<'ctx> HasModule<'ctx> for Constructor<'ctx> {
97    fn module(&self) -> Option<&'ctx str> {
98        self.raw.find_module()
99    }
100}
101
102impl std::fmt::Debug for Constructor<'_> {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        let mut s = f.debug_struct("Constructor");
105        s.field("kind", &self.kind())
106            .field("containing_type", &self.containing_type())
107            .field("module", &self.module())
108            .field("labels", &self.labels())
109            .field("is_async", &self.is_async())
110            .field("is_throwing", &self.is_throwing())
111            .field("is_failable", &self.is_failable())
112            .field("is_extension", &self.is_extension())
113            .field("is_generic", &self.is_generic());
114        if let Some(sig) = self.signature() {
115            s.field("signature", &sig);
116        }
117        if self.is_extension() {
118            s.field("extension_module", &self.extension_module());
119            let ext_requirements = self.extension_generic_requirements();
120            if !ext_requirements.is_empty() {
121                s.field("extension_generic_requirements", &ext_requirements);
122            }
123        }
124        let requirements = self.generic_requirements();
125        if !requirements.is_empty() {
126            s.field("generic_requirements", &requirements);
127        }
128        s.finish()
129    }
130}
131
132impl std::fmt::Display for Constructor<'_> {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        write!(f, "{}", self.raw)
135    }
136}
137
138/// The kind of constructor.
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub enum ConstructorKind {
141    /// A regular constructor (init).
142    Regular,
143    /// An allocating constructor (allocates memory then initializes).
144    Allocating,
145}
146
147/// A Swift destructor (deinitializer) symbol.
148#[derive(Clone, Copy)]
149pub struct Destructor<'ctx> {
150    raw: Node<'ctx>,
151}
152
153impl<'ctx> Destructor<'ctx> {
154    /// Create a Destructor from a raw node.
155    pub fn new(raw: Node<'ctx>) -> Self {
156        Self { raw }
157    }
158
159    /// Get the underlying raw node.
160    pub fn raw(&self) -> Node<'ctx> {
161        self.raw
162    }
163
164    /// Get the context (location) where this destructor is defined.
165    pub fn context(&self) -> SymbolContext<'ctx> {
166        extract_context(self.raw)
167    }
168
169    /// Get the kind of destructor.
170    pub fn kind(&self) -> DestructorKind {
171        match self.raw.kind() {
172            NodeKind::Deallocator => DestructorKind::Deallocating,
173            NodeKind::IsolatedDeallocator => DestructorKind::IsolatedDeallocating,
174            NodeKind::Destructor => DestructorKind::Regular,
175            _ => DestructorKind::Regular,
176        }
177    }
178
179    /// Get the type containing this destructor.
180    pub fn containing_type(&self) -> Option<&'ctx str> {
181        self.raw.find_containing_type()
182    }
183
184    /// Check if the containing type is a class (reference type).
185    pub fn containing_type_is_class(&self) -> bool {
186        self.raw.containing_type_is_class()
187    }
188
189    /// Check if the containing type is a protocol.
190    pub fn containing_type_is_protocol(&self) -> bool {
191        self.raw.containing_type_is_protocol()
192    }
193
194    /// Get the type being destroyed.
195    pub fn destroyed_type(&self) -> Option<TypeRef<'ctx>> {
196        for child in self.raw.children() {
197            match child.kind() {
198                NodeKind::Class | NodeKind::Structure | NodeKind::Enum => {
199                    return Some(TypeRef::new(child));
200                }
201                _ => {}
202            }
203        }
204        None
205    }
206}
207
208impl<'ctx> HasModule<'ctx> for Destructor<'ctx> {
209    fn module(&self) -> Option<&'ctx str> {
210        self.raw.find_module()
211    }
212}
213
214impl std::fmt::Debug for Destructor<'_> {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        f.debug_struct("Destructor")
217            .field("kind", &self.kind())
218            .field("containing_type", &self.containing_type())
219            .field("module", &self.module())
220            .finish()
221    }
222}
223
224impl std::fmt::Display for Destructor<'_> {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        write!(f, "{}", self.raw)
227    }
228}
229
230/// The kind of destructor.
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232pub enum DestructorKind {
233    /// A regular destructor (deinit).
234    Regular,
235    /// A deallocating destructor (deinitializes then deallocates).
236    Deallocating,
237    /// An isolated deallocating destructor (for actors).
238    IsolatedDeallocating,
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244
245    #[test]
246    fn test_constructor_kind() {
247        assert_eq!(ConstructorKind::Regular, ConstructorKind::Regular);
248        assert_ne!(ConstructorKind::Regular, ConstructorKind::Allocating);
249    }
250
251    #[test]
252    fn test_destructor_kind() {
253        assert_eq!(DestructorKind::Regular, DestructorKind::Regular);
254        assert_ne!(DestructorKind::Regular, DestructorKind::Deallocating);
255    }
256
257    // Note: Full constructor/destructor tests require specific mangled symbols
258    // which can be added from manglings.txt
259}