• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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