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 "varbinder/variableFlags.h"
17#include "checker/checker.h"
18#include "checker/checkerContext.h"
19#include "checker/types/ets/etsObjectType.h"
20#include "checker/types/ets/etsTupleType.h"
21#include "ir/astNode.h"
22#include "lexer/token/tokenType.h"
23#include "ir/base/catchClause.h"
24#include "ir/expression.h"
25#include "ir/typeNode.h"
26#include "ir/base/scriptFunction.h"
27#include "ir/base/classProperty.h"
28#include "ir/base/methodDefinition.h"
29#include "ir/statements/variableDeclarator.h"
30#include "ir/statements/switchCaseStatement.h"
31#include "ir/expressions/identifier.h"
32#include "ir/expressions/arrayExpression.h"
33#include "ir/expressions/callExpression.h"
34#include "ir/expressions/memberExpression.h"
35#include "ir/expressions/binaryExpression.h"
36#include "ir/expressions/assignmentExpression.h"
37#include "ir/statements/labelledStatement.h"
38#include "ir/ets/etsFunctionType.h"
39#include "ir/ets/etsNewClassInstanceExpression.h"
40#include "ir/ts/tsTypeAliasDeclaration.h"
41#include "ir/ts/tsEnumMember.h"
42#include "ir/ts/tsTypeParameter.h"
43#include "ir/ets/etsTypeReference.h"
44#include "ir/ets/etsTypeReferencePart.h"
45#include "varbinder/variable.h"
46#include "varbinder/scope.h"
47#include "varbinder/declaration.h"
48#include "checker/ETSchecker.h"
49#include "checker/ets/typeRelationContext.h"
50#include "util/helpers.h"
51
52namespace ark::es2panda::checker {
53void ETSChecker::ValidatePropertyAccess(varbinder::Variable *var, ETSObjectType *obj, const lexer::SourcePosition &pos)
54{
55    if ((Context().Status() & CheckerStatus::IGNORE_VISIBILITY) != 0U) {
56        return;
57    }
58    if (var->HasFlag(varbinder::VariableFlags::METHOD)) {
59        return;
60    }
61
62    if (var->HasFlag(varbinder::VariableFlags::PRIVATE) || var->HasFlag(varbinder::VariableFlags::PROTECTED)) {
63        if ((Context().ContainingClass() == obj ||
64             Context().ContainingClass()->GetOriginalBaseType() == obj->GetOriginalBaseType()) &&
65            obj->IsPropertyInherited(var)) {
66            return;
67        }
68
69        if (var->HasFlag(varbinder::VariableFlags::PROTECTED) && Context().ContainingClass()->IsDescendantOf(obj) &&
70            obj->IsPropertyInherited(var)) {
71            return;
72        }
73
74        auto *currentOutermost = Context().ContainingClass()->OutermostClass();
75        auto *objOutermost = obj->OutermostClass();
76
77        if (currentOutermost != nullptr && objOutermost != nullptr && currentOutermost == objOutermost &&
78            obj->IsPropertyInherited(var)) {
79            return;
80        }
81
82        LogTypeError({"Property ", var->Name(), " is not visible here."}, pos);
83        var->SetTsType(GlobalTypeError());
84    }
85}
86
87void ETSChecker::ValidateCallExpressionIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved,
88                                                  Type *const type)
89{
90    if (resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE) &&
91        ident->Parent()->AsCallExpression()->Callee() != ident) {
92        LogTypeError({"Class or interface '", ident->Name(), "' cannot be used as object"}, ident->Start());
93        ident->Variable()->SetTsType(GlobalTypeError());
94    }
95
96    if (ident->Parent()->AsCallExpression()->Callee() != ident) {
97        return;
98    }
99    if (ident->Variable() != nullptr &&  // It should always be true!
100        ident->Variable()->Declaration()->Node() != nullptr &&
101        ident->Variable()->Declaration()->Node()->IsImportNamespaceSpecifier()) {
102        LogTypeError({"Namespace style identifier ", ident->Name(), " is not callable."}, ident->Start());
103        ident->Variable()->SetTsType(GlobalTypeError());
104    }
105    if (type->IsETSFunctionType() || type->IsETSDynamicType() ||
106        (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) {
107        return;
108    }
109    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
110    if (TryTransformingToStaticInvoke(ident, type)) {
111        return;
112    }
113
114    if (type->IsETSUnionType()) {
115        for (auto it : type->AsETSUnionType()->ConstituentTypes()) {
116            if (it->IsETSFunctionType() || it->IsETSDynamicType() ||
117                (it->IsETSObjectType() && it->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) {
118                return;
119            }
120        }
121    }
122
123    LogTypeError({"This expression is not callable."}, ident->Start());
124    ident->Variable()->SetTsType(GlobalTypeError());
125}
126
127void ETSChecker::ValidateNewClassInstanceIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved)
128{
129    if (ident->Parent()->AsETSNewClassInstanceExpression()->GetTypeRef() == ident && (resolved != nullptr) &&
130        !resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE)) {
131        LogUnResolvedError(ident);
132        return;
133    }
134}
135
136void ETSChecker::ValidateMemberIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved,
137                                          Type *const type)
138{
139    if (resolved->Declaration()->Node()->IsTSEnumDeclaration() &&
140        ident->Parent()->AsMemberExpression()->HasMemberKind(ir::MemberExpressionKind::ELEMENT_ACCESS)) {
141        return;
142    }
143    if (ident->Parent()->AsMemberExpression()->IsComputed()) {
144        if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
145            WrongContextErrorClassifyByType(ident, resolved);
146        }
147
148        return;
149    }
150
151    if (!IsReferenceType(type) && !type->IsETSEnumType() && !type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) {
152        LogUnResolvedError(ident);
153        return;
154    }
155}
156
157void ETSChecker::ValidatePropertyOrDeclaratorIdentifier(ir::Identifier *const ident,
158                                                        varbinder::Variable *const resolved)
159{
160    const auto [target_ident, typeAnnotation] = GetTargetIdentifierAndType(ident);
161
162    if ((resolved != nullptr) && resolved->TsType()->IsETSFunctionType()) {
163        CheckEtsFunctionType(ident, target_ident);
164        return;
165    }
166
167    if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
168        LogUnResolvedError(ident);
169        return;
170    }
171}
172
173void ETSChecker::ValidateAssignmentIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved,
174                                              Type *const type)
175{
176    const auto *const assignmentExpr = ident->Parent()->AsAssignmentExpression();
177    if (assignmentExpr->Left() == ident && (resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
178        WrongContextErrorClassifyByType(ident, resolved);
179        return;
180    }
181
182    if (assignmentExpr->Right() == ident && (resolved != nullptr) &&
183        (!resolved->Declaration()->PossibleTDZ() && !type->IsETSFunctionType())) {
184        WrongContextErrorClassifyByType(ident, resolved);
185        return;
186    }
187}
188
189bool ETSChecker::ValidateBinaryExpressionIdentifier(ir::Identifier *const ident, Type *const type)
190{
191    const auto *const binaryExpr = ident->Parent()->AsBinaryExpression();
192    bool isFinished = false;
193    if (binaryExpr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF && binaryExpr->Right() == ident) {
194        if (!IsReferenceType(type)) {
195            LogTypeError({R"(Using the "instance of" operator with non-object type ")", ident->Name(), "\""},
196                         ident->Start());
197            ident->Variable()->SetTsType(GlobalTypeError());
198        }
199        isFinished = true;
200    }
201    return isFinished;
202}
203
204void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved)
205{
206    auto *smartType = Context().GetSmartCast(resolved);
207    auto *const resolvedType = GetApparentType(smartType != nullptr ? smartType : GetTypeOfVariable(resolved));
208
209    switch (ident->Parent()->Type()) {
210        case ir::AstNodeType::CALL_EXPRESSION: {
211            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
212            ValidateCallExpressionIdentifier(ident, resolved, resolvedType);
213            break;
214        }
215        case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: {
216            ValidateNewClassInstanceIdentifier(ident, resolved);
217            break;
218        }
219        case ir::AstNodeType::MEMBER_EXPRESSION: {
220            ValidateMemberIdentifier(ident, resolved, resolvedType);
221            break;
222        }
223        case ir::AstNodeType::BINARY_EXPRESSION: {
224            if (ValidateBinaryExpressionIdentifier(ident, resolvedType)) {
225                return;
226            }
227
228            [[fallthrough]];
229        }
230        case ir::AstNodeType::UPDATE_EXPRESSION:
231        case ir::AstNodeType::UNARY_EXPRESSION: {
232            if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ()) {
233                WrongContextErrorClassifyByType(ident, resolved);
234            }
235            break;
236        }
237        case ir::AstNodeType::CLASS_PROPERTY:
238        case ir::AstNodeType::VARIABLE_DECLARATOR: {
239            ValidatePropertyOrDeclaratorIdentifier(ident, resolved);
240            break;
241        }
242        case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
243            ValidateAssignmentIdentifier(ident, resolved, resolvedType);
244            break;
245        }
246        default: {
247            if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) {
248                WrongContextErrorClassifyByType(ident, resolved);
249            }
250            break;
251        }
252    }
253}
254
255void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable)
256{
257    if (variable == nullptr || IsVariableGetterSetter(variable) || variable->Declaration() == nullptr) {
258        return;
259    }
260
261    if (variable->Declaration()->IsConstDecl() || variable->Declaration()->IsReadonlyDecl()) {
262        std::string_view fieldType = variable->Declaration()->IsConstDecl() ? "constant" : "readonly";
263        if (HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK) &&
264            !variable->HasFlag(varbinder::VariableFlags::EXPLICIT_INIT_REQUIRED)) {
265            LogTypeError({"Cannot reassign ", fieldType, " ", variable->Name()},
266                         variable->Declaration()->Node()->Start());
267            variable->SetTsType(GlobalTypeError());
268            return;
269        }
270        if (!HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK)) {
271            LogTypeError({"Cannot assign to a ", fieldType, " variable ", variable->Name()},
272                         variable->Declaration()->Node()->Start());
273            variable->SetTsType(GlobalTypeError());
274        }
275    }
276}
277
278void ETSChecker::ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration *const typeAliasNode,
279                                                       const ir::TSTypeParameterInstantiation *const exactTypeParams)
280{
281    static std::string_view const TRANSFORMATION_NAME = __func__;
282
283    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
284    auto *const clonedNode = typeAliasNode->TypeAnnotation()->Clone(Allocator(), typeAliasNode);
285
286    // Basic check, we really don't want to change the original type nodes, more precise checking should be made
287    ASSERT(clonedNode != typeAliasNode->TypeAnnotation());
288
289    // Currently only reference types are checked. This should be extended for other types in a follow up patch, but for
290    // complete usability, if the type isn't a simple reference type, then doN't check type alias declaration at all.
291    bool checkTypealias = true;
292
293    // Only transforming a temporary cloned node, so no modification is made in the AST
294    clonedNode->TransformChildrenRecursively(
295        [this, &checkTypealias, &exactTypeParams, typeAliasNode](ir::AstNode *const node) -> ir::AstNode * {
296            if (!node->IsETSTypeReference()) {
297                return node;
298            }
299
300            const auto *const nodeIdent = node->AsETSTypeReference()->Part()->Name()->AsIdentifier();
301
302            size_t typeParamIdx = 0;
303            for (const auto *const typeParam : typeAliasNode->TypeParams()->Params()) {
304                if (typeParam->Name()->AsIdentifier()->Variable() == nodeIdent->Variable()) {
305                    break;
306                }
307                typeParamIdx++;
308            }
309
310            if (typeParamIdx == typeAliasNode->TypeParams()->Params().size()) {
311                return node;
312            }
313
314            auto *const typeParamType = exactTypeParams->Params().at(typeParamIdx);
315
316            if (!typeParamType->IsETSTypeReference()) {
317                checkTypealias = false;
318                return node;
319            }
320
321            return typeParamType->Clone(Allocator(), nullptr);
322        },
323        TRANSFORMATION_NAME);
324
325    if (checkTypealias) {
326        clonedNode->Check(this);
327    }
328}
329
330bool ETSChecker::ValidateTupleMinElementSize(ir::ArrayExpression *const arrayExpr, ETSTupleType *tuple)
331{
332    if (arrayExpr->Elements().size() < static_cast<size_t>(tuple->GetMinTupleSize())) {
333        LogTypeError({"Few elements in array initializer for tuple with size of ",
334                      static_cast<size_t>(tuple->GetMinTupleSize())},
335                     arrayExpr->Start());
336        return false;
337    }
338    return true;
339}
340}  // namespace ark::es2panda::checker
341