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