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