1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use oxc_ast::{
    ast::{
        AccessorProperty, ClassBody, ClassElement, MethodDefinition, MethodDefinitionKind,
        PrivateIdentifier, PropertyDefinition,
    },
    AstKind,
};
use oxc_span::GetSpan;
use oxc_syntax::class::{ClassId, ElementKind};

use super::{
    table::{Element, PrivateIdentifierReference},
    ClassTable,
};
use crate::{AstNodeId, AstNodes};

#[derive(Debug, Default)]
pub struct ClassTableBuilder {
    pub current_class_id: Option<ClassId>,
    pub classes: ClassTable,
}

impl ClassTableBuilder {
    pub fn new() -> Self {
        Self { current_class_id: None, classes: ClassTable::default() }
    }

    pub fn build(self) -> ClassTable {
        self.classes
    }

    pub fn declare_class_body(
        &mut self,
        class: &ClassBody,
        current_node_id: AstNodeId,
        nodes: &AstNodes,
    ) {
        let parent_id = nodes.parent_id(current_node_id).unwrap_or_else(|| unreachable!());
        self.current_class_id = Some(self.classes.declare_class(self.current_class_id, parent_id));

        for element in &class.body {
            match element {
                ClassElement::PropertyDefinition(definition) => {
                    self.declare_class_property(definition);
                }
                ClassElement::MethodDefinition(definition) => {
                    self.declare_class_method(definition);
                }
                ClassElement::AccessorProperty(definition) => {
                    self.declare_class_accessor(definition);
                }
                _ => {}
            }
        }
    }

    pub fn declare_class_accessor(&mut self, property: &AccessorProperty) {
        let is_private = property.key.is_private_identifier();
        let name = property.key.name();

        if let Some(name) = name {
            if let Some(class_id) = self.current_class_id {
                self.classes.add_element(
                    class_id,
                    Element::new(
                        name,
                        property.key.span(),
                        property.r#static,
                        is_private,
                        ElementKind::Accessor,
                    ),
                );
            }
        }
    }

    pub fn declare_class_property(&mut self, property: &PropertyDefinition) {
        let is_private = property.key.is_private_identifier();
        let name = property.key.name();

        if let Some(name) = name {
            if let Some(class_id) = self.current_class_id {
                self.classes.add_element(
                    class_id,
                    Element::new(
                        name,
                        property.key.span(),
                        property.r#static,
                        is_private,
                        ElementKind::Property,
                    ),
                );
            }
        }
    }

    pub fn add_private_identifier_reference(
        &mut self,
        ident: &PrivateIdentifier,
        current_node_id: AstNodeId,
        nodes: &AstNodes,
    ) {
        let parent_kind = nodes.parent_kind(current_node_id);
        if let Some(parent_kind) = parent_kind {
            if matches!(parent_kind, AstKind::PrivateInExpression(_) | AstKind::MemberExpression(_))
            {
                if let Some(class_id) = self.current_class_id {
                    let element_ids = self.classes.get_element_ids(class_id, &ident.name);

                    let reference = PrivateIdentifierReference::new(
                        current_node_id,
                        ident.name.to_compact_str(),
                        ident.span,
                        element_ids,
                    );
                    self.classes.add_private_identifier_reference(class_id, reference);
                }
            }
        }
    }

    pub fn declare_class_method(&mut self, method: &MethodDefinition) {
        if method.kind.is_constructor() || method.value.is_typescript_syntax() {
            return;
        }
        let is_private = method.key.is_private_identifier();
        let name = if is_private {
            method.key.private_name().map(|name| name.to_compact_str())
        } else {
            method.key.static_name()
        };

        if let Some(name) = name {
            if let Some(class_id) = self.current_class_id {
                self.classes.add_element(
                    class_id,
                    Element::new(
                        name,
                        method.key.span(),
                        method.r#static,
                        is_private,
                        match method.kind {
                            MethodDefinitionKind::Method => ElementKind::Method,
                            MethodDefinitionKind::Get => ElementKind::Method | ElementKind::Getter,
                            MethodDefinitionKind::Set => ElementKind::Method | ElementKind::Setter,
                            MethodDefinitionKind::Constructor => {
                                // Skip constructor
                                unreachable!()
                            }
                        },
                    ),
                );
            }
        }
    }

    pub fn pop_class(&mut self) {
        self.current_class_id = self
            .current_class_id
            .and_then(|current_class_id| self.classes.parent_ids.get(&current_class_id).copied());
    }
}