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 <string>
17 #include <utility>
18 #include <vector>
19 #include "compiler/lowering/util.h"
20 #include "get_safe_delete_info.h"
21 #include "ir/astNode.h"
22 #include "internal_api.h"
23 #include "public/public.h"
24 #include "references.h"
25
26 namespace {
27 constexpr size_t BUILTIN_TYPE_COUNT = 9;
28 constexpr std::array<const char *, BUILTIN_TYPE_COUNT> BUILTIN_TYPES = {
29 "Number", "String", "Boolean", "void", "BigInt", "Never", "undefined", "null", "Object"};
30 } // namespace
31
32 namespace ark::es2panda::lsp {
33
IsBuiltinTypeReference(ir::AstNode * node)34 bool IsBuiltinTypeReference(ir::AstNode *node)
35 {
36 if (node == nullptr || node->Type() != ir::AstNodeType::IDENTIFIER) {
37 return false;
38 }
39 auto *parent = node->Parent();
40 while (parent != nullptr) {
41 if (parent->Type() == ir::AstNodeType::ETS_TYPE_REFERENCE_PART ||
42 parent->Type() == ir::AstNodeType::ETS_TYPE_REFERENCE) {
43 std::string nameStr(node->AsIdentifier()->Name());
44 auto it =
45 std::find_if(BUILTIN_TYPES.begin(), BUILTIN_TYPES.end(), [&](const char *s) { return nameStr == s; });
46 if (it != BUILTIN_TYPES.end()) {
47 return true;
48 }
49 }
50 parent = parent->Parent();
51 }
52 return false;
53 }
54
IsDeletableDecl(ir::AstNode * node)55 bool IsDeletableDecl(ir::AstNode *node)
56 {
57 return node->IsFunctionDeclaration() || node->IsVariableDeclarator() || node->IsClassProperty() ||
58 node->Type() == ir::AstNodeType::METHOD_DEFINITION || node->Type() == ir::AstNodeType::CLASS_DECLARATION ||
59 node->IsTSTypeParameterDeclaration() || node->IsImportDefaultSpecifier() ||
60 node->Type() == ir::AstNodeType::ETS_TYPE_REFERENCE_PART || node->IsCallExpression() ||
61 node->IsETSImportDeclaration() || node->IsImportSpecifier() || node->IsBinaryExpression() ||
62 node->IsTSInterfaceDeclaration() || node->IsETSTypeReferencePart() || node->IsImportNamespaceSpecifier() ||
63 node->IsTSTypeAliasDeclaration() || node->IsExpressionStatement() || node->IsMemberExpression() ||
64 node->IsTypeofExpression();
65 }
66
FindNearestDeletableDecl(ir::AstNode * node)67 ir::AstNode *FindNearestDeletableDecl(ir::AstNode *node)
68 {
69 ir::AstNode *cur = node;
70 while (cur != nullptr) {
71 if (IsDeletableDecl(cur)) {
72 return cur;
73 }
74 cur = cur->Parent();
75 }
76 return nullptr;
77 }
78
NormalizeFilePath(const std::string & filePath)79 std::string NormalizeFilePath(const std::string &filePath)
80 {
81 std::string normPath = filePath;
82 std::transform(normPath.begin(), normPath.end(), normPath.begin(), ::tolower);
83 std::replace(normPath.begin(), normPath.end(), '\\', '/');
84 return normPath;
85 }
86
87 /**
88 * Distinguish a namespace from a class by checking whether the class body contains only the $init$ method and has
89 * no constructor
90 */
IsNamespaceClass(ir::AstNode * astNode)91 bool IsNamespaceClass(ir::AstNode *astNode)
92 {
93 if (astNode->Type() != ir::AstNodeType::CLASS_DECLARATION) {
94 return false;
95 }
96 auto *classDecl = static_cast<ir::ClassDeclaration *>(astNode);
97 const auto &body = classDecl->Definition()->Body();
98 bool hasConstructor = false;
99 bool onlyInit = true;
100 for (auto *member : body) {
101 if (member->Type() == ir::AstNodeType::METHOD_DEFINITION) {
102 auto *method = static_cast<ir::MethodDefinition *>(member);
103 util::StringView keyName = method->Key()->AsIdentifier()->Name();
104 if (keyName == "constructor") {
105 hasConstructor = true;
106 }
107 if (keyName != "_$init$_") {
108 onlyInit = false;
109 }
110 }
111 }
112 return !hasConstructor && onlyInit;
113 }
114
GetSafeDeleteInfoImpl(es2panda_Context * context,size_t position)115 bool GetSafeDeleteInfoImpl(es2panda_Context *context, size_t position)
116 {
117 auto astNode = GetTouchingToken(context, position, false);
118 if (astNode == nullptr) {
119 return false;
120 }
121
122 auto declInfo = GetDeclInfoImpl(astNode);
123 auto fileName = std::get<0>(declInfo);
124 std::string normFileName = NormalizeFilePath(fileName);
125 if (!normFileName.empty() && normFileName.find("ets1.2/build-tools/ets2panda/lib/stdlib") != std::string::npos) {
126 return false;
127 }
128 if (!normFileName.empty() && normFileName.find("/sdk/") != std::string::npos) {
129 return false;
130 }
131
132 if (IsBuiltinTypeReference(astNode)) {
133 return false;
134 }
135
136 astNode = FindNearestDeletableDecl(astNode);
137 if (astNode == nullptr) {
138 return false;
139 }
140
141 if (IsNamespaceClass(astNode)) {
142 return false;
143 }
144
145 if (astNode->IsTSTypeParameterDeclaration()) {
146 return false;
147 }
148
149 return true;
150 }
151 } // namespace ark::es2panda::lsp
152