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 "ast_verifier_test.h"
17 #include "checker/ETSchecker.h"
18 #include "ir/astNode.h"
19
20 #include <gtest/gtest.h>
21
22 using ark::es2panda::checker::ETSChecker;
23 using ark::es2panda::compiler::ast_verifier::ASTVerifier;
24 using ark::es2panda::compiler::ast_verifier::InvariantNameSet;
25 using ark::es2panda::ir::AstNode;
26 using ark::es2panda::ir::MethodDefinitionKind;
27
28 namespace {
TEST_F(ASTVerifierTest,ValidateGetterReturnTypeAnnotation)29 TEST_F(ASTVerifierTest, ValidateGetterReturnTypeAnnotation)
30 {
31 ETSChecker checker {};
32 ASTVerifier verifier {Allocator()};
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): void {
43 this._value = v
44 }
45 }
46 )";
47
48 es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.sts");
49 impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED);
50 ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED);
51
52 auto *ast = reinterpret_cast<AstNode *>(impl_->ProgramAst(impl_->ContextProgram(ctx)));
53
54 // Change annotation return type to void
55 ast->IterateRecursively([&checker](ark::es2panda::ir::AstNode *child) {
56 if (child->IsMethodDefinition()) {
57 auto *const method = child->AsMethodDefinition();
58 if (method->Kind() == MethodDefinitionKind::GET && method->Value()->IsFunctionExpression()) {
59 auto *const function = method->Value()->AsFunctionExpression()->Function();
60 ASSERT_NE(function->ReturnTypeAnnotation(), nullptr);
61 function->ReturnTypeAnnotation()->SetTsType(checker.GlobalVoidType());
62 }
63 }
64 });
65
66 InvariantNameSet checks;
67 checks.insert("GetterSetterValidationForAll");
68 const auto &messages = verifier.Verify(ast, checks);
69
70 ASSERT_EQ(messages.size(), 1);
71 ASSERT_EQ(messages[0].Cause(), "GETTER METHOD HAS VOID RETURN TYPE IN RETURN TYPE ANNOTATION");
72
73 impl_->DestroyContext(ctx);
74 }
75
TEST_F(ASTVerifierTest,ValidateGetterHasReturnStatement)76 TEST_F(ASTVerifierTest, ValidateGetterHasReturnStatement)
77 {
78 ASTVerifier verifier {Allocator()};
79
80 // Program with no type annotation for getter
81 char const *text =
82 R"(
83 class A {
84 private _value: number = 0;
85 get value() {
86 return this._value
87 }
88
89 set value(v: number): void {
90 this._value = v
91 }
92 }
93 )";
94
95 es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.sts");
96 impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED);
97 ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED);
98
99 auto *ast = reinterpret_cast<AstNode *>(impl_->ProgramAst(impl_->ContextProgram(ctx)));
100
101 // Remove return statements from getter
102 ast->IterateRecursively([](ark::es2panda::ir::AstNode *child) {
103 if (child->IsMethodDefinition()) {
104 auto *const method = child->AsMethodDefinition();
105 if (method->Kind() == MethodDefinitionKind::GET && method->Value()->IsFunctionExpression()) {
106 auto *const function = method->Value()->AsFunctionExpression()->Function();
107 auto &returns = function->ReturnStatements();
108 returns.clear();
109 }
110 }
111 });
112
113 InvariantNameSet checks;
114 checks.insert("GetterSetterValidationForAll");
115 const auto &messages = verifier.Verify(ast, checks);
116
117 ASSERT_EQ(messages.size(), 1);
118 ASSERT_EQ(messages[0].Cause(), "MISSING RETURN TYPE ANNOTATION AND RETURN STATEMENT IN GETTER METHOD");
119
120 impl_->DestroyContext(ctx);
121 }
122
TEST_F(ASTVerifierTest,ValidateGetterVoidReturnStatement)123 TEST_F(ASTVerifierTest, ValidateGetterVoidReturnStatement)
124 {
125 ETSChecker checker {};
126 ASTVerifier verifier {Allocator()};
127
128 char const *text =
129 R"(
130 class A {
131 private _value: number = 0;
132 get value() {
133 return this._value
134 }
135
136 set value(v: number): void {
137 this._value = v
138 }
139 }
140 )";
141
142 es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.sts");
143 impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED);
144 ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_CHECKED);
145
146 auto *ast = reinterpret_cast<AstNode *>(impl_->ProgramAst(impl_->ContextProgram(ctx)));
147
148 // Change return statement type to void
149 ast->IterateRecursively([&checker](ark::es2panda::ir::AstNode *child) {
150 if (child->IsMethodDefinition()) {
151 auto *const method = child->AsMethodDefinition();
152 if (method->Kind() == MethodDefinitionKind::GET && method->Value()->IsFunctionExpression()) {
153 auto *const function = method->Value()->AsFunctionExpression()->Function();
154 auto &returns = function->ReturnStatements();
155 ASSERT_EQ(returns.size(), 1);
156
157 returns[0]->SetArgument(nullptr);
158 returns[0]->SetReturnType(&checker, checker.GlobalVoidType());
159 }
160 }
161 });
162
163 InvariantNameSet checks;
164 checks.insert("GetterSetterValidationForAll");
165 const auto &messages = verifier.Verify(ast, checks);
166
167 ASSERT_EQ(messages.size(), 1);
168 ASSERT_EQ(messages[0].Cause(), "GETTER METHOD HAS VOID RETURN TYPE");
169
170 impl_->DestroyContext(ctx);
171 }
172
173 } // anonymous namespace
174