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