1/*
2 * Copyright (c) 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 "getterSetterValidation.h"
17#include "ir/expression.h"
18#include "ir/expressions/functionExpression.h"
19#include "ir/typeNode.h"
20#include "ir/base/methodDefinition.h"
21#include "ir/base/scriptFunction.h"
22
23namespace ark::es2panda::compiler::ast_verifier {
24
25[[nodiscard]] CheckResult GetterSetterValidation::operator()(CheckContext &ctx, const ir::AstNode *ast)
26{
27    if (!ast->IsMethodDefinition()) {
28        return {CheckDecision::CORRECT, CheckAction::CONTINUE};
29    }
30
31    bool errorFound = false;
32    auto const validateMethod = [&ctx, &errorFound, this](ir::MethodDefinition const *const method) {
33        auto const kind = method->Kind();
34        if (kind == ir::MethodDefinitionKind::GET) {
35            errorFound |= !ValidateGetter(ctx, method);
36        } else if (kind == ir::MethodDefinitionKind::SET) {
37            errorFound |= !ValidateSetter(ctx, method);
38        };
39    };
40
41    auto const *const method = ast->AsMethodDefinition();
42    validateMethod(method);
43    for (auto const *const overload : method->Overloads()) {
44        validateMethod(overload);
45    }
46
47    if (errorFound) {
48        return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
49    }
50
51    return {CheckDecision::CORRECT, CheckAction::CONTINUE};
52}
53
54bool GetterSetterValidation::ValidateGetter(CheckContext &ctx, ir::MethodDefinition const *const method) const
55{
56    if (!method->Value()->IsFunctionExpression()) {
57        return true;
58    }
59
60    bool result = true;
61    auto const report = [&ctx, &result, method](const std::string &msg) {
62        ctx.AddCheckMessage(msg, *method, method->Start());
63        result = false;
64    };
65
66    auto const *const function = method->Value()->AsFunctionExpression()->Function();
67
68    // Check getter flag
69    if (!function->IsGetter()) {
70        report("GETTER METHOD DOES NOT HAVE GETTER FLAG");
71    }
72
73    // Check return type annotation if it exists
74    if (function->ReturnTypeAnnotation() != nullptr) {
75        auto const *const type = function->ReturnTypeAnnotation()->TsType();
76        if (type != nullptr && type->IsETSVoidType()) {
77            report("GETTER METHOD HAS VOID RETURN TYPE IN RETURN TYPE ANNOTATION");
78        }
79    }
80
81    // For non-abstract and non-ambient getters return statement should always exist
82    if (!function->HasReturnStatement() && !function->IsAbstract() && !function->IsDeclare()) {
83        report("MISSING RETURN STATEMENT IN GETTER METHOD");
84    }
85
86    // Check return statements
87    auto const &returns = function->ReturnStatements();
88    if (function->ReturnTypeAnnotation() == nullptr) {
89        if (returns.empty()) {
90            report("MISSING RETURN TYPE ANNOTATION AND RETURN STATEMENT IN GETTER METHOD");
91        }
92    }
93
94    // Check that all return statements are not void
95    for (auto const *const stmt : returns) {
96        if (stmt->ReturnType()->IsETSVoidType()) {
97            // All getters should have non-void return type
98            report("GETTER METHOD HAS VOID RETURN TYPE");
99        }
100    }
101
102    // Check number of arguments
103    auto const &params = function->Params();
104    if (!params.empty()) {
105        report("GETTER METHOD HAS INCORRECT NUMBER OF ARGUMENTS");
106    }
107
108    return result;
109}
110
111bool GetterSetterValidation::ValidateSetter(CheckContext &ctx, ir::MethodDefinition const *const method) const
112{
113    if (!method->Value()->IsFunctionExpression()) {
114        return true;
115    }
116
117    bool result = true;
118    auto const report = [&ctx, &result, method](const std::string &msg) {
119        ctx.AddCheckMessage(msg, *method, method->Start());
120        result = false;
121    };
122
123    auto const *const function = method->Value()->AsFunctionExpression()->Function();
124
125    // Check setter flag
126    if (!function->IsSetter()) {
127        report("SETTER METHOD DOES NOT HAVE SETTER FLAG");
128    }
129
130    // Check return type annotation
131    if (function->ReturnTypeAnnotation() != nullptr) {
132        auto const *const type = function->ReturnTypeAnnotation()->TsType();
133        if (type != nullptr && !type->IsETSVoidType()) {
134            report("SETTER METHOD HAS NON-VOID RETURN TYPE");
135        }
136    }
137
138    // Check number of arguments
139    auto const &params = function->Params();
140    if (params.size() != 1) {
141        report("SETTER METHOD HAS INCORRECT NUMBER OF ARGUMENTS");
142    }
143
144    return result;
145}
146
147}  // namespace ark::es2panda::compiler::ast_verifier
148