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