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/expressions/assignmentExpression.h"
17 #include "ir/expressions/memberExpression.h"
18 
19 #include "checker/TSchecker.h"
20 
21 namespace ark::es2panda::checker {
22 
CheckBooleanLikeType(Type *leftType, Type *rightType, ir::AstNode *expr, lexer::TokenType op)23 void TSChecker::CheckBooleanLikeType(Type *leftType, Type *rightType, ir::AstNode *expr, lexer::TokenType op)
24 {
25     if (leftType->HasTypeFlag(TypeFlag::BOOLEAN_LIKE) && rightType->HasTypeFlag(TypeFlag::BOOLEAN_LIKE)) {
26         lexer::TokenType suggestedOp;
27         switch (op) {
28             case lexer::TokenType::PUNCTUATOR_BITWISE_OR:
29             case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: {
30                 suggestedOp = lexer::TokenType::PUNCTUATOR_LOGICAL_OR;
31                 break;
32             }
33             case lexer::TokenType::PUNCTUATOR_BITWISE_AND:
34             case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: {
35                 suggestedOp = lexer::TokenType::PUNCTUATOR_LOGICAL_AND;
36                 break;
37             }
38             case lexer::TokenType::PUNCTUATOR_BITWISE_XOR:
39             case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: {
40                 suggestedOp = lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL;
41                 break;
42             }
43             default: {
44                 suggestedOp = lexer::TokenType::EOS;
45                 break;
46             }
47         }
48 
49         if (suggestedOp != lexer::TokenType::EOS) {
50             ThrowTypeError(
51                 {"The ", op, " operator is not allowed for boolean types. Consider using ", suggestedOp, " instead"},
52                 expr->Start());
53         }
54     }
55 }
CheckBinaryOperator(ExpressionTypeInfo *leftRightType, ir::Expression *leftExpr, ir::Expression *rightExpr, ir::AstNode *expr, lexer::TokenType op)56 Type *TSChecker::CheckBinaryOperator(ExpressionTypeInfo *leftRightType, ir::Expression *leftExpr,
57                                      ir::Expression *rightExpr, ir::AstNode *expr, lexer::TokenType op)
58 {
59     CheckNonNullType(leftRightType->leftType, leftExpr->Start());
60     CheckNonNullType(leftRightType->rightType, rightExpr->Start());
61 
62     CheckBooleanLikeType(leftRightType->leftType, leftRightType->rightType, expr, op);
63 
64     if (!leftRightType->leftType->HasTypeFlag(TypeFlag::VALID_ARITHMETIC_TYPE)) {
65         ThrowTypeError(
66             "The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an "
67             "enum "
68             "type.",
69             expr->Start());
70     }
71 
72     if (!leftRightType->rightType->HasTypeFlag(TypeFlag::VALID_ARITHMETIC_TYPE)) {
73         ThrowTypeError(
74             "The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an "
75             "enum "
76             "type.",
77             rightExpr->Start());
78     }
79 
80     Type *resultType = nullptr;
81     if ((leftRightType->leftType->IsAnyType() && leftRightType->rightType->IsAnyType()) ||
82         !(leftRightType->leftType->HasTypeFlag(TypeFlag::BIGINT_LIKE) ||
83           leftRightType->rightType->HasTypeFlag(TypeFlag::BIGINT_LIKE))) {
84         resultType = GlobalNumberType();
85     } else if (leftRightType->leftType->HasTypeFlag(TypeFlag::BIGINT_LIKE) &&
86                leftRightType->rightType->HasTypeFlag(TypeFlag::BIGINT_LIKE)) {
87         if (op == lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT ||
88             op == lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL) {
89             ThrowTypeError({"operator ", op, " cannot be applied to types 'bigint' and 'bigint'"}, expr->Start());
90         }
91         resultType = GlobalBigintType();
92     } else {
93         ThrowBinaryLikeError(op, leftRightType->leftType, leftRightType->rightType, expr->Start());
94     }
95 
96     CheckAssignmentOperator(op, leftExpr, leftRightType->leftType, resultType);
97     return resultType;
98 }
99 
CheckPlusOperator(ExpressionTypeInfo *leftRightType, ir::Expression *leftExpr, ir::Expression *rightExpr, ir::AstNode *expr, lexer::TokenType op)100 Type *TSChecker::CheckPlusOperator(ExpressionTypeInfo *leftRightType, ir::Expression *leftExpr,
101                                    ir::Expression *rightExpr, ir::AstNode *expr, lexer::TokenType op)
102 {
103     if (!leftRightType->leftType->HasTypeFlag(TypeFlag::STRING_LIKE) &&
104         !leftRightType->rightType->HasTypeFlag(TypeFlag::STRING_LIKE)) {
105         CheckNonNullType(leftRightType->leftType, leftExpr->Start());
106         CheckNonNullType(leftRightType->rightType, rightExpr->Start());
107     }
108 
109     Type *resultType = nullptr;
110     if (IsTypeAssignableTo(leftRightType->leftType, GlobalNumberType()) &&
111         IsTypeAssignableTo(leftRightType->rightType, GlobalNumberType())) {
112         resultType = GlobalNumberType();
113     } else if (IsTypeAssignableTo(leftRightType->leftType, GlobalBigintType()) &&
114                IsTypeAssignableTo(leftRightType->rightType, GlobalBigintType())) {
115         resultType = GlobalBigintType();
116     } else if (IsTypeAssignableTo(leftRightType->leftType, GlobalStringType()) ||
117                IsTypeAssignableTo(leftRightType->rightType, GlobalStringType())) {
118         resultType = GlobalStringType();
119     } else if (MaybeTypeOfKind(leftRightType->leftType, TypeFlag::UNKNOWN)) {
120         ThrowTypeError("object is of type 'unknown'", leftExpr->Start());
121     } else if (MaybeTypeOfKind(leftRightType->rightType, TypeFlag::UNKNOWN)) {
122         ThrowTypeError("object is of type 'unknown'", rightExpr->Start());
123     } else if (leftRightType->leftType->IsAnyType() || leftRightType->rightType->IsAnyType()) {
124         resultType = GlobalAnyType();
125     } else {
126         ThrowBinaryLikeError(op, leftRightType->leftType, leftRightType->rightType, expr->Start());
127     }
128 
129     if (op == lexer::TokenType::PUNCTUATOR_PLUS_EQUAL) {
130         CheckAssignmentOperator(op, leftExpr, leftRightType->leftType, resultType);
131     }
132 
133     return resultType;
134 }
135 
CheckCompareOperator(ExpressionTypeInfo *leftRightType, ir::Expression *leftExpr, ir::Expression *rightExpr, ir::AstNode *expr, lexer::TokenType op)136 Type *TSChecker::CheckCompareOperator(ExpressionTypeInfo *leftRightType, ir::Expression *leftExpr,
137                                       ir::Expression *rightExpr, ir::AstNode *expr, lexer::TokenType op)
138 {
139     CheckNonNullType(leftRightType->leftType, leftExpr->Start());
140     CheckNonNullType(leftRightType->rightType, rightExpr->Start());
141 
142     if (AreTypesComparable(leftRightType->leftType, leftRightType->rightType) ||
143         (IsTypeAssignableTo(leftRightType->leftType, GlobalNumberOrBigintType()) &&
144          IsTypeAssignableTo(leftRightType->rightType, GlobalNumberOrBigintType()))) {
145         return GlobalBooleanType();
146     }
147 
148     ThrowBinaryLikeError(op, leftRightType->leftType, leftRightType->rightType, expr->Start());
149 
150     return GlobalAnyType();
151 }
152 
CheckAndOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr)153 Type *TSChecker::CheckAndOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr)
154 {
155     CheckTruthinessOfType(leftType, leftExpr->Start());
156 
157     if ((static_cast<uint64_t>(leftType->GetTypeFacts()) & static_cast<uint64_t>(TypeFacts::TRUTHY)) != 0U) {
158         Type *resultType = CreateUnionType({ExtractDefinitelyFalsyTypes(rightType), rightType});
159         return resultType;
160     }
161 
162     return leftType;
163 }
164 
CheckOrOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr)165 Type *TSChecker::CheckOrOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr)
166 {
167     CheckTruthinessOfType(leftType, leftExpr->Start());
168 
169     if ((static_cast<uint64_t>(leftType->GetTypeFacts()) & static_cast<uint64_t>(TypeFacts::FALSY)) != 0U) {
170         // NOTE: aszilagyi. subtype reduction in the result union
171         Type *resultType = CreateUnionType({RemoveDefinitelyFalsyTypes(leftType), rightType});
172         return resultType;
173     }
174 
175     return leftType;
176 }
177 
TypeHasCallOrConstructSignatures(Type *type)178 static bool TypeHasCallOrConstructSignatures(Type *type)
179 {
180     return type->IsObjectType() &&
181            (!type->AsObjectType()->CallSignatures().empty() || !type->AsObjectType()->ConstructSignatures().empty());
182 }
183 
CheckInstanceofExpression(Type *leftType, Type *rightType, ir::Expression *rightExpr, ir::AstNode *expr)184 Type *TSChecker::CheckInstanceofExpression(Type *leftType, Type *rightType, ir::Expression *rightExpr,
185                                            ir::AstNode *expr)
186 {
187     if (leftType->TypeFlags() != TypeFlag::ANY && IsAllTypesAssignableTo(leftType, GlobalPrimitiveType())) {
188         ThrowTypeError({"The left-hand side of an 'instanceof' expression must be of type 'any',",
189                         " an object type or a type parameter."},
190                        expr->Start());
191     }
192 
193     // NOTE: aszilagyi. Check if right type is subtype of globalFunctionType
194     if (rightType->TypeFlags() != TypeFlag::ANY && !TypeHasCallOrConstructSignatures(rightType)) {
195         ThrowTypeError({"The right-hand side of an 'instanceof' expression must be of type 'any'",
196                         " or of a type assignable to the 'Function' interface type."},
197                        rightExpr->Start());
198     }
199 
200     return GlobalBooleanType();
201 }
202 
CheckInExpression(Type *leftType, Type *rightType, ir::Expression *leftExpr, ir::Expression *rightExpr, ir::AstNode *expr)203 Type *TSChecker::CheckInExpression(Type *leftType, Type *rightType, ir::Expression *leftExpr, ir::Expression *rightExpr,
204                                    ir::AstNode *expr)
205 {
206     CheckNonNullType(leftType, leftExpr->Start());
207     CheckNonNullType(rightType, rightExpr->Start());
208 
209     // NOTE: aszilagyi. Check IsAllTypesAssignableTo with ESSymbol too
210     if (leftType->TypeFlags() != TypeFlag::ANY && !IsAllTypesAssignableTo(leftType, GlobalStringOrNumberType())) {
211         ThrowTypeError(
212             {"The left-hand side of an 'in' expression must be of type 'any',", " 'string', 'number', or 'symbol'."},
213             expr->Start());
214     }
215 
216     // NOTE: aszilagyi. Handle type parameters
217     if (!IsAllTypesAssignableTo(rightType, GlobalNonPrimitiveType())) {
218         ThrowTypeError("The right-hand side of an 'in' expression must not be a primitive.", rightExpr->Start());
219     }
220 
221     return GlobalBooleanType();
222 }
223 
CheckAssignmentOperator(lexer::TokenType op, ir::Expression *leftExpr, Type *leftType, Type *valueType)224 void TSChecker::CheckAssignmentOperator(lexer::TokenType op, ir::Expression *leftExpr, Type *leftType, Type *valueType)
225 {
226     if (IsAssignmentOperator(op)) {
227         CheckReferenceExpression(
228             leftExpr, "the left hand side of an assignment expression must be a variable or a property access",
229             "The left-hand side of an assignment expression may not be an optional property access.");
230 
231         if (!IsTypeAssignableTo(valueType, leftType)) {
232             ThrowAssignmentError(valueType, leftType, leftExpr->Start(),
233                                  leftExpr->Parent()->AsAssignmentExpression()->Right()->IsMemberExpression() ||
234                                      leftExpr->Parent()->AsAssignmentExpression()->Right()->IsChainExpression());
235         }
236     }
237 }
238 }  // namespace ark::es2panda::checker
239