Skip to main content

tsz_solver/
apparent.rs

1use crate::TypeDatabase;
2use crate::types::{IntrinsicKind, TypeId};
3
4pub enum ApparentMemberKind {
5    Value(TypeId),
6    Method(TypeId),
7}
8
9pub struct ApparentMember {
10    pub name: &'static str,
11    pub kind: ApparentMemberKind,
12}
13
14const STRING_METHODS_RETURN_STRING: &[&str] = &[
15    "anchor",
16    "at",
17    "big",
18    "blink",
19    "bold",
20    "charAt",
21    "concat",
22    "fixed",
23    "fontcolor",
24    "fontsize",
25    "italics",
26    "link",
27    "normalize",
28    "padEnd",
29    "padStart",
30    "repeat",
31    "replace",
32    "replaceAll",
33    "slice",
34    "small",
35    "strike",
36    "sub",
37    "substr",
38    "substring",
39    "sup",
40    "toLocaleLowerCase",
41    "toLocaleUpperCase",
42    "toLowerCase",
43    "toString",
44    "toUpperCase",
45    "trim",
46    "trimEnd",
47    "trimLeft",
48    "trimRight",
49    "trimStart",
50    "toWellFormed",
51    "valueOf",
52];
53const STRING_METHODS_RETURN_NUMBER: &[&str] = &[
54    "charCodeAt",
55    "codePointAt",
56    "indexOf",
57    "lastIndexOf",
58    "localeCompare",
59    "search",
60];
61const STRING_METHODS_RETURN_BOOLEAN: &[&str] =
62    &["endsWith", "includes", "isWellFormed", "startsWith"];
63const STRING_METHODS_RETURN_ANY: &[&str] = &["match", "matchAll"];
64const STRING_METHODS_RETURN_STRING_ARRAY: &[&str] = &["split"];
65
66const NUMBER_METHODS_RETURN_STRING: &[&str] = &[
67    "toExponential",
68    "toFixed",
69    "toLocaleString",
70    "toPrecision",
71    "toString",
72];
73
74const BOOLEAN_METHODS_RETURN_STRING: &[&str] = &["toLocaleString", "toString"];
75
76const BIGINT_METHODS_RETURN_STRING: &[&str] = &["toLocaleString", "toString"];
77
78const OBJECT_METHODS_RETURN_BOOLEAN: &[&str] =
79    &["hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable"];
80const OBJECT_METHODS_RETURN_STRING: &[&str] = &["toString"];
81const OBJECT_METHODS_RETURN_ANY: &[&str] = &["valueOf"];
82
83fn is_member(name: &str, list: &[&str]) -> bool {
84    list.contains(&name)
85}
86
87fn object_member_kind(name: &str, include_to_locale: bool) -> Option<ApparentMemberKind> {
88    if name == "constructor" {
89        return Some(ApparentMemberKind::Value(TypeId::FUNCTION));
90    }
91    if name == "toString" {
92        return Some(ApparentMemberKind::Method(TypeId::STRING));
93    }
94    if name == "valueOf" {
95        return Some(ApparentMemberKind::Method(TypeId::ANY));
96    }
97    if is_member(name, OBJECT_METHODS_RETURN_BOOLEAN) {
98        return Some(ApparentMemberKind::Method(TypeId::BOOLEAN));
99    }
100    if is_member(name, OBJECT_METHODS_RETURN_STRING) {
101        return Some(ApparentMemberKind::Method(TypeId::STRING));
102    }
103    if is_member(name, OBJECT_METHODS_RETURN_ANY) {
104        return Some(ApparentMemberKind::Method(TypeId::ANY));
105    }
106    if include_to_locale && name == "toLocaleString" {
107        return Some(ApparentMemberKind::Method(TypeId::STRING));
108    }
109    None
110}
111
112fn push_object_members(members: &mut Vec<ApparentMember>, include_to_locale: bool) {
113    members.push(ApparentMember {
114        name: "constructor",
115        kind: ApparentMemberKind::Value(TypeId::ANY),
116    });
117    members.push(ApparentMember {
118        name: "toString",
119        kind: ApparentMemberKind::Method(TypeId::STRING),
120    });
121    members.push(ApparentMember {
122        name: "valueOf",
123        kind: ApparentMemberKind::Method(TypeId::ANY),
124    });
125    for &name in OBJECT_METHODS_RETURN_BOOLEAN {
126        members.push(ApparentMember {
127            name,
128            kind: ApparentMemberKind::Method(TypeId::BOOLEAN),
129        });
130    }
131    for &name in OBJECT_METHODS_RETURN_STRING {
132        members.push(ApparentMember {
133            name,
134            kind: ApparentMemberKind::Method(TypeId::STRING),
135        });
136    }
137    for &name in OBJECT_METHODS_RETURN_ANY {
138        members.push(ApparentMember {
139            name,
140            kind: ApparentMemberKind::Method(TypeId::ANY),
141        });
142    }
143    if include_to_locale {
144        members.push(ApparentMember {
145            name: "toLocaleString",
146            kind: ApparentMemberKind::Method(TypeId::STRING),
147        });
148    }
149}
150
151pub fn apparent_object_member_kind(name: &str) -> Option<ApparentMemberKind> {
152    object_member_kind(name, true)
153}
154
155pub fn apparent_primitive_member_kind(
156    interner: &dyn TypeDatabase,
157    kind: IntrinsicKind,
158    name: &str,
159) -> Option<ApparentMemberKind> {
160    match kind {
161        IntrinsicKind::String => {
162            if name == "length" {
163                return Some(ApparentMemberKind::Value(TypeId::NUMBER));
164            }
165            if is_member(name, STRING_METHODS_RETURN_STRING) {
166                return Some(ApparentMemberKind::Method(TypeId::STRING));
167            }
168            if is_member(name, STRING_METHODS_RETURN_NUMBER) {
169                return Some(ApparentMemberKind::Method(TypeId::NUMBER));
170            }
171            if is_member(name, STRING_METHODS_RETURN_BOOLEAN) {
172                return Some(ApparentMemberKind::Method(TypeId::BOOLEAN));
173            }
174            if is_member(name, STRING_METHODS_RETURN_ANY) {
175                return Some(ApparentMemberKind::Method(TypeId::ANY));
176            }
177            if is_member(name, STRING_METHODS_RETURN_STRING_ARRAY) {
178                let string_array = interner.array(TypeId::STRING);
179                return Some(ApparentMemberKind::Method(string_array));
180            }
181            object_member_kind(name, true)
182        }
183        IntrinsicKind::Number => {
184            if is_member(name, NUMBER_METHODS_RETURN_STRING) {
185                return Some(ApparentMemberKind::Method(TypeId::STRING));
186            }
187            if name == "valueOf" {
188                return Some(ApparentMemberKind::Method(TypeId::NUMBER));
189            }
190            object_member_kind(name, false)
191        }
192        IntrinsicKind::Boolean => {
193            if is_member(name, BOOLEAN_METHODS_RETURN_STRING) {
194                return Some(ApparentMemberKind::Method(TypeId::STRING));
195            }
196            if name == "valueOf" {
197                return Some(ApparentMemberKind::Method(TypeId::BOOLEAN));
198            }
199            object_member_kind(name, false)
200        }
201        IntrinsicKind::Bigint => {
202            if is_member(name, BIGINT_METHODS_RETURN_STRING) {
203                return Some(ApparentMemberKind::Method(TypeId::STRING));
204            }
205            if name == "valueOf" {
206                return Some(ApparentMemberKind::Method(TypeId::BIGINT));
207            }
208            object_member_kind(name, false)
209        }
210        IntrinsicKind::Symbol => {
211            if name == "description" {
212                let description = interner.union2(TypeId::STRING, TypeId::UNDEFINED);
213                return Some(ApparentMemberKind::Value(description));
214            }
215            if name == "toString" {
216                return Some(ApparentMemberKind::Method(TypeId::STRING));
217            }
218            if name == "valueOf" {
219                return Some(ApparentMemberKind::Method(TypeId::SYMBOL));
220            }
221            object_member_kind(name, true)
222        }
223        IntrinsicKind::Object => object_member_kind(name, true),
224        _ => None,
225    }
226}
227
228pub fn apparent_primitive_members(
229    interner: &dyn TypeDatabase,
230    kind: IntrinsicKind,
231) -> Vec<ApparentMember> {
232    let mut members = Vec::new();
233
234    match kind {
235        IntrinsicKind::String => {
236            members.push(ApparentMember {
237                name: "length",
238                kind: ApparentMemberKind::Value(TypeId::NUMBER),
239            });
240            for &name in STRING_METHODS_RETURN_STRING {
241                members.push(ApparentMember {
242                    name,
243                    kind: ApparentMemberKind::Method(TypeId::STRING),
244                });
245            }
246            for &name in STRING_METHODS_RETURN_NUMBER {
247                members.push(ApparentMember {
248                    name,
249                    kind: ApparentMemberKind::Method(TypeId::NUMBER),
250                });
251            }
252            for &name in STRING_METHODS_RETURN_BOOLEAN {
253                members.push(ApparentMember {
254                    name,
255                    kind: ApparentMemberKind::Method(TypeId::BOOLEAN),
256                });
257            }
258            for &name in STRING_METHODS_RETURN_ANY {
259                members.push(ApparentMember {
260                    name,
261                    kind: ApparentMemberKind::Method(TypeId::ANY),
262                });
263            }
264            let string_array = interner.array(TypeId::STRING);
265            for &name in STRING_METHODS_RETURN_STRING_ARRAY {
266                members.push(ApparentMember {
267                    name,
268                    kind: ApparentMemberKind::Method(string_array),
269                });
270            }
271            push_object_members(&mut members, true);
272        }
273        IntrinsicKind::Number => {
274            for &name in NUMBER_METHODS_RETURN_STRING {
275                members.push(ApparentMember {
276                    name,
277                    kind: ApparentMemberKind::Method(TypeId::STRING),
278                });
279            }
280            members.push(ApparentMember {
281                name: "valueOf",
282                kind: ApparentMemberKind::Method(TypeId::NUMBER),
283            });
284            push_object_members(&mut members, false);
285        }
286        IntrinsicKind::Boolean => {
287            for &name in BOOLEAN_METHODS_RETURN_STRING {
288                members.push(ApparentMember {
289                    name,
290                    kind: ApparentMemberKind::Method(TypeId::STRING),
291                });
292            }
293            members.push(ApparentMember {
294                name: "valueOf",
295                kind: ApparentMemberKind::Method(TypeId::BOOLEAN),
296            });
297            push_object_members(&mut members, false);
298        }
299        IntrinsicKind::Bigint => {
300            for &name in BIGINT_METHODS_RETURN_STRING {
301                members.push(ApparentMember {
302                    name,
303                    kind: ApparentMemberKind::Method(TypeId::STRING),
304                });
305            }
306            members.push(ApparentMember {
307                name: "valueOf",
308                kind: ApparentMemberKind::Method(TypeId::BIGINT),
309            });
310            push_object_members(&mut members, false);
311        }
312        IntrinsicKind::Symbol => {
313            let description = interner.union2(TypeId::STRING, TypeId::UNDEFINED);
314            members.push(ApparentMember {
315                name: "description",
316                kind: ApparentMemberKind::Value(description),
317            });
318            members.push(ApparentMember {
319                name: "toString",
320                kind: ApparentMemberKind::Method(TypeId::STRING),
321            });
322            members.push(ApparentMember {
323                name: "valueOf",
324                kind: ApparentMemberKind::Method(TypeId::SYMBOL),
325            });
326            push_object_members(&mut members, true);
327        }
328        IntrinsicKind::Object => {
329            push_object_members(&mut members, true);
330        }
331        _ => {}
332    }
333
334    members
335}