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 "ir/typeNode.h"
17#include "ir/base/scriptFunction.h"
18#include "ir/expressions/assignmentExpression.h"
19#include "ir/expressions/binaryExpression.h"
20#include "ir/expressions/memberExpression.h"
21#include "ir/expressions/identifier.h"
22#include "ir/statements/variableDeclarator.h"
23#include "ir/ts/tsQualifiedName.h"
24#include "ir/base/tsPropertySignature.h"
25#include "ir/ts/tsTypeAliasDeclaration.h"
26#include "ir/ts/tsTypeReference.h"
27#include "ir/ts/tsTypeParameterDeclaration.h"
28#include "ir/ts/tsTypeParameter.h"
29#include "varbinder/variable.h"
30#include "varbinder/scope.h"
31#include "util/helpers.h"
32
33#include "checker/ts/typeElaborationContext.h"
34#include "checker/TSchecker.h"
35
36namespace ark::es2panda::checker {
37void TSChecker::CheckTruthinessOfType(Type *type, lexer::SourcePosition lineInfo)
38{
39    if (type->IsVoidType()) {
40        ThrowTypeError("An expression of type void cannot be tested for truthiness", lineInfo);
41    }
42}
43
44Type *TSChecker::CheckNonNullType(Type *type, lexer::SourcePosition lineInfo)
45{
46    if (type->IsNullType()) {
47        ThrowTypeError("Object is possibly 'null'.", lineInfo);
48    }
49
50    if (type->IsUndefinedType()) {
51        ThrowTypeError("Object is possibly 'undefined'.", lineInfo);
52    }
53
54    return type;
55}
56
57Type *TSChecker::GetBaseTypeOfLiteralType(Type *type)
58{
59    if (HasStatus(CheckerStatus::KEEP_LITERAL_TYPE)) {
60        return type;
61    }
62
63    if (type->IsStringLiteralType()) {
64        return GlobalStringType();
65    }
66
67    if (type->IsNumberLiteralType()) {
68        return GlobalNumberType();
69    }
70
71    if (type->IsBooleanLiteralType()) {
72        return GlobalBooleanType();
73    }
74
75    if (type->IsBigintLiteralType()) {
76        return GlobalBigintType();
77    }
78
79    if (type->IsUnionType()) {
80        auto &constituentTypes = type->AsUnionType()->ConstituentTypes();
81        ArenaVector<Type *> newConstituentTypes(Allocator()->Adapter());
82
83        newConstituentTypes.reserve(constituentTypes.size());
84        for (auto *it : constituentTypes) {
85            newConstituentTypes.push_back(GetBaseTypeOfLiteralType(it));
86        }
87
88        return CreateUnionType(std::move(newConstituentTypes));
89    }
90
91    return type;
92}
93
94void TSChecker::CheckReferenceExpression(ir::Expression *expr, const char *invalidReferenceMsg,
95                                         const char *invalidOptionalChainMsg)
96{
97    if (expr->IsIdentifier()) {
98        const util::StringView &name = expr->AsIdentifier()->Name();
99        auto result = Scope()->Find(name);
100        ASSERT(result.variable);
101
102        if (result.variable->HasFlag(varbinder::VariableFlags::ENUM_LITERAL)) {
103            ThrowTypeError({"Cannot assign to '", name, "' because it is not a variable."}, expr->Start());
104        }
105    } else if (!expr->IsMemberExpression()) {
106        if (expr->IsChainExpression()) {
107            ThrowTypeError(invalidOptionalChainMsg, expr->Start());
108        }
109
110        ThrowTypeError(invalidReferenceMsg, expr->Start());
111    }
112}
113
114void TSChecker::CheckTestingKnownTruthyCallableOrAwaitableType([[maybe_unused]] ir::Expression *condExpr,
115                                                               [[maybe_unused]] Type *type,
116                                                               [[maybe_unused]] ir::AstNode *body)
117{
118    // NOTE: aszilagyi. rework this
119}
120
121Type *TSChecker::ExtractDefinitelyFalsyTypes(Type *type)
122{
123    if (type->IsStringType()) {
124        return GlobalEmptyStringType();
125    }
126
127    if (type->IsNumberType()) {
128        return GlobalZeroType();
129    }
130
131    if (type->IsBigintType()) {
132        return GlobalZeroBigintType();
133    }
134
135    if (type == GlobalFalseType() || type->DefinitelyETSNullish() || type->HasTypeFlag(TypeFlag::ANY_OR_UNKNOWN) ||
136        type->HasTypeFlag(TypeFlag::VOID) ||
137        (type->IsStringLiteralType() && IsTypeIdenticalTo(type, GlobalEmptyStringType())) ||
138        (type->IsNumberLiteralType() && IsTypeIdenticalTo(type, GlobalZeroType())) ||
139        (type->IsBigintLiteralType() && IsTypeIdenticalTo(type, GlobalZeroBigintType()))) {
140        return type;
141    }
142
143    if (type->IsUnionType()) {
144        auto &constituentTypes = type->AsUnionType()->ConstituentTypes();
145        ArenaVector<Type *> newConstituentTypes(Allocator()->Adapter());
146
147        newConstituentTypes.reserve(constituentTypes.size());
148        for (auto &it : constituentTypes) {
149            newConstituentTypes.push_back(ExtractDefinitelyFalsyTypes(it));
150        }
151
152        return CreateUnionType(std::move(newConstituentTypes));
153    }
154
155    return GlobalNeverType();
156}
157
158Type *TSChecker::RemoveDefinitelyFalsyTypes(Type *type)
159{
160    if ((static_cast<uint64_t>(GetFalsyFlags(type)) & static_cast<uint64_t>(TypeFlag::DEFINITELY_FALSY)) != 0U) {
161        if (!type->IsUnionType()) {
162            return GlobalNeverType();
163        }
164
165        auto &constituentTypes = type->AsUnionType()->ConstituentTypes();
166        ArenaVector<Type *> newConstituentTypes(Allocator()->Adapter());
167
168        for (auto &it : constituentTypes) {
169            if ((static_cast<uint64_t>(GetFalsyFlags(it)) & static_cast<uint64_t>(TypeFlag::DEFINITELY_FALSY)) == 0U) {
170                newConstituentTypes.push_back(it);
171            }
172        }
173
174        if (newConstituentTypes.empty()) {
175            return GlobalNeverType();
176        }
177
178        if (newConstituentTypes.size() == 1) {
179            return newConstituentTypes[0];
180        }
181
182        return CreateUnionType(std::move(newConstituentTypes));
183    }
184
185    return type;
186}
187
188TypeFlag TSChecker::GetFalsyFlags(Type *type)
189{
190    if (type->IsStringLiteralType()) {
191        return type->AsStringLiteralType()->Value().Empty() ? TypeFlag::STRING_LITERAL : TypeFlag::NONE;
192    }
193
194    if (type->IsNumberLiteralType()) {
195        return type->AsNumberLiteralType()->Value() == 0 ? TypeFlag::NUMBER_LITERAL : TypeFlag::NONE;
196    }
197
198    if (type->IsBigintLiteralType()) {
199        return type->AsBigintLiteralType()->Value() == "0n" ? TypeFlag::BIGINT_LITERAL : TypeFlag::NONE;
200    }
201
202    if (type->IsBooleanLiteralType()) {
203        return type->AsBooleanLiteralType()->Value() ? TypeFlag::NONE : TypeFlag::BOOLEAN_LITERAL;
204    }
205
206    if (type->IsUnionType()) {
207        auto &constituentTypes = type->AsUnionType()->ConstituentTypes();
208        TypeFlag returnFlag = TypeFlag::NONE;
209
210        for (auto &it : constituentTypes) {
211            returnFlag |= GetFalsyFlags(it);
212        }
213
214        return returnFlag;
215    }
216
217    return static_cast<TypeFlag>(type->TypeFlags() & TypeFlag::POSSIBLY_FALSY);
218}
219
220bool TSChecker::IsVariableUsedInConditionBody(ir::AstNode *parent, varbinder::Variable *searchVar)
221{
222    bool found = false;
223
224    parent->Iterate([this, searchVar, &found](ir::AstNode *childNode) -> void {
225        varbinder::Variable *resultVar = nullptr;
226        if (childNode->IsIdentifier()) {
227            auto result = Scope()->Find(childNode->AsIdentifier()->Name());
228            ASSERT(result.variable);
229            resultVar = result.variable;
230        }
231
232        if (searchVar == resultVar) {
233            found = true;
234            return;
235        }
236
237        if (!childNode->IsMemberExpression()) {
238            IsVariableUsedInConditionBody(childNode, searchVar);
239        }
240    });
241
242    return found;
243}
244
245bool TSChecker::FindVariableInBinaryExpressionChain(ir::AstNode *parent, varbinder::Variable *searchVar)
246{
247    bool found = false;
248
249    parent->Iterate([this, searchVar, &found](ir::AstNode *childNode) -> void {
250        if (childNode->IsIdentifier()) {
251            auto result = Scope()->Find(childNode->AsIdentifier()->Name());
252            ASSERT(result.variable);
253            if (result.variable == searchVar) {
254                found = true;
255                return;
256            }
257        }
258
259        FindVariableInBinaryExpressionChain(childNode, searchVar);
260    });
261
262    return found;
263}
264
265bool TSChecker::IsVariableUsedInBinaryExpressionChain(ir::AstNode *parent, varbinder::Variable *searchVar)
266{
267    while (parent->IsBinaryExpression() &&
268           parent->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
269        if (FindVariableInBinaryExpressionChain(parent, searchVar)) {
270            return true;
271        }
272
273        parent = parent->Parent();
274    }
275
276    return false;
277}
278
279void TSChecker::ThrowBinaryLikeError(lexer::TokenType op, Type *leftType, Type *rightType,
280                                     lexer::SourcePosition lineInfo)
281{
282    if (!HasStatus(CheckerStatus::IN_CONST_CONTEXT)) {
283        ThrowTypeError({"operator ", op, " cannot be applied to types ", leftType, " and ", AsSrc(rightType)},
284                       lineInfo);
285    }
286
287    ThrowTypeError({"operator ", op, " cannot be applied to types ", leftType, " and ", rightType}, lineInfo);
288}
289
290void TSChecker::ThrowAssignmentError(Type *source, Type *target, lexer::SourcePosition lineInfo, bool isAsSrcLeftType)
291{
292    if (isAsSrcLeftType || !target->HasTypeFlag(TypeFlag::LITERAL)) {
293        ThrowTypeError({"Type '", AsSrc(source), "' is not assignable to type '", target, "'."}, lineInfo);
294    }
295
296    ThrowTypeError({"Type '", source, "' is not assignable to type '", target, "'."}, lineInfo);
297}
298
299Type *TSChecker::GetUnaryResultType(Type *operandType)
300{
301    if (checker::TSChecker::MaybeTypeOfKind(operandType, checker::TypeFlag::BIGINT_LIKE)) {
302        if (operandType->HasTypeFlag(checker::TypeFlag::UNION_OR_INTERSECTION) &&
303            checker::TSChecker::MaybeTypeOfKind(operandType, checker::TypeFlag::NUMBER_LIKE)) {
304            return GlobalNumberOrBigintType();
305        }
306
307        return GlobalBigintType();
308    }
309
310    return GlobalNumberType();
311}
312
313void TSChecker::ElaborateElementwise(Type *targetType, ir::Expression *sourceNode, const lexer::SourcePosition &pos)
314{
315    auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE | CheckerStatus::KEEP_LITERAL_TYPE);
316
317    Type *sourceType = CheckTypeCached(sourceNode);
318
319    if (IsTypeAssignableTo(sourceType, targetType)) {
320        return;
321    }
322
323    if (targetType->IsArrayType() && sourceNode->IsArrayExpression()) {
324        ArrayElaborationContext(this, targetType, sourceType, sourceNode, pos).Start();
325    } else if (targetType->IsObjectType() || targetType->IsUnionType()) {
326        if (sourceNode->IsObjectExpression()) {
327            ObjectElaborationContext(this, targetType, sourceType, sourceNode, pos).Start();
328        } else if (sourceNode->IsArrayExpression()) {
329            ArrayElaborationContext(this, targetType, sourceType, sourceNode, pos).Start();
330        }
331    }
332
333    ThrowAssignmentError(sourceType, targetType, pos);
334}
335
336void TSChecker::InferSimpleVariableDeclaratorType(ir::VariableDeclarator *declarator)
337{
338    ASSERT(declarator->Id()->IsIdentifier());
339
340    varbinder::Variable *var = declarator->Id()->AsIdentifier()->Variable();
341    ASSERT(var);
342
343    if (declarator->Id()->AsIdentifier()->TypeAnnotation() != nullptr) {
344        var->SetTsType(declarator->Id()->AsIdentifier()->TypeAnnotation()->GetType(this));
345        return;
346    }
347
348    if (declarator->Init() != nullptr) {
349        var->SetTsType(CheckTypeCached(declarator->Init()));
350        return;
351    }
352
353    ThrowTypeError({"Variable ", declarator->Id()->AsIdentifier()->Name(), " implicitly has an any type."},
354                   declarator->Id()->Start());
355}
356
357void TSChecker::GetTypeVar(varbinder::Decl *decl)
358{
359    ir::AstNode *declarator =
360        util::Helpers::FindAncestorGivenByType(decl->Node(), ir::AstNodeType::VARIABLE_DECLARATOR);
361    ASSERT(declarator);
362
363    if (declarator->AsVariableDeclarator()->Id()->IsIdentifier()) {
364        InferSimpleVariableDeclaratorType(declarator->AsVariableDeclarator());
365        return;
366    }
367
368    declarator->Check(this);
369}
370
371void TSChecker::GetTypeParam(varbinder::Variable *var, varbinder::Decl *decl)
372{
373    ir::AstNode *declaration = FindAncestorUntilGivenType(decl->Node(), ir::AstNodeType::SCRIPT_FUNCTION);
374
375    if (declaration->IsIdentifier()) {
376        auto *ident = declaration->AsIdentifier();
377        if (ident->TypeAnnotation() != nullptr) {
378            ASSERT(ident->Variable() == var);
379            var->SetTsType(ident->TypeAnnotation()->GetType(this));
380            return;
381        }
382
383        ThrowTypeError({"Parameter ", ident->Name(), " implicitly has an 'any' type."}, ident->Start());
384    }
385
386    if (declaration->IsAssignmentPattern() && declaration->AsAssignmentPattern()->Left()->IsIdentifier()) {
387        ir::Identifier *ident = declaration->AsAssignmentPattern()->Left()->AsIdentifier();
388
389        if (ident->TypeAnnotation() != nullptr) {
390            ASSERT(ident->Variable() == var);
391            var->SetTsType(ident->TypeAnnotation()->GetType(this));
392            return;
393        }
394
395        var->SetTsType(declaration->AsAssignmentPattern()->Right()->Check(this));
396    }
397
398    CheckFunctionParameter(declaration->AsExpression(), nullptr);
399}
400
401void TSChecker::GetTypeEnum(varbinder::Variable *var, varbinder::Decl *decl)
402{
403    ASSERT(var->IsEnumVariable());
404    varbinder::EnumVariable *enumVar = var->AsEnumVariable();
405
406    if (std::holds_alternative<bool>(enumVar->Value())) {
407        ThrowTypeError(
408            "A member initializer in a enum declaration cannot reference members declared after it, "
409            "including "
410            "members defined in other enums.",
411            decl->Node()->Start());
412    }
413
414    var->SetTsType(std::holds_alternative<double>(enumVar->Value()) ? GlobalNumberType() : GlobalStringType());
415}
416
417Type *TSChecker::GetDeclTsType(varbinder::Variable *var, varbinder::Decl *decl)
418{
419    switch (decl->Type()) {
420        case varbinder::DeclType::CONST:
421        case varbinder::DeclType::LET: {
422            if (!decl->Node()->Parent()->IsTSTypeQuery()) {
423                ThrowTypeError({"Block-scoped variable '", var->Name(), "' used before its declaration"},
424                               decl->Node()->Start());
425                break;
426            }
427
428            [[fallthrough]];
429        }
430        case varbinder::DeclType::VAR: {
431            GetTypeVar(decl);
432            break;
433        }
434        case varbinder::DeclType::PROPERTY: {
435            var->SetTsType(decl->Node()->AsTSPropertySignature()->TypeAnnotation()->GetType(this));
436            break;
437        }
438        case varbinder::DeclType::METHOD: {
439            auto *signatureInfo = Allocator()->New<checker::SignatureInfo>(Allocator());
440            auto *callSignature = Allocator()->New<checker::Signature>(signatureInfo, GlobalAnyType());
441            var->SetTsType(CreateFunctionTypeWithSignature(callSignature));
442            break;
443        }
444        case varbinder::DeclType::FUNC: {
445            checker::ScopeContext scopeCtx(this, decl->Node()->AsScriptFunction()->Scope());
446            InferFunctionDeclarationType(decl->AsFunctionDecl(), var);
447            break;
448        }
449        case varbinder::DeclType::PARAM: {
450            GetTypeParam(var, decl);
451            break;
452        }
453        case varbinder::DeclType::ENUM: {
454            GetTypeEnum(var, decl);
455            break;
456        }
457        case varbinder::DeclType::ENUM_LITERAL: {
458            UNREACHABLE();  // NOTE: aszilagyi.
459        }
460        default: {
461            break;
462        }
463    }
464
465    return var->TsType();
466}
467
468Type *TSChecker::GetTypeOfVariable(varbinder::Variable *var)
469{
470    if (var->TsType() != nullptr) {
471        return var->TsType();
472    }
473
474    varbinder::Decl *decl = var->Declaration();
475
476    TypeStackElement tse(
477        this, decl->Node(),
478        std::initializer_list<TypeErrorMessageElement> {
479            "'", var->Name(), "' is referenced directly or indirectly in its ", "own initializer ot type annotation."},
480        decl->Node()->Start());
481    if (tse.HasTypeError()) {
482        return GlobalErrorType();
483    }
484
485    return GetDeclTsType(var, decl);
486}
487
488Type *TSChecker::GetTypeFromClassOrInterfaceReference([[maybe_unused]] ir::TSTypeReference *node,
489                                                      varbinder::Variable *var)
490{
491    Type *resolvedType = var->TsType();
492
493    if (resolvedType == nullptr) {
494        ObjectDescriptor *desc = Allocator()->New<ObjectDescriptor>(Allocator());
495        resolvedType = Allocator()->New<InterfaceType>(Allocator(), var->Name(), desc);
496        resolvedType->SetVariable(var);
497        var->SetTsType(resolvedType);
498    }
499
500    return resolvedType;
501}
502
503Type *TSChecker::GetTypeFromTypeAliasReference(ir::TSTypeReference *node, varbinder::Variable *var)
504{
505    Type *resolvedType = var->TsType();
506
507    if (resolvedType != nullptr) {
508        return resolvedType;
509    }
510
511    TypeStackElement tse(this, var, {"Type alias ", var->Name(), " circularly refences itself"}, node->Start());
512    if (tse.HasTypeError()) {
513        return GlobalErrorType();
514    }
515
516    ASSERT(var->Declaration()->Node() && var->Declaration()->Node()->IsTSTypeAliasDeclaration());
517    ir::TSTypeAliasDeclaration *declaration = var->Declaration()->Node()->AsTSTypeAliasDeclaration();
518    resolvedType = declaration->TypeAnnotation()->GetType(this);
519    var->SetTsType(resolvedType);
520
521    return resolvedType;
522}
523
524Type *TSChecker::GetTypeReferenceType(ir::TSTypeReference *node, varbinder::Variable *var)
525{
526    ASSERT(var->Declaration());
527    varbinder::Decl *decl = var->Declaration();
528
529    if (decl->IsInterfaceDecl()) {
530        return GetTypeFromClassOrInterfaceReference(node, var);
531    }
532
533    if (decl->IsTypeAliasDecl()) {
534        return GetTypeFromTypeAliasReference(node, var);
535    }
536
537    ThrowTypeError("This reference refers to a value, but is being used as a type here. Did you mean to use 'typeof'?",
538                   node->Start());
539    return nullptr;
540}
541}  // namespace ark::es2panda::checker
542