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