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
21namespace ark::es2panda::checker {
22
23void 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}
56Type *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
100Type *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
136Type *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
153Type *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
165Type *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
178static bool TypeHasCallOrConstructSignatures(Type *type)
179{
180    return type->IsObjectType() &&
181           (!type->AsObjectType()->CallSignatures().empty() || !type->AsObjectType()->ConstructSignatures().empty());
182}
183
184Type *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
203Type *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
224void 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