• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /**
3  * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "etsWarningAnalyzer.h"
18 
19 #include "generated/diagnostic.h"
20 #include "parser/program/program.h"
21 #include "util/options.h"
22 #include "ir/expressions/binaryExpression.h"
23 #include "ir/base/methodDefinition.h"
24 #include "ir/base/scriptFunction.h"
25 #include "ir/statements/classDeclaration.h"
26 #include "ir/statements/expressionStatement.h"
27 #include "ir/statements/blockStatement.h"
28 #include "ir/expressions/assignmentExpression.h"
29 #include "ir/expressions/callExpression.h"
30 #include "ir/expressions/identifier.h"
31 #include "ir/expressions/memberExpression.h"
32 #include "ir/ets/etsTypeReferencePart.h"
33 #include "ir/ets/etsTypeReference.h"
34 #include "ir/base/classDefinition.h"
35 #include "ir/statements/forOfStatement.h"
36 #include "ir/statements/variableDeclarator.h"
37 #include "ir/statements/variableDeclaration.h"
38 #include "ir/expressions/updateExpression.h"
39 
40 namespace ark::es2panda::checker {
41 
AnalyzeClassDefForFinalModifier(const ir::ClassDefinition * classDef)42 void ETSWarningAnalyzer::AnalyzeClassDefForFinalModifier(const ir::ClassDefinition *classDef)
43 {
44     ES2PANDA_ASSERT(classDef != nullptr);
45 
46     if (program_ == nullptr || classDef->IsFinal() || classDef->IsAbstract() || classDef->IsStatic() ||
47         classDef->IsGlobal() || classDef->IsExported() || classDef->HasExportAlias()) {
48         return;
49     }
50 
51     const auto statements = program_->Ast()->Statements();
52     for (const auto *it : statements) {
53         if (!it->IsClassDeclaration() ||
54             classDef->Ident()->Name() == it->AsClassDeclaration()->Definition()->Ident()->Name()) {
55             continue;
56         }
57 
58         const auto *itAsClassDef = it->AsClassDeclaration()->Definition();
59 
60         if (!itAsClassDef->IsGlobal()) {
61             const auto *superClass = itAsClassDef->Super();
62 
63             if (superClass == nullptr) {
64                 continue;
65             }
66 
67             if (superClass->IsETSTypeReference() &&
68                 superClass->AsETSTypeReference()->Part()->GetIdent()->Name() == classDef->Ident()->Name()) {
69                 return;
70             }
71         }
72     }
73 
74     LogWarning(diagnostic::SUGGEST_FINAL_MODIFIER_FOR_CLASS, classDef->Ident()->Start());
75 }
76 
AnalyzeClassMethodForFinalModifier(const ir::MethodDefinition * methodDef,const ir::ClassDefinition * classDef)77 void ETSWarningAnalyzer::AnalyzeClassMethodForFinalModifier(const ir::MethodDefinition *methodDef,
78                                                             const ir::ClassDefinition *classDef)
79 {
80     ES2PANDA_ASSERT(methodDef != nullptr && classDef != nullptr);
81 
82     if (methodDef->IsAbstract() || methodDef->IsStatic() || classDef->IsFinal() || program_ == nullptr ||
83         methodDef->IsFinal() || methodDef->IsConstructor() || classDef->IsGlobal()) {
84         return;
85     }
86 
87     bool suggestFinal = true;
88 
89     const auto statements = program_->Ast()->Statements();
90     for (const auto *it : statements) {
91         if (!it->IsClassDeclaration() || it->AsClassDeclaration()->Definition()->IsGlobal() ||
92             classDef->Ident()->Name() == it->AsClassDeclaration()->Definition()->Ident()->Name()) {
93             continue;
94         }
95 
96         const auto *statementDef = it->AsClassDeclaration()->Definition();
97         for (const auto *bodyPart : statementDef->Body()) {
98             if (!bodyPart->IsMethodDefinition()) {
99                 continue;
100             }
101             static auto classAsETSObject = classDef->TsType()->AsETSObjectType();
102             static auto potentialDescendant = statementDef->TsType()->AsETSObjectType();
103             if (!potentialDescendant->IsDescendantOf(classAsETSObject)) {
104                 continue;
105             }
106             auto signature = ETSChecker::GetSignatureFromMethodDefinition(bodyPart->AsMethodDefinition());
107             ES2PANDA_ASSERT(signature != nullptr);
108             const util::StringView bodyMethodName = signature->Function()->Id()->Name();
109             const auto *func = methodDef->Function();
110             ES2PANDA_ASSERT(func != nullptr);
111             if (bodyPart->IsOverride() && bodyMethodName != compiler::Signatures::CTOR &&
112                 bodyMethodName == func->Id()->Name()) {
113                 suggestFinal = false;
114                 break;
115             }
116         }
117     }
118 
119     if (suggestFinal) {
120         LogWarning(diagnostic::SUGGEST_FINAL_MODIFIER_FOR_METHOD, classDef->Ident()->Start());
121     }
122 }
123 
ETSWarningSuggestFinal(const ir::AstNode * node)124 void ETSWarningAnalyzer::ETSWarningSuggestFinal(const ir::AstNode *node)
125 {
126     if (node->IsClassDeclaration() && !program_->NodeContainsETSNolint(node, ETSWarnings::ETS_SUGGEST_FINAL)) {
127         if (node->AsClassDeclaration()->Definition()->IsClassDefinition()) {
128             AnalyzeClassDefForFinalModifier(node->AsClassDeclaration()->Definition());
129         }
130 
131         const auto classBody = node->AsClassDeclaration()->Definition()->Body();
132         for (const auto *it : classBody) {
133             if (it->IsMethodDefinition()) {
134                 AnalyzeClassMethodForFinalModifier(it->AsMethodDefinition(), node->AsClassDeclaration()->Definition());
135             }
136         }
137     }
138     node->Iterate([&](auto *childNode) { ETSWarningSuggestFinal(childNode); });
139 }
140 
CheckTopLevelExpressions(const ir::Expression * expression)141 void ETSWarningAnalyzer::CheckTopLevelExpressions(const ir::Expression *expression)
142 {
143     if (expression->IsCallExpression()) {
144         const auto exprCallee = expression->AsCallExpression()->Callee();
145         lexer::SourcePosition pos = exprCallee->Start();
146         if (exprCallee->IsMemberExpression()) {
147             pos = exprCallee->AsMemberExpression()->Object()->Start();
148             LogWarning(diagnostic::PROHIBIT_TOP_LEVEL_STATEMENTS, pos);
149         }
150     } else if (expression->IsAssignmentExpression()) {
151         const auto assignmentExpr = expression->AsAssignmentExpression();
152         LogWarning(diagnostic::PROHIBIT_TOP_LEVEL_STATEMENTS, assignmentExpr->Left()->Start());
153     }
154 }
155 
CheckProhibitedTopLevelStatements(const ir::Statement * statement)156 void ETSWarningAnalyzer::CheckProhibitedTopLevelStatements(const ir::Statement *statement)
157 {
158     switch (statement->Type()) {
159         case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION:
160         case ir::AstNodeType::FUNCTION_DECLARATION:
161         case ir::AstNodeType::SCRIPT_FUNCTION:
162         case ir::AstNodeType::ETS_FUNCTION_TYPE:
163         case ir::AstNodeType::IMPORT_NAMESPACE_SPECIFIER:
164         case ir::AstNodeType::CLASS_DECLARATION:
165         case ir::AstNodeType::CLASS_EXPRESSION:
166         case ir::AstNodeType::VARIABLE_DECLARATION:
167         case ir::AstNodeType::CLASS_DEFINITION:
168         case ir::AstNodeType::CLASS_PROPERTY:
169             break;
170         default:
171             LogWarning(diagnostic::PROHIBIT_TOP_LEVEL_STATEMENTS, statement->Start());
172             break;
173     }
174 }
175 
ETSWarningAnnotationUnusedGenericAliasWarn(const ir::AstNode * node)176 void ETSWarningAnalyzer::ETSWarningAnnotationUnusedGenericAliasWarn(const ir::AstNode *node)
177 {
178     if (!node->IsTSTypeAliasDeclaration()) {
179         node->Iterate([&](auto *childNode) { ETSWarningAnnotationUnusedGenericAliasWarn(childNode); });
180         return;
181     }
182 
183     auto st = node->AsTSTypeAliasDeclaration();
184     for (auto *const param : st->TypeParams()->Params()) {
185         const auto *const res = st->TypeAnnotation()->FindChild([&param](const ir::AstNode *const astNode) {
186             if (!astNode->IsIdentifier()) {
187                 return false;
188             }
189 
190             return param->Name()->AsIdentifier()->Variable() == astNode->AsIdentifier()->Variable();
191         });
192 
193         if (res == nullptr) {
194             util::DiagnosticMessageParams diagnosticParams = {param->Name()->Name()};
195             LogWarning(diagnostic::ANNOTATION_UNUSED_GENERIC_ALIAS_WARN, diagnosticParams, param->Start());
196             return;
197         }
198     }
199 }
200 
ETSWarningsProhibitTopLevelStatements(const ir::AstNode * node)201 void ETSWarningAnalyzer::ETSWarningsProhibitTopLevelStatements(const ir::AstNode *node)
202 {
203     if (!node->IsClassDeclaration() ||
204         program_->NodeContainsETSNolint(node, ETSWarnings::ETS_PROHIBIT_TOP_LEVEL_STATEMENTS)) {
205         node->Iterate([&](auto *childNode) { ETSWarningsProhibitTopLevelStatements(childNode); });
206         return;
207     }
208 
209     const auto *classDef = node->AsClassDeclaration()->Definition();
210     if (!classDef->IsGlobal()) {
211         node->Iterate([&](auto *childNode) { ETSWarningsProhibitTopLevelStatements(childNode); });
212         return;
213     }
214 
215     for (const auto *itBody : classDef->Body()) {
216         if (!itBody->IsMethodDefinition() || itBody->AsMethodDefinition()->Id() == nullptr ||
217             itBody->AsMethodDefinition()->Id()->Name() != compiler::Signatures::INIT_METHOD) {
218             continue;
219         }
220         const auto *func = itBody->AsMethodDefinition()->Function();
221         ES2PANDA_ASSERT(func != nullptr);
222         for (const auto *statement : func->Body()->AsBlockStatement()->Statements()) {
223             if (program_->NodeContainsETSNolint(statement, ETSWarnings::ETS_PROHIBIT_TOP_LEVEL_STATEMENTS)) {
224                 continue;
225             }
226 
227             if (!statement->IsExpressionStatement()) {
228                 CheckProhibitedTopLevelStatements(statement);
229                 continue;
230             }
231 
232             // Rewrite this part after fixing AST issue about tiop-level
233             CheckTopLevelExpressions(statement->AsExpressionStatement()->GetExpression());
234         }
235     }
236 }
237 
ETSWarningBoostEqualityStatement(const ir::AstNode * node)238 void ETSWarningAnalyzer::ETSWarningBoostEqualityStatement(const ir::AstNode *node)
239 {
240     ES2PANDA_ASSERT(node != nullptr);
241 
242     if (node->IsBinaryExpression() &&
243         !program_->NodeContainsETSNolint(node, ETSWarnings::ETS_BOOST_EQUALITY_STATEMENT)) {
244         const auto binExpr = node->AsBinaryExpression();
245         if (binExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_EQUAL ||
246             binExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_NOT_EQUAL) {
247             if (binExpr->Right()->IsNullLiteral() && !binExpr->Left()->IsNullLiteral()) {
248                 LogWarning(diagnostic::BOOST_EQUALITY_STATEMENT, node->Start());
249             }
250         }
251     }
252     node->Iterate([&](auto *childNode) { ETSWarningBoostEqualityStatement(childNode); });
253 }
254 
ETSWarningRemoveAsync(const ir::AstNode * node)255 void ETSWarningAnalyzer::ETSWarningRemoveAsync(const ir::AstNode *node)
256 {
257     if (node->IsMethodDefinition() && !program_->NodeContainsETSNolint(node, ETSWarnings::ETS_REMOVE_ASYNC)) {
258         const auto methodDefinition = node->AsMethodDefinition();
259         if (methodDefinition->IsAsync()) {
260             LogWarning(diagnostic::REPLACE_ASYNC_FUNCTION_WITH_COROUTINE, methodDefinition->Start());
261         }
262     }
263     node->Iterate([&](auto *childNode) { ETSWarningRemoveAsync(childNode); });
264 }
265 
ETSWarningRemoveLambda(const ir::AstNode * node)266 void ETSWarningAnalyzer::ETSWarningRemoveLambda(const ir::AstNode *node)
267 {
268     ES2PANDA_ASSERT(node != nullptr);
269 
270     if (node->IsArrowFunctionExpression() && !program_->NodeContainsETSNolint(node, ETSWarnings::ETS_REMOVE_LAMBDA)) {
271         LogWarning(diagnostic::REPLACE_LAMBDA_FUNCTION_WITH_REGULAR_FUNCTION, node->Start());
272     }
273     node->Iterate([&](auto *childNode) { ETSWarningRemoveLambda(childNode); });
274 }
275 
CheckTypeOfBoxing(const ir::AstNode * node)276 void ETSWarningAnalyzer::CheckTypeOfBoxing(const ir::AstNode *node)
277 {
278     ES2PANDA_ASSERT(node != nullptr);
279     const auto flags = node->GetBoxingUnboxingFlags();
280     if ((flags & ir::BoxingUnboxingFlags::BOXING_FLAG) != 0) {
281         std::string diagnosticParam;
282         switch (static_cast<ir::BoxingUnboxingFlags>(flags & ir::BoxingUnboxingFlags::BOXING_FLAG)) {
283             case ir::BoxingUnboxingFlags::BOX_TO_INT:
284                 diagnosticParam = "Int";
285                 break;
286             case ir::BoxingUnboxingFlags::BOX_TO_BOOLEAN:
287                 diagnosticParam = "Boolean";
288                 break;
289             case ir::BoxingUnboxingFlags::BOX_TO_BYTE:
290                 diagnosticParam = "Byte";
291                 break;
292             case ir::BoxingUnboxingFlags::BOX_TO_CHAR:
293                 diagnosticParam = "Char";
294                 break;
295             case ir::BoxingUnboxingFlags::BOX_TO_DOUBLE:
296                 diagnosticParam = "Double";
297                 break;
298             case ir::BoxingUnboxingFlags::BOX_TO_FLOAT:
299                 diagnosticParam = "Float";
300                 break;
301             case ir::BoxingUnboxingFlags::BOX_TO_LONG:
302                 diagnosticParam = "Long";
303                 break;
304             case ir::BoxingUnboxingFlags::BOX_TO_SHORT:
305                 diagnosticParam = "Short";
306                 break;
307             default:
308                 break;
309         }
310 
311         if (!diagnosticParam.empty()) {
312             util::DiagnosticMessageParams diagnosticParams = {diagnosticParam, GetBoxingUnboxingType(node)};
313             LogWarning(diagnostic::IMPLICIT_BOXING_TO, diagnosticParams, node->Start());
314         }
315     }
316 }
317 
CheckTypeOfUnboxing(const ir::AstNode * node)318 void ETSWarningAnalyzer::CheckTypeOfUnboxing(const ir::AstNode *node)
319 {
320     ES2PANDA_ASSERT(node != nullptr);
321     const auto flags = node->GetBoxingUnboxingFlags();
322     if ((flags & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0) {
323         std::string diagnosticParam;
324         switch (static_cast<ir::BoxingUnboxingFlags>(flags & ir::BoxingUnboxingFlags::UNBOXING_FLAG)) {
325             case ir::BoxingUnboxingFlags::UNBOX_TO_INT:
326                 diagnosticParam = "Int";
327                 break;
328             case ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN:
329                 diagnosticParam = "Boolean";
330                 break;
331             case ir::BoxingUnboxingFlags::UNBOX_TO_BYTE:
332                 diagnosticParam = "Byte";
333                 break;
334             case ir::BoxingUnboxingFlags::UNBOX_TO_CHAR:
335                 diagnosticParam = "Char";
336                 break;
337             case ir::BoxingUnboxingFlags::UNBOX_TO_DOUBLE:
338                 diagnosticParam = "Double";
339                 break;
340             case ir::BoxingUnboxingFlags::UNBOX_TO_FLOAT:
341                 diagnosticParam = "Float";
342                 break;
343             case ir::BoxingUnboxingFlags::UNBOX_TO_LONG:
344                 diagnosticParam = "Long";
345                 break;
346             case ir::BoxingUnboxingFlags::UNBOX_TO_SHORT:
347                 diagnosticParam = "Short";
348                 break;
349             default:
350                 break;
351         }
352 
353         if (!diagnosticParam.empty()) {
354             util::DiagnosticMessageParams diagnosticParams = {diagnosticParam, GetBoxingUnboxingType(node)};
355             LogWarning(diagnostic::IMPLICIT_BOXING_TO, diagnosticParams, node->Start());
356         }
357     }
358 }
359 
CheckTypeOfBoxingUnboxing(const ir::AstNode * node)360 void ETSWarningAnalyzer::CheckTypeOfBoxingUnboxing(const ir::AstNode *node)
361 {
362     ES2PANDA_ASSERT(node != nullptr);
363 
364     CheckTypeOfBoxing(node);
365     CheckTypeOfUnboxing(node);
366 }
367 
GetBoxingUnboxingType(const ir::AstNode * node)368 std::string ETSWarningAnalyzer::GetBoxingUnboxingType(const ir::AstNode *node)
369 {
370     ES2PANDA_ASSERT(node->Parent() != nullptr);
371     switch (node->Parent()->Type()) {
372         case ir::AstNodeType::VARIABLE_DECLARATOR: {
373             return " in Variable Declaration";
374         }
375         case ir::AstNodeType::CALL_EXPRESSION: {
376             return " in Call Method/Function";
377         }
378         case ir::AstNodeType::SWITCH_STATEMENT: {
379             return " in Switch-case Statement";
380         }
381         case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
382             return " in Assignment Expression";
383         }
384         case ir::AstNodeType::BINARY_EXPRESSION: {
385             return " in Binary Expression";
386         }
387         case ir::AstNodeType::UNARY_EXPRESSION: {
388             return " in Unary Expression";
389         }
390         case ir::AstNodeType::UPDATE_EXPRESSION: {
391             return " in Update Expression";
392         }
393         case ir::AstNodeType::MEMBER_EXPRESSION: {
394             return " in Member Expression";
395         }
396         default:
397             return "";
398     }
399 }
400 
ETSWarningImplicitBoxingUnboxing(const ir::AstNode * node)401 void ETSWarningAnalyzer::ETSWarningImplicitBoxingUnboxing(const ir::AstNode *node)
402 {
403     ES2PANDA_ASSERT(node != nullptr);
404 
405     switch (node->Type()) {
406         case ir::AstNodeType::VARIABLE_DECLARATOR:
407         case ir::AstNodeType::SWITCH_STATEMENT:
408         case ir::AstNodeType::CALL_EXPRESSION:
409         case ir::AstNodeType::BINARY_EXPRESSION:
410         case ir::AstNodeType::ASSIGNMENT_EXPRESSION:
411         case ir::AstNodeType::UNARY_EXPRESSION:
412         case ir::AstNodeType::UPDATE_EXPRESSION:
413         case ir::AstNodeType::MEMBER_EXPRESSION: {
414             if (!program_->NodeContainsETSNolint(node, ETSWarnings::ETS_IMPLICIT_BOXING_UNBOXING)) {
415                 node->Iterate([this](auto *childNode) { CheckTypeOfBoxingUnboxing(childNode); });
416             }
417             break;
418         }
419         default: {
420             break;
421         }
422     }
423 
424     node->Iterate([&](auto *childNode) { ETSWarningImplicitBoxingUnboxing(childNode); });
425 }
426 
LogWarning(const diagnostic::DiagnosticKind & diagnostic,const lexer::SourcePosition & position) const427 void ETSWarningAnalyzer::LogWarning(const diagnostic::DiagnosticKind &diagnostic,
428                                     const lexer::SourcePosition &position) const
429 {
430     this->LogWarning(diagnostic, {}, position);
431 }
432 
LogWarning(const diagnostic::DiagnosticKind & diagnostic,const util::DiagnosticMessageParams & diagnosticParams,const lexer::SourcePosition & position) const433 void ETSWarningAnalyzer::LogWarning(const diagnostic::DiagnosticKind &diagnostic,
434                                     const util::DiagnosticMessageParams &diagnosticParams,
435                                     const lexer::SourcePosition &position) const
436 {
437     diagnosticEngine_.LogDiagnostic(diagnostic, diagnosticParams, position);
438 }
439 
440 }  // namespace ark::es2panda::checker
441