• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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