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