1 /**
2 * Copyright (c) 2024-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 "ast_verifier_test.h"
17 #include "checker/ETSchecker.h"
18 #include "ir/astNode.h"
19 #include "util/diagnosticEngine.h"
20 #include <gtest/gtest.h>
21 #include <cstddef>
22
23 using ark::es2panda::checker::ETSChecker;
24 using ark::es2panda::compiler::ast_verifier::GetterSetterValidation;
25 using ark::es2panda::ir::ETSParameterExpression;
26 using ark::es2panda::ir::Identifier;
27 using ark::es2panda::util::DiagnosticEngine;
28 namespace {
TEST_F(ASTVerifierTest,ValidateGetterReturnTypeAnnotation)29 TEST_F(ASTVerifierTest, ValidateGetterReturnTypeAnnotation)
30 {
31 DiagnosticEngine de {};
32 ETSChecker checker {de};
33
34 char const *text =
35 R"(
36 class A {
37 private _value: number = 0;
38 get value(): number {
39 return this._value
40 }
41
42 set value(v: number) {
43 this._value = v
44 }
45 }
46 )";
47
48 auto cb = [&checker](ark::es2panda::ir::AstNode *child) {
49 if (child->IsMethodDefinition()) {
50 auto *const method = child->AsMethodDefinition();
51 if (method->IsGetter() && method->Value()->IsFunctionExpression()) {
52 auto *const function = method->Value()->AsFunctionExpression()->Function();
53 ASSERT_NE(function->ReturnTypeAnnotation(), nullptr);
54 function->ReturnTypeAnnotation()->SetTsType(checker.GlobalVoidType());
55 }
56 }
57 };
58
59 CONTEXT(ES2PANDA_STATE_CHECKED, text)
60 {
61 // Change annotation return type to void
62 GetAst()->IterateRecursively(cb);
63 EXPECT_TRUE(Verify<GetterSetterValidation>(
64 ExpectVerifierMessage {"GETTER METHOD HAS VOID RETURN TYPE IN RETURN TYPE ANNOTATION"}));
65 }
66 }
67
TEST_F(ASTVerifierTest,ValidateGetterHasReturnStatement)68 TEST_F(ASTVerifierTest, ValidateGetterHasReturnStatement)
69 {
70 // Program with no type annotation for getter
71 char const *text =
72 R"(
73 class A {
74 private _value: number = 0;
75 get value() {
76 return this._value
77 }
78
79 set value(v: number) {
80 this._value = v
81 }
82 }
83 )";
84
85 auto cb = [](ark::es2panda::ir::AstNode *child) {
86 if (child->IsMethodDefinition()) {
87 auto *const method = child->AsMethodDefinition();
88 if (method->IsGetter() && method->Value()->IsFunctionExpression()) {
89 auto *const function = method->Value()->AsFunctionExpression()->Function();
90 auto &returns = function->ReturnStatements();
91 returns.clear();
92 }
93 }
94 };
95 CONTEXT(ES2PANDA_STATE_CHECKED, text)
96 {
97 // Remove return statements from getter
98 GetAst()->IterateRecursively(cb);
99 EXPECT_TRUE(Verify<GetterSetterValidation>(
100 ExpectVerifierMessage {"MISSING RETURN TYPE ANNOTATION AND RETURN STATEMENT IN GETTER METHOD"}));
101 }
102 }
103
TEST_F(ASTVerifierTest,ValidateGetterVoidReturnStatement)104 TEST_F(ASTVerifierTest, ValidateGetterVoidReturnStatement)
105 {
106 DiagnosticEngine de {};
107 ETSChecker checker {de};
108
109 char const *text =
110 R"(
111 class A {
112 private _value: number = 0;
113 get value() {
114 return this._value
115 }
116
117 set value(v: number) {
118 this._value = v
119 }
120 }
121 )";
122 auto cb = [&checker](ark::es2panda::ir::AstNode *child) {
123 if (child->IsMethodDefinition()) {
124 auto *const method = child->AsMethodDefinition();
125 if (method->IsGetter() && method->Value()->IsFunctionExpression()) {
126 auto *const function = method->Value()->AsFunctionExpression()->Function();
127 auto &returns = function->ReturnStatements();
128 ASSERT_EQ(returns.size(), 1);
129 returns[0]->SetArgument(nullptr);
130 returns[0]->SetReturnType(&checker, checker.GlobalVoidType());
131 }
132 }
133 };
134 CONTEXT(ES2PANDA_STATE_CHECKED, text)
135 {
136 // Change return statement type to void
137 GetAst()->IterateRecursively(cb);
138 EXPECT_TRUE(Verify<GetterSetterValidation>(ExpectVerifierMessage {"GETTER METHOD HAS VOID RETURN TYPE"}));
139 }
140 }
141
TEST_F(ASTVerifierTest,ValidateGetterArguments)142 TEST_F(ASTVerifierTest, ValidateGetterArguments)
143 {
144 DiagnosticEngine de {};
145 ETSChecker checker {de};
146
147 char const *text =
148 R"(
149 class A {
150 private _value: number = 0;
151 get value(): number {
152 return this._value
153 }
154
155 set value(v: number) {
156 this._value = v
157 }
158 }
159 )";
160
161 // Create argument
162 auto *ident = checker.AllocNode<Identifier>("ident", Allocator());
163 auto *param = checker.AllocNode<ETSParameterExpression>(ident, nullptr, Allocator());
164 auto cb = [param](ark::es2panda::ir::AstNode *child) {
165 if (child->IsMethodDefinition()) {
166 auto *const method = child->AsMethodDefinition();
167 if (method->IsGetter() && method->Value()->IsFunctionExpression()) {
168 auto *const function = method->Value()->AsFunctionExpression()->Function();
169 auto ¶ms = function->Params();
170 ASSERT_EQ(params.size(), 0);
171 params.push_back(param);
172 }
173 }
174 };
175 CONTEXT(ES2PANDA_STATE_CHECKED, text)
176 {
177 // Add argument to getter
178 GetAst()->IterateRecursively(cb);
179 EXPECT_TRUE(
180 Verify<GetterSetterValidation>(ExpectVerifierMessage {"GETTER METHOD HAS INCORRECT NUMBER OF ARGUMENTS"}));
181 }
182 }
183
TEST_F(ASTVerifierTest,ValidateSetterReturnType)184 TEST_F(ASTVerifierTest, ValidateSetterReturnType)
185 {
186 DiagnosticEngine de {};
187 ETSChecker checker {de};
188
189 char const *text =
190 R"(
191 class A {
192 private _value: number = 0;
193 set value(v: number) {
194 this._value = v
195 }
196
197 get value(): number {
198 return this._value
199 }
200 }
201 )";
202 auto cb = [&checker](ark::es2panda::ir::AstNode *child) {
203 if (child->IsMethodDefinition()) {
204 auto *const method = child->AsMethodDefinition();
205 if (method->IsSetter() && method->Value()->IsFunctionExpression()) {
206 auto *const function = method->Value()->AsFunctionExpression()->Function();
207 ASSERT_EQ(function->ReturnTypeAnnotation(), nullptr);
208 auto *const thisType = function->Signature()->Params()[0]->TsType();
209 auto *const thisTypeAnnotation =
210 function->Params()[0]->AsETSParameterExpression()->Ident()->TypeAnnotation();
211 function->Signature()->SetReturnType(thisType);
212 function->SetReturnTypeAnnotation(thisTypeAnnotation->Clone(checker.Allocator(), function));
213 }
214 }
215 };
216
217 CONTEXT(ES2PANDA_STATE_CHECKED, text)
218 {
219 // Change setter return type
220 GetAst()->IterateRecursively(cb);
221 EXPECT_TRUE(Verify<GetterSetterValidation>(ExpectVerifierMessage {"SETTER METHOD MUST NOT HAVE RETURN TYPE"}));
222 }
223 }
224
TEST_F(ASTVerifierTest,ValidateSetterArguments)225 TEST_F(ASTVerifierTest, ValidateSetterArguments)
226 {
227 DiagnosticEngine de {};
228 ETSChecker checker {de};
229
230 char const *text =
231 R"(
232 class A {
233 private _value: number = 0;
234 set value(v: number) {
235 this._value = v
236 }
237
238 get value(): number {
239 return this._value
240 }
241 }
242 )";
243 auto cb = [](ark::es2panda::ir::AstNode *child) {
244 if (child->IsMethodDefinition()) {
245 auto *const method = child->AsMethodDefinition();
246 if (method->IsSetter() && method->Value()->IsFunctionExpression()) {
247 auto *const function = method->Value()->AsFunctionExpression()->Function();
248 auto ¶ms = function->Params();
249 ASSERT_EQ(params.size(), 1);
250 params.clear();
251 }
252 }
253 };
254 CONTEXT(ES2PANDA_STATE_CHECKED, text)
255 {
256 // Change setter arguments
257 GetAst()->IterateRecursively(cb);
258 EXPECT_TRUE(
259 Verify<GetterSetterValidation>(ExpectVerifierMessage {"SETTER METHOD HAS INCORRECT NUMBER OF ARGUMENTS"}));
260 }
261 }
262
263 } // anonymous namespace
264