1use crate::narrowing::{DiscriminantInfo, NarrowingContext};
7use crate::subtype::SubtypeChecker;
8use crate::types::{IntrinsicKind, LiteralValue, TypeData, TypeId, TypeListId, TypeParamInfo};
9use crate::visitor::{TypeVisitor, is_object_like_type_db, literal_value, union_list_id};
10use crate::{QueryDatabase, TypeDatabase};
11use tsz_common::interner::Atom;
12
13pub(crate) struct NarrowingVisitor<'a> {
15 pub(crate) db: &'a dyn QueryDatabase,
16 pub(crate) narrower: TypeId,
17 pub(crate) checker: SubtypeChecker<'a>,
19}
20
21impl<'a> TypeVisitor for NarrowingVisitor<'a> {
22 type Output = TypeId;
23
24 fn visit_type(&mut self, types: &dyn TypeDatabase, type_id: TypeId) -> Self::Output {
28 if let Some(type_key) = types.lookup(type_id) {
30 match type_key {
31 TypeData::Lazy(_) => {
33 let resolved = self.db.evaluate_type(type_id);
35 if resolved != type_id {
37 return self.visit_type(types, resolved);
38 }
39 }
41 TypeData::TypeQuery(_) | TypeData::Application(_) => {
43 let resolved = self.db.evaluate_type(type_id);
44 if resolved != type_id {
45 return self.visit_type(types, resolved);
46 }
47 }
48 TypeData::Object(_) => {
50 self.checker.reset();
53 if self.checker.is_subtype_of(type_id, self.narrower) {
54 return type_id;
55 }
56 self.checker.reset();
59 if self.checker.is_subtype_of(self.narrower, type_id) {
60 return self.narrower;
61 }
62 if is_object_like_type_db(self.db, self.narrower) {
66 return self.db.intersection2(type_id, self.narrower);
67 }
68 return TypeId::NEVER;
70 }
71 TypeData::Function(_) => {
73 self.checker.reset();
75 if self.checker.is_subtype_of(type_id, self.narrower) {
76 return type_id;
77 }
78 self.checker.reset();
80 if self.checker.is_subtype_of(self.narrower, type_id) {
81 return self.narrower;
82 }
83 return TypeId::NEVER;
85 }
86 _ => {}
87 }
88 }
89
90 <Self as TypeVisitor>::visit_type(self, types, type_id)
93 }
94
95 fn visit_intrinsic(&mut self, kind: IntrinsicKind) -> Self::Output {
96 match kind {
97 IntrinsicKind::Any => {
98 self.narrower
100 }
101 IntrinsicKind::Unknown => {
102 self.narrower
104 }
105 IntrinsicKind::Never => {
106 TypeId::NEVER
108 }
109 _ => {
110 let type_id = TypeId(kind as u32);
113
114 self.checker.reset();
117 if self.checker.is_subtype_of(self.narrower, type_id) {
118 self.narrower
119 }
120 else {
123 self.checker.reset();
124 if self.checker.is_subtype_of(type_id, self.narrower) {
125 type_id
126 }
127 else {
130 TypeId::NEVER
131 }
132 }
133 }
134 }
135 }
136
137 fn visit_literal(&mut self, _value: &LiteralValue) -> Self::Output {
138 self.narrower
142 }
143
144 fn visit_union(&mut self, list_id: u32) -> Self::Output {
145 let members = self.db.type_list(TypeListId(list_id));
146
147 let filtered: Vec<TypeId> = members
151 .iter()
152 .filter_map(|&member| {
153 let narrowed = self.visit_type(self.db, member);
154 if narrowed == TypeId::NEVER {
155 None
156 } else {
157 Some(narrowed)
158 }
159 })
160 .collect();
161
162 if filtered.is_empty() {
163 TypeId::NEVER
164 } else if filtered.len() == members.len() {
165 self.db.union(filtered)
167 } else if filtered.len() == 1 {
168 filtered[0]
169 } else {
170 self.db.union(filtered)
171 }
172 }
173
174 fn visit_intersection(&mut self, list_id: u32) -> Self::Output {
175 let members = self.db.type_list(TypeListId(list_id));
176
177 let narrowed_members: Vec<TypeId> = members
180 .iter()
181 .filter_map(|&member| {
182 let narrowed = self.visit_type(self.db, member);
183 if narrowed == TypeId::NEVER {
184 None
185 } else {
186 Some(narrowed)
187 }
188 })
189 .collect();
190
191 if narrowed_members.is_empty() {
192 TypeId::NEVER
193 } else if narrowed_members.len() == 1 {
194 narrowed_members[0]
195 } else {
196 self.db.intersection(narrowed_members)
197 }
198 }
199
200 fn visit_type_parameter(&mut self, info: &TypeParamInfo) -> Self::Output {
201 if let Some(constraint) = info.constraint {
204 self.db.intersection2(constraint, self.narrower)
205 } else {
206 self.narrower
208 }
209 }
210
211 fn visit_lazy(&mut self, _def_id: u32) -> Self::Output {
212 self.narrower
215 }
216
217 fn visit_ref(&mut self, _symbol_ref: u32) -> Self::Output {
218 self.narrower
221 }
222
223 fn visit_application(&mut self, _app_id: u32) -> Self::Output {
224 self.narrower
227 }
228
229 fn visit_object(&mut self, _shape_id: u32) -> Self::Output {
230 self.narrower
233 }
234
235 fn visit_function(&mut self, _shape_id: u32) -> Self::Output {
236 self.narrower
239 }
240
241 fn visit_callable(&mut self, _shape_id: u32) -> Self::Output {
242 self.narrower
244 }
245
246 fn visit_tuple(&mut self, _list_id: u32) -> Self::Output {
247 self.narrower
249 }
250
251 fn visit_array(&mut self, _element_type: TypeId) -> Self::Output {
252 self.narrower
254 }
255
256 fn default_output() -> Self::Output {
257 TypeId::NEVER
263 }
264}
265
266pub fn find_discriminants(
268 interner: &dyn QueryDatabase,
269 union_type: TypeId,
270) -> Vec<DiscriminantInfo> {
271 let ctx = NarrowingContext::new(interner);
272 ctx.find_discriminants(union_type)
273}
274
275pub fn narrow_by_discriminant(
277 interner: &dyn QueryDatabase,
278 union_type: TypeId,
279 property_path: &[Atom],
280 literal_value: TypeId,
281) -> TypeId {
282 let ctx = NarrowingContext::new(interner);
283 ctx.narrow_by_discriminant(union_type, property_path, literal_value)
284}
285
286pub fn narrow_by_typeof(
288 interner: &dyn QueryDatabase,
289 source_type: TypeId,
290 typeof_result: &str,
291) -> TypeId {
292 let ctx = NarrowingContext::new(interner);
293 ctx.narrow_by_typeof(source_type, typeof_result)
294}
295
296fn top_level_union_members(types: &dyn TypeDatabase, type_id: TypeId) -> Option<Vec<TypeId>> {
301 union_list_id(types, type_id).map(|list_id| types.type_list(list_id).to_vec())
302}
303
304const fn is_nullish_intrinsic(type_id: TypeId) -> bool {
305 matches!(type_id, TypeId::NULL | TypeId::UNDEFINED | TypeId::VOID)
306}
307
308const fn is_undefined_intrinsic(type_id: TypeId) -> bool {
309 matches!(type_id, TypeId::UNDEFINED | TypeId::VOID)
310}
311
312fn normalize_nullish(type_id: TypeId) -> TypeId {
313 if type_id == TypeId::VOID {
314 TypeId::UNDEFINED
315 } else {
316 type_id
317 }
318}
319
320pub fn is_nullish_type(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
322 if is_nullish_intrinsic(type_id) {
323 return true;
324 }
325 if let Some(members) = top_level_union_members(types, type_id) {
326 return members.iter().any(|&member| is_nullish_type(types, member));
327 }
328 false
329}
330
331pub fn type_contains_nullish(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
333 is_nullish_type(types, type_id)
334}
335
336pub fn type_contains_undefined(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
338 if is_undefined_intrinsic(type_id) {
339 return true;
340 }
341 if let Some(members) = top_level_union_members(types, type_id) {
342 return members
343 .iter()
344 .any(|&member| type_contains_undefined(types, member));
345 }
346 false
347}
348
349pub fn is_definitely_nullish(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
351 if is_nullish_intrinsic(type_id) {
352 return true;
353 }
354 if let Some(members) = top_level_union_members(types, type_id) {
355 return members
356 .iter()
357 .all(|&member| is_definitely_nullish(types, member));
358 }
359 false
360}
361
362pub fn can_be_nullish(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
364 is_nullish_type(types, type_id)
365}
366
367fn split_nullish_members(
368 types: &dyn TypeDatabase,
369 type_id: TypeId,
370 non_nullish: &mut Vec<TypeId>,
371 nullish: &mut Vec<TypeId>,
372) {
373 if is_nullish_intrinsic(type_id) {
374 nullish.push(normalize_nullish(type_id));
375 return;
376 }
377
378 if let Some(members) = top_level_union_members(types, type_id) {
379 for member in members {
380 split_nullish_members(types, member, non_nullish, nullish);
381 }
382 return;
383 }
384
385 non_nullish.push(type_id);
386}
387
388pub fn split_nullish_type(
390 types: &dyn TypeDatabase,
391 type_id: TypeId,
392) -> (Option<TypeId>, Option<TypeId>) {
393 let mut non_nullish = Vec::new();
394 let mut nullish = Vec::new();
395
396 split_nullish_members(types, type_id, &mut non_nullish, &mut nullish);
397
398 if nullish.is_empty() {
399 return (Some(type_id), None);
400 }
401
402 let non_nullish_type = if non_nullish.is_empty() {
403 None
404 } else if non_nullish.len() == 1 {
405 Some(non_nullish[0])
406 } else {
407 Some(types.union(non_nullish))
408 };
409
410 let nullish_type = if nullish.len() == 1 {
411 Some(nullish[0])
412 } else {
413 Some(types.union(nullish))
414 };
415
416 (non_nullish_type, nullish_type)
417}
418
419pub fn remove_nullish(types: &dyn TypeDatabase, type_id: TypeId) -> TypeId {
421 let (non_nullish, _) = split_nullish_type(types, type_id);
422 non_nullish.unwrap_or(TypeId::NEVER)
423}
424
425pub fn remove_definitely_falsy_types(types: &dyn TypeDatabase, type_id: TypeId) -> TypeId {
430 if is_always_falsy(types, type_id) {
431 return TypeId::NEVER;
432 }
433 if let Some(members_id) = union_list_id(types, type_id) {
434 let members = types.type_list(members_id);
435 let remaining: Vec<TypeId> = members
436 .iter()
437 .copied()
438 .filter(|&m| !is_always_falsy(types, m))
439 .collect();
440 if remaining.is_empty() {
441 return TypeId::NEVER;
442 }
443 if remaining.len() == 1 {
444 return remaining[0];
445 }
446 if remaining.len() == members.len() {
447 return type_id;
448 }
449 return types.union(remaining);
450 }
451 type_id
452}
453
454fn is_always_falsy(types: &dyn TypeDatabase, type_id: TypeId) -> bool {
456 if matches!(type_id, TypeId::NULL | TypeId::UNDEFINED | TypeId::VOID) {
457 return true;
458 }
459 if let Some(lit) = literal_value(types, type_id) {
460 return match lit {
461 LiteralValue::Boolean(false) => true,
462 LiteralValue::Number(n) => n.0 == 0.0 || n.0.is_nan(),
463 LiteralValue::String(atom) => types.resolve_atom_ref(atom).is_empty(),
464 LiteralValue::BigInt(atom) => types.resolve_atom_ref(atom).as_ref() == "0",
465 _ => false,
466 };
467 }
468 false
469}