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}