1 /*
2 * Copyright (c) 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 "checker/IsolatedDeclgenChecker.h"
17 #include "clang.h"
18 #include "utils/logger.h"
19
20 namespace ark::es2panda::checker {
21 using AstNodePtr = ir::AstNode *;
22
LogError(const diagnostic::DiagnosticKind & diagnostic,const util::DiagnosticMessageParams & diagnosticParams,const lexer::SourcePosition & pos)23 void IsolatedDeclgenChecker::LogError(const diagnostic::DiagnosticKind &diagnostic,
24 const util::DiagnosticMessageParams &diagnosticParams,
25 const lexer::SourcePosition &pos)
26 {
27 diagnosticEngine_.LogDiagnostic(diagnostic, diagnosticParams, pos);
28 }
29
Check(ir::ClassProperty * ast)30 void IsolatedDeclgenChecker::Check(ir::ClassProperty *ast)
31 {
32 if (ast->Parent()->IsAnnotationUsage()) {
33 return;
34 }
35 if (ast->TypeAnnotation() == nullptr) {
36 LogError(diagnostic::PROPERTY_MUST_HAVE_EXPLICIT_TYPE_ANNOTATION_WITH_ISOLATED_DECL, {}, ast->Start());
37 }
38 }
39
Check(ir::ObjectExpression * ast)40 void IsolatedDeclgenChecker::Check(ir::ObjectExpression *ast)
41 {
42 for (auto property : ast->Properties()) {
43 if (property->IsSpreadElement()) {
44 LogError(diagnostic::OBJECTS_THAT_CONTAIN_SPREAD_ASSIGNMENTS_CANNOT_BE_INFERRED_WITH_ISOLATED_DECL, {},
45 property->Start());
46 }
47 }
48 }
49
IsEnumArray(ir::ArrayExpression * ast)50 static bool IsEnumArray(ir::ArrayExpression *ast)
51 {
52 if (ast->Elements().empty()) {
53 return false;
54 }
55 auto *arrayParent = ast->Parent();
56 if (arrayParent == nullptr || !arrayParent->IsClassProperty()) {
57 return false;
58 }
59 auto *classProperty = arrayParent->AsClassProperty();
60 if (classProperty == nullptr || classProperty->Parent() == nullptr) {
61 return false;
62 }
63 auto *classDecl = classProperty->Parent()->AsClassDefinition();
64 return !(classDecl == nullptr || !classDecl->InternalName().StartsWith("enum."));
65 }
66
Check(ir::ArrayExpression * ast)67 void IsolatedDeclgenChecker::Check(ir::ArrayExpression *ast)
68 {
69 if (IsEnumArray(ast)) {
70 return;
71 }
72 for (auto element : ast->Elements()) {
73 if (!element->IsStringLiteral() && !element->IsNumberLiteral() && !element->IsBooleanLiteral()) {
74 LogError(diagnostic::ONLY_CONST_ARRAYS_CAN_BE_INFERRED_WITH_ISOLATED_DECL, {}, element->Start());
75 }
76 }
77 }
78
Check(ir::ETSParameterExpression * ast)79 void IsolatedDeclgenChecker::Check(ir::ETSParameterExpression *ast)
80 {
81 if (ast->TypeAnnotation() == nullptr) {
82 LogError(diagnostic::PARAMETER_MUST_HAVE_EXPLICIT_TYPE_ANNOTATION_WITH_ISOLATED_DECL, {}, ast->Start());
83 }
84 }
85
Check(ir::ExportDefaultDeclaration * ast)86 void IsolatedDeclgenChecker::Check(ir::ExportDefaultDeclaration *ast)
87 {
88 auto *decl = ast->Decl();
89 if (decl == nullptr) {
90 LogError(diagnostic::DEFAULT_EXPORTS__CANNOT_BE_INFERRED_WITH_ISOLATED_DECL, {}, ast->Start());
91 return;
92 }
93 if (decl->IsClassDeclaration()) {
94 auto *classDecl = decl->AsClassDeclaration();
95 if (classDecl == nullptr) {
96 LogError(diagnostic::DEFAULT_EXPORTS__CANNOT_BE_INFERRED_WITH_ISOLATED_DECL, {}, ast->Start());
97 }
98 return;
99 }
100 if (decl->IsTSInterfaceDeclaration()) {
101 auto *interfaceDecl = decl->AsTSInterfaceDeclaration();
102 if (interfaceDecl == nullptr) {
103 LogError(diagnostic::DEFAULT_EXPORTS__CANNOT_BE_INFERRED_WITH_ISOLATED_DECL, {}, ast->Start());
104 }
105 return;
106 }
107 if (decl->IsTSEnumDeclaration()) {
108 auto *enumDecl = decl->AsTSEnumDeclaration();
109 if (enumDecl == nullptr) {
110 LogError(diagnostic::DEFAULT_EXPORTS__CANNOT_BE_INFERRED_WITH_ISOLATED_DECL, {}, ast->Start());
111 }
112 return;
113 }
114 if (decl->IsFunctionDeclaration()) {
115 auto *funcDecl = decl->AsFunctionDeclaration();
116 if (funcDecl == nullptr) {
117 LogError(diagnostic::DEFAULT_EXPORTS__CANNOT_BE_INFERRED_WITH_ISOLATED_DECL, {}, ast->Start());
118 }
119 return;
120 }
121 if (decl->IsClassProperty()) {
122 auto *classProperty = decl->AsClassProperty();
123 if (classProperty == nullptr) {
124 LogError(diagnostic::DEFAULT_EXPORTS__CANNOT_BE_INFERRED_WITH_ISOLATED_DECL, {}, ast->Start());
125 }
126 return;
127 }
128 LogError(diagnostic::DEFAULT_EXPORTS__CANNOT_BE_INFERRED_WITH_ISOLATED_DECL, {}, ast->Start());
129 }
130
GetLiteralType(const ir::Literal * literal)131 std::string GetLiteralType(const ir::Literal *literal)
132 {
133 if (literal->IsStringLiteral()) {
134 return "String";
135 }
136 if (literal->IsNumberLiteral()) {
137 return "Number";
138 }
139 if (literal->IsBooleanLiteral()) {
140 return "Boolean";
141 }
142 return "";
143 }
144
ProcessLiteralArgument(ir::Literal * literal,ir::ReturnStatement * returnStatement)145 std::string IsolatedDeclgenChecker::ProcessLiteralArgument(ir::Literal *literal, ir::ReturnStatement *returnStatement)
146 {
147 std::string literalType = GetLiteralType(literal);
148 if (!literalType.empty()) {
149 return literalType;
150 }
151 LogError(diagnostic::FUNCTION_MUST_HAVE_AN_EXPLICIT_RETURN_TYPE_ANNOTATION_WITH_ISOLATED_DECL, {},
152 returnStatement->Start());
153 return "";
154 }
155
ProcessIdentifierArgument(ir::Identifier * identifier,ir::ReturnStatement * returnStatement)156 std::string IsolatedDeclgenChecker::ProcessIdentifierArgument(ir::Identifier *identifier,
157 ir::ReturnStatement *returnStatement)
158 {
159 auto idVar = identifier->Variable();
160 if (idVar == nullptr) {
161 LogError(diagnostic::FUNCTION_MUST_HAVE_AN_EXPLICIT_RETURN_TYPE_ANNOTATION_WITH_ISOLATED_DECL, {},
162 returnStatement->Start());
163 return "";
164 }
165 auto decl = idVar->Declaration();
166 if (decl == nullptr) {
167 LogError(diagnostic::FUNCTION_MUST_HAVE_AN_EXPLICIT_RETURN_TYPE_ANNOTATION_WITH_ISOLATED_DECL, {},
168 returnStatement->Start());
169 return "";
170 }
171
172 if (decl->IsLetOrConstDecl()) {
173 return decl->Node()
174 ->AsClassProperty()
175 ->TypeAnnotation()
176 ->AsETSTypeReference()
177 ->Part()
178 ->Name()
179 ->AsIdentifier()
180 ->Name()
181 .Mutf8();
182 }
183 LogError(diagnostic::FUNCTION_MUST_HAVE_AN_EXPLICIT_RETURN_TYPE_ANNOTATION_WITH_ISOLATED_DECL, {},
184 returnStatement->Start());
185 return "";
186 }
187
ProcessConstArrayArgument(ir::ArrayExpression * arrayExpr,ir::ReturnStatement * returnStatement)188 std::string IsolatedDeclgenChecker::ProcessConstArrayArgument(ir::ArrayExpression *arrayExpr,
189 ir::ReturnStatement *returnStatement)
190 {
191 if (arrayExpr->Elements().empty() || !arrayExpr->Elements()[0]->IsLiteral()) {
192 LogError(diagnostic::FUNCTION_MUST_HAVE_AN_EXPLICIT_RETURN_TYPE_ANNOTATION_WITH_ISOLATED_DECL, {},
193 returnStatement->Start());
194 return "";
195 }
196
197 return "Array<" + GetLiteralType((arrayExpr->Elements()[0])->AsLiteral()) + ">";
198 }
199
ProcessNewClassInstanceExpressionArgument(ir::ETSNewClassInstanceExpression * newClassInstance,ir::ReturnStatement * returnStatement)200 std::string IsolatedDeclgenChecker::ProcessNewClassInstanceExpressionArgument(
201 ir::ETSNewClassInstanceExpression *newClassInstance, ir::ReturnStatement *returnStatement)
202 {
203 auto *classType = newClassInstance->GetTypeRef();
204 if (classType == nullptr) {
205 LogError(diagnostic::FUNCTION_MUST_HAVE_AN_EXPLICIT_RETURN_TYPE_ANNOTATION_WITH_ISOLATED_DECL, {},
206 returnStatement->Start());
207 return "";
208 }
209 return classType->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Name().Mutf8();
210 }
211
InferReturnTypeForArgument(ir::ReturnStatement * returnStatement)212 std::string IsolatedDeclgenChecker::InferReturnTypeForArgument(ir::ReturnStatement *returnStatement)
213 {
214 if (returnStatement->Argument() == nullptr) {
215 return "void";
216 }
217 auto *returnType = returnStatement->ReturnType();
218 auto returnTypeStr = returnStatement->ReturnType()->ToString();
219 if (returnType != nullptr && returnTypeStr.find(ERROR_TYPE) == std::string::npos) {
220 return returnTypeStr;
221 }
222 auto *argument = returnStatement->Argument();
223 if (argument->IsLiteral()) {
224 auto *literal = argument->AsLiteral();
225 return ProcessLiteralArgument(literal, returnStatement);
226 }
227 if (argument->IsETSNewClassInstanceExpression()) {
228 auto *newClassInstance = argument->AsETSNewClassInstanceExpression();
229 return ProcessNewClassInstanceExpressionArgument(newClassInstance, returnStatement);
230 }
231 if (argument->IsETSParameterExpression()) {
232 auto *etsParam = argument->AsETSParameterExpression();
233 return etsParam->TypeAnnotation()->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Name().Mutf8();
234 }
235 if (argument->IsArrayExpression()) {
236 auto *arrayExpr = argument->AsArrayExpression();
237 return ProcessConstArrayArgument(arrayExpr, returnStatement);
238 }
239 if (argument->IsIdentifier()) {
240 auto *identifier = argument->AsIdentifier();
241 return ProcessIdentifierArgument(identifier, returnStatement);
242 }
243
244 LogError(diagnostic::FUNCTION_MUST_HAVE_AN_EXPLICIT_RETURN_TYPE_ANNOTATION_WITH_ISOLATED_DECL, {},
245 returnStatement->Start());
246 return "";
247 }
248
Check(ir::ScriptFunction * ast)249 std::string IsolatedDeclgenChecker::Check(ir::ScriptFunction *ast)
250 {
251 auto &returnStatements = ast->ReturnStatements();
252 if (returnStatements.empty()) {
253 return "void";
254 }
255 if (returnStatements.size() == 1) {
256 return InferReturnTypeForArgument(returnStatements[0]);
257 }
258
259 std::set<std::string> returnTypes;
260 for (const auto &returnStatement : returnStatements) {
261 auto returnType = InferReturnTypeForArgument(returnStatement);
262 if (!returnType.empty()) {
263 returnTypes.insert(returnType);
264 } else {
265 return "";
266 }
267 }
268
269 if (returnTypes.size() == 1) {
270 return *returnTypes.begin();
271 }
272 std::string returnTypeStr;
273 for (const auto &returnType : returnTypes) {
274 if (returnTypeStr.empty()) {
275 returnTypeStr = returnType;
276 } else {
277 returnTypeStr += " | " + returnType;
278 }
279 }
280
281 return returnTypeStr;
282 }
283
CheckBeforeChecker()284 void IsolatedDeclgenChecker::CheckBeforeChecker()
285 {
286 program_.Ast()->TransformChildrenRecursively(
287 [&](ir::AstNode *ast) -> AstNodePtr {
288 if (ast->IsClassProperty()) {
289 Check(ast->AsClassProperty());
290 return ast;
291 }
292
293 if (ast->IsETSParameterExpression()) {
294 this->Check(ast->AsETSParameterExpression());
295 return ast;
296 }
297
298 if (ast->IsArrayExpression()) {
299 Check(ast->AsArrayExpression());
300 return ast;
301 }
302
303 if (ast->IsObjectExpression()) {
304 Check(ast->AsObjectExpression());
305 return ast;
306 }
307 if (ast->IsExportDefaultDeclaration()) {
308 Check(ast->AsExportDefaultDeclaration());
309 return ast;
310 }
311
312 return ast;
313 },
314 "CheckIsolatedDeclBeforeChecker");
315 }
316
CheckAfterChecker()317 void IsolatedDeclgenChecker::CheckAfterChecker()
318 {
319 program_.Ast()->TransformChildrenRecursively(
320 [&](ir::AstNode *ast) -> AstNodePtr {
321 if (!ast->IsScriptFunction()) {
322 return ast;
323 }
324
325 auto *scriptFunction = ast->AsScriptFunction();
326 auto *returnType = scriptFunction->Signature()->ReturnType();
327 if (returnType == nullptr) {
328 return ast;
329 }
330 std::string returnTypeStr = Check(scriptFunction);
331 if (returnType->ToString().find(ERROR_TYPE) == std::string::npos) {
332 scriptFunction->SetIsolatedDeclgenReturnType(returnType->ToString());
333 return ast;
334 }
335 scriptFunction->SetIsolatedDeclgenReturnType(returnTypeStr);
336
337 return ast;
338 },
339 "CheckIsolatedDeclAfterChecker");
340 }
341 } // namespace ark::es2panda::checker