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 "assignAnalyzer.h"
17#include <cstddef>
18
19#include "ir/base/classDefinition.h"
20#include "ir/base/classProperty.h"
21#include "ir/base/classStaticBlock.h"
22#include "ir/base/methodDefinition.h"
23#include "ir/base/scriptFunction.h"
24#include "ir/statements/classDeclaration.h"
25#include "ir/statements/variableDeclaration.h"
26#include "ir/statements/doWhileStatement.h"
27#include "ir/statements/expressionStatement.h"
28#include "ir/statements/whileStatement.h"
29#include "ir/statements/forUpdateStatement.h"
30#include "ir/statements/labelledStatement.h"
31#include "ir/statements/forOfStatement.h"
32#include "ir/statements/blockStatement.h"
33#include "ir/statements/ifStatement.h"
34#include "ir/statements/switchStatement.h"
35#include "ir/statements/variableDeclarator.h"
36#include "ir/statements/throwStatement.h"
37#include "ir/statements/switchCaseStatement.h"
38#include "ir/statements/breakStatement.h"
39#include "ir/statements/continueStatement.h"
40#include "ir/statements/returnStatement.h"
41#include "ir/statements/tryStatement.h"
42#include "ir/statements/assertStatement.h"
43#include "ir/expressions/callExpression.h"
44#include "ir/expressions/identifier.h"
45#include "ir/expressions/arrowFunctionExpression.h"
46#include "ir/expressions/assignmentExpression.h"
47#include "ir/expressions/binaryExpression.h"
48#include "ir/expressions/conditionalExpression.h"
49#include "ir/expressions/memberExpression.h"
50#include "ir/expressions/objectExpression.h"
51#include "ir/expressions/unaryExpression.h"
52#include "ir/expressions/updateExpression.h"
53#include "ir/expressions/typeofExpression.h"
54#include "ir/ets/etsNewClassInstanceExpression.h"
55#include "ir/ets/etsStructDeclaration.h"
56#include "ir/ts/tsInterfaceDeclaration.h"
57#include "varbinder/ETSBinder.h"
58#include "varbinder/variable.h"
59#include "varbinder/scope.h"
60#include "varbinder/declaration.h"
61#include "checker/ETSchecker.h"
62#include "ir/base/catchClause.h"
63#include "parser/program/program.h"
64#include "checker/types/ts/objectType.h"
65
66namespace ark::es2panda::checker {
67
68static constexpr NodeId INVALID_ID = -1;
69static constexpr bool CHECK_ALL_PROPERTIES = true;
70// NOTE(pantos) generic field initialization issue, skip them for now
71static constexpr bool CHECK_GENERIC_NON_READONLY_PROPERTIES = false;
72static constexpr bool WARN_NO_INIT_ONCE_PER_VARIABLE = false;
73static constexpr int LOOP_PHASES = 2;
74
75template <typename... Ts>
76struct ScopeGuard {
77    // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
78    std::tuple<Ts...> values;
79    std::tuple<Ts &...> refs;
80    // NOLINTEND(misc-non-private-member-variables-in-classes)
81
82    explicit ScopeGuard(Ts &...ts) : values(ts...), refs(ts...) {}
83    ~ScopeGuard()
84    {
85        refs = values;
86    }
87
88    DEFAULT_COPY_SEMANTIC(ScopeGuard);
89    DEFAULT_MOVE_SEMANTIC(ScopeGuard);
90};
91
92static std::string Capitalize(const util::StringView &str)
93{
94    if (str.Empty()) {
95        return "";
96    }
97    std::string ret(str.Utf8());
98    ret[0] = std::toupper(ret[0]);
99    return ret;
100}
101
102void Set::Reset()
103{
104    reset_ = true;
105}
106
107bool Set::IsReset()
108{
109    return reset_;
110}
111
112void Set::Incl(const int id)
113{
114    nodes_.insert(id);
115}
116
117void Set::InclRange(const int start, const int limit)
118{
119    for (int x = start; x < limit; x++) {
120        nodes_.insert(x);
121    }
122}
123
124void Set::Excl(const int id)
125{
126    nodes_.erase(id);
127}
128
129void Set::ExcludeFrom(const int start)
130{
131    auto it = nodes_.lower_bound(start);
132    nodes_.erase(nodes_.begin(), it);
133}
134
135bool Set::IsMember(const int id) const
136{
137    return nodes_.find(id) != nodes_.end();
138}
139
140Set &Set::AndSet(const Set &xs)
141{
142    std::set<int> res;
143    std::set_intersection(nodes_.begin(), nodes_.end(), xs.nodes_.begin(), xs.nodes_.end(),
144                          std::inserter(res, res.begin()));
145    nodes_ = res;
146    return *this;
147}
148
149Set &Set::OrSet(const Set &xs)
150{
151    std::set<int> res;
152    std::set_union(nodes_.begin(), nodes_.end(), xs.nodes_.begin(), xs.nodes_.end(), std::inserter(res, res.begin()));
153    nodes_ = res;
154    return *this;
155}
156
157Set &Set::DiffSet(const Set &xs)
158{
159    std::set<int> res;
160    std::set_difference(nodes_.begin(), nodes_.end(), xs.nodes_.begin(), xs.nodes_.end(),
161                        std::inserter(res, res.begin()));
162    nodes_ = res;
163    return *this;
164}
165
166int Set::Next(const int id)
167{
168    auto it = nodes_.upper_bound(id);
169    if (it != nodes_.end()) {
170        return *it;
171    }
172    return -1;
173}
174
175AssignAnalyzer::AssignAnalyzer(ETSChecker *checker)
176    : checker_(checker),
177      varDecls_(checker->Allocator()->Adapter()),
178      nodeIdMap_(checker->Allocator()->Adapter()),
179      foundErrors_(checker->Allocator()->Adapter())
180{
181}
182
183void AssignAnalyzer::Analyze(const ir::AstNode *node)
184{
185    const auto program = checker_->VarBinder()->Program();
186    globalClass_ = program->GlobalClass();
187
188    AnalyzeClassDef(globalClass_);
189    globalClassIsVisited_ = true;
190
191    firstNonGlobalAdr_ = nextAdr_;
192
193    AnalyzeNodes(node);
194
195    if (numErrors_ > 0) {
196        checker_->LogTypeError("There were errors during assign analysis (" + std::to_string(numErrors_) + ")",
197                               node->Start());
198    }
199}
200
201void AssignAnalyzer::Warning(const std::string_view message, const lexer::SourcePosition &pos)
202{
203    ++numErrors_;
204    checker_->Warning(message, pos);
205}
206
207void AssignAnalyzer::Warning(std::initializer_list<TypeErrorMessageElement> list, const lexer::SourcePosition &pos)
208{
209    ++numErrors_;
210    checker_->ReportWarning(list, pos);
211}
212
213void AssignAnalyzer::AnalyzeNodes(const ir::AstNode *node)
214{
215    node->Iterate([this](auto *childNode) { AnalyzeNode(childNode); });
216}
217
218void AssignAnalyzer::AnalyzeNode(const ir::AstNode *node)
219{
220    if (node == nullptr) {
221        return;
222    }
223
224    // NOTE(pantos) these are dummy methods to conform the CI's method size and complexity requirements
225    if (AnalyzeStmtNode1(node) || AnalyzeStmtNode2(node) || AnalyzeExprNode1(node) || AnalyzeExprNode2(node)) {
226        return;
227    }
228
229    switch (node->Type()) {
230        case ir::AstNodeType::STRUCT_DECLARATION: {
231            AnalyzeStructDecl(node->AsETSStructDeclaration());
232            break;
233        }
234        case ir::AstNodeType::CLASS_DECLARATION: {
235            AnalyzeClassDecl(node->AsClassDeclaration());
236            break;
237        }
238        case ir::AstNodeType::CLASS_DEFINITION: {
239            AnalyzeClassDef(node->AsClassDefinition());
240            break;
241        }
242        case ir::AstNodeType::METHOD_DEFINITION: {
243            AnalyzeMethodDef(node->AsMethodDefinition());
244            break;
245        }
246        case ir::AstNodeType::VARIABLE_DECLARATION: {
247            AnalyzeVarDef(node->AsVariableDeclaration());
248            break;
249        }
250        default: {
251            AnalyzeNodes(node);
252            if (node->IsExpression()) {
253                if (inits_.IsReset()) {
254                    Merge();
255                }
256            }
257            break;
258        }
259    }
260}
261
262bool AssignAnalyzer::AnalyzeStmtNode1(const ir::AstNode *node)
263{
264    switch (node->Type()) {
265        case ir::AstNodeType::EXPRESSION_STATEMENT: {
266            AnalyzeNode(node->AsExpressionStatement()->GetExpression());
267            break;
268        }
269        case ir::AstNodeType::BLOCK_STATEMENT: {
270            AnalyzeBlock(node->AsBlockStatement());
271            break;
272        }
273        case ir::AstNodeType::DO_WHILE_STATEMENT: {
274            AnalyzeDoLoop(node->AsDoWhileStatement());
275            break;
276        }
277        case ir::AstNodeType::WHILE_STATEMENT: {
278            AnalyzeWhileLoop(node->AsWhileStatement());
279            break;
280        }
281        case ir::AstNodeType::FOR_UPDATE_STATEMENT: {
282            AnalyzeForLoop(node->AsForUpdateStatement());
283            break;
284        }
285        case ir::AstNodeType::FOR_OF_STATEMENT: {
286            AnalyzeForOfLoop(node->AsForOfStatement());
287            break;
288        }
289        case ir::AstNodeType::IF_STATEMENT: {
290            AnalyzeIf(node->AsIfStatement());
291            break;
292        }
293        default:
294            return false;
295    }
296
297    return true;
298}
299
300bool AssignAnalyzer::AnalyzeStmtNode2(const ir::AstNode *node)
301{
302    switch (node->Type()) {
303        case ir::AstNodeType::LABELLED_STATEMENT: {
304            AnalyzeLabelled(node->AsLabelledStatement());
305            break;
306        }
307        case ir::AstNodeType::SWITCH_STATEMENT: {
308            AnalyzeSwitch(node->AsSwitchStatement());
309            break;
310        }
311        case ir::AstNodeType::TRY_STATEMENT: {
312            AnalyzeTry(node->AsTryStatement());
313            break;
314        }
315        case ir::AstNodeType::BREAK_STATEMENT: {
316            AnalyzeBreak(node->AsBreakStatement());
317            break;
318        }
319        case ir::AstNodeType::CONTINUE_STATEMENT: {
320            AnalyzeContinue(node->AsContinueStatement());
321            break;
322        }
323        case ir::AstNodeType::RETURN_STATEMENT: {
324            AnalyzeReturn(node->AsReturnStatement());
325            break;
326        }
327        case ir::AstNodeType::THROW_STATEMENT: {
328            AnalyzeThrow(node->AsThrowStatement());
329            break;
330        }
331        case ir::AstNodeType::ASSERT_STATEMENT: {
332            AnalyzeAssert(node->AsAssertStatement());
333            break;
334        }
335        default:
336            return false;
337    }
338
339    return true;
340}
341
342bool AssignAnalyzer::AnalyzeExprNode1(const ir::AstNode *node)
343{
344    switch (node->Type()) {
345        case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: {
346            AnalyzeNewClass(node->AsETSNewClassInstanceExpression());
347            break;
348        }
349        case ir::AstNodeType::CALL_EXPRESSION: {
350            AnalyzeCallExpr(node->AsCallExpression());
351            break;
352        }
353        case ir::AstNodeType::IDENTIFIER: {
354            AnalyzeId(node->AsIdentifier());
355            break;
356        }
357        case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
358            AnalyzeAssignExpr(node->AsAssignmentExpression());
359            break;
360        }
361        case ir::AstNodeType::CONDITIONAL_EXPRESSION: {
362            AnalyzeCondExpr(node->AsConditionalExpression());
363            break;
364        }
365        case ir::AstNodeType::MEMBER_EXPRESSION: {
366            AnalyzeMemberExpr(node->AsMemberExpression());
367            break;
368        }
369        default:
370            return false;
371    }
372
373    return true;
374}
375
376bool AssignAnalyzer::AnalyzeExprNode2(const ir::AstNode *node)
377{
378    switch (node->Type()) {
379        case ir::AstNodeType::BINARY_EXPRESSION: {
380            AnalyzeBinaryExpr(node->AsBinaryExpression());
381            break;
382        }
383        case ir::AstNodeType::UNARY_EXPRESSION: {
384            AnalyzeUnaryExpr(node->AsUnaryExpression());
385            break;
386        }
387        case ir::AstNodeType::UPDATE_EXPRESSION: {
388            AnalyzeUpdateExpr(node->AsUpdateExpression());
389            break;
390        }
391        case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION: {
392            AnalyzeArrowFunctionExpr(node->AsArrowFunctionExpression());
393            break;
394        }
395        default:
396            return false;
397    }
398
399    return true;
400}
401
402void AssignAnalyzer::AnalyzeStat(const ir::AstNode *node)
403{
404    if (node == nullptr) {
405        return;
406    }
407
408    AnalyzeNode(node);
409}
410
411void AssignAnalyzer::AnalyzeStats(const ArenaVector<ir::Statement *> &stats)
412{
413    for (const auto it : stats) {
414        AnalyzeStat(it);
415    }
416}
417
418void AssignAnalyzer::AnalyzeBlock(const ir::BlockStatement *blockStmt)
419{
420    ScopeGuard save(nextAdr_);
421
422    AnalyzeStats(blockStmt->Statements());
423}
424
425void AssignAnalyzer::AnalyzeStructDecl(const ir::ETSStructDeclaration *structDecl)
426{
427    AnalyzeNode(structDecl->Definition());
428}
429
430void AssignAnalyzer::AnalyzeClassDecl(const ir::ClassDeclaration *classDecl)
431{
432    AnalyzeNode(classDecl->Definition());
433}
434
435void AssignAnalyzer::AnalyzeClassDef(const ir::ClassDefinition *classDef)
436{
437    if (classDef == globalClass_ && globalClassIsVisited_) {
438        return;
439    }
440
441    SetOldPendingExits(PendingExits());
442
443    ScopeGuard save(firstAdr_, nextAdr_, classDef_, classFirstAdr_);
444
445    classDef_ = classDef;
446    firstAdr_ = nextAdr_;
447    classFirstAdr_ = nextAdr_;
448
449    ProcessClassDefStaticFields(classDef_);
450
451    // define all the instance fields
452    for (const auto it : classDef->Body()) {
453        if (it->IsClassProperty() && !it->IsStatic()) {
454            const auto prop = it->AsClassProperty();
455            NewVar(prop);
456            if (prop->Value() != nullptr) {
457                LetInit(prop);
458            }
459        }
460    }
461
462    CheckAnonymousClassCtor(classDef_);
463
464    // process all the methods
465    std::vector<const ir::AstNode *> methods;
466    for (const auto it : classDef->Body()) {
467        if (it->IsMethodDefinition()) {
468            const auto methodDef = it->AsMethodDefinition();
469            if (methodDef->Key()->AsIdentifier()->Name().Is(compiler::Signatures::INIT_METHOD)) {
470                // skip the special init method as we have already checked it
471                continue;
472            }
473
474            methods.push_back(methodDef);
475
476            for (const auto it2 : methodDef->Overloads()) {
477                methods.push_back(it2);
478            }
479        }
480    }
481
482    for (const auto it : methods) {
483        AnalyzeNode(it);
484    }
485
486    SetPendingExits(OldPendingExits());
487}
488
489// NOTE (pantos) awkward methods to conform method length/complexity requirements of CI...
490void AssignAnalyzer::ProcessClassDefStaticFields(const ir::ClassDefinition *classDef)
491{
492    // define all the static fields
493    for (const auto it : classDef->Body()) {
494        if (it->IsClassProperty() && it->IsStatic()) {
495            const auto prop = it->AsClassProperty();
496            NewVar(prop);
497            if (prop->Value() != nullptr) {
498                LetInit(prop);
499            }
500        }
501    }
502
503    // process all the static initializers
504    for (const auto it : classDef->Body()) {
505        if (it->IsClassStaticBlock() ||
506            (it->IsStatic() && it->IsMethodDefinition() &&
507             it->AsMethodDefinition()->Key()->AsIdentifier()->Name().Is(compiler::Signatures::INIT_METHOD))) {
508            AnalyzeNodes(it);
509            CheckPendingExits(false);
510        }
511    }
512
513    // verify all static const fields got initailized
514    if (classDef != globalClass_) {
515        for (int i = firstAdr_; i < nextAdr_; i++) {
516            const ir::AstNode *var = varDecls_[i];
517            if (var->IsStatic() && (var->IsConst() || CHECK_ALL_PROPERTIES)) {
518                CheckInit(var);
519            }
520        }
521    }
522}
523
524void AssignAnalyzer::CheckAnonymousClassCtor(const ir::ClassDefinition *classDef)
525{
526    if (classDef == globalClass_) {
527        return;
528    }
529
530    // NOTE(pantos) anonymous classes of new expressions has no default ctor right now
531    // but this feature might be completely removed from the spec...
532    bool hasCtor = false;
533    for (const auto it : classDef->Body()) {
534        if (it->IsMethodDefinition() && it->AsMethodDefinition()->IsConstructor()) {
535            hasCtor = true;
536            break;
537        }
538    }
539    if (!hasCtor) {
540        for (int i = firstAdr_; i < nextAdr_; i++) {
541            const ir::AstNode *var = varDecls_[i];
542            if (!var->IsStatic() && (var->IsConst() || CHECK_ALL_PROPERTIES)) {
543                CheckInit(var);
544            }
545        }
546    }
547}
548
549// NOTE(pantos) modified version of ETSChecker::CheckCyclicConstructorCall
550static bool IsInitialConstructor(const ir::AstNode *node)
551{
552    if (!node->IsMethodDefinition() || !node->AsMethodDefinition()->IsConstructor()) {
553        return false;
554    }
555
556    const auto methodDef = node->AsMethodDefinition();
557    if (methodDef->Function()->Body() == nullptr || methodDef->Function()->IsExternal()) {
558        return false;
559    }
560
561    const auto funcBody = node->AsMethodDefinition()->Function()->Body()->AsBlockStatement();
562
563    return !(!funcBody->Statements().empty() && funcBody->Statements()[0]->IsExpressionStatement() &&
564             funcBody->Statements()[0]->AsExpressionStatement()->GetExpression()->IsCallExpression() &&
565             funcBody->Statements()[0]
566                 ->AsExpressionStatement()
567                 ->GetExpression()
568                 ->AsCallExpression()
569                 ->Callee()
570                 ->IsThisExpression());
571}
572
573void AssignAnalyzer::AnalyzeMethodDef(const ir::MethodDefinition *methodDef)
574{
575    auto *func = methodDef->Function();
576
577    if (func->Body() == nullptr || func->IsProxy()) {
578        return;
579    }
580
581    Set initsPrev = inits_;
582    Set uninitsPrev = uninits_;
583
584    ScopeGuard save(firstAdr_, nextAdr_, returnAdr_, isInitialConstructor_);
585
586    isInitialConstructor_ = IsInitialConstructor(methodDef);
587    if (!isInitialConstructor_) {
588        firstAdr_ = nextAdr_;
589    }
590
591    AnalyzeStat(func->Body());
592
593    if (isInitialConstructor_) {
594        for (int i = firstAdr_; i < nextAdr_; i++) {
595            const ir::AstNode *var = varDecls_[i];
596            if (!var->IsStatic() && (var->IsConst() || CHECK_ALL_PROPERTIES)) {
597                CheckInit(var);
598            }
599        }
600    }
601
602    CheckPendingExits(true);
603
604    inits_ = initsPrev;
605    uninits_ = uninitsPrev;
606}
607
608void AssignAnalyzer::AnalyzeVarDef(const ir::VariableDeclaration *varDef)
609{
610    for (auto *var : varDef->Declarators()) {
611        NewVar(var);
612
613        if (var->Init() != nullptr) {
614            AnalyzeNode(var->Init());
615            LetInit(var);
616        }
617    }
618}
619
620void AssignAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *doWhileStmt)
621{
622    SetOldPendingExits(PendingExits());
623
624    Set initsSkip {};
625    Set uninitsSkip {};
626    int prevErrors = numErrors_;
627
628    for (int phase = 1; phase <= LOOP_PHASES; phase++) {
629        Set uninitsEntry = uninits_;
630        uninitsEntry.ExcludeFrom(nextAdr_);
631
632        AnalyzeStat(doWhileStmt->Body());
633
634        ResolveContinues(doWhileStmt);
635
636        AnalyzeCond(doWhileStmt->Test());
637
638        if (phase == 1) {
639            initsSkip = initsWhenFalse_;
640            uninitsSkip = uninitsWhenFalse_;
641        }
642
643        if (prevErrors != numErrors_ || phase == LOOP_PHASES ||
644            uninitsEntry.DiffSet(uninitsWhenTrue_).Next(firstAdr_) == -1) {
645            break;
646        }
647
648        inits_ = initsWhenTrue_;
649        uninits_ = uninitsEntry.AndSet(uninitsWhenTrue_);
650    }
651
652    inits_ = initsSkip;
653    uninits_ = uninitsSkip;
654
655    ResolveBreaks(doWhileStmt);
656}
657
658void AssignAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *whileStmt)
659{
660    SetOldPendingExits(PendingExits());
661
662    Set initsSkip {};
663    Set uninitsSkip {};
664    int prevErrors = numErrors_;
665
666    Set uninitsEntry = uninits_;
667    uninitsEntry.ExcludeFrom(nextAdr_);
668
669    for (int phase = 1; phase <= LOOP_PHASES; phase++) {
670        AnalyzeCond(whileStmt->Test());
671
672        if (phase == 1) {
673            initsSkip = initsWhenFalse_;
674            uninitsSkip = uninitsWhenFalse_;
675        }
676
677        inits_ = initsWhenTrue_;
678        uninits_ = uninitsWhenTrue_;
679
680        AnalyzeStat(whileStmt->Body());
681
682        ResolveContinues(whileStmt);
683
684        if (prevErrors != numErrors_ || phase == LOOP_PHASES || uninitsEntry.DiffSet(uninits_).Next(firstAdr_) == -1) {
685            break;
686        }
687
688        uninits_ = uninitsEntry.AndSet(uninits_);
689    }
690
691    inits_ = initsSkip;
692    uninits_ = uninitsSkip;
693
694    ResolveBreaks(whileStmt);
695}
696
697void AssignAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *forStmt)
698{
699    ScopeGuard save(nextAdr_);
700
701    AnalyzeNode(forStmt->Init());
702
703    Set initsSkip {};
704    Set uninitsSkip {};
705    int prevErrors = numErrors_;
706
707    SetOldPendingExits(PendingExits());
708
709    for (int phase = 1; phase <= LOOP_PHASES; phase++) {
710        Set uninitsEntry = uninits_;
711        uninitsEntry.ExcludeFrom(nextAdr_);
712
713        if (forStmt->Test() != nullptr) {
714            AnalyzeCond(forStmt->Test());
715
716            if (phase == 1) {
717                initsSkip = initsWhenFalse_;
718                uninitsSkip = uninitsWhenFalse_;
719            }
720
721            inits_ = initsWhenTrue_;
722            uninits_ = uninitsWhenTrue_;
723        } else if (phase == 1) {
724            initsSkip = inits_;
725            initsSkip.InclRange(firstAdr_, nextAdr_);
726            uninitsSkip = uninits_;
727            uninitsSkip.InclRange(firstAdr_, nextAdr_);
728        }
729
730        AnalyzeStat(forStmt->Body());
731
732        ResolveContinues(forStmt);
733
734        AnalyzeNode(forStmt->Update());
735
736        if (prevErrors != numErrors_ || phase == LOOP_PHASES || uninitsEntry.DiffSet(uninits_).Next(firstAdr_) == -1) {
737            break;
738        }
739
740        uninits_ = uninitsEntry.AndSet(uninits_);
741    }
742
743    inits_ = initsSkip;
744    uninits_ = uninitsSkip;
745
746    ResolveBreaks(forStmt);
747}
748
749void AssignAnalyzer::AnalyzeForOfLoop(const ir::ForOfStatement *forOfStmt)
750{
751    ScopeGuard save(nextAdr_);
752
753    if (forOfStmt->Left()->IsVariableDeclaration()) {
754        AnalyzeVarDef(forOfStmt->Left()->AsVariableDeclaration());
755        for (auto *var : forOfStmt->Left()->AsVariableDeclaration()->Declarators()) {
756            LetInit(var);
757        }
758    } else {
759        LetInit(forOfStmt->Left());
760    }
761
762    AnalyzeNode(forOfStmt->Right());
763
764    Set initsStart = inits_;
765    Set uninitsStart = uninits_;
766    int prevErrors = numErrors_;
767
768    SetOldPendingExits(PendingExits());
769
770    for (int phase = 1; phase <= LOOP_PHASES; phase++) {
771        Set uninitsEntry = uninits_;
772        uninitsEntry.ExcludeFrom(nextAdr_);
773
774        AnalyzeStat(forOfStmt->Body());
775
776        ResolveContinues(forOfStmt);
777
778        if (prevErrors != numErrors_ || phase == LOOP_PHASES || uninitsEntry.DiffSet(uninits_).Next(firstAdr_) == -1) {
779            break;
780        }
781
782        uninits_ = uninitsEntry.AndSet(uninits_);
783    }
784
785    inits_ = initsStart;
786    uninits_ = uninitsStart.AndSet(uninits_);
787
788    ResolveBreaks(forOfStmt);
789}
790
791void AssignAnalyzer::AnalyzeIf(const ir::IfStatement *ifStmt)
792{
793    AnalyzeCond(ifStmt->Test());
794
795    Set initsBeforeElse = initsWhenFalse_;
796    Set uninitsBeforeElse = uninitsWhenFalse_;
797    inits_ = initsWhenTrue_;
798    uninits_ = uninitsWhenTrue_;
799
800    AnalyzeStat(ifStmt->Consequent());
801
802    if (ifStmt->Alternate() != nullptr) {
803        Set initsAfterThen = inits_;
804        Set uninitsAfterThen = uninits_;
805        inits_ = initsBeforeElse;
806        uninits_ = uninitsBeforeElse;
807
808        AnalyzeStat(ifStmt->Alternate());
809
810        inits_.AndSet(initsAfterThen);
811        uninits_.AndSet(uninitsAfterThen);
812    } else {
813        inits_.AndSet(initsBeforeElse);
814        uninits_.AndSet(uninitsBeforeElse);
815    }
816}
817
818void AssignAnalyzer::AnalyzeLabelled(const ir::LabelledStatement *labelledStmt)
819{
820    SetOldPendingExits(PendingExits());
821
822    AnalyzeStat(labelledStmt->Body());
823
824    ResolveBreaks(labelledStmt);
825}
826
827void AssignAnalyzer::AnalyzeSwitch(const ir::SwitchStatement *switchStmt)
828{
829    SetOldPendingExits(PendingExits());
830
831    ScopeGuard save(nextAdr_);
832
833    AnalyzeNode(switchStmt->Discriminant());
834
835    Set initsSwitch = inits_;
836    Set uninitsSwitch = uninits_;
837
838    bool hasDefault = false;
839
840    for (const auto caseClause : switchStmt->Cases()) {
841        inits_ = initsSwitch;
842        uninits_ = uninits_.AndSet(uninitsSwitch);
843
844        if (caseClause->Test() == nullptr) {
845            hasDefault = true;
846        } else {
847            AnalyzeNode(caseClause->Test());
848        }
849
850        if (hasDefault) {
851            inits_ = initsSwitch;
852            uninits_ = uninits_.AndSet(uninitsSwitch);
853        }
854
855        AnalyzeStats(caseClause->Consequent());
856
857        for (const auto stmt : caseClause->Consequent()) {
858            if (!stmt->IsVariableDeclaration()) {
859                continue;
860            }
861            for (auto *var : stmt->AsVariableDeclaration()->Declarators()) {
862                NodeId adr = GetNodeId(var);
863                ASSERT(adr >= 0);
864                initsSwitch.Excl(adr);
865                uninitsSwitch.Incl(adr);
866            }
867        }
868
869        if (!hasDefault) {
870            inits_ = initsSwitch;
871            uninits_ = uninits_.AndSet(uninitsSwitch);
872        }
873    }
874
875    if (!hasDefault) {
876        inits_.AndSet(initsSwitch);
877    }
878
879    ResolveBreaks(switchStmt);
880}
881
882void AssignAnalyzer::AnalyzeTry(const ir::TryStatement *tryStmt)
883{
884    Set uninitsTryPrev = uninitsTry_;
885
886    PendingExitsVector prevPendingExits = PendingExits();
887    SetOldPendingExits(prevPendingExits);
888
889    Set initsTry = inits_;
890    uninitsTry_ = uninits_;
891
892    AnalyzeNode(tryStmt->Block());
893
894    uninitsTry_.AndSet(uninits_);
895
896    Set initsEnd = inits_;
897    Set uninitsEnd = uninits_;
898    int nextAdrCatch = nextAdr_;
899
900    Set initsCatchPrev = initsTry;  // NOLINT(performance-unnecessary-copy-initialization)
901    Set uninitsCatchPrev = uninitsTry_;
902
903    for (const auto catchClause : tryStmt->CatchClauses()) {
904        inits_ = initsCatchPrev;
905        uninits_ = uninitsCatchPrev;
906
907        AnalyzeNode(catchClause->Body());
908
909        initsEnd.AndSet(inits_);
910        uninitsEnd.AndSet(uninits_);
911        nextAdr_ = nextAdrCatch;
912    }
913
914    if (tryStmt->FinallyBlock() != nullptr) {
915        inits_ = initsTry;
916        uninits_ = uninitsTry_;
917
918        PendingExitsVector exits = PendingExits();
919        SetPendingExits(prevPendingExits);
920
921        AnalyzeNode(tryStmt->FinallyBlock());
922
923        if (tryStmt->FinallyCanCompleteNormally()) {
924            uninits_.AndSet(uninitsEnd);
925            for (auto exit : exits) {
926                exit.exitInits_.OrSet(inits_);
927                exit.exitUninits_.AndSet(uninits_);
928                PendingExits().push_back(exit);
929            }
930            inits_.OrSet(initsEnd);
931        }
932    } else {
933        inits_ = initsEnd;
934        uninits_ = uninitsEnd;
935
936        PendingExitsVector exits = PendingExits();
937        SetPendingExits(prevPendingExits);
938
939        for (const auto &exit : exits) {
940            PendingExits().push_back(exit);
941        }
942    }
943
944    uninitsTry_.AndSet(uninitsTryPrev).AndSet(uninits_);
945}
946
947void AssignAnalyzer::AnalyzeBreak(const ir::BreakStatement *breakStmt)
948{
949    RecordExit(AssignPendingExit(breakStmt, inits_, uninits_));
950}
951
952void AssignAnalyzer::AnalyzeContinue(const ir::ContinueStatement *contStmt)
953{
954    RecordExit(AssignPendingExit(contStmt, inits_, uninits_));
955}
956
957void AssignAnalyzer::AnalyzeReturn(const ir::ReturnStatement *retStmt)
958{
959    AnalyzeNode(retStmt->Argument());
960    RecordExit(AssignPendingExit(retStmt, inits_, uninits_));
961}
962
963void AssignAnalyzer::AnalyzeThrow(const ir::ThrowStatement *throwStmt)
964{
965    AnalyzeNode(throwStmt->Argument());
966    MarkDead();
967}
968
969void AssignAnalyzer::AnalyzeAssert(const ir::AssertStatement *assertStmt)
970{
971    Set initsExit = inits_;
972    Set uninitsExit = uninits_;
973
974    AnalyzeCond(assertStmt->Test());
975
976    uninitsExit.AndSet(uninitsWhenTrue_);
977
978    if (assertStmt->Second() != nullptr) {
979        inits_ = initsWhenFalse_;
980        uninits_ = uninitsWhenFalse_;
981        AnalyzeExpr(assertStmt->Second());
982    }
983
984    inits_ = initsExit;
985    uninits_ = uninitsExit;
986}
987
988void AssignAnalyzer::AnalyzeExpr(const ir::AstNode *node)
989{
990    if (node != nullptr) {
991        AnalyzeNode(node);
992        if (inits_.IsReset()) {
993            Merge();
994        }
995    }
996}
997
998void AssignAnalyzer::AnalyzeExprs(const ArenaVector<ir::Expression *> &exprs)
999{
1000    for (const auto it : exprs) {
1001        AnalyzeExpr(it);
1002    }
1003}
1004
1005void AssignAnalyzer::AnalyzeCond(const ir::AstNode *node)
1006{
1007    ASSERT(node->IsExpression());
1008    const ir::Expression *expr = node->AsExpression();
1009
1010    if (auto etype = expr->TsTypeOrError();
1011        etype != nullptr && etype->IsETSBooleanType() && etype->HasTypeFlag(TypeFlag::CONSTANT)) {
1012        const ETSBooleanType *condType = etype->AsETSBooleanType();
1013        if (inits_.IsReset()) {
1014            Merge();
1015        }
1016        if (condType->GetValue()) {
1017            initsWhenFalse_ = inits_;
1018            initsWhenFalse_.InclRange(firstAdr_, nextAdr_);
1019            uninitsWhenFalse_ = uninits_;
1020            uninitsWhenFalse_.InclRange(firstAdr_, nextAdr_);
1021            initsWhenTrue_ = inits_;
1022            uninitsWhenTrue_ = uninits_;
1023        } else {
1024            initsWhenTrue_ = inits_;
1025            initsWhenTrue_.InclRange(firstAdr_, nextAdr_);
1026            uninitsWhenTrue_ = uninits_;
1027            uninitsWhenTrue_.InclRange(firstAdr_, nextAdr_);
1028            initsWhenFalse_ = inits_;
1029            uninitsWhenFalse_ = uninits_;
1030        }
1031    } else {
1032        AnalyzeNode(node);
1033        if (!inits_.IsReset()) {
1034            Split(true);
1035        }
1036    }
1037
1038    inits_.Reset();
1039    uninits_.Reset();
1040}
1041
1042void AssignAnalyzer::AnalyzeId(const ir::Identifier *id)
1043{
1044    if (id->Parent()->IsProperty() && id->Parent()->AsProperty()->Key() == id &&
1045        id->Parent()->Parent()->IsObjectExpression()) {
1046        return;  // inside ObjectExpression
1047    }
1048
1049    if (id->Parent()->IsTypeofExpression() && id->Parent()->AsTypeofExpression()->Argument() == id) {
1050        return;  // according to the spec 'typeof' works on uninitialized variables too
1051    }
1052
1053    if (id->Parent()->IsBinaryExpression()) {
1054        const ir::BinaryExpression *binExpr = id->Parent()->AsBinaryExpression();
1055        if ((binExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_EQUAL ||
1056             binExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_NOT_EQUAL) &&
1057            (binExpr->Left()->IsNullLiteral() || binExpr->Right()->IsNullLiteral() ||
1058             binExpr->Left()->IsUndefinedLiteral() || binExpr->Right()->IsUndefinedLiteral())) {
1059            return;  // null/undefined comparison with == or != operators (e.g. in assert statement)
1060        }
1061    }
1062
1063    if (id->Parent()->IsMemberExpression()) {
1064        const ir::MemberExpression *membExpr = id->Parent()->AsMemberExpression();
1065        if (id == membExpr->Property() && !membExpr->Object()->IsThisExpression() &&
1066            membExpr->HasMemberKind(ir::MemberExpressionKind::PROPERTY_ACCESS)) {
1067            return;  // something.property
1068        }
1069    }
1070
1071    if (id->Variable() != nullptr) {
1072        CheckInit(id);
1073    }
1074}
1075
1076static bool IsIdentOrThisDotIdent(const ir::AstNode *node)
1077{
1078    return node->IsIdentifier() ||
1079           (node->IsMemberExpression() && node->AsMemberExpression()->Object()->IsThisExpression());
1080}
1081
1082void AssignAnalyzer::AnalyzeAssignExpr(const ir::AssignmentExpression *assignExpr)
1083{
1084    if (!IsIdentOrThisDotIdent(assignExpr->Left())) {
1085        AnalyzeExpr(assignExpr->Left());
1086    }
1087
1088    AnalyzeExpr(assignExpr->Right());
1089
1090    if (assignExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
1091        LetInit(assignExpr->Left());
1092    } else {
1093        CheckInit(assignExpr->Left());
1094    }
1095}
1096
1097void AssignAnalyzer::AnalyzeCondExpr(const ir::ConditionalExpression *condExpr)
1098{
1099    AnalyzeCond(condExpr->Test());
1100
1101    Set initsBeforeElse = initsWhenFalse_;
1102    Set uninitsBeforeElse = uninitsWhenFalse_;
1103    inits_ = initsWhenTrue_;
1104    uninits_ = uninitsWhenTrue_;
1105
1106    ASSERT(condExpr->Consequent()->TsType() && condExpr->Alternate()->TsType());
1107
1108    if (condExpr->Consequent()->TsType()->IsETSBooleanType() && condExpr->Alternate()->TsType()->IsETSBooleanType()) {
1109        AnalyzeCond(condExpr->Consequent());
1110
1111        Set initsAfterThenWhenTrue = initsWhenTrue_;
1112        Set initsAfterThenWhenFalse = initsWhenFalse_;
1113        Set uninitsAfterThenWhenTrue = uninitsWhenTrue_;
1114        Set uninitsAfterThenWhenFalse = uninitsWhenFalse_;
1115        inits_ = initsBeforeElse;
1116        uninits_ = uninitsBeforeElse;
1117
1118        AnalyzeCond(condExpr->Alternate());
1119
1120        initsWhenTrue_.AndSet(initsAfterThenWhenTrue);
1121        initsWhenFalse_.AndSet(initsAfterThenWhenFalse);
1122        uninitsWhenTrue_.AndSet(uninitsAfterThenWhenTrue);
1123        uninitsWhenFalse_.AndSet(uninitsAfterThenWhenFalse);
1124    } else {
1125        AnalyzeExpr(condExpr->Consequent());
1126
1127        Set initsAfterThen = inits_;
1128        Set uninitsAfterThen = uninits_;
1129        inits_ = initsBeforeElse;
1130        uninits_ = uninitsBeforeElse;
1131
1132        AnalyzeExpr(condExpr->Alternate());
1133
1134        inits_.AndSet(initsAfterThen);
1135        uninits_.AndSet(uninitsAfterThen);
1136    }
1137}
1138
1139void AssignAnalyzer::AnalyzeCallExpr(const ir::CallExpression *callExpr)
1140{
1141    AnalyzeExpr(callExpr->Callee());
1142    AnalyzeExprs(callExpr->Arguments());
1143}
1144
1145void AssignAnalyzer::AnalyzeMemberExpr(const ir::MemberExpression *membExpr)
1146{
1147    if (membExpr->Object()->IsThisExpression() && membExpr->HasMemberKind(ir::MemberExpressionKind::PROPERTY_ACCESS)) {
1148        CheckInit(membExpr);
1149    } else {
1150        AnalyzeNode(membExpr->Object());
1151        AnalyzeNode(membExpr->Property());
1152    }
1153}
1154
1155void AssignAnalyzer::AnalyzeNewClass(const ir::ETSNewClassInstanceExpression *newClass)
1156{
1157    AnalyzeExpr(newClass->GetTypeRef());
1158    AnalyzeExprs(newClass->GetArguments());
1159    AnalyzeNode(newClass->ClassDefinition());
1160}
1161
1162void AssignAnalyzer::AnalyzeUnaryExpr(const ir::UnaryExpression *unaryExpr)
1163{
1164    AnalyzeCond(unaryExpr->Argument());
1165
1166    switch (unaryExpr->OperatorType()) {
1167        case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: {
1168            Set t = initsWhenFalse_;
1169            initsWhenFalse_ = initsWhenTrue_;
1170            initsWhenTrue_ = t;
1171            t = uninitsWhenFalse_;
1172            uninitsWhenFalse_ = uninitsWhenTrue_;
1173            uninitsWhenTrue_ = t;
1174            break;
1175        }
1176        default: {
1177            AnalyzeExpr(unaryExpr->Argument());
1178            break;
1179        }
1180    }
1181}
1182
1183void AssignAnalyzer::AnalyzeBinaryExpr(const ir::BinaryExpression *binExpr)
1184{
1185    switch (binExpr->OperatorType()) {
1186        case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
1187            AnalyzeCond(binExpr->Left());
1188            Set initsWhenFalseLeft = initsWhenFalse_;
1189            Set uninitsWhenFalseLeft = uninitsWhenFalse_;
1190            inits_ = initsWhenTrue_;
1191            uninits_ = uninitsWhenTrue_;
1192            AnalyzeCond(binExpr->Right());
1193            initsWhenFalse_.AndSet(initsWhenFalseLeft);
1194            uninitsWhenFalse_.AndSet(uninitsWhenFalseLeft);
1195            break;
1196        }
1197        case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
1198            AnalyzeCond(binExpr->Left());
1199            Set initsWhenTrueLeft = initsWhenTrue_;
1200            Set uninitsWhenTrueLeft = uninitsWhenTrue_;
1201            inits_ = initsWhenFalse_;
1202            uninits_ = uninitsWhenFalse_;
1203            AnalyzeCond(binExpr->Right());
1204            initsWhenTrue_.AndSet(initsWhenTrueLeft);
1205            uninitsWhenTrue_.AndSet(uninitsWhenTrueLeft);
1206            break;
1207        }
1208        default: {
1209            AnalyzeExpr(binExpr->Left());
1210            AnalyzeExpr(binExpr->Right());
1211            break;
1212        }
1213    }
1214}
1215
1216void AssignAnalyzer::AnalyzeUpdateExpr(const ir::UpdateExpression *updateExpr)
1217{
1218    AnalyzeExpr(updateExpr->Argument());
1219    LetInit(updateExpr->Argument());
1220}
1221
1222void AssignAnalyzer::AnalyzeArrowFunctionExpr(const ir::ArrowFunctionExpression *arrowFuncExpr)
1223{
1224    // NOTE (pantos) handle lamdas correctly
1225    (void)arrowFuncExpr;
1226}
1227
1228util::StringView AssignAnalyzer::GetVariableType(const ir::AstNode *node) const
1229{
1230    switch (node->Type()) {
1231        case ir::AstNodeType::CLASS_PROPERTY:
1232            if (node->AsClassProperty()->Parent() == globalClass_) {
1233                return "variable";
1234            } else {
1235                return "property";
1236            }
1237        case ir::AstNodeType::VARIABLE_DECLARATOR:
1238            return "variable";
1239        default:
1240            UNREACHABLE();
1241    }
1242}
1243
1244util::StringView AssignAnalyzer::GetVariableName(const ir::AstNode *node) const
1245{
1246    switch (node->Type()) {
1247        case ir::AstNodeType::CLASS_PROPERTY:
1248            return node->AsClassProperty()->Id()->Name();
1249        case ir::AstNodeType::VARIABLE_DECLARATOR:
1250            return node->AsVariableDeclarator()->Id()->AsIdentifier()->Name();
1251        default:
1252            UNREACHABLE();
1253    }
1254}
1255
1256const lexer::SourcePosition &AssignAnalyzer::GetVariablePosition(const ir::AstNode *node) const
1257{
1258    switch (node->Type()) {
1259        case ir::AstNodeType::CLASS_PROPERTY:
1260            return node->AsClassProperty()->Key()->Start();
1261        case ir::AstNodeType::VARIABLE_DECLARATOR:
1262        default:
1263            return node->Start();
1264    }
1265}
1266
1267NodeId AssignAnalyzer::GetNodeId(const ir::AstNode *node) const
1268{
1269    auto res = nodeIdMap_.find(node);
1270    if (res != nodeIdMap_.end()) {
1271        return res->second;
1272    }
1273    return INVALID_ID;
1274}
1275
1276bool AssignAnalyzer::Trackable(const ir::AstNode *node) const
1277{
1278    switch (node->Type()) {
1279        case ir::AstNodeType::CLASS_PROPERTY:
1280        case ir::AstNodeType::VARIABLE_DECLARATOR:
1281            return true;
1282        default:
1283            return false;
1284    }
1285}
1286
1287bool AssignAnalyzer::IsConstUninitializedField(const ir::AstNode *node) const
1288{
1289    return node->IsClassProperty() && node->IsConst();
1290}
1291
1292bool AssignAnalyzer::IsConstUninitializedStaticField(const ir::AstNode *node) const
1293{
1294    return IsConstUninitializedField(node) && node->IsStatic();
1295}
1296
1297void AssignAnalyzer::NewVar(const ir::AstNode *node)
1298{
1299    if (!Trackable(node)) {
1300        return;
1301    }
1302
1303    if (GetNodeId(node) != INVALID_ID) {
1304        return;
1305    }
1306
1307    nodeIdMap_[node] = nextAdr_;
1308    varDecls_.reserve(nextAdr_ + 1);
1309    varDecls_.insert(varDecls_.begin() + nextAdr_, node);
1310    inits_.Excl(nextAdr_);
1311    uninits_.Incl(nextAdr_);
1312    ++nextAdr_;
1313}
1314
1315varbinder::Variable *AssignAnalyzer::GetBoundVariable(const ir::AstNode *node)
1316{
1317    varbinder::Variable *ret = nullptr;
1318
1319    if (node->IsClassProperty()) {
1320        ret = node->AsClassProperty()->Id()->Variable();
1321    } else if (node->IsVariableDeclarator()) {
1322        ret = node->AsVariableDeclarator()->Id()->AsIdentifier()->Variable();
1323    } else {
1324        UNREACHABLE();
1325    }
1326
1327    return ret;
1328}
1329
1330const ir::AstNode *AssignAnalyzer::GetDeclaringNode(const ir::AstNode *node)
1331{
1332    if (node->IsClassProperty() || node->IsVariableDeclarator()) {
1333        return node;
1334    }
1335
1336    const ir::AstNode *ret = nullptr;
1337
1338    if (node->IsMemberExpression()) {
1339        const ir::MemberExpression *membExpr = node->AsMemberExpression();
1340        if (membExpr->PropVar() != nullptr) {
1341            if (membExpr->PropVar()->Declaration() != nullptr) {
1342                ret = membExpr->PropVar()->Declaration()->Node();
1343            }
1344        }
1345    } else if (node->IsIdentifier()) {
1346        const ir::Identifier *id = node->AsIdentifier();
1347        if (id->Variable() != nullptr) {
1348            if (id->Variable()->Declaration() != nullptr) {
1349                ret = id->Variable()->Declaration()->Node();
1350            }
1351        }
1352    }
1353
1354    if (ret != nullptr) {
1355        if (ret->IsIdentifier() && ret->Parent()->IsVariableDeclarator() &&
1356            ret == ret->Parent()->AsVariableDeclarator()->Id()) {
1357            ret = ret->Parent();
1358        }
1359    }
1360
1361    return ret;
1362}
1363
1364bool AssignAnalyzer::VariableHasDefaultValue(const ir::AstNode *node)
1365{
1366    ASSERT(node != nullptr);
1367
1368    const checker::Type *type = nullptr;
1369    bool isNonReadonlyField = false;
1370
1371    if (node->IsClassProperty()) {
1372        type = node->AsClassProperty()->TsType();
1373        isNonReadonlyField = !node->IsReadonly();  // NOTE(pantos) readonly is true, const is not set?
1374    } else if (node->IsVariableDeclarator()) {
1375        varbinder::Variable *variable = GetBoundVariable(node);
1376        if (variable != nullptr) {
1377            type = variable->TsType();
1378        }
1379    } else {
1380        UNREACHABLE();
1381    }
1382
1383    return type != nullptr &&
1384           (type->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE) ||
1385            (type->PossiblyETSNullish() && (!type->HasTypeFlag(checker::TypeFlag::GENERIC) ||
1386                                            (isNonReadonlyField && !CHECK_GENERIC_NON_READONLY_PROPERTIES))));
1387}
1388
1389void AssignAnalyzer::LetInit(const ir::AstNode *node)
1390{
1391    const ir::AstNode *declNode = GetDeclaringNode(node);
1392
1393    if (declNode == nullptr || declNode->IsDeclare()) {
1394        return;
1395    }
1396
1397    NodeId adr = GetNodeId(declNode);
1398    if (adr == INVALID_ID) {
1399        return;
1400    }
1401
1402    if (node != declNode && declNode->IsConst()) {
1403        // check reassignment of readonly properties
1404        util::StringView type = GetVariableType(declNode);
1405        util::StringView name = GetVariableName(declNode);
1406        const lexer::SourcePosition &pos = GetVariablePosition(node);
1407
1408        auto uninit = [this](NodeId a) {
1409            uninits_.Excl(a);
1410            if (!inits_.IsMember(a)) {
1411                uninitsTry_.Excl(a);
1412            }
1413        };
1414
1415        if (classDef_ == globalClass_ || (adr < classFirstAdr_ || adr >= firstAdr_)) {
1416            if (declNode->IsClassProperty() && classDef_ != declNode->Parent()) {
1417                Warning({"Cannot assign to '", name, "' because it is a read-only property."}, pos);
1418            } else if (!uninits_.IsMember(adr)) {
1419                Warning({Capitalize(type).c_str(), " '", name, "' might already have been assigned."}, pos);
1420            } else {
1421                uninit(adr);
1422            }
1423        }
1424    }
1425
1426    inits_.Incl(adr);
1427}
1428
1429void AssignAnalyzer::CheckInit(const ir::AstNode *node)
1430{
1431    const ir::AstNode *declNode = GetDeclaringNode(node);
1432
1433    if (declNode == nullptr || declNode->IsDeclare()) {
1434        return;
1435    }
1436
1437    NodeId adr = GetNodeId(declNode);
1438    if (adr == INVALID_ID) {
1439        return;
1440    }
1441
1442    if (VariableHasDefaultValue(declNode)) {
1443        // no explicit init is required (primitive, nullish)
1444        return;
1445    }
1446
1447    if (declNode->IsClassProperty()) {
1448        if (!CHECK_ALL_PROPERTIES && !declNode->IsConst()) {
1449            // non readonly property
1450            return;
1451        }
1452
1453        if (declNode->Parent() == globalClass_) {
1454            // NOTE(pantos) dont check global variable accesses
1455            return;
1456        }
1457
1458        if (declNode->Parent() != classDef_) {
1459            // property of an other class
1460            return;
1461        }
1462    }
1463
1464    if (classDef_ == globalClass_ || (adr < classFirstAdr_ || adr >= firstAdr_)) {
1465        if (!inits_.IsMember(adr)) {
1466            if (WARN_NO_INIT_ONCE_PER_VARIABLE && !foundErrors_.insert(declNode).second) {
1467                return;
1468            }
1469
1470            util::StringView type = GetVariableType(declNode);
1471            util::StringView name = GetVariableName(declNode);
1472            const lexer::SourcePosition &pos = GetVariablePosition(node);
1473
1474            std::stringstream ss;
1475            if (node->IsClassProperty()) {
1476                ss << "Property '" << name << "' might not have been initialized.";
1477            } else {
1478                ss << Capitalize(type) << " '" << name << "' is used before being assigned.";
1479            }
1480
1481            Warning(ss.str(), pos);
1482        }
1483    }
1484}
1485
1486void AssignAnalyzer::Split(const bool setToNull)
1487{
1488    initsWhenFalse_ = inits_;
1489    uninitsWhenFalse_ = uninits_;
1490    initsWhenTrue_ = inits_;
1491    uninitsWhenTrue_ = uninits_;
1492    if (setToNull) {
1493        inits_.Reset();
1494        uninits_.Reset();
1495    }
1496}
1497
1498void AssignAnalyzer::Merge()
1499{
1500    inits_ = initsWhenFalse_.AndSet(initsWhenTrue_);
1501    uninits_ = uninitsWhenFalse_.AndSet(uninitsWhenTrue_);
1502}
1503
1504void AssignAnalyzer::CheckPendingExits(bool inMethod)
1505{
1506    PendingExitsVector exits = PendingExits();
1507
1508    for (auto &it : exits) {
1509        // NOTE(pantos) pending exits should be refactored, break/continue may stay in this
1510        if (inMethod && !it.Node()->IsReturnStatement()) {
1511            continue;
1512        }
1513
1514        if (inMethod && isInitialConstructor_) {
1515            inits_ = it.exitInits_;
1516
1517            for (int i = firstAdr_; i < nextAdr_; i++) {
1518                CheckInit(varDecls_[i]);
1519            }
1520        }
1521    }
1522
1523    ClearPendingExits();
1524}
1525
1526void AssignAnalyzer::MarkDead()
1527{
1528    if (!isInitialConstructor_) {
1529        inits_.InclRange(returnAdr_, nextAdr_);
1530    } else {
1531        for (int address = returnAdr_; address < nextAdr_; address++) {
1532            if (!IsConstUninitializedStaticField(varDecls_[address])) {
1533                inits_.Incl(address);
1534            }
1535        }
1536    }
1537    uninits_.InclRange(returnAdr_, nextAdr_);
1538}
1539
1540}  // namespace ark::es2panda::checker
1541