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