• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "destructuringContext.h"
17 
18 #include <ir/expressions/identifier.h>
19 #include <ir/expressions/objectExpression.h>
20 #include <ir/expressions/assignmentExpression.h>
21 #include <ir/expressions/arrayExpression.h>
22 #include <ir/base/spreadElement.h>
23 #include <ir/base/property.h>
24 #include <ir/typeNode.h>
25 
26 namespace panda::es2panda::checker {
Prepare(const ir::Expression * typeAnnotation,const ir::Expression * initializer,const lexer::SourcePosition & loc)27 void DestructuringContext::Prepare(const ir::Expression *typeAnnotation, const ir::Expression *initializer,
28                                    const lexer::SourcePosition &loc)
29 {
30     if (typeAnnotation) {
31         typeAnnotation->Check(checker_);
32         Type *annotationType = typeAnnotation->AsTypeNode()->GetType(checker_);
33 
34         if (initializer) {
35             checker_->ElaborateElementwise(annotationType, initializer, loc);
36         }
37 
38         validateTypeAnnotation_ = true;
39         inferedType_ = annotationType;
40         return;
41     }
42 
43     if (initializer) {
44         if (!initializer->IsObjectExpression()) {
45             validateObjectPatternInitializer_ = false;
46         }
47 
48         inferedType_ = initializer->Check(checker_);
49     }
50 }
51 
HandleDestructuringAssignment(const ir::Identifier * ident,Type * inferedType,Type * defaultType)52 void DestructuringContext::HandleDestructuringAssignment(const ir::Identifier *ident, Type *inferedType,
53                                                          Type *defaultType)
54 {
55     if (!ident->Variable()) {
56         checker_->ThrowTypeError({"Cannot find name '", ident->Name(), "'."}, ident->Start());
57     }
58 
59     binder::Variable *variable = ident->Variable();
60     ASSERT(variable->TsType());
61 
62     if (defaultType && !checker_->IsTypeAssignableTo(defaultType, variable->TsType())) {
63         checker_->ThrowAssignmentError(defaultType, variable->TsType(), ident->Start());
64     }
65 
66     if (inferedType && !checker_->IsTypeAssignableTo(inferedType, variable->TsType())) {
67         checker_->ThrowAssignmentError(inferedType, variable->TsType(), ident->Start());
68     }
69 }
70 
SetInferedTypeForVariable(binder::Variable * var,Type * inferedType,const lexer::SourcePosition & loc)71 void DestructuringContext::SetInferedTypeForVariable(binder::Variable *var, Type *inferedType,
72                                                      const lexer::SourcePosition &loc)
73 {
74     ASSERT(var);
75 
76     if (!checker_->HasStatus(CheckerStatus::IN_CONST_CONTEXT)) {
77         inferedType = checker_->GetBaseTypeOfLiteralType(inferedType);
78     }
79 
80     if (var->TsType()) {
81         checker_->IsTypeIdenticalTo(var->TsType(), inferedType,
82                                     {"Subsequent variable declaration must have the same type. Variable '", var->Name(),
83                                      "' must be of type '", var->TsType(), "', but here has type '", inferedType, "'."},
84                                     loc);
85         return;
86     }
87 
88     if (signatureInfo_) {
89         signatureInfo_->params.push_back(var->AsLocalVariable());
90         signatureInfo_->minArgCount++;
91     }
92 
93     var->SetTsType(inferedType);
94 }
95 
ValidateObjectLiteralType(ObjectType * objType,const ir::ObjectExpression * objPattern)96 void DestructuringContext::ValidateObjectLiteralType(ObjectType *objType, const ir::ObjectExpression *objPattern)
97 {
98     for (const auto *sourceProp : objType->Properties()) {
99         const util::StringView &sourceName = sourceProp->Name();
100         bool found = false;
101 
102         for (const auto *targetProp : objPattern->Properties()) {
103             if (targetProp->IsRestElement()) {
104                 continue;
105             }
106 
107             ASSERT(targetProp->IsProperty());
108             const util::StringView &targetName = targetProp->AsProperty()->Key()->AsIdentifier()->Name();
109 
110             if (sourceName == targetName) {
111                 found = true;
112                 break;
113             }
114         }
115 
116         if (!found) {
117             checker_->ThrowTypeError({"Object literal may only specify known properties, and property '", sourceName,
118                                       "' does not exist in the pattern."},
119                                      objPattern->Start());
120         }
121     }
122 }
123 
HandleAssignmentPattern(const ir::AssignmentExpression * assignmentPattern,Type * inferedType,bool validateDefault)124 void DestructuringContext::HandleAssignmentPattern(const ir::AssignmentExpression *assignmentPattern, Type *inferedType,
125                                                    bool validateDefault)
126 {
127     CHECK_NOT_NULL(assignmentPattern);
128     if (!assignmentPattern->Left()->IsArrayPattern()) {
129         checker_->RemoveStatus(CheckerStatus::FORCE_TUPLE);
130     }
131 
132     Type *defaultType = assignmentPattern->Right()->Check(checker_);
133 
134     if (!checker_->HasStatus(CheckerStatus::IN_CONST_CONTEXT)) {
135         defaultType = checker_->GetBaseTypeOfLiteralType(defaultType);
136     }
137 
138     if (validateDefault && assignmentPattern->Right()->IsObjectExpression() &&
139         assignmentPattern->Left()->IsObjectPattern()) {
140         if (defaultType != nullptr && assignmentPattern != nullptr && assignmentPattern->Left() != nullptr) {
141             ValidateObjectLiteralType(defaultType->AsObjectType(), assignmentPattern->Left()->AsObjectPattern());
142         }
143     }
144 
145     Type *initType = inferedType;
146     checker_->AddStatus(CheckerStatus::FORCE_TUPLE);
147 
148     if (validateTypeAnnotation_) {
149         if (!inferedType) {
150             inferedType = checker_->GlobalUndefinedType();
151         }
152     } else {
153         if (!inferedType) {
154             inferedType = defaultType;
155         } else if (inferedType->IsUnionType()) {
156             inferedType->AsUnionType()->AddConstituentType(defaultType, checker_->Relation());
157         } else {
158             inferedType = checker_->CreateUnionType({inferedType, defaultType});
159         }
160     }
161 
162     if (assignmentPattern->Left()->IsIdentifier()) {
163         HandleIdentifierPattern(assignmentPattern, initType, inferedType, defaultType);
164         return;
165     }
166 
167     if (assignmentPattern->Left()->IsArrayPattern()) {
168         ArrayDestructuringContext nextContext = ArrayDestructuringContext(
169             checker_, assignmentPattern->Left(), inAssignment_, convertTupleToArray_, nullptr, nullptr);
170         nextContext.SetInferedType(inferedType);
171         nextContext.Start();
172         return;
173     }
174 
175     ASSERT(assignmentPattern->Left()->IsObjectPattern());
176     ObjectDestructuringContext nextContext = ObjectDestructuringContext(
177         checker_, assignmentPattern->Left(), inAssignment_, convertTupleToArray_, nullptr, nullptr);
178     nextContext.SetInferedType(inferedType);
179     nextContext.Start();
180 }
181 
HandleIdentifierPattern(const ir::AssignmentExpression * assignmentPattern,Type * initType,Type * inferedType,Type * defaultType)182 void DestructuringContext::HandleIdentifierPattern(const ir::AssignmentExpression *assignmentPattern, Type *initType,
183                                                    Type *inferedType, Type *defaultType)
184 {
185     if (inAssignment_) {
186         HandleDestructuringAssignment(assignmentPattern->Left()->AsIdentifier(), initType, defaultType);
187         return;
188     }
189 
190     if (validateTypeAnnotation_ && !checker_->IsTypeAssignableTo(defaultType, inferedType)) {
191         checker_->ThrowAssignmentError(defaultType, inferedType, assignmentPattern->Left()->Start());
192     }
193 
194     SetInferedTypeForVariable(assignmentPattern->Left()->AsIdentifier()->Variable(), inferedType,
195                               assignmentPattern->Start());
196 }
197 
ValidateInferedType()198 void ArrayDestructuringContext::ValidateInferedType()
199 {
200     CHECK_NOT_NULL(inferedType_);
201     if (!inferedType_->IsArrayType() && !inferedType_->IsUnionType() &&
202         (!inferedType_->IsObjectType() || !inferedType_->AsObjectType()->IsTupleType())) {
203         checker_->ThrowTypeError(
204             {"Type ", inferedType_, " must have a '[Symbol.iterator]()' method that returns an iterator."},
205             id_->Start());
206     }
207 
208     if (inferedType_->IsUnionType()) {
209         for (auto *it : inferedType_->AsUnionType()->ConstituentTypes()) {
210             if (!it->IsArrayType() && (!it->IsObjectType() || !it->AsObjectType()->IsTupleType())) {
211                 checker_->ThrowTypeError(
212                     {"Type ", inferedType_, " must have a '[Symbol.iterator]()' method that returns an iterator."},
213                     id_->Start());
214             }
215         }
216     }
217 }
218 
GetTypeFromTupleByIndex(TupleType * tuple)219 Type *ArrayDestructuringContext::GetTypeFromTupleByIndex(TupleType *tuple)
220 {
221     util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), index_);
222     binder::Variable *memberVar = tuple->GetProperty(memberIndex, false);
223 
224     if (!memberVar) {
225         return nullptr;
226     }
227 
228     return memberVar->TsType();
229 }
230 
NextInferedType(const util::StringView & searchName,bool throwError)231 Type *ArrayDestructuringContext::NextInferedType([[maybe_unused]] const util::StringView &searchName, bool throwError)
232 {
233     if (inferedType_->IsArrayType()) {
234         return inferedType_->AsArrayType()->ElementType();
235     }
236 
237     if (inferedType_->IsObjectType()) {
238         ASSERT(inferedType_->AsObjectType()->IsTupleType());
239         Type *returnType = GetTypeFromTupleByIndex(inferedType_->AsObjectType()->AsTupleType());
240 
241         if (!returnType && throwError) {
242             if (!validateTypeAnnotation_ && checker_->HasStatus(CheckerStatus::IN_PARAMETER)) {
243                 return returnType;
244             }
245 
246             checker_->ThrowTypeError({"Tuple type ", inferedType_, " of length ",
247                                       inferedType_->AsObjectType()->AsTupleType()->FixedLength(),
248                                       " has no element at index ", index_, "."},
249                                      id_->Start());
250         }
251 
252         return returnType;
253     }
254 
255     ASSERT(inferedType_->IsUnionType());
256 
257     ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
258 
259     for (auto *type : inferedType_->AsUnionType()->ConstituentTypes()) {
260         if (type->IsArrayType()) {
261             unionTypes.push_back(type->AsArrayType()->ElementType());
262             continue;
263         }
264 
265         ASSERT(type->IsObjectType() && type->AsObjectType()->IsTupleType());
266         Type *elementType = GetTypeFromTupleByIndex(type->AsObjectType()->AsTupleType());
267 
268         if (!elementType) {
269             continue;
270         }
271 
272         unionTypes.push_back(elementType);
273     }
274 
275     if (unionTypes.empty()) {
276         if (throwError) {
277             checker_->ThrowTypeError({"Property ", index_, " does not exist on type ", inferedType_, "."},
278                                      id_->Start());
279         }
280 
281         return nullptr;
282     }
283 
284     return checker_->CreateUnionType(std::move(unionTypes));
285 }
286 
CreateArrayTypeForRest(UnionType * inferedType)287 Type *ArrayDestructuringContext::CreateArrayTypeForRest(UnionType *inferedType)
288 {
289     ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
290     uint32_t savedIdx = index_;
291 
292     for (auto *it : inferedType->ConstituentTypes()) {
293         if (it->IsArrayType()) {
294             unionTypes.push_back(it->AsArrayType()->ElementType());
295             continue;
296         }
297 
298         ASSERT(it->IsObjectType() && it->AsObjectType()->IsTupleType());
299         Type *tupleElementType = GetTypeFromTupleByIndex(it->AsObjectType()->AsTupleType());
300 
301         while (tupleElementType) {
302             unionTypes.push_back(tupleElementType);
303             index_++;
304             tupleElementType = GetTypeFromTupleByIndex(it->AsObjectType()->AsTupleType());
305         }
306 
307         index_ = savedIdx;
308     }
309 
310     Type *restArrayElementType = checker_->CreateUnionType(std::move(unionTypes));
311     return checker_->Allocator()->New<ArrayType>(restArrayElementType);
312 }
313 
CreateTupleTypeForRest(TupleType * tuple)314 Type *ArrayDestructuringContext::CreateTupleTypeForRest(TupleType *tuple)
315 {
316     ObjectDescriptor *desc = checker_->Allocator()->New<ObjectDescriptor>(checker_->Allocator());
317     CHECK_NOT_NULL(desc);
318     ArenaVector<ElementFlags> elementFlags(checker_->Allocator()->Adapter());
319     uint32_t savedIdx = index_;
320     uint32_t iterIndex = 0;
321 
322     Type *tupleElementType = GetTypeFromTupleByIndex(tuple);
323 
324     while (tupleElementType) {
325         ElementFlags memberFlag = ElementFlags::REQUIRED;
326         util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), iterIndex);
327         auto *memberVar =
328             binder::Scope::CreateVar(checker_->Allocator(), memberIndex, binder::VariableFlags::PROPERTY, nullptr);
329         memberVar->SetTsType(tupleElementType);
330         elementFlags.push_back(memberFlag);
331         desc->properties.push_back(memberVar);
332 
333         index_++;
334         iterIndex++;
335 
336         tupleElementType = GetTypeFromTupleByIndex(tuple);
337     }
338 
339     index_ = savedIdx;
340     return checker_->CreateTupleType(desc, std::move(elementFlags), ElementFlags::REQUIRED, iterIndex, iterIndex,
341                                      false);
342 }
343 
GetRestType(const lexer::SourcePosition & loc)344 Type *ArrayDestructuringContext::GetRestType([[maybe_unused]] const lexer::SourcePosition &loc)
345 {
346     if (inferedType_->IsArrayType()) {
347         return inferedType_;
348     }
349 
350     if (inferedType_->IsObjectType() && inferedType_->AsObjectType()->IsTupleType()) {
351         return CreateTupleTypeForRest(inferedType_->AsObjectType()->AsTupleType());
352     }
353 
354     ASSERT(inferedType_->IsUnionType());
355     bool createArrayType = false;
356 
357     for (auto *it : inferedType_->AsUnionType()->ConstituentTypes()) {
358         if (it->IsArrayType()) {
359             createArrayType = true;
360             break;
361         }
362     }
363 
364     if (createArrayType) {
365         return CreateArrayTypeForRest(inferedType_->AsUnionType());
366     }
367 
368     ArenaVector<Type *> tupleUnion(checker_->Allocator()->Adapter());
369 
370     for (auto *it : inferedType_->AsUnionType()->ConstituentTypes()) {
371         ASSERT(it->IsObjectType() && it->AsObjectType()->IsTupleType());
372         Type *newTuple = CreateTupleTypeForRest(it->AsObjectType()->AsTupleType());
373         tupleUnion.push_back(newTuple);
374     }
375 
376     return checker_->CreateUnionType(std::move(tupleUnion));
377 }
378 
HandleRest(const ir::SpreadElement * rest)379 void ArrayDestructuringContext::HandleRest(const ir::SpreadElement *rest)
380 {
381     Type *inferedRestType = GetRestType(rest->Start());
382 
383     if (rest->Argument()->IsIdentifier()) {
384         if (inAssignment_) {
385             HandleDestructuringAssignment(rest->Argument()->AsIdentifier(), inferedRestType, nullptr);
386             return;
387         }
388 
389         SetInferedTypeForVariable(rest->Argument()->AsIdentifier()->Variable(), inferedRestType, rest->Start());
390         return;
391     }
392 
393     if (rest->Argument()->IsArrayPattern()) {
394         ArrayDestructuringContext nextContext = ArrayDestructuringContext(checker_, rest->Argument(), inAssignment_,
395                                                                           convertTupleToArray_, nullptr, nullptr);
396         nextContext.SetInferedType(inferedRestType);
397         nextContext.Start();
398         return;
399     }
400 
401     ASSERT(rest->Argument()->IsObjectPattern());
402     ObjectDestructuringContext nextContext =
403         ObjectDestructuringContext(checker_, rest->Argument(), inAssignment_, convertTupleToArray_, nullptr, nullptr);
404     nextContext.SetInferedType(inferedRestType);
405     nextContext.Start();
406 }
407 
ConvertTupleTypeToArrayTypeIfNecessary(const ir::AstNode * node,Type * type)408 Type *ArrayDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(const ir::AstNode *node, Type *type)
409 {
410     if (!convertTupleToArray_) {
411         return type;
412     }
413 
414     if (!type) {
415         return type;
416     }
417 
418     if (node->IsArrayPattern() ||
419         (node->IsAssignmentPattern() && node->AsAssignmentPattern()->Left()->IsArrayPattern())) {
420         return type;
421     }
422 
423     if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) {
424         return type->AsObjectType()->AsTupleType()->ConvertToArrayType(checker_);
425     }
426 
427     return type;
428 }
429 
SetParameterType(const ir::AstNode * parent,Type * type)430 static void SetParameterType(const ir::AstNode *parent, Type *type)
431 {
432     parent->Iterate([type](ir::AstNode *childNode) -> void {
433         if (childNode->IsIdentifier() && childNode->AsIdentifier()->Variable()) {
434             childNode->AsIdentifier()->Variable()->SetTsType(type);
435             return;
436         }
437 
438         SetParameterType(childNode, type);
439     });
440 }
441 
SetRemainingPatameterTypes()442 void ArrayDestructuringContext::SetRemainingPatameterTypes()
443 {
444     do {
445         const auto *it = id_->AsArrayPattern()->Elements()[index_];
446         ASSERT(it);
447         SetParameterType(it, checker_->GlobalAnyType());
448     } while (++index_ != id_->AsArrayPattern()->Elements().size());
449 }
450 
Start()451 void ArrayDestructuringContext::Start()
452 {
453     ASSERT(id_->IsArrayPattern());
454 
455     ValidateInferedType();
456 
457     util::StringView name = util::Helpers::ToStringView(checker_->Allocator(), 0);
458 
459     for (const auto *it : id_->AsArrayPattern()->Elements()) {
460         if (it->IsRestElement()) {
461             HandleRest(it->AsRestElement());
462             break;
463         }
464 
465         Type *nextInferedType =
466             ConvertTupleTypeToArrayTypeIfNecessary(it, NextInferedType(name, !it->IsAssignmentPattern()));
467 
468         if (!nextInferedType && checker_->HasStatus(CheckerStatus::IN_PARAMETER)) {
469             SetRemainingPatameterTypes();
470             return;
471         }
472 
473         if (convertTupleToArray_ && nextInferedType && inferedType_->IsObjectType()) {
474             ASSERT(inferedType_->AsObjectType()->IsTupleType());
475 
476             binder::Variable *currentTupleElement = inferedType_->AsObjectType()->Properties()[index_];
477 
478             if (currentTupleElement) {
479                 currentTupleElement->SetTsType(nextInferedType);
480             }
481         }
482 
483         switch (it->Type()) {
484             case ir::AstNodeType::IDENTIFIER: {
485                 if (inAssignment_) {
486                     HandleDestructuringAssignment(it->AsIdentifier(), nextInferedType, nullptr);
487                     break;
488                 }
489 
490                 SetInferedTypeForVariable(it->AsIdentifier()->Variable(), nextInferedType, it->Start());
491                 break;
492             }
493             case ir::AstNodeType::ARRAY_PATTERN: {
494                 ArrayDestructuringContext nextContext =
495                     ArrayDestructuringContext(checker_, it, inAssignment_, convertTupleToArray_, nullptr, nullptr);
496                 nextContext.SetInferedType(nextInferedType);
497                 nextContext.Start();
498                 break;
499             }
500             case ir::AstNodeType::OBJECT_PATTERN: {
501                 ObjectDestructuringContext nextContext =
502                     ObjectDestructuringContext(checker_, it, inAssignment_, convertTupleToArray_, nullptr, nullptr);
503                 nextContext.SetInferedType(nextInferedType);
504                 nextContext.Start();
505                 break;
506             }
507             case ir::AstNodeType::ASSIGNMENT_PATTERN: {
508                 HandleAssignmentPattern(it->AsAssignmentPattern(), nextInferedType, false);
509                 break;
510             }
511             case ir::AstNodeType::OMITTED_EXPRESSION: {
512                 break;
513             }
514             default: {
515                 UNREACHABLE();
516             }
517         }
518 
519         index_++;
520     }
521 }
522 
ValidateInferedType()523 void ObjectDestructuringContext::ValidateInferedType()
524 {
525     CHECK_NOT_NULL(inferedType_);
526     if (!inferedType_->IsObjectType()) {
527         return;
528     }
529 
530     ValidateObjectLiteralType(inferedType_->AsObjectType(), id_->AsObjectPattern());
531 }
532 
HandleRest(const ir::SpreadElement * rest)533 void ObjectDestructuringContext::HandleRest(const ir::SpreadElement *rest)
534 {
535     Type *inferedRestType = GetRestType(rest->Start());
536     ASSERT(rest->Argument()->IsIdentifier());
537 
538     if (inAssignment_) {
539         HandleDestructuringAssignment(rest->Argument()->AsIdentifier(), inferedRestType, nullptr);
540         return;
541     }
542 
543     SetInferedTypeForVariable(rest->Argument()->AsIdentifier()->Variable(), inferedRestType, rest->Start());
544 }
545 
CreateObjectTypeForRest(ObjectType * objType)546 Type *ObjectDestructuringContext::CreateObjectTypeForRest(ObjectType *objType)
547 {
548     ObjectDescriptor *desc = checker_->Allocator()->New<ObjectDescriptor>(checker_->Allocator());
549     CHECK_NOT_NULL(desc);
550 
551     for (auto *it : objType->AsObjectType()->Properties()) {
552         if (!it->HasFlag(binder::VariableFlags::INFERED_IN_PATTERN)) {
553             auto *memberVar =
554                 binder::Scope::CreateVar(checker_->Allocator(), it->Name(), binder::VariableFlags::NONE, nullptr);
555             CHECK_NOT_NULL(memberVar);
556             memberVar->SetTsType(it->TsType());
557             memberVar->AddFlag(it->Flags());
558             desc->properties.push_back(memberVar);
559         }
560     }
561 
562     Type *returnType = checker_->Allocator()->New<ObjectLiteralType>(desc);
563     CHECK_NOT_NULL(returnType);
564     returnType->AsObjectType()->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS);
565     return returnType;
566 }
567 
GetRestType(const lexer::SourcePosition & loc)568 Type *ObjectDestructuringContext::GetRestType([[maybe_unused]] const lexer::SourcePosition &loc)
569 {
570     if (inferedType_->IsUnionType()) {
571         ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
572 
573         for (auto *it : inferedType_->AsUnionType()->ConstituentTypes()) {
574             if (it->IsObjectType()) {
575                 unionTypes.push_back(CreateObjectTypeForRest(it->AsObjectType()));
576                 continue;
577             }
578 
579             checker_->ThrowTypeError("Rest types may only be created from object types.", loc);
580         }
581 
582         return checker_->CreateUnionType(std::move(unionTypes));
583     }
584 
585     if (inferedType_->IsObjectType()) {
586         return CreateObjectTypeForRest(inferedType_->AsObjectType());
587     }
588 
589     checker_->ThrowTypeError("Rest types may only be created from object types.", loc);
590 }
591 
ConvertTupleTypeToArrayTypeIfNecessary(const ir::AstNode * node,Type * type)592 Type *ObjectDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(const ir::AstNode *node, Type *type)
593 {
594     if (!convertTupleToArray_) {
595         return type;
596     }
597 
598     if (!type) {
599         return type;
600     }
601 
602     ASSERT(node->IsProperty());
603 
604     const ir::Property *property = node->AsProperty();
605 
606     if (property->Value()->IsArrayPattern()) {
607         return type;
608     }
609 
610     if (property->Value()->IsAssignmentPattern() &&
611         property->Value()->AsAssignmentPattern()->Left()->IsArrayPattern()) {
612         return type;
613     }
614 
615     if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) {
616         return type->AsObjectType()->AsTupleType()->ConvertToArrayType(checker_);
617     }
618 
619     return type;
620 }
621 
NextInferedType(const util::StringView & searchName,bool throwError)622 Type *ObjectDestructuringContext::NextInferedType([[maybe_unused]] const util::StringView &searchName, bool throwError)
623 {
624     binder::Variable *prop =
625         checker_->GetPropertyOfType(inferedType_, searchName, !throwError, binder::VariableFlags::INFERED_IN_PATTERN);
626 
627     if (prop) {
628         prop->AddFlag(binder::VariableFlags::INFERED_IN_PATTERN);
629         return prop->TsType();
630     }
631 
632     if (inferedType_->IsObjectType()) {
633         checker::ObjectType *objType = inferedType_->AsObjectType();
634 
635         if (objType->StringIndexInfo()) {
636             return objType->StringIndexInfo()->GetType();
637         }
638     }
639 
640     if (throwError) {
641         checker_->ThrowTypeError({"Property ", searchName, " does not exist on type ", inferedType_, "."},
642                                  id_->Start());
643     }
644 
645     return nullptr;
646 }
647 
Start()648 void ObjectDestructuringContext::Start()
649 {
650     ASSERT(id_->IsObjectPattern());
651 
652     if (!id_->AsObjectPattern()->Properties().back()->IsRestElement() && validateObjectPatternInitializer_) {
653         ValidateInferedType();
654     }
655 
656     for (const auto *it : id_->AsObjectPattern()->Properties()) {
657         switch (it->Type()) {
658             case ir::AstNodeType::PROPERTY: {
659                 const ir::Property *property = it->AsProperty();
660 
661                 if (property->IsComputed()) {
662                     // TODO(aszilagyi)
663                     return;
664                 }
665 
666                 Type *nextInferedType = ConvertTupleTypeToArrayTypeIfNecessary(
667                     it->AsProperty(),
668                     NextInferedType(property->Key()->AsIdentifier()->Name(),
669                                     (!property->Value()->IsAssignmentPattern() || validateTypeAnnotation_)));
670 
671                 if (property->Value()->IsIdentifier()) {
672                     if (inAssignment_) {
673                         HandleDestructuringAssignment(property->Value()->AsIdentifier(), nextInferedType, nullptr);
674                         break;
675                     }
676 
677                     SetInferedTypeForVariable(property->Value()->AsIdentifier()->Variable(), nextInferedType,
678                                               it->Start());
679                     break;
680                 }
681 
682                 if (property->Value()->IsArrayPattern()) {
683                     ArrayDestructuringContext nextContext =
684                         ArrayDestructuringContext(checker_, property->Value()->AsArrayPattern(), inAssignment_,
685                                                   convertTupleToArray_, nullptr, nullptr);
686                     nextContext.SetInferedType(nextInferedType);
687                     nextContext.Start();
688                     break;
689                 }
690 
691                 if (property->Value()->IsObjectPattern()) {
692                     ObjectDestructuringContext nextContext =
693                         ObjectDestructuringContext(checker_, property->Value()->AsObjectPattern(), inAssignment_,
694                                                    convertTupleToArray_, nullptr, nullptr);
695                     nextContext.SetInferedType(nextInferedType);
696                     nextContext.Start();
697                     break;
698                 }
699 
700                 ASSERT(property->Value()->IsAssignmentPattern());
701                 HandleAssignmentPattern(property->Value()->AsAssignmentPattern(), nextInferedType, true);
702                 break;
703             }
704             case ir::AstNodeType::REST_ELEMENT: {
705                 HandleRest(it->AsRestElement());
706                 break;
707             }
708             default: {
709                 UNREACHABLE();
710             }
711         }
712     }
713 }
714 }  // namespace panda::es2panda::checker
715