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 
36 namespace ark::es2panda::checker {
CheckTruthinessOfType(Type *type, lexer::SourcePosition lineInfo)37 void 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 
CheckNonNullType(Type *type, lexer::SourcePosition lineInfo)44 Type *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 
GetBaseTypeOfLiteralType(Type *type)57 Type *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 
CheckReferenceExpression(ir::Expression *expr, const char *invalidReferenceMsg, const char *invalidOptionalChainMsg)94 void 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 
CheckTestingKnownTruthyCallableOrAwaitableType([[maybe_unused]] ir::Expression *condExpr, [[maybe_unused]] Type *type, [[maybe_unused]] ir::AstNode *body)114 void 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 
ExtractDefinitelyFalsyTypes(Type *type)121 Type *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 
RemoveDefinitelyFalsyTypes(Type *type)158 Type *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 
GetFalsyFlags(Type *type)188 TypeFlag 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 
IsVariableUsedInConditionBody(ir::AstNode *parent, varbinder::Variable *searchVar)220 bool 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 
FindVariableInBinaryExpressionChain(ir::AstNode *parent, varbinder::Variable *searchVar)245 bool 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 
IsVariableUsedInBinaryExpressionChain(ir::AstNode *parent, varbinder::Variable *searchVar)265 bool 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 
ThrowBinaryLikeError(lexer::TokenType op, Type *leftType, Type *rightType, lexer::SourcePosition lineInfo)279 void 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 
ThrowAssignmentError(Type *source, Type *target, lexer::SourcePosition lineInfo, bool isAsSrcLeftType)290 void 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 
GetUnaryResultType(Type *operandType)299 Type *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 
ElaborateElementwise(Type *targetType, ir::Expression *sourceNode, const lexer::SourcePosition &pos)313 void 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 
InferSimpleVariableDeclaratorType(ir::VariableDeclarator *declarator)336 void 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 
GetTypeVar(varbinder::Decl *decl)357 void 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 
GetTypeParam(varbinder::Variable *var, varbinder::Decl *decl)371 void 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 
GetTypeEnum(varbinder::Variable *var, varbinder::Decl *decl)401 void 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 
GetDeclTsType(varbinder::Variable *var, varbinder::Decl *decl)417 Type *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 
GetTypeOfVariable(varbinder::Variable *var)468 Type *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 
GetTypeFromClassOrInterfaceReference([[maybe_unused]] ir::TSTypeReference *node, varbinder::Variable *var)488 Type *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 
GetTypeFromTypeAliasReference(ir::TSTypeReference *node, varbinder::Variable *var)503 Type *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 
GetTypeReferenceType(ir::TSTypeReference *node, varbinder::Variable *var)524 Type *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