• 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 
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