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 "class_hierarchy_info.h"
17 #include "internal_api.h"
18 #include "public/public.h"
19 #include "compiler/lowering/util.h"
20 #include "quick_info.h"
21
22 namespace ark::es2panda::lsp {
GetNameFromIdentifierNode(const ir::AstNode * node)23 std::string GetNameFromIdentifierNode(const ir::AstNode *node)
24 {
25 if (node == nullptr || !node->IsIdentifier()) {
26 return "";
27 }
28 return node->AsIdentifier()->ToString();
29 }
30
31 // Currently only considering enum scenarios.
IsClassLiteralDefinition(const ir::AstNode * node)32 bool IsClassLiteralDefinition(const ir::AstNode *node)
33 {
34 return !(compiler::ClassDefinitionIsEnumTransformed(node));
35 }
36
GetClassDefinitionFromIdentifierNode(const ir::AstNode * node)37 ir::ClassDefinition *GetClassDefinitionFromIdentifierNode(const ir::AstNode *node)
38 {
39 auto decl = compiler::DeclarationFromIdentifier(node->AsIdentifier());
40 if (decl == nullptr) {
41 return nullptr;
42 }
43 if (decl->IsClassDeclaration()) {
44 decl = decl->AsClassDeclaration()->Definition();
45 }
46 if (!IsClassLiteralDefinition(decl)) {
47 return nullptr;
48 }
49 return decl->AsClassDefinition();
50 }
51
SpliceFunctionDetailStr(const std::string & functionName,const std::vector<FunctionParamStyle> & params,const std::string & returnType)52 std::string SpliceFunctionDetailStr(const std::string &functionName, const std::vector<FunctionParamStyle> ¶ms,
53 const std::string &returnType)
54 {
55 std::string result;
56 if (functionName.empty()) {
57 return result;
58 }
59 result.append(functionName).append("(");
60 auto iter = params.cbegin();
61 while (iter != params.cend()) {
62 auto name = iter->GetParamName();
63 auto kind = iter->GetParamKind();
64 if (name.empty() || kind.empty()) {
65 ++iter;
66 continue;
67 }
68 result.append(name).append(": ").append(kind);
69 ++iter;
70 if (iter != params.cend()) {
71 result.append(", ");
72 }
73 }
74 result.append(")");
75 if (!returnType.empty()) {
76 result.append(": ").append(returnType);
77 }
78 return result;
79 }
80
SplicePropertyDetailStr(const std::string & name,const std::string & type)81 std::string SplicePropertyDetailStr(const std::string &name, const std::string &type)
82 {
83 if (name.empty() || type.empty()) {
84 return "";
85 }
86 return name + ": " + type;
87 }
88
GetFunctionNameFromScriptFunction(const ir::ScriptFunction * function)89 std::string GetFunctionNameFromScriptFunction(const ir::ScriptFunction *function)
90 {
91 if (function == nullptr || function->Id() == nullptr) {
92 return "";
93 }
94 return function->Id()->ToString();
95 }
96
GetParamListFromScriptFunction(const ir::ScriptFunction * function)97 std::vector<FunctionParamStyle> GetParamListFromScriptFunction(const ir::ScriptFunction *function)
98 {
99 std::vector<FunctionParamStyle> params;
100 if (function == nullptr) {
101 return params;
102 }
103 auto nodeParams = function->Params();
104 for (const auto *it : nodeParams) {
105 if (it == nullptr || !it->IsETSParameterExpression()) {
106 continue;
107 }
108 std::string paramName;
109 std::string paramKind;
110 auto nodeParam = it->AsETSParameterExpression();
111 if (nodeParam->IsRestParameter()) {
112 paramName = nodeParam->RestParameter()->ToString();
113 } else {
114 paramName = GetNameFromIdentifierNode(nodeParam->Ident());
115 }
116 if (paramName == INVALID_EXPRESSION || nodeParam->TypeAnnotation() == nullptr) {
117 continue;
118 }
119 paramKind = GetNameForTypeNode(nodeParam->TypeAnnotation());
120 params.emplace_back(FunctionParamStyle(std::move(paramName), std::move(paramKind)));
121 }
122 return params;
123 }
124
GetReturnTypeFromScriptFunction(const ir::ScriptFunction * function)125 std::string GetReturnTypeFromScriptFunction(const ir::ScriptFunction *function)
126 {
127 if (function == nullptr) {
128 return "";
129 }
130 auto returnNode = function->ReturnTypeAnnotation();
131 if (returnNode == nullptr) {
132 return "";
133 }
134 auto returnType = GetNameForTypeNode(returnNode);
135 return returnType;
136 }
137
CreateSetterStyle(ir::MethodDefinitionKind kind)138 SetterStyle CreateSetterStyle(ir::MethodDefinitionKind kind)
139 {
140 SetterStyle setter = SetterStyle::NONE;
141 switch (kind) {
142 case ir::MethodDefinitionKind::GET:
143 case ir::MethodDefinitionKind::EXTENSION_GET:
144 setter = SetterStyle::GETTER;
145 break;
146 case ir::MethodDefinitionKind::SET:
147 case ir::MethodDefinitionKind::EXTENSION_SET:
148 setter = SetterStyle::SETTER;
149 break;
150 default:
151 break;
152 }
153 return setter;
154 }
155
CreateClassMethodItem(const ir::MethodDefinition * methodDefinition,const std::string & funcName,std::string detail)156 std::shared_ptr<ClassMethodItem> CreateClassMethodItem(const ir::MethodDefinition *methodDefinition,
157 const std::string &funcName, std::string detail)
158 {
159 if (methodDefinition == nullptr || funcName.empty() || detail.empty()) {
160 return nullptr;
161 }
162 auto setter = CreateSetterStyle(methodDefinition->Kind());
163 AccessModifierStyle access = AccessModifierStyle::PUBLIC;
164 if (methodDefinition->IsProtected()) {
165 access = AccessModifierStyle::PROTECTED;
166 }
167 auto item = std::make_shared<ClassMethodItem>(access, std::move(detail), setter);
168 item->SetFunctionName(funcName);
169 return item;
170 }
171
CreateClassPropertyItem(const ir::ClassProperty * property,const std::string & propertyName,std::string detail)172 std::shared_ptr<ClassPropertyItem> CreateClassPropertyItem(const ir::ClassProperty *property,
173 const std::string &propertyName, std::string detail)
174 {
175 if (property == nullptr || propertyName.empty() || detail.empty()) {
176 return nullptr;
177 }
178 AccessModifierStyle access = AccessModifierStyle::PUBLIC;
179 if (property->IsProtected()) {
180 access = AccessModifierStyle::PROTECTED;
181 }
182 auto item = std::make_shared<ClassPropertyItem>(access, std::move(detail));
183 item->SetVariableName(propertyName);
184 return item;
185 }
186
ParseFunctionStyleWithCreateItem(const ir::MethodDefinition * methodDefinition,bool isCurrentToken)187 std::shared_ptr<ClassMethodItem> ParseFunctionStyleWithCreateItem(const ir::MethodDefinition *methodDefinition,
188 bool isCurrentToken)
189 {
190 if (methodDefinition == nullptr) {
191 return nullptr;
192 }
193 if ((isCurrentToken && methodDefinition->IsStatic()) ||
194 (!isCurrentToken &&
195 (methodDefinition->IsPrivate() || methodDefinition->IsStatic() || methodDefinition->IsConstructor()))) {
196 return nullptr;
197 }
198 auto function = methodDefinition->Function();
199 auto functionName = GetFunctionNameFromScriptFunction(function);
200 if (functionName.empty()) {
201 return nullptr;
202 }
203 auto paramList = GetParamListFromScriptFunction(function);
204 auto returnType = GetReturnTypeFromScriptFunction(function);
205 auto functionDetail = SpliceFunctionDetailStr(functionName, paramList, returnType);
206 return CreateClassMethodItem(methodDefinition, functionName, functionDetail);
207 }
208
GetIdentFromNewClassExprPart(const ir::Expression * value)209 ir::Identifier *GetIdentFromNewClassExprPart(const ir::Expression *value)
210 {
211 if (value == nullptr || !value->IsETSNewClassInstanceExpression()) {
212 return nullptr;
213 }
214 auto typeRef = value->AsETSNewClassInstanceExpression()->GetTypeRef();
215 if (typeRef == nullptr || !typeRef->IsETSTypeReference()) {
216 return nullptr;
217 }
218 auto part = typeRef->AsETSTypeReference()->Part();
219 if (part == nullptr) {
220 return nullptr;
221 }
222 return part->GetIdent();
223 }
224
ParsePropertyStyleWithCreateItem(const ir::ClassProperty * property,bool isCurrentToken)225 std::shared_ptr<ClassPropertyItem> ParsePropertyStyleWithCreateItem(const ir::ClassProperty *property,
226 bool isCurrentToken)
227 {
228 if (property == nullptr) {
229 return nullptr;
230 }
231 if ((isCurrentToken && property->IsStatic()) ||
232 (!isCurrentToken && (property->IsPrivate() || property->IsStatic()))) {
233 return nullptr;
234 }
235 std::string propertyName = GetNameFromIdentifierNode(property->Key());
236 std::string type;
237 if (property->TypeAnnotation() == nullptr) {
238 auto value = property->Value();
239 auto ident = GetIdentFromNewClassExprPart(value);
240 type = GetNameFromIdentifierNode(ident);
241 } else {
242 type = GetNameForTypeNode(property->TypeAnnotation());
243 }
244 if (propertyName == INVALID_EXPRESSION || type == INVALID_EXPRESSION) {
245 return nullptr;
246 }
247 auto detail = SplicePropertyDetailStr(propertyName, type);
248 return CreateClassPropertyItem(property, propertyName, detail);
249 }
250
CreateClassHierarchyInfoFromBody(const ir::ClassDefinition * classDefinition,const std::string & className,bool isCurrentToken)251 ClassHierarchyInfo CreateClassHierarchyInfoFromBody(const ir::ClassDefinition *classDefinition,
252 const std::string &className, bool isCurrentToken)
253 {
254 ClassHierarchyInfo result;
255 if (classDefinition == nullptr) {
256 return result;
257 }
258 result.SetClassName(className);
259 auto bodyNodes = classDefinition->Body();
260 for (const auto &node : bodyNodes) {
261 if (node == nullptr) {
262 continue;
263 }
264 if (node->IsMethodDefinition()) {
265 auto methodDefinition = node->AsMethodDefinition();
266 if (methodDefinition == nullptr) {
267 continue;
268 }
269 auto item = ParseFunctionStyleWithCreateItem(methodDefinition, isCurrentToken);
270 result.AddItemToMethodList(item);
271 auto overLoads = methodDefinition->Overloads();
272 for (const auto *overLoadMethodDefinition : overLoads) {
273 auto overLoadItem = ParseFunctionStyleWithCreateItem(overLoadMethodDefinition, isCurrentToken);
274 result.AddItemToMethodList(overLoadItem);
275 }
276 } else if (node->IsClassProperty()) {
277 auto property = node->AsClassProperty();
278 if (property == nullptr) {
279 continue;
280 }
281 auto item = ParsePropertyStyleWithCreateItem(property, isCurrentToken);
282 result.AddItemToPropertyList(item);
283 }
284 }
285 return result;
286 }
287
GetSuperClassNode(const ir::ClassDefinition * classDefinition)288 ir::AstNode *GetSuperClassNode(const ir::ClassDefinition *classDefinition)
289 {
290 if (classDefinition == nullptr) {
291 return nullptr;
292 }
293 auto super = const_cast<ir::Expression *>(classDefinition->Super());
294 if (super == nullptr) {
295 return nullptr;
296 }
297 return GetIdentifierFromSuper(super);
298 }
299
ComputeClassHierarchyInfo(const ClassHierarchyInfo & deriveInfo,ClassHierarchyInfo & superInfo)300 void ComputeClassHierarchyInfo(const ClassHierarchyInfo &deriveInfo, ClassHierarchyInfo &superInfo)
301 {
302 auto deriveMethods = deriveInfo.GetMethodItemList();
303 for (const auto &method : deriveMethods) {
304 superInfo.DeleteTargetItemInMethodList(method.second);
305 }
306 auto deriveProperties = deriveInfo.GetPropertyItemList();
307 for (const auto &property : deriveProperties) {
308 superInfo.DeleteTargetItemInPropertyList(property.second);
309 }
310 }
311
FillBaseClassHierarchyInfo(const ClassHierarchyInfo & extraInfo,ClassHierarchyInfo & baseInfo)312 void FillBaseClassHierarchyInfo(const ClassHierarchyInfo &extraInfo, ClassHierarchyInfo &baseInfo)
313 {
314 auto extraMethods = extraInfo.GetMethodItemList();
315 for (const auto &method : extraMethods) {
316 baseInfo.AddItemToMethodList(method.second);
317 }
318 auto extraProperties = extraInfo.GetPropertyItemList();
319 for (const auto &property : extraProperties) {
320 baseInfo.AddItemToPropertyList(property.second);
321 }
322 }
323
ProcessClassHierarchy(const ir::AstNode * token,ClassHierarchyInfo & baseInfo,ClassHierarchy & result)324 void ProcessClassHierarchy(const ir::AstNode *token, ClassHierarchyInfo &baseInfo, ClassHierarchy &result)
325 {
326 if (token == nullptr || !token->IsIdentifier()) {
327 return;
328 }
329 std::string className = GetNameFromIdentifierNode(token);
330 auto classDefinition = GetClassDefinitionFromIdentifierNode(token);
331 if (classDefinition == nullptr) {
332 return;
333 }
334 auto info = CreateClassHierarchyInfoFromBody(classDefinition, className, false);
335 if (!className.empty()) {
336 // Calculate the difference between the obtained parent class info and the current clicked node class info.
337 ComputeClassHierarchyInfo(baseInfo, info);
338 if (info.GetClassName() == className &&
339 (!info.GetMethodItemList().empty() || !info.GetPropertyItemList().empty())) {
340 result.emplace_back(info);
341 FillBaseClassHierarchyInfo(info, baseInfo);
342 }
343 }
344 auto superClass = GetSuperClassNode(classDefinition);
345 if (superClass == nullptr) {
346 return;
347 }
348 ProcessClassHierarchy(superClass, baseInfo, result);
349 }
350
GetTargetClassDeclarationByPosition(es2panda_Context * context,size_t position)351 ir::AstNode *GetTargetClassDeclarationByPosition(es2panda_Context *context, size_t position)
352 {
353 if (context == nullptr) {
354 return nullptr;
355 }
356 auto token = GetTouchingToken(context, position, false);
357 auto tmp = token;
358 while (tmp != nullptr) {
359 if (tmp->IsClassDeclaration()) {
360 return tmp;
361 }
362 tmp = tmp->Parent();
363 }
364 return nullptr;
365 }
366
GetClassHierarchyInfoImpl(es2panda_Context * context,size_t position)367 ClassHierarchy GetClassHierarchyInfoImpl(es2panda_Context *context, size_t position)
368 {
369 ClassHierarchy result;
370 auto classDeclaration = GetTargetClassDeclarationByPosition(context, position);
371 if (classDeclaration == nullptr) {
372 return result;
373 }
374 auto classDefinition = classDeclaration->AsClassDeclaration()->Definition();
375 auto currentInfo = CreateClassHierarchyInfoFromBody(classDefinition, "", true);
376 auto superClass = GetSuperClassNode(classDefinition);
377 if (superClass == nullptr) {
378 return result;
379 }
380 ProcessClassHierarchy(superClass, currentInfo, result);
381 return result;
382 }
383 } // namespace ark::es2panda::lsp
384