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/expressions/literals/stringLiteral.h"
19 #include "ir/expressions/identifier.h"
20 #include "ir/expressions/literals/numberLiteral.h"
21 #include "ir/expressions/literals/booleanLiteral.h"
22 #include "macros.h"
23 #include "parser/ETSparser.h"
24 #include "varbinder/ETSBinder.h"
25
26 #include <gtest/gtest.h>
27
28 using ark::es2panda::CompilerOptions;
29 using ark::es2panda::ScriptExtension;
30 using ark::es2panda::checker::ETSChecker;
31 using ark::es2panda::compiler::ast_verifier::ASTVerifier;
32 using ark::es2panda::compiler::ast_verifier::InvariantNameSet;
33 using ark::es2panda::ir::BinaryExpression;
34 using ark::es2panda::ir::BooleanLiteral;
35 using ark::es2panda::ir::Expression;
36 using ark::es2panda::ir::Identifier;
37 using ark::es2panda::ir::NumberLiteral;
38 using ark::es2panda::ir::SequenceExpression;
39 using ark::es2panda::ir::StringLiteral;
40 using ark::es2panda::lexer::Number;
41 using ark::es2panda::lexer::TokenType;
42 using ark::es2panda::parser::ETSParser;
43 using ark::es2panda::parser::Program;
44 using ark::es2panda::util::StringView;
45 using ark::es2panda::varbinder::ETSBinder;
46 using ark::es2panda::varbinder::FunctionScope;
47 using ark::es2panda::varbinder::LetDecl;
48 using ark::es2panda::varbinder::LocalScope;
49 using ark::es2panda::varbinder::LocalVariable;
50 using ark::es2panda::varbinder::VariableFlags;
51
TEST_F(ASTVerifierTest, NullParent)52 TEST_F(ASTVerifierTest, NullParent)
53 {
54 ark::es2panda::compiler::ast_verifier::ASTVerifier verifier {Allocator()};
55 StringLiteral emptyNode;
56
57 const auto check = "NodeHasParent";
58 auto checks = ark::es2panda::compiler::ast_verifier::InvariantNameSet {};
59 checks.insert(check);
60 const auto &messages = verifier.Verify(&emptyNode, checks);
61 bool hasParent = messages.empty();
62 ASSERT_FALSE(hasParent);
63 ASSERT_EQ(messages.size(), 1);
64
65 ASSERT_EQ(messages[0].Invariant(), check);
66 }
67
TEST_F(ASTVerifierTest, NullRange)68 TEST_F(ASTVerifierTest, NullRange)
69 {
70 ark::es2panda::compiler::ast_verifier::ASTVerifier verifier {Allocator()};
71 StringLiteral emptyNode;
72
73 const auto check = "NodeHasSourceRange";
74 auto checks = ark::es2panda::compiler::ast_verifier::InvariantNameSet {};
75 checks.insert(check);
76 const auto &messages = verifier.Verify(&emptyNode, checks);
77 bool hasSourceRange = messages.empty();
78 ASSERT_FALSE(hasSourceRange);
79 ASSERT_EQ(messages.size(), 1);
80
81 ASSERT_EQ(messages[0].Invariant(), check);
82 }
83
TEST_F(ASTVerifierTest, NullType)84 TEST_F(ASTVerifierTest, NullType)
85 {
86 ASTVerifier verifier {Allocator()};
87 StringLiteral emptyNode;
88
89 auto check = "NodeHasType";
90 auto checks = InvariantNameSet {};
91 checks.insert(check);
92 const auto &messages = verifier.Verify(&emptyNode, checks);
93 bool hasType = messages.empty();
94 ASSERT_EQ(hasType, false);
95 ASSERT_NE(messages.size(), 0);
96
97 ASSERT_EQ(messages[0].Invariant(), check);
98 }
99
TEST_F(ASTVerifierTest, WithoutScope)100 TEST_F(ASTVerifierTest, WithoutScope)
101 {
102 ASTVerifier verifier {Allocator()};
103 StringLiteral emptyNode;
104
105 auto checks = InvariantNameSet {};
106 checks.insert("VariableHasScope");
107 const auto &messages = verifier.Verify(&emptyNode, checks);
108
109 ASSERT_EQ(messages.size(), 0);
110 }
111
TEST_F(ASTVerifierTest, ScopeTest)112 TEST_F(ASTVerifierTest, ScopeTest)
113 {
114 ASTVerifier verifier {Allocator()};
115 Identifier ident(StringView("var_decl"), Allocator());
116 LetDecl decl("test", &ident);
117 LocalVariable local(&decl, VariableFlags::LOCAL);
118 ident.SetVariable(&local);
119
120 LocalScope scope(Allocator(), nullptr);
121 FunctionScope parentScope(Allocator(), nullptr);
122 scope.SetParent(&parentScope);
123 scope.AddDecl(Allocator(), &decl, ScriptExtension::ETS);
124 scope.BindNode(&ident);
125
126 local.SetScope(&scope);
127
128 auto checks = InvariantNameSet {};
129 checks.insert("VariableHasScope");
130 const auto &messages = verifier.Verify(&ident, checks);
131
132 ASSERT_EQ(messages.size(), 0);
133 }
134
TEST_F(ASTVerifierTest, ScopeNodeTest)135 TEST_F(ASTVerifierTest, ScopeNodeTest)
136 {
137 ASTVerifier verifier {Allocator()};
138 Identifier ident(StringView("var_decl"), Allocator());
139 LetDecl decl("test", &ident);
140 LocalVariable local(&decl, VariableFlags::LOCAL);
141 ident.SetVariable(&local);
142
143 LocalScope scope(Allocator(), nullptr);
144 FunctionScope parentScope(Allocator(), nullptr);
145 scope.SetParent(&parentScope);
146 scope.AddDecl(Allocator(), &decl, ScriptExtension::ETS);
147 scope.BindNode(&ident);
148 parentScope.BindNode(&ident);
149
150 local.SetScope(&scope);
151
152 auto checks = InvariantNameSet {};
153 checks.insert("VariableHasEnclosingScope");
154 const auto &messages = verifier.Verify(&ident, checks);
155
156 ASSERT_EQ(messages.size(), 0);
157 }
158
TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect1)159 TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect1)
160 {
161 ETSChecker etschecker {};
162 ASTVerifier verifier {Allocator()};
163 auto program = Program::NewProgram<ETSBinder>(Allocator());
164 auto parser = ETSParser(&program, CompilerOptions {});
165
166 auto left = NumberLiteral(Number {1});
167 auto right = NumberLiteral(Number {6});
168 auto arithmeticExpression = BinaryExpression(&left, &right, TokenType::PUNCTUATOR_PLUS);
169
170 left.SetTsType(etschecker.GlobalIntType());
171 right.SetTsType(etschecker.GlobalIntType());
172
173 auto checks = InvariantNameSet {};
174 checks.insert("ArithmeticOperationValid");
175 const auto &messages = verifier.Verify(arithmeticExpression.AsBinaryExpression(), checks);
176 ASSERT_EQ(messages.size(), 0);
177 }
178
TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect2)179 TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect2)
180 {
181 ETSChecker etschecker {};
182 ASTVerifier verifier {Allocator()};
183 auto program = Program::NewProgram<ETSBinder>(Allocator());
184 auto parser = ETSParser(&program, CompilerOptions {});
185
186 constexpr uint32_t LEFT1_PARAM = 1;
187 constexpr uint32_t LEFT2_PARAM = 12;
188 constexpr uint32_t RIGHT2_PARAM = 6;
189 auto left1 = NumberLiteral(Number {LEFT1_PARAM});
190 auto left2 = NumberLiteral(Number {LEFT2_PARAM});
191 auto right2 = NumberLiteral(Number {RIGHT2_PARAM});
192 auto right1 = BinaryExpression(&left2, &right2, TokenType::PUNCTUATOR_MULTIPLY);
193 auto arithmeticExpression = BinaryExpression(&left1, &right1, TokenType::PUNCTUATOR_PLUS);
194
195 left1.SetTsType(etschecker.GlobalIntType());
196 right1.SetTsType(etschecker.GlobalIntType());
197 left2.SetTsType(etschecker.GlobalIntType());
198 right2.SetTsType(etschecker.GlobalIntType());
199
200 auto checks = InvariantNameSet {};
201 checks.insert("ArithmeticOperationValid");
202 const auto &messages = verifier.Verify(arithmeticExpression.AsBinaryExpression(), checks);
203 ASSERT_EQ(messages.size(), 0);
204 }
205
TEST_F(ASTVerifierTest, ArithmeticExpressionNegative1)206 TEST_F(ASTVerifierTest, ArithmeticExpressionNegative1)
207 {
208 ETSChecker etschecker {};
209 ASTVerifier verifier {Allocator()};
210 auto program = Program::NewProgram<ETSBinder>(Allocator());
211 auto parser = ETSParser(&program, CompilerOptions {});
212
213 const StringView leftParam("1");
214 constexpr uint32_t RIGHT_PARAM = 1;
215 auto left = StringLiteral(leftParam);
216 auto right = NumberLiteral(Number {RIGHT_PARAM});
217 auto arithmeticExpression = BinaryExpression(&left, &right, TokenType::PUNCTUATOR_DIVIDE);
218
219 left.SetTsType(etschecker.GlobalETSStringLiteralType());
220 right.SetTsType(etschecker.GlobalIntType());
221
222 auto checks = InvariantNameSet {};
223 checks.insert("ArithmeticOperationValid");
224 const auto &messages = verifier.Verify(arithmeticExpression.AsBinaryExpression(), checks);
225
226 ASSERT_EQ(messages.size(), 1);
227 }
228
TEST_F(ASTVerifierTest, ArithmeticExpressionNegative2)229 TEST_F(ASTVerifierTest, ArithmeticExpressionNegative2)
230 {
231 ETSChecker etschecker {};
232 ASTVerifier verifier {Allocator()};
233 auto program = Program::NewProgram<ETSBinder>(Allocator());
234 auto parser = ETSParser(&program, CompilerOptions {});
235 auto left = BooleanLiteral(true);
236 auto right = NumberLiteral(Number {1});
237 auto arithmeticExpression = BinaryExpression(&left, &right, TokenType::PUNCTUATOR_DIVIDE);
238
239 left.SetTsType(etschecker.GlobalETSStringLiteralType());
240 right.SetTsType(etschecker.GlobalIntType());
241
242 auto checks = InvariantNameSet {};
243 checks.insert("ArithmeticOperationValid");
244 const auto &messages = verifier.Verify(arithmeticExpression.AsBinaryExpression(), checks);
245
246 ASSERT_EQ(messages.size(), 1);
247 }
248
TEST_F(ASTVerifierTest, SequenceExpressionType)249 TEST_F(ASTVerifierTest, SequenceExpressionType)
250 {
251 ASTVerifier verifier {Allocator()};
252 auto checker = ETSChecker();
253 auto *last = Tree(Node<NumberLiteral>(Number {3}));
254 auto *sequenceExpression = Tree(Node<SequenceExpression>(
255 Nodes<Expression>(Node<NumberLiteral>(Number {1}), Node<NumberLiteral>(Number {2}), last)));
256
257 last->SetTsType(checker.GlobalIntType());
258 sequenceExpression->SetTsType(checker.GlobalIntType());
259
260 auto checks = InvariantNameSet {};
261 checks.insert("SequenceExpressionHasLastType");
262 const auto &messages = verifier.Verify(sequenceExpression, checks);
263
264 ASSERT_EQ(messages.size(), 0);
265 }
266
TEST_F(ASTVerifierTest, VariableNameIdentifierNameSameNegative)267 TEST_F(ASTVerifierTest, VariableNameIdentifierNameSameNegative)
268 {
269 ark::es2panda::compiler::ast_verifier::ASTVerifier verifier {Allocator()};
270 char const *text = R"(
271 function main(): void {
272 let tmp = 1;
273 let lambda2: (value: int) => Int = (value: int): Int => {
274 let a = 42;
275 let num: Int = new Int(a + value);
276 return num;
277 }
278 let n_tmp = tmp + 2;
279 return 1;
280 }
281 )";
282
283 es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.sts");
284 impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED);
285
286 auto *ast = reinterpret_cast<ark::es2panda::ir::ETSScript *>(impl_->ProgramAst(impl_->ContextProgram(ctx)));
287
288 // Note(@kirillbychkov): Change Identifier name in variable lambda2
289 ast->AsETSScript()
290 ->Statements()[0]
291 ->AsClassDeclaration()
292 ->Definition()
293 ->AsClassDefinition()
294 ->Body()[1]
295 ->AsClassElement()
296 ->Value()
297 ->AsFunctionExpression()
298 ->Function()
299 ->AsScriptFunction()
300 ->Body()
301 ->AsBlockStatement()
302 ->Statements()[1]
303 ->AsVariableDeclaration()
304 ->Declarators()[0]
305 ->AsVariableDeclarator()
306 ->Id()
307 ->AsIdentifier()
308 ->SetName("not_name");
309
310 const auto check = "VariableNameIdentifierNameSameForAll";
311 auto checks = ark::es2panda::compiler::ast_verifier::InvariantNameSet {};
312 checks.insert(check);
313 const auto &messages = verifier.Verify(ast, checks);
314 ASSERT_EQ(messages.size(), 1);
315
316 ASSERT_EQ(messages[0].Invariant(), check);
317
318 impl_->DestroyContext(ctx);
319 }
320
TEST_F(ASTVerifierTest, VariableNameIdentifierNameSame)321 TEST_F(ASTVerifierTest, VariableNameIdentifierNameSame)
322 {
323 ark::es2panda::compiler::ast_verifier::ASTVerifier verifier {Allocator()};
324 char const *text = R"(
325 function main(): void {
326 let tmp = 1;
327 let lambda2: (value: int) => Int = (value: int): Int => {
328 let a = 42;
329 let num: Int = new Int(a + value);
330 return num;
331 }
332 let n_tmp = tmp + 2;
333 return 1;
334 }
335 )";
336
337 es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.sts");
338 impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED);
339
340 auto *ast = reinterpret_cast<ark::es2panda::ir::ETSScript *>(impl_->ProgramAst(impl_->ContextProgram(ctx)));
341
342 auto checks = ark::es2panda::compiler::ast_verifier::InvariantNameSet {};
343 checks.insert("VariableNameIdentifierNameSameForAll");
344 const auto &messages = verifier.Verify(ast, checks);
345 ASSERT_EQ(messages.size(), 0);
346 impl_->DestroyContext(ctx);
347 }
348