Skip to main content

swift_demangler/
symbol.rs

1//! Top-level symbol representation.
2//!
3//! This module provides the main `Symbol` enum that categorizes demangled Swift symbols
4//! into specific types for easier consumption.
5
6use crate::accessor::Accessor;
7use crate::async_symbol::AsyncSymbol;
8use crate::autodiff::AutoDiff;
9use crate::closure::Closure;
10use crate::constructor::{Constructor, Destructor};
11use crate::descriptor::Descriptor;
12use crate::enum_case::EnumCase;
13use crate::function::Function;
14use crate::helpers::{HasModule, NodeExt};
15use crate::macro_symbol::MacroSymbol;
16use crate::metadata::Metadata;
17use crate::outlined::Outlined;
18use crate::raw::{Context, Node, NodeKind};
19use crate::specialization::Specialization;
20use crate::thunk::Thunk;
21use crate::types::TypeRef;
22use crate::witness_table::WitnessTable;
23
24/// A parsed Swift symbol.
25///
26/// This enum categorizes symbols based on the first child of the `Global` root node.
27/// It provides a high-level view of what a symbol represents.
28#[derive(Debug)]
29pub enum Symbol<'ctx> {
30    /// A function symbol.
31    Function(Function<'ctx>),
32    /// A constructor (initializer).
33    Constructor(Constructor<'ctx>),
34    /// A destructor (deinitializer).
35    Destructor(Destructor<'ctx>),
36    /// An enum case constructor.
37    EnumCase(EnumCase<'ctx>),
38    /// A property accessor (getter, setter, etc.).
39    Accessor(Accessor<'ctx>),
40    /// A global variable.
41    Variable(Variable<'ctx>),
42    /// A closure.
43    Closure(Closure<'ctx>),
44    /// A thunk (dispatch, reabstraction, partial apply, etc.).
45    Thunk(Thunk<'ctx>),
46    /// A generic specialization (pre-compiled with specific type arguments).
47    Specialization(SpecializedSymbol<'ctx>),
48    /// A witness table (protocol or value).
49    WitnessTable(WitnessTable<'ctx>),
50    /// A metadata descriptor (protocol conformance, type, etc.).
51    Descriptor(Descriptor<'ctx>),
52    /// Type metadata (runtime type information).
53    Metadata(Metadata<'ctx>),
54    /// A type symbol (class, struct, enum, etc.).
55    Type(TypeRef<'ctx>),
56    /// A symbol with an attribute (@objc, @nonobjc, dynamic, distributed).
57    Attributed(AttributedSymbol<'ctx>),
58    /// A default argument initializer.
59    DefaultArgument(DefaultArgument<'ctx>),
60    /// An outlined operation (compiler-generated helpers).
61    Outlined(OutlinedSymbol<'ctx>),
62    /// An async/coroutine symbol.
63    Async(AsyncSymbol<'ctx>),
64    /// A macro or macro expansion.
65    Macro(MacroSymbol<'ctx>),
66    /// An automatic differentiation symbol.
67    AutoDiff(AutoDiff<'ctx>),
68    /// A bare identifier (name reference without full symbol info).
69    Identifier(Node<'ctx>),
70    /// A symbol with an unmangled suffix.
71    Suffixed(SuffixedSymbol<'ctx>),
72    /// Fallback for unhandled symbol kinds.
73    Other(Node<'ctx>),
74}
75
76/// A symbol with an attribute modifier (@objc, @nonobjc, dynamic, distributed).
77pub struct AttributedSymbol<'ctx> {
78    /// The attribute kind.
79    pub attribute: SymbolAttribute,
80    /// The inner symbol.
81    pub inner: Box<Symbol<'ctx>>,
82    /// The raw attribute node.
83    raw: Node<'ctx>,
84}
85
86impl<'ctx> AttributedSymbol<'ctx> {
87    /// Get the raw attribute node.
88    pub fn raw(&self) -> Node<'ctx> {
89        self.raw
90    }
91}
92
93impl std::fmt::Debug for AttributedSymbol<'_> {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        f.debug_struct("AttributedSymbol")
96            .field("attribute", &self.attribute)
97            .field("inner", &self.inner)
98            .finish()
99    }
100}
101
102/// Symbol attributes that can be applied to declarations.
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum SymbolAttribute {
105    /// `@objc` attribute - exposed to Objective-C.
106    ObjC,
107    /// `@nonobjc` attribute - not exposed to Objective-C.
108    NonObjC,
109    /// `dynamic` attribute - uses dynamic dispatch.
110    Dynamic,
111    /// `distributed` attribute - for distributed actors.
112    Distributed,
113}
114
115impl SymbolAttribute {
116    /// Get a human-readable name for this attribute.
117    pub fn name(&self) -> &'static str {
118        match self {
119            SymbolAttribute::ObjC => "@objc",
120            SymbolAttribute::NonObjC => "@nonobjc",
121            SymbolAttribute::Dynamic => "dynamic",
122            SymbolAttribute::Distributed => "distributed",
123        }
124    }
125}
126
127/// A default argument initializer.
128pub struct DefaultArgument<'ctx> {
129    raw: Node<'ctx>,
130}
131
132impl<'ctx> DefaultArgument<'ctx> {
133    /// Create a new DefaultArgument.
134    pub fn new(raw: Node<'ctx>) -> Self {
135        Self { raw }
136    }
137
138    /// Get the raw node.
139    pub fn raw(&self) -> Node<'ctx> {
140        self.raw
141    }
142
143    /// Get the argument index (0-based).
144    pub fn index(&self) -> Option<u64> {
145        for child in self.raw.children() {
146            if child.kind() == NodeKind::Number {
147                return child.index();
148            }
149        }
150        None
151    }
152
153    /// Get the function this default argument belongs to.
154    pub fn function(&self) -> Option<Function<'ctx>> {
155        for child in self.raw.children() {
156            if child.kind() == NodeKind::Function {
157                return Some(Function::new(child));
158            }
159        }
160        None
161    }
162
163    /// Get the module containing this default argument.
164    pub fn module(&self) -> Option<&'ctx str> {
165        self.function().and_then(|f| f.module())
166    }
167}
168
169impl std::fmt::Debug for DefaultArgument<'_> {
170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171        f.debug_struct("DefaultArgument")
172            .field("index", &self.index())
173            .field("function", &self.function())
174            .field("module", &self.module())
175            .finish()
176    }
177}
178
179/// A specialized symbol containing both the specialization metadata and the inner symbol.
180#[derive(Debug)]
181pub struct SpecializedSymbol<'ctx> {
182    /// The specialization metadata.
183    pub specialization: Specialization<'ctx>,
184    /// The inner symbol being specialized.
185    pub inner: Box<Symbol<'ctx>>,
186}
187
188/// An outlined symbol containing both the outlined operation metadata and the context symbol.
189#[derive(Debug)]
190pub struct OutlinedSymbol<'ctx> {
191    /// The outlined operation metadata.
192    pub outlined: Outlined<'ctx>,
193    /// The context symbol (usually a function) that this outlined operation is part of.
194    pub context: Box<Symbol<'ctx>>,
195}
196
197/// A symbol with an unmangled suffix.
198#[derive(Debug)]
199pub struct SuffixedSymbol<'ctx> {
200    /// The unmangled suffix text.
201    pub suffix: &'ctx str,
202    /// The inner symbol.
203    pub inner: Box<Symbol<'ctx>>,
204}
205
206impl<'ctx> Symbol<'ctx> {
207    /// Parse a mangled Swift symbol.
208    ///
209    /// Returns `None` if the symbol cannot be parsed.
210    pub fn parse(ctx: &'ctx Context, mangled: &str) -> Option<Self> {
211        let root = Node::parse(ctx, mangled)?;
212        Self::from_node(root)
213    }
214
215    /// Create a Symbol from a parsed root node.
216    ///
217    /// The node should be a `Global` node (the root of a demangled symbol tree).
218    pub fn from_node(root: Node<'ctx>) -> Option<Self> {
219        if root.kind() != NodeKind::Global {
220            return None;
221        }
222
223        let first_child = root.child(0)?;
224
225        // Check if this is a specialization - they have the specialization metadata
226        // as the first child and the inner symbol as subsequent children.
227        // For nested specializations (spec of spec of func), all are siblings in Global.
228        if Self::is_specialization_kind(first_child.kind()) {
229            // Find the innermost non-specialization symbol and build the chain
230            return Some(Self::build_specialization_chain(root, 0));
231        }
232
233        // Check if this is an outlined operation - they have the outlined node
234        // as the first child and the context (usually a function) as the second child
235        // Pattern: [Outlined, (Attribute)*, Function/Type]
236        if Self::is_outlined_kind(first_child.kind()) {
237            // Find the context node, skipping over any attributes and suffixes
238            let mut context_idx = 1;
239            let mut attr_nodes = Vec::new();
240            while let Some(node) = root.child(context_idx) {
241                if Self::get_attribute_kind(node.kind()).is_some() {
242                    attr_nodes.push(node);
243                    context_idx += 1;
244                } else if node.kind() == NodeKind::Suffix {
245                    // Skip suffix nodes - they're just metadata, not context
246                    context_idx += 1;
247                } else {
248                    break;
249                }
250            }
251
252            if let Some(context_node) = root.child(context_idx) {
253                // Sibling pattern: [Outlined, (Attr)*, Function/Type]
254                let mut context = Self::classify(context_node);
255                // Wrap with attributes if present (in reverse order so outermost is first)
256                for attr_node in attr_nodes.into_iter().rev() {
257                    if let Some(attr) = Self::get_attribute_kind(attr_node.kind()) {
258                        context = Symbol::Attributed(AttributedSymbol {
259                            attribute: attr,
260                            inner: Box::new(context),
261                            raw: attr_node,
262                        });
263                    }
264                }
265                return Some(Symbol::Outlined(OutlinedSymbol {
266                    outlined: Outlined::new(first_child),
267                    context: Box::new(context),
268                }));
269            }
270            // Child pattern: Outlined has Type as child (no sibling)
271            // Try to extract a type from the outlined node's children
272            if let Some(type_ref) = first_child.extract_type_ref() {
273                return Some(Symbol::Outlined(OutlinedSymbol {
274                    outlined: Outlined::new(first_child),
275                    context: Box::new(Symbol::Type(type_ref)),
276                }));
277            }
278            // No context found at all
279            return Some(Symbol::Outlined(OutlinedSymbol {
280                outlined: Outlined::new(first_child),
281                context: Box::new(Symbol::Other(first_child)),
282            }));
283        }
284
285        // Check for sibling modifier patterns where the first child is an attribute
286        // and the second child is the actual symbol (e.g., @objc, @nonobjc, dynamic)
287        if let Some(attribute) = Self::get_attribute_kind(first_child.kind())
288            && let Some(inner_node) = root.child(1)
289        {
290            let inner = Self::classify(inner_node);
291            return Some(Symbol::Attributed(AttributedSymbol {
292                attribute,
293                inner: Box::new(inner),
294                raw: first_child,
295            }));
296        }
297
298        // Check for marker kinds (async/coro, metadata, thunk markers) that have the actual symbol as a sibling
299        // These can be chained: [CoroFunctionPointer, DefaultOverride, Accessor]
300        if Self::is_async_marker_kind(first_child.kind())
301            || Self::is_metadata_marker_kind(first_child.kind())
302            || Self::is_thunk_marker_kind(first_child.kind())
303        {
304            return Some(Self::build_marker_chain(root, 0));
305        }
306
307        // Check for Suffix sibling and wrap the symbol if present
308        let suffix = root.child_of_kind(NodeKind::Suffix).and_then(|c| c.text());
309
310        let inner = Self::classify(first_child);
311
312        if let Some(suffix) = suffix {
313            Some(Symbol::Suffixed(SuffixedSymbol {
314                suffix,
315                inner: Box::new(inner),
316            }))
317        } else {
318            Some(inner)
319        }
320    }
321
322    fn is_specialization_kind(kind: NodeKind) -> bool {
323        matches!(
324            kind,
325            NodeKind::GenericSpecialization
326                | NodeKind::GenericSpecializationNotReAbstracted
327                | NodeKind::GenericSpecializationInResilienceDomain
328                | NodeKind::GenericSpecializationPrespecialized
329                | NodeKind::GenericPartialSpecialization
330                | NodeKind::GenericPartialSpecializationNotReAbstracted
331                | NodeKind::FunctionSignatureSpecialization
332        )
333    }
334
335    /// Build a chain of nested specializations.
336    ///
337    /// For nested specializations like `spec of spec of func`, all nodes are
338    /// siblings in Global: [Spec1, Spec2, Func]. This function recursively
339    /// builds the nested SpecializedSymbol structure.
340    fn build_specialization_chain(root: Node<'ctx>, index: usize) -> Symbol<'ctx> {
341        let Some(current) = root.child(index) else {
342            // No more children - shouldn't happen for well-formed symbols
343            return Symbol::Other(root);
344        };
345
346        if Self::is_specialization_kind(current.kind()) {
347            // This is a specialization - its inner is the next sibling
348            let inner = if let Some(next) = root.child(index + 1) {
349                if Self::is_specialization_kind(next.kind()) {
350                    // Next sibling is also a specialization - recurse
351                    Self::build_specialization_chain(root, index + 1)
352                } else {
353                    // Next sibling is the actual function/symbol
354                    Self::classify(next)
355                }
356            } else {
357                // No next sibling - use current as fallback
358                Symbol::Other(current)
359            };
360
361            Symbol::Specialization(SpecializedSymbol {
362                specialization: Specialization::new(current),
363                inner: Box::new(inner),
364            })
365        } else {
366            // Not a specialization - just classify it
367            Self::classify(current)
368        }
369    }
370
371    /// Build a chain of nested marker symbols (async/coro pointers, default overrides, thunk markers).
372    ///
373    /// For chains like `[CoroFunctionPointer, DefaultOverride, Accessor]`, this builds
374    /// nested wrapper symbols.
375    fn build_marker_chain(root: Node<'ctx>, index: usize) -> Symbol<'ctx> {
376        let Some(current) = root.child(index) else {
377            return Symbol::Other(root);
378        };
379
380        // Get the inner symbol (either another marker or the actual symbol)
381        let inner = if let Some(next) = root.child(index + 1) {
382            if Self::is_async_marker_kind(next.kind())
383                || Self::is_metadata_marker_kind(next.kind())
384                || Self::is_thunk_marker_kind(next.kind())
385            {
386                // Next sibling is also a marker - recurse
387                Self::build_marker_chain(root, index + 1)
388            } else {
389                // Next sibling is the actual symbol
390                Self::classify(next)
391            }
392        } else {
393            // No next sibling - use current as fallback
394            return Symbol::Other(current);
395        };
396
397        // Wrap the inner symbol with the appropriate marker
398        if Self::is_async_marker_kind(current.kind()) {
399            Symbol::Async(AsyncSymbol::with_inner(current, inner))
400        } else if Self::is_metadata_marker_kind(current.kind()) {
401            Symbol::Metadata(Metadata::with_inner(current, inner))
402        } else if Self::is_thunk_marker_kind(current.kind()) {
403            Symbol::Thunk(Thunk::new_marker(current, inner))
404        } else {
405            // Shouldn't happen, but fall back to the inner symbol
406            inner
407        }
408    }
409
410    fn is_outlined_kind(kind: NodeKind) -> bool {
411        matches!(
412            kind,
413            NodeKind::OutlinedBridgedMethod
414                | NodeKind::OutlinedVariable
415                | NodeKind::OutlinedCopy
416                | NodeKind::OutlinedConsume
417                | NodeKind::OutlinedRetain
418                | NodeKind::OutlinedRelease
419                | NodeKind::OutlinedInitializeWithTake
420                | NodeKind::OutlinedInitializeWithCopy
421                | NodeKind::OutlinedAssignWithTake
422                | NodeKind::OutlinedAssignWithCopy
423                | NodeKind::OutlinedDestroy
424                | NodeKind::OutlinedInitializeWithCopyNoValueWitness
425                | NodeKind::OutlinedInitializeWithTakeNoValueWitness
426                | NodeKind::OutlinedAssignWithCopyNoValueWitness
427                | NodeKind::OutlinedAssignWithTakeNoValueWitness
428                | NodeKind::OutlinedDestroyNoValueWitness
429                | NodeKind::OutlinedReadOnlyObject
430        )
431    }
432
433    fn get_attribute_kind(kind: NodeKind) -> Option<SymbolAttribute> {
434        match kind {
435            NodeKind::ObjCAttribute => Some(SymbolAttribute::ObjC),
436            NodeKind::NonObjCAttribute => Some(SymbolAttribute::NonObjC),
437            NodeKind::DynamicAttribute => Some(SymbolAttribute::Dynamic),
438            NodeKind::DistributedAccessor => Some(SymbolAttribute::Distributed),
439            _ => None,
440        }
441    }
442
443    fn is_async_marker_kind(kind: NodeKind) -> bool {
444        matches!(
445            kind,
446            NodeKind::CoroFunctionPointer
447                | NodeKind::AsyncFunctionPointer
448                | NodeKind::AsyncAwaitResumePartialFunction
449                | NodeKind::AsyncSuspendResumePartialFunction
450        )
451    }
452
453    fn is_metadata_marker_kind(kind: NodeKind) -> bool {
454        matches!(kind, NodeKind::DefaultOverride | NodeKind::HasSymbolQuery)
455    }
456
457    fn is_thunk_marker_kind(kind: NodeKind) -> bool {
458        matches!(
459            kind,
460            NodeKind::InlinedGenericFunction
461                | NodeKind::MergedFunction
462                | NodeKind::DistributedThunk
463        )
464    }
465
466    fn classify(node: Node<'ctx>) -> Self {
467        Self::classify_with_static(node, false)
468    }
469
470    /// Classify a node into a Symbol.
471    ///
472    /// This is used internally and by Thunk::inner() to classify wrapped symbols.
473    pub fn classify_node(node: Node<'ctx>) -> Self {
474        Self::classify_with_static(node, false)
475    }
476
477    fn classify_with_static(node: Node<'ctx>, is_static: bool) -> Self {
478        match node.kind() {
479            // Static wrapper - recurse with static flag
480            NodeKind::Static => {
481                if let Some(inner) = node.child(0) {
482                    return Self::classify_with_static(inner, true);
483                }
484                Symbol::Other(node)
485            }
486
487            // Functions
488            NodeKind::Function => {
489                if is_static {
490                    Symbol::Function(Function::new_static(node))
491                } else {
492                    Symbol::Function(Function::new(node))
493                }
494            }
495
496            // Constructors
497            NodeKind::Constructor | NodeKind::Allocator => {
498                Symbol::Constructor(Constructor::new(node))
499            }
500
501            // Destructors
502            NodeKind::Destructor | NodeKind::Deallocator | NodeKind::IsolatedDeallocator => {
503                Symbol::Destructor(Destructor::new(node))
504            }
505
506            // Enum cases
507            NodeKind::EnumCase => Symbol::EnumCase(EnumCase::new(node)),
508
509            // Accessors
510            NodeKind::Getter
511            | NodeKind::Setter
512            | NodeKind::ModifyAccessor
513            | NodeKind::Modify2Accessor
514            | NodeKind::ReadAccessor
515            | NodeKind::Read2Accessor
516            | NodeKind::WillSet
517            | NodeKind::DidSet
518            | NodeKind::GlobalGetter
519            | NodeKind::MaterializeForSet
520            | NodeKind::InitAccessor
521            | NodeKind::UnsafeAddressor
522            | NodeKind::UnsafeMutableAddressor
523            | NodeKind::OwningAddressor
524            | NodeKind::OwningMutableAddressor
525            | NodeKind::NativeOwningAddressor
526            | NodeKind::NativeOwningMutableAddressor
527            | NodeKind::NativePinningAddressor
528            | NodeKind::NativePinningMutableAddressor => {
529                if is_static {
530                    Symbol::Accessor(Accessor::new_static(node))
531                } else {
532                    Symbol::Accessor(Accessor::new(node))
533                }
534            }
535
536            // Variables
537            NodeKind::Variable => Symbol::Variable(Variable::new(node)),
538
539            // Closures
540            NodeKind::ExplicitClosure | NodeKind::ImplicitClosure => {
541                Symbol::Closure(Closure::new(node))
542            }
543
544            // Thunks and optimization-related wrappers
545            NodeKind::DispatchThunk
546            | NodeKind::VTableThunk
547            | NodeKind::DistributedThunk
548            | NodeKind::ReabstractionThunk
549            | NodeKind::ReabstractionThunkHelper
550            | NodeKind::ReabstractionThunkHelperWithSelf
551            | NodeKind::ReabstractionThunkHelperWithGlobalActor
552            | NodeKind::AutoDiffSelfReorderingReabstractionThunk
553            | NodeKind::PartialApplyForwarder
554            | NodeKind::PartialApplyObjCForwarder
555            | NodeKind::CurryThunk
556            | NodeKind::ProtocolWitness
557            | NodeKind::ProtocolSelfConformanceWitness
558            | NodeKind::KeyPathGetterThunkHelper
559            | NodeKind::KeyPathSetterThunkHelper
560            | NodeKind::KeyPathUnappliedMethodThunkHelper
561            | NodeKind::KeyPathAppliedMethodThunkHelper
562            | NodeKind::KeyPathEqualsThunkHelper
563            | NodeKind::KeyPathHashThunkHelper
564            | NodeKind::AutoDiffSubsetParametersThunk
565            | NodeKind::AutoDiffDerivativeVTableThunk
566            | NodeKind::BackDeploymentThunk
567            | NodeKind::BackDeploymentFallback
568            | NodeKind::MergedFunction
569            | NodeKind::InlinedGenericFunction => Symbol::Thunk(Thunk::new(node)),
570
571            // Witness tables and value witnesses
572            NodeKind::ProtocolWitnessTable
573            | NodeKind::ProtocolWitnessTableAccessor
574            | NodeKind::ProtocolWitnessTablePattern
575            | NodeKind::GenericProtocolWitnessTable
576            | NodeKind::GenericProtocolWitnessTableInstantiationFunction
577            | NodeKind::ResilientProtocolWitnessTable
578            | NodeKind::LazyProtocolWitnessTableAccessor
579            | NodeKind::LazyProtocolWitnessTableCacheVariable
580            | NodeKind::ProtocolSelfConformanceWitnessTable
581            | NodeKind::ValueWitnessTable
582            | NodeKind::ValueWitness
583            | NodeKind::AssociatedTypeWitnessTableAccessor
584            | NodeKind::BaseWitnessTableAccessor
585            | NodeKind::ConcreteProtocolConformance => {
586                Symbol::WitnessTable(WitnessTable::new(node))
587            }
588
589            // Metadata descriptors
590            NodeKind::ProtocolConformanceDescriptor
591            | NodeKind::ProtocolConformanceDescriptorRecord
592            | NodeKind::OpaqueTypeDescriptor
593            | NodeKind::OpaqueTypeDescriptorRecord
594            | NodeKind::OpaqueTypeDescriptorAccessor
595            | NodeKind::OpaqueTypeDescriptorAccessorImpl
596            | NodeKind::OpaqueTypeDescriptorAccessorKey
597            | NodeKind::OpaqueTypeDescriptorAccessorVar
598            | NodeKind::NominalTypeDescriptor
599            | NodeKind::NominalTypeDescriptorRecord
600            | NodeKind::PropertyDescriptor
601            | NodeKind::ProtocolDescriptor
602            | NodeKind::ProtocolDescriptorRecord
603            | NodeKind::ProtocolRequirementsBaseDescriptor
604            | NodeKind::MethodDescriptor
605            | NodeKind::AssociatedTypeDescriptor
606            | NodeKind::AssociatedConformanceDescriptor
607            | NodeKind::DefaultAssociatedConformanceAccessor
608            | NodeKind::BaseConformanceDescriptor
609            | NodeKind::ExtensionDescriptor
610            | NodeKind::AnonymousDescriptor
611            | NodeKind::ModuleDescriptor
612            | NodeKind::ReflectionMetadataAssocTypeDescriptor
613            | NodeKind::AccessibleFunctionRecord => Symbol::Descriptor(Descriptor::new(node)),
614
615            // Type metadata
616            NodeKind::TypeMetadata
617            | NodeKind::FullTypeMetadata
618            | NodeKind::TypeMetadataAccessFunction
619            | NodeKind::TypeMetadataCompletionFunction
620            | NodeKind::TypeMetadataInstantiationFunction
621            | NodeKind::TypeMetadataInstantiationCache
622            | NodeKind::TypeMetadataLazyCache
623            | NodeKind::TypeMetadataSingletonInitializationCache
624            | NodeKind::TypeMetadataDemanglingCache
625            | NodeKind::GenericTypeMetadataPattern
626            | NodeKind::MetadataInstantiationCache
627            | NodeKind::NoncanonicalSpecializedGenericTypeMetadata
628            | NodeKind::NoncanonicalSpecializedGenericTypeMetadataCache
629            | NodeKind::CanonicalSpecializedGenericTypeMetadataAccessFunction
630            | NodeKind::AssociatedTypeMetadataAccessor
631            | NodeKind::DefaultAssociatedTypeMetadataAccessor
632            | NodeKind::ClassMetadataBaseOffset
633            | NodeKind::ObjCMetadataUpdateFunction
634            | NodeKind::FieldOffset
635            | NodeKind::Metaclass
636            | NodeKind::IVarInitializer
637            | NodeKind::IVarDestroyer
638            | NodeKind::HasSymbolQuery
639            | NodeKind::DefaultOverride
640            | NodeKind::PropertyWrapperBackingInitializer
641            | NodeKind::MethodLookupFunction => Symbol::Metadata(Metadata::new(node)),
642
643            // Default argument initializers
644            NodeKind::DefaultArgumentInitializer => {
645                Symbol::DefaultArgument(DefaultArgument::new(node))
646            }
647
648            // Type symbols
649            NodeKind::TypeMangling => {
650                if let Some(type_node) = node.child(0) {
651                    Symbol::Type(TypeRef::new(type_node))
652                } else {
653                    Symbol::Other(node)
654                }
655            }
656
657            // Named types as symbols
658            NodeKind::Class
659            | NodeKind::Structure
660            | NodeKind::Enum
661            | NodeKind::Protocol
662            | NodeKind::TypeAlias
663            | NodeKind::OtherNominalType
664            | NodeKind::BuiltinTypeName
665            | NodeKind::BoundGenericStructure
666            | NodeKind::BoundGenericClass
667            | NodeKind::BoundGenericEnum
668            | NodeKind::Tuple
669            | NodeKind::BuiltinFixedArray => Symbol::Type(TypeRef::new(node)),
670
671            // Subscript is an accessor
672            NodeKind::Subscript => {
673                if is_static {
674                    Symbol::Accessor(Accessor::new_static(node))
675                } else {
676                    Symbol::Accessor(Accessor::new(node))
677                }
678            }
679
680            // Note: Outlined operations are handled in from_node() as sibling patterns,
681            // not here in classify_with_static()
682
683            // Async and coroutine symbols
684            NodeKind::AsyncAwaitResumePartialFunction
685            | NodeKind::AsyncSuspendResumePartialFunction
686            | NodeKind::AsyncFunctionPointer
687            | NodeKind::CoroFunctionPointer
688            | NodeKind::CoroutineContinuationPrototype => Symbol::Async(AsyncSymbol::new(node)),
689
690            // Macro symbols
691            NodeKind::Macro
692            | NodeKind::FreestandingMacroExpansion
693            | NodeKind::MacroExpansionUniqueName
694            | NodeKind::AccessorAttachedMacroExpansion
695            | NodeKind::BodyAttachedMacroExpansion
696            | NodeKind::ConformanceAttachedMacroExpansion
697            | NodeKind::ExtensionAttachedMacroExpansion
698            | NodeKind::MemberAttachedMacroExpansion
699            | NodeKind::MemberAttributeAttachedMacroExpansion
700            | NodeKind::PeerAttachedMacroExpansion
701            | NodeKind::PreambleAttachedMacroExpansion => Symbol::Macro(MacroSymbol::new(node)),
702
703            // Automatic differentiation symbols
704            NodeKind::AutoDiffFunction | NodeKind::DifferentiabilityWitness => {
705                Symbol::AutoDiff(AutoDiff::new(node))
706            }
707
708            // Bare identifier (e.g., function name reference in specializations)
709            NodeKind::Identifier => Symbol::Identifier(node),
710
711            // Fallback
712            _ => Symbol::Other(node),
713        }
714    }
715
716    /// Get the raw node for this symbol.
717    pub fn raw(&self) -> Node<'ctx> {
718        match self {
719            Symbol::Function(f) => f.raw(),
720            Symbol::Constructor(c) => c.raw(),
721            Symbol::Destructor(d) => d.raw(),
722            Symbol::EnumCase(e) => e.raw(),
723            Symbol::Accessor(a) => a.raw(),
724            Symbol::Variable(v) => v.raw(),
725            Symbol::Closure(c) => c.raw(),
726            Symbol::Thunk(t) => t.raw(),
727            Symbol::Specialization(s) => s.specialization.raw(),
728            Symbol::WitnessTable(w) => w.raw(),
729            Symbol::Descriptor(d) => d.raw(),
730            Symbol::Metadata(m) => m.raw(),
731            Symbol::Type(t) => t.raw(),
732            Symbol::Attributed(a) => a.raw,
733            Symbol::DefaultArgument(d) => d.raw(),
734            Symbol::Outlined(o) => o.outlined.raw(),
735            Symbol::Async(a) => a.raw(),
736            Symbol::Macro(m) => m.raw(),
737            Symbol::AutoDiff(a) => a.raw(),
738            Symbol::Identifier(n) => *n,
739            Symbol::Suffixed(s) => s.inner.raw(),
740            Symbol::Other(n) => *n,
741        }
742    }
743
744    /// Get the display string for this symbol.
745    pub fn display(&self) -> String {
746        self.raw().to_string()
747    }
748
749    /// Check if this is a function symbol.
750    pub fn is_function(&self) -> bool {
751        matches!(self, Symbol::Function(_))
752    }
753
754    /// Check if this is a constructor symbol.
755    pub fn is_constructor(&self) -> bool {
756        matches!(self, Symbol::Constructor(_))
757    }
758
759    /// Check if this is a destructor symbol.
760    pub fn is_destructor(&self) -> bool {
761        matches!(self, Symbol::Destructor(_))
762    }
763
764    /// Check if this is an enum case symbol.
765    pub fn is_enum_case(&self) -> bool {
766        matches!(self, Symbol::EnumCase(_))
767    }
768
769    /// Check if this is an accessor symbol.
770    pub fn is_accessor(&self) -> bool {
771        matches!(self, Symbol::Accessor(_))
772    }
773
774    /// Check if this is a variable symbol.
775    pub fn is_variable(&self) -> bool {
776        matches!(self, Symbol::Variable(_))
777    }
778
779    /// Check if this is a closure symbol.
780    pub fn is_closure(&self) -> bool {
781        matches!(self, Symbol::Closure(_))
782    }
783
784    /// Check if this is a type symbol.
785    pub fn is_type(&self) -> bool {
786        matches!(self, Symbol::Type(_))
787    }
788
789    /// Check if this is a thunk symbol.
790    pub fn is_thunk(&self) -> bool {
791        matches!(self, Symbol::Thunk(_))
792    }
793
794    /// Check if this is a specialization symbol.
795    pub fn is_specialization(&self) -> bool {
796        matches!(self, Symbol::Specialization(_))
797    }
798
799    /// Check if this is a witness table symbol.
800    pub fn is_witness_table(&self) -> bool {
801        matches!(self, Symbol::WitnessTable(_))
802    }
803
804    /// Check if this is a descriptor symbol.
805    pub fn is_descriptor(&self) -> bool {
806        matches!(self, Symbol::Descriptor(_))
807    }
808
809    /// Check if this is a metadata symbol.
810    pub fn is_metadata(&self) -> bool {
811        matches!(self, Symbol::Metadata(_))
812    }
813
814    /// Check if this is an attributed symbol.
815    pub fn is_attributed(&self) -> bool {
816        matches!(self, Symbol::Attributed(_))
817    }
818
819    /// Check if this is a default argument symbol.
820    pub fn is_default_argument(&self) -> bool {
821        matches!(self, Symbol::DefaultArgument(_))
822    }
823
824    /// Try to get this as a function.
825    pub fn as_function(&self) -> Option<&Function<'ctx>> {
826        match self {
827            Symbol::Function(f) => Some(f),
828            _ => None,
829        }
830    }
831
832    /// Try to get this as a constructor.
833    pub fn as_constructor(&self) -> Option<&Constructor<'ctx>> {
834        match self {
835            Symbol::Constructor(c) => Some(c),
836            _ => None,
837        }
838    }
839
840    /// Try to get this as a destructor.
841    pub fn as_destructor(&self) -> Option<&Destructor<'ctx>> {
842        match self {
843            Symbol::Destructor(d) => Some(d),
844            _ => None,
845        }
846    }
847
848    /// Try to get this as an enum case.
849    pub fn as_enum_case(&self) -> Option<&EnumCase<'ctx>> {
850        match self {
851            Symbol::EnumCase(e) => Some(e),
852            _ => None,
853        }
854    }
855
856    /// Try to get this as an accessor.
857    pub fn as_accessor(&self) -> Option<&Accessor<'ctx>> {
858        match self {
859            Symbol::Accessor(a) => Some(a),
860            _ => None,
861        }
862    }
863
864    /// Try to get this as a variable.
865    pub fn as_variable(&self) -> Option<&Variable<'ctx>> {
866        match self {
867            Symbol::Variable(v) => Some(v),
868            _ => None,
869        }
870    }
871
872    /// Try to get this as a closure.
873    pub fn as_closure(&self) -> Option<&Closure<'ctx>> {
874        match self {
875            Symbol::Closure(c) => Some(c),
876            _ => None,
877        }
878    }
879
880    /// Try to get this as a type.
881    pub fn as_type(&self) -> Option<&TypeRef<'ctx>> {
882        match self {
883            Symbol::Type(t) => Some(t),
884            _ => None,
885        }
886    }
887
888    /// Try to get this as a thunk.
889    pub fn as_thunk(&self) -> Option<&Thunk<'ctx>> {
890        match self {
891            Symbol::Thunk(t) => Some(t),
892            _ => None,
893        }
894    }
895
896    /// Try to get this as a specialization.
897    pub fn as_specialization(&self) -> Option<&SpecializedSymbol<'ctx>> {
898        match self {
899            Symbol::Specialization(s) => Some(s),
900            _ => None,
901        }
902    }
903
904    /// Try to get this as a witness table.
905    pub fn as_witness_table(&self) -> Option<&WitnessTable<'ctx>> {
906        match self {
907            Symbol::WitnessTable(w) => Some(w),
908            _ => None,
909        }
910    }
911
912    /// Try to get this as a descriptor.
913    pub fn as_descriptor(&self) -> Option<&Descriptor<'ctx>> {
914        match self {
915            Symbol::Descriptor(d) => Some(d),
916            _ => None,
917        }
918    }
919
920    /// Try to get this as metadata.
921    pub fn as_metadata(&self) -> Option<&Metadata<'ctx>> {
922        match self {
923            Symbol::Metadata(m) => Some(m),
924            _ => None,
925        }
926    }
927
928    /// Try to get this as an attributed symbol.
929    pub fn as_attributed(&self) -> Option<&AttributedSymbol<'ctx>> {
930        match self {
931            Symbol::Attributed(a) => Some(a),
932            _ => None,
933        }
934    }
935
936    /// Try to get this as a default argument.
937    pub fn as_default_argument(&self) -> Option<&DefaultArgument<'ctx>> {
938        match self {
939            Symbol::DefaultArgument(d) => Some(d),
940            _ => None,
941        }
942    }
943
944    /// Check if this is an outlined operation symbol.
945    pub fn is_outlined(&self) -> bool {
946        matches!(self, Symbol::Outlined(_))
947    }
948
949    /// Check if this is an async symbol.
950    pub fn is_async(&self) -> bool {
951        matches!(self, Symbol::Async(_))
952    }
953
954    /// Check if this is a macro symbol.
955    pub fn is_macro(&self) -> bool {
956        matches!(self, Symbol::Macro(_))
957    }
958
959    /// Check if this is an auto-diff symbol.
960    pub fn is_autodiff(&self) -> bool {
961        matches!(self, Symbol::AutoDiff(_))
962    }
963
964    /// Try to get this as an outlined operation.
965    pub fn as_outlined(&self) -> Option<&OutlinedSymbol<'ctx>> {
966        match self {
967            Symbol::Outlined(o) => Some(o),
968            _ => None,
969        }
970    }
971
972    /// Try to get this as an async symbol.
973    pub fn as_async(&self) -> Option<&AsyncSymbol<'ctx>> {
974        match self {
975            Symbol::Async(a) => Some(a),
976            _ => None,
977        }
978    }
979
980    /// Try to get this as a macro.
981    pub fn as_macro(&self) -> Option<&MacroSymbol<'ctx>> {
982        match self {
983            Symbol::Macro(m) => Some(m),
984            _ => None,
985        }
986    }
987
988    /// Try to get this as an auto-diff symbol.
989    pub fn as_autodiff(&self) -> Option<&AutoDiff<'ctx>> {
990        match self {
991            Symbol::AutoDiff(a) => Some(a),
992            _ => None,
993        }
994    }
995}
996
997impl std::fmt::Display for Symbol<'_> {
998    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
999        write!(f, "{}", self.display())
1000    }
1001}
1002
1003/// A global variable symbol.
1004#[derive(Clone, Copy)]
1005pub struct Variable<'ctx> {
1006    raw: Node<'ctx>,
1007}
1008
1009impl<'ctx> Variable<'ctx> {
1010    /// Create a Variable from a raw node.
1011    pub fn new(raw: Node<'ctx>) -> Self {
1012        Self { raw }
1013    }
1014
1015    /// Get the underlying raw node.
1016    pub fn raw(&self) -> Node<'ctx> {
1017        self.raw
1018    }
1019
1020    /// Get the name of this variable.
1021    pub fn name(&self) -> Option<&'ctx str> {
1022        for child in self.raw.children() {
1023            if child.kind() == NodeKind::Identifier {
1024                return child.text();
1025            }
1026        }
1027        None
1028    }
1029
1030    /// Get the module containing this variable.
1031    pub fn module(&self) -> Option<&'ctx str> {
1032        for child in self.raw.children() {
1033            if child.kind() == NodeKind::Module {
1034                return child.text();
1035            }
1036        }
1037        // Check nested context
1038        for child in self.raw.children() {
1039            match child.kind() {
1040                NodeKind::Class | NodeKind::Structure | NodeKind::Enum | NodeKind::Extension => {
1041                    for inner in child.descendants() {
1042                        if inner.kind() == NodeKind::Module {
1043                            return inner.text();
1044                        }
1045                    }
1046                }
1047                _ => {}
1048            }
1049        }
1050        None
1051    }
1052
1053    /// Get the type of this variable.
1054    pub fn variable_type(&self) -> Option<TypeRef<'ctx>> {
1055        self.raw.extract_type_ref()
1056    }
1057
1058    /// Get the context where this variable is defined.
1059    pub fn context(&self) -> crate::context::SymbolContext<'ctx> {
1060        crate::context::extract_context(self.raw)
1061    }
1062}
1063
1064impl std::fmt::Debug for Variable<'_> {
1065    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1066        f.debug_struct("Variable")
1067            .field("name", &self.name())
1068            .field("module", &self.module())
1069            .field("type", &self.variable_type())
1070            .finish()
1071    }
1072}
1073
1074#[cfg(test)]
1075mod tests {
1076    use super::*;
1077
1078    #[test]
1079    fn test_parse_function() {
1080        let ctx = Context::new();
1081        let symbol = Symbol::parse(&ctx, "$s4main5helloSSyYaKF").unwrap();
1082        assert!(symbol.is_function());
1083    }
1084
1085    #[test]
1086    fn test_parse_variable() {
1087        let ctx = Context::new();
1088        // foo.bar : Swift.Int
1089        let symbol = Symbol::parse(&ctx, "_Tv3foo3barSi").unwrap();
1090        assert!(symbol.is_variable());
1091        if let Symbol::Variable(var) = symbol {
1092            assert_eq!(var.name(), Some("bar"));
1093            assert_eq!(var.module(), Some("foo"));
1094        }
1095    }
1096
1097    #[test]
1098    fn test_parse_getter() {
1099        let ctx = Context::new();
1100        // foo.bar.getter : Swift.Int
1101        let symbol = Symbol::parse(&ctx, "_TF3foog3barSi").unwrap();
1102        assert!(symbol.is_accessor());
1103    }
1104
1105    #[test]
1106    fn test_parse_setter() {
1107        let ctx = Context::new();
1108        // foo.bar.setter : Swift.Int
1109        let symbol = Symbol::parse(&ctx, "_TF3foos3barSi").unwrap();
1110        assert!(symbol.is_accessor());
1111    }
1112
1113    #[test]
1114    fn test_symbol_display() {
1115        let ctx = Context::new();
1116        let symbol = Symbol::parse(&ctx, "$s4main5helloSSyYaKF").unwrap();
1117        let display = symbol.display();
1118        assert!(display.contains("hello"));
1119    }
1120
1121    #[test]
1122    fn test_symbol_as_methods() {
1123        let ctx = Context::new();
1124        let symbol = Symbol::parse(&ctx, "$s4main5helloSSyYaKF").unwrap();
1125        assert!(symbol.as_function().is_some());
1126        assert!(symbol.as_constructor().is_none());
1127        assert!(symbol.as_accessor().is_none());
1128    }
1129}