• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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     ES2PANDA_ASSERT(type != nullptr);
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         ES2PANDA_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             ES2PANDA_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             ES2PANDA_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(const util::DiagnosticMessageParams & list,const lexer::SourcePosition & pos)279 void TSChecker::ThrowTypeError(const util::DiagnosticMessageParams &list, const lexer::SourcePosition &pos)
280 {
281     DiagnosticEngine().ThrowSemanticError(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     DiagnosticEngine().ThrowSemanticError(message, pos);
287 }
288 
ThrowBinaryLikeError(lexer::TokenType op,Type * leftType,Type * rightType,lexer::SourcePosition lineInfo)289 void TSChecker::ThrowBinaryLikeError(lexer::TokenType op, Type *leftType, Type *rightType,
290                                      lexer::SourcePosition lineInfo)
291 {
292     if (!HasStatus(CheckerStatus::IN_CONST_CONTEXT)) {
293         ThrowTypeError({"operator ", op, " cannot be applied to types ", leftType, " and ", util::AsSrc(rightType)},
294                        lineInfo);
295     }
296 
297     ThrowTypeError({"operator ", op, " cannot be applied to types ", leftType, " and ", rightType}, lineInfo);
298 }
299 
ThrowAssignmentError(Type * source,Type * target,lexer::SourcePosition lineInfo,bool isAsSrcLeftType)300 void TSChecker::ThrowAssignmentError(Type *source, Type *target, lexer::SourcePosition lineInfo, bool isAsSrcLeftType)
301 {
302     if (isAsSrcLeftType || !target->HasTypeFlag(TypeFlag::LITERAL)) {
303         ThrowTypeError({"Type '", util::AsSrc(source), "' is not assignable to type '", target, "'."}, lineInfo);
304     }
305 
306     ThrowTypeError({"Type '", source, "' is not assignable to type '", target, "'."}, lineInfo);
307 }
308 
GetUnaryResultType(Type * operandType)309 Type *TSChecker::GetUnaryResultType(Type *operandType)
310 {
311     if (checker::TSChecker::MaybeTypeOfKind(operandType, checker::TypeFlag::BIGINT_LIKE)) {
312         if (operandType->HasTypeFlag(checker::TypeFlag::UNION_OR_INTERSECTION) &&
313             checker::TSChecker::MaybeTypeOfKind(operandType, checker::TypeFlag::NUMBER_LIKE)) {
314             return GlobalNumberOrBigintType();
315         }
316 
317         return GlobalBigintType();
318     }
319 
320     return GlobalNumberType();
321 }
322 
ElaborateElementwise(Type * targetType,ir::Expression * sourceNode,const lexer::SourcePosition & pos)323 void TSChecker::ElaborateElementwise(Type *targetType, ir::Expression *sourceNode, const lexer::SourcePosition &pos)
324 {
325     auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE | CheckerStatus::KEEP_LITERAL_TYPE);
326 
327     Type *sourceType = CheckTypeCached(sourceNode);
328 
329     if (IsTypeAssignableTo(sourceType, targetType)) {
330         return;
331     }
332 
333     if (targetType->IsArrayType() && sourceNode->IsArrayExpression()) {
334         ArrayElaborationContext(this, targetType, sourceType, sourceNode, pos).Start();
335     } else if (targetType->IsObjectType() || targetType->IsUnionType()) {
336         if (sourceNode->IsObjectExpression()) {
337             ObjectElaborationContext(this, targetType, sourceType, sourceNode, pos).Start();
338         } else if (sourceNode->IsArrayExpression()) {
339             ArrayElaborationContext(this, targetType, sourceType, sourceNode, pos).Start();
340         }
341     }
342 
343     ThrowAssignmentError(sourceType, targetType, pos);
344 }
345 
InferSimpleVariableDeclaratorType(ir::VariableDeclarator * declarator)346 void TSChecker::InferSimpleVariableDeclaratorType(ir::VariableDeclarator *declarator)
347 {
348     ES2PANDA_ASSERT(declarator->Id()->IsIdentifier());
349 
350     varbinder::Variable *var = declarator->Id()->AsIdentifier()->Variable();
351     ES2PANDA_ASSERT(var);
352 
353     if (declarator->Id()->AsIdentifier()->TypeAnnotation() != nullptr) {
354         var->SetTsType(declarator->Id()->AsIdentifier()->TypeAnnotation()->GetType(this));
355         return;
356     }
357 
358     if (declarator->Init() != nullptr) {
359         var->SetTsType(CheckTypeCached(declarator->Init()));
360         return;
361     }
362 
363     ThrowTypeError({"Variable ", declarator->Id()->AsIdentifier()->Name(), " implicitly has an any type."},
364                    declarator->Id()->Start());
365 }
366 
GetTypeVar(varbinder::Decl * decl)367 void TSChecker::GetTypeVar(varbinder::Decl *decl)
368 {
369     ir::AstNode *declarator =
370         util::Helpers::FindAncestorGivenByType(decl->Node(), ir::AstNodeType::VARIABLE_DECLARATOR);
371     ES2PANDA_ASSERT(declarator);
372 
373     ES2PANDA_ASSERT(declarator);
374     if (declarator->AsVariableDeclarator()->Id()->IsIdentifier()) {
375         InferSimpleVariableDeclaratorType(declarator->AsVariableDeclarator());
376         return;
377     }
378 
379     declarator->Check(this);
380 }
381 
GetTypeParam(varbinder::Variable * var,varbinder::Decl * decl)382 void TSChecker::GetTypeParam(varbinder::Variable *var, varbinder::Decl *decl)
383 {
384     ir::AstNode *declaration = FindAncestorUntilGivenType(decl->Node(), ir::AstNodeType::SCRIPT_FUNCTION);
385 
386     ES2PANDA_ASSERT(declaration != nullptr);
387     if (declaration->IsIdentifier()) {
388         auto *ident = declaration->AsIdentifier();
389         if (ident->TypeAnnotation() != nullptr) {
390             ES2PANDA_ASSERT(ident->Variable() == var);
391             var->SetTsType(ident->TypeAnnotation()->GetType(this));
392             return;
393         }
394 
395         ThrowTypeError({"Parameter ", ident->Name(), " implicitly has an 'any' type."}, ident->Start());
396     }
397 
398     if (declaration->IsAssignmentPattern() && declaration->AsAssignmentPattern()->Left()->IsIdentifier()) {
399         ir::Identifier *ident = declaration->AsAssignmentPattern()->Left()->AsIdentifier();
400 
401         if (ident->TypeAnnotation() != nullptr) {
402             ES2PANDA_ASSERT(ident->Variable() == var);
403             var->SetTsType(ident->TypeAnnotation()->GetType(this));
404             return;
405         }
406 
407         var->SetTsType(declaration->AsAssignmentPattern()->Right()->Check(this));
408     }
409 
410     CheckFunctionParameter(declaration->AsExpression(), nullptr);
411 }
412 
GetTypeEnum(varbinder::Variable * var,varbinder::Decl * decl)413 void TSChecker::GetTypeEnum(varbinder::Variable *var, varbinder::Decl *decl)
414 {
415     ES2PANDA_ASSERT(var->IsEnumVariable());
416     varbinder::EnumVariable *enumVar = var->AsEnumVariable();
417 
418     if (std::holds_alternative<bool>(enumVar->Value())) {
419         ThrowTypeError(
420             "A member initializer in a enum declaration cannot reference members declared after it, "
421             "including "
422             "members defined in other enums.",
423             decl->Node()->Start());
424     }
425 
426     var->SetTsType(std::holds_alternative<double>(enumVar->Value()) ? GlobalNumberType() : GlobalStringType());
427 }
428 
GetDeclTsType(varbinder::Variable * var,varbinder::Decl * decl)429 Type *TSChecker::GetDeclTsType(varbinder::Variable *var, varbinder::Decl *decl)
430 {
431     switch (decl->Type()) {
432         case varbinder::DeclType::CONST:
433         case varbinder::DeclType::LET: {
434             if (!decl->Node()->Parent()->IsTSTypeQuery()) {
435                 ThrowTypeError({"Block-scoped variable '", var->Name(), "' used before its declaration"},
436                                decl->Node()->Start());
437                 break;
438             }
439 
440             [[fallthrough]];
441         }
442         case varbinder::DeclType::VAR: {
443             GetTypeVar(decl);
444             break;
445         }
446         case varbinder::DeclType::PROPERTY: {
447             var->SetTsType(decl->Node()->AsTSPropertySignature()->TypeAnnotation()->GetType(this));
448             break;
449         }
450         case varbinder::DeclType::METHOD: {
451             auto *signatureInfo = Allocator()->New<checker::SignatureInfo>(Allocator());
452             auto *callSignature = Allocator()->New<checker::Signature>(signatureInfo, GlobalAnyType());
453             var->SetTsType(CreateFunctionTypeWithSignature(callSignature));
454             break;
455         }
456         case varbinder::DeclType::FUNC: {
457             checker::ScopeContext scopeCtx(this, decl->Node()->AsScriptFunction()->Scope());
458             InferFunctionDeclarationType(decl->AsFunctionDecl(), var);
459             break;
460         }
461         case varbinder::DeclType::PARAM: {
462             GetTypeParam(var, decl);
463             break;
464         }
465         case varbinder::DeclType::ENUM: {
466             GetTypeEnum(var, decl);
467             break;
468         }
469         case varbinder::DeclType::ENUM_LITERAL: {
470             ES2PANDA_UNREACHABLE();  // NOTE: aszilagyi.
471         }
472         default: {
473             break;
474         }
475     }
476 
477     return var->TsType();
478 }
479 
GetTypeOfVariable(varbinder::Variable * var)480 Type *TSChecker::GetTypeOfVariable(varbinder::Variable *var)
481 {
482     if (var->TsType() != nullptr) {
483         return var->TsType();
484     }
485 
486     varbinder::Decl *decl = var->Declaration();
487 
488     TypeStackElement tse(this, decl->Node(), {{diagnostic::CYCLIC_VAR_REF, {var->Name()}}}, decl->Node()->Start());
489     if (tse.HasTypeError()) {
490         return GlobalErrorType();
491     }
492 
493     return GetDeclTsType(var, decl);
494 }
495 
GetTypeFromClassOrInterfaceReference(ir::TSTypeReference * node,varbinder::Variable * var)496 Type *TSChecker::GetTypeFromClassOrInterfaceReference([[maybe_unused]] ir::TSTypeReference *node,
497                                                       varbinder::Variable *var)
498 {
499     Type *resolvedType = var->TsType();
500 
501     if (resolvedType == nullptr) {
502         ObjectDescriptor *desc = Allocator()->New<ObjectDescriptor>(Allocator());
503         resolvedType = Allocator()->New<InterfaceType>(Allocator(), var->Name(), desc);
504         ES2PANDA_ASSERT(resolvedType != nullptr);
505         resolvedType->SetVariable(var);
506         var->SetTsType(resolvedType);
507     }
508 
509     return resolvedType;
510 }
511 
GetTypeFromTypeAliasReference(ir::TSTypeReference * node,varbinder::Variable * var)512 Type *TSChecker::GetTypeFromTypeAliasReference(ir::TSTypeReference *node, varbinder::Variable *var)
513 {
514     Type *resolvedType = var->TsType();
515 
516     if (resolvedType != nullptr) {
517         return resolvedType;
518     }
519 
520     TypeStackElement tse(this, var, {{diagnostic::CYCLIC_ALIAS_2, {var->Name()}}}, node->Start());
521     if (tse.HasTypeError()) {
522         return GlobalErrorType();
523     }
524 
525     ES2PANDA_ASSERT(var->Declaration()->Node() && var->Declaration()->Node()->IsTSTypeAliasDeclaration());
526     ir::TSTypeAliasDeclaration *declaration = var->Declaration()->Node()->AsTSTypeAliasDeclaration();
527     resolvedType = declaration->TypeAnnotation()->GetType(this);
528     var->SetTsType(resolvedType);
529 
530     return resolvedType;
531 }
532 
GetTypeReferenceType(ir::TSTypeReference * node,varbinder::Variable * var)533 Type *TSChecker::GetTypeReferenceType(ir::TSTypeReference *node, varbinder::Variable *var)
534 {
535     ES2PANDA_ASSERT(var->Declaration());
536     varbinder::Decl *decl = var->Declaration();
537 
538     if (decl->IsInterfaceDecl()) {
539         return GetTypeFromClassOrInterfaceReference(node, var);
540     }
541 
542     if (decl->IsTypeAliasDecl()) {
543         return GetTypeFromTypeAliasReference(node, var);
544     }
545 
546     ThrowTypeError("This reference refers to a value, but is being used as a type here. Did you mean to use 'typeof'?",
547                    node->Start());
548     return nullptr;
549 }
550 }  // namespace ark::es2panda::checker
551