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 "signature_help.h"
17 #include "internal_api.h"
18 #include "utils/arena_containers.h"
19 #include <cstddef>
20 #include <iostream>
21 #include <optional>
22 #include <string>
23 #include <variant>
24 #include <vector>
25 #include "create_type_help_items.h"
26
27 namespace ark::es2panda::lsp {
28
FindTokenOnLeftOfPosition(es2panda_Context * context,size_t position)29 ir::AstNode *FindTokenOnLeftOfPosition(es2panda_Context *context, size_t position)
30 {
31 auto const tokenAtPosition = GetTouchingToken(context, position, false);
32 if (tokenAtPosition->Start().index < position && tokenAtPosition->End().index > position) {
33 return tokenAtPosition;
34 }
35 const auto ctx = reinterpret_cast<public_lib::Context *>(context);
36 return FindPrecedingToken(position, ctx->parserProgram->Ast(), ctx->allocator);
37 }
38
CreateTextSpanForNode(const ir::AstNode * node)39 TextSpan CreateTextSpanForNode(const ir::AstNode *node)
40 {
41 TextSpan span {0, 0};
42 span.start = node->Start().index;
43 span.length = node->End().index - node->Start().index;
44 return span;
45 }
IsSyntacticOwner(const ir::AstNode * node)46 bool IsSyntacticOwner(const ir::AstNode *node)
47 {
48 return node->IsCallExpression() || node->IsNewExpression();
49 }
GetResolvedSignatureForSignatureHelp(const ir::AstNode * call,const ir::AstNode * parent,std::vector<checker::Signature * > & candidates)50 checker::Signature *GetResolvedSignatureForSignatureHelp(const ir::AstNode *call, const ir::AstNode *parent,
51 std::vector<checker::Signature *> &candidates)
52 {
53 parent->FindChild([&call, &candidates](ir::AstNode *n) {
54 switch (n->Type()) {
55 case ir::AstNodeType::METHOD_DEFINITION:
56 if (call->AsCallExpression()->Callee()->ToString() == n->AsMethodDefinition()->Id()->ToString()) {
57 candidates.push_back(n->AsMethodDefinition()->Function()->Signature());
58 }
59 break;
60
61 case ir::AstNodeType::CALL_EXPRESSION:
62 if (call->AsCallExpression()->Callee()->ToString() == n->AsCallExpression()->Callee()->ToString()) {
63 candidates.push_back(n->AsCallExpression()->Signature());
64 }
65 break;
66
67 default:
68 break;
69 }
70 return false;
71 });
72 if (call->IsCallExpression()) {
73 auto callExpr = call->AsCallExpression();
74 return callExpr->Signature();
75 }
76 return nullptr;
77 }
78
GetCandidateOrTypeInfo(const std::optional<ArgumentListInfo> info,ir::AstNode * parent,const bool onlyUseSyntacticOwners)79 std::optional<InfoType> GetCandidateOrTypeInfo(const std::optional<ArgumentListInfo> info, ir::AstNode *parent,
80 const bool onlyUseSyntacticOwners)
81 {
82 if (const auto *call = std::get_if<CallInvocation>(&info->GetInvocation());
83 call != nullptr && call->callExpressionNode != nullptr) {
84 if (onlyUseSyntacticOwners && !IsSyntacticOwner(call->callExpressionNode)) {
85 return std::nullopt;
86 }
87 std::vector<checker::Signature *> candidates;
88 checker::Signature *resolvedSignature = nullptr;
89 if (call->callExpressionNode != nullptr && call->callExpressionNode->IsCallExpression()) {
90 resolvedSignature = GetResolvedSignatureForSignatureHelp(call->callExpressionNode, parent, candidates);
91 } else {
92 resolvedSignature =
93 GetResolvedSignatureForSignatureHelp(call->callExpressionNode->Parent(), parent, candidates);
94 }
95 if (!candidates.empty()) {
96 const auto can = CandidateInfo {CandidateOrTypeKind::CANDIDATE, candidates, resolvedSignature};
97 return std::make_optional(can);
98 }
99 } else if (const auto *typeArgs = std::get_if<TypeArgsInvocation>(&info->GetInvocation())) {
100 auto called = typeArgs->identifierNode;
101 const auto tp = CandidateOrTypeKind::TYPEENUM;
102 TypeInfo val = TypeInfo {tp, called};
103 return std::make_optional(val);
104 } else if (const auto *context = std::get_if<ContextualInvocation>(&info->GetInvocation()); context != nullptr) {
105 auto node = context->node;
106 std::vector<checker::Signature *> candidates;
107 if (node != nullptr && node->IsMethodDefinition()) {
108 auto funcSignature = node->AsMethodDefinition()->Function()->Signature();
109 candidates.push_back(funcSignature);
110 const auto can = CandidateInfo {CandidateOrTypeKind::CANDIDATE, candidates, funcSignature};
111 return std::make_optional(can);
112 }
113 }
114 return std::nullopt;
115 }
116
IsReasonCharacterTyped(const SignatureHelpTriggerReason & triggerReason)117 std::string IsReasonCharacterTyped(const SignatureHelpTriggerReason &triggerReason)
118 {
119 return std::visit(
120 [](const auto &reason) {
121 using T = std::decay_t<decltype(reason)>;
122 if constexpr (std::is_same_v<T, SignatureHelpCharacterTypedReason>) {
123 return reason.GetKind();
124 }
125 return "";
126 },
127 triggerReason);
128 }
129
IsManuallyInvoked(const SignatureHelpTriggerReason & triggerReason)130 std::string IsManuallyInvoked(const SignatureHelpTriggerReason &triggerReason)
131 {
132 return std::visit(
133 [](const auto &reason) {
134 using T = std::decay_t<decltype(reason)>;
135 if constexpr (std::is_same_v<T, SignatureHelpInvokedReason>) {
136 return reason.GetKind();
137 }
138 return "";
139 },
140 triggerReason);
141 }
142
GetSignatureHelpItems(es2panda_Context * ctx,size_t position,SignatureHelpTriggerReason triggeredReason,CancellationToken cancellationToken)143 SignatureHelpItems GetSignatureHelpItems(es2panda_Context *ctx, size_t position,
144 SignatureHelpTriggerReason triggeredReason,
145 CancellationToken cancellationToken)
146 {
147 auto const startingToken = FindTokenOnLeftOfPosition(ctx, position);
148 if (startingToken == nullptr) {
149 return {};
150 }
151
152 auto context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
153 auto astNode = reinterpret_cast<ark::es2panda::ir::AstNode *>(context->parserProgram->Ast());
154
155 const auto onlyUseSyntacticOwners = IsReasonCharacterTyped(triggeredReason) == "characterTyped";
156 if (onlyUseSyntacticOwners) {
157 return {};
158 }
159 const auto isManuallyInvoked = IsManuallyInvoked(triggeredReason) == "invoked";
160 const auto argumentInfo = GetContainingArgumentInfo(startingToken, position, isManuallyInvoked);
161 if (argumentInfo == std::nullopt) {
162 return {};
163 }
164 if (cancellationToken.IsCancellationRequested()) {
165 return {};
166 }
167 const auto candidateInfoOpt = GetCandidateOrTypeInfo(argumentInfo, astNode, onlyUseSyntacticOwners);
168 if (candidateInfoOpt == std::nullopt) {
169 return {};
170 }
171
172 const auto &candidateInfo = *candidateInfoOpt;
173
174 auto res = SignatureHelpItems();
175 if (std::holds_alternative<TypeInfo>(candidateInfo)) {
176 const auto &typeInfo = std::get<TypeInfo>(candidateInfo);
177 res = CreateTypeHelpItems(typeInfo.GetSymbol(), typeInfo.GetSymbol()->Range(),
178 CreateTextSpanForNode(typeInfo.GetSymbol()));
179 } else if (std::holds_alternative<CandidateInfo>(candidateInfo)) {
180 auto candidate = std::get<CandidateInfo>(candidateInfo);
181 res = CreateSignatureHelpItems(candidate.GetSignatures(), candidate.GetResolvedSignature(), argumentInfo);
182 }
183 return res;
184 }
GetHighestBinary(ir::AstNode * node)185 ir::AstNode *GetHighestBinary(ir::AstNode *node)
186 {
187 return node->Parent()->IsBinaryExpression() ? GetHighestBinary(node->Parent()) : node;
188 }
189
CountBinaryExpressionParameters(ir::AstNode * node)190 size_t CountBinaryExpressionParameters(ir::AstNode *node)
191 {
192 const size_t binaryReturnParam = 2;
193 return node->AsBinaryExpression()->Left()->IsBinaryExpression()
194 ? CountBinaryExpressionParameters(node->AsBinaryExpression()->Left()) + 1
195 : binaryReturnParam;
196 }
197
GetImmediatelyContainingArgumentOrContextualParameterInfo(ir::AstNode * node,size_t position)198 std::optional<ArgumentListInfo> GetImmediatelyContainingArgumentOrContextualParameterInfo(ir::AstNode *node,
199 size_t position)
200 {
201 return TryGetParameterInfo(node).has_value() ? TryGetParameterInfo(node)
202 : (GetImmediatelyContainingArgumentInfo(node, position));
203 }
204
GetContainingArgumentInfo(ir::AstNode * node,size_t position,bool isManuallyInvoked)205 std::optional<ArgumentListInfo> GetContainingArgumentInfo(ir::AstNode *node, size_t position, bool isManuallyInvoked)
206 {
207 if (!isManuallyInvoked) {
208 return std::nullopt;
209 }
210
211 return GetImmediatelyContainingArgumentOrContextualParameterInfo(node, position);
212 }
213
GetChildListThatStartsWithOpenerToken(ir::AstNode * parent,ir::AstNode * openerToken)214 ir::AstNode *GetChildListThatStartsWithOpenerToken(ir::AstNode *parent, ir::AstNode *openerToken)
215 {
216 std::vector<ir::AstNode *> children;
217 parent->FindChild([&children](ir::AstNode *n) {
218 children.push_back(n);
219 return false;
220 });
221
222 auto const indexOfOpenerToken =
223 std::distance(children.begin(), std::find(children.begin(), children.end(), openerToken));
224 if (!(indexOfOpenerToken >= 0 && (int)children.size() > indexOfOpenerToken + 1)) {
225 return nullptr;
226 }
227 return children.at(indexOfOpenerToken + 1);
228 }
229
GetArgumentCount(ir::AstNode * node,bool ignoreTrailingComma)230 size_t GetArgumentCount(ir::AstNode *node, bool ignoreTrailingComma)
231 {
232 int argumentCount = 0;
233
234 node->FindChild([&argumentCount, ignoreTrailingComma](ir::AstNode *child) {
235 if (!ignoreTrailingComma && child->IsTSTypeParameter()) {
236 argumentCount++;
237 } else if (!ignoreTrailingComma && child->IsMemberExpression()) {
238 argumentCount++;
239 }
240 return false;
241 });
242 return argumentCount;
243 }
GetArgumentOrParameterListAndIndex(ir::AstNode * node,std::vector<ArgumentListInfo> & list)244 std::vector<ArgumentListInfo> GetArgumentOrParameterListAndIndex(ir::AstNode *node, std::vector<ArgumentListInfo> &list)
245 {
246 if (node->IsMethodDefinition()) {
247 const auto params = node->AsMethodDefinition()->Function()->Params();
248 for (const auto param : params) {
249 auto argum = ArgumentListInfo();
250 argum.SetInvocation(Invocation(CallInvocation {InvocationKind::CALL, param}));
251 argum.SetApplicableSpan(CreateTextSpanForNode(param));
252 argum.SetArgumentIndex(param->Start().index);
253 argum.SetArgumentCount(params.size());
254 list.push_back(argum);
255 }
256 }
257 if (node->IsCallExpression()) {
258 const auto params = node->AsCallExpression()->Arguments();
259 for (const auto param : params) {
260 auto argum = ArgumentListInfo();
261 argum.SetInvocation(Invocation(CallInvocation {InvocationKind::CALL, param}));
262 argum.SetApplicableSpan(CreateTextSpanForNode(param));
263 argum.SetArgumentIndex(param->Start().index);
264 argum.SetArgumentCount(params.size());
265 list.push_back(argum);
266 }
267 }
268
269 return list;
270 }
GetArgumentOrParameterListInfo(ir::AstNode * node)271 ContextualSignatureLocationInfo GetArgumentOrParameterListInfo(ir::AstNode *node)
272 {
273 std::vector<ArgumentListInfo> info;
274 info = GetArgumentOrParameterListAndIndex(node, info);
275 if (info.empty()) {
276 return ContextualSignatureLocationInfo {};
277 }
278
279 const auto argumentCount = GetArgumentCount(node, false);
280 auto textSpan = CreateTextSpanForNode(node);
281 return {info, node->Start().index, argumentCount, textSpan};
282 }
283
TryGetParameterInfo(ir::AstNode * node)284 std::optional<ArgumentListInfo> TryGetParameterInfo(ir::AstNode *node)
285 {
286 auto const info = GetContextualSignatureLocationInfo(node);
287 if (!info) {
288 return std::nullopt;
289 }
290 auto const index = info->GetArgumentIndex();
291 auto const count = info->GetArgumentCount();
292 auto const span = info->GetArgumentsSpan();
293
294 std::optional<ArgumentListInfo> argumentList = ArgumentListInfo();
295 if (node->IsCallExpression()) {
296 const ContextualInvocation invocation =
297 ContextualInvocation {InvocationKind::CONTEXTUAL, node->AsCallExpression()->Signature(), node};
298 argumentList->SetInvocation(invocation);
299 } else if (node->IsMethodDefinition()) {
300 const ContextualInvocation invocation = ContextualInvocation {
301 InvocationKind::CONTEXTUAL, node->AsMethodDefinition()->Function()->Signature(), node};
302 argumentList->SetInvocation(invocation);
303 }
304 argumentList->SetApplicableSpan(span);
305 argumentList->SetArgumentIndex(index);
306 argumentList->SetArgumentCount(count);
307 return argumentList;
308 }
309
GetArgumentIndexForTemplatePiece(size_t spanIndex,ir::AstNode * node,size_t position)310 size_t GetArgumentIndexForTemplatePiece(size_t spanIndex, ir::AstNode *node, size_t position)
311 {
312 const size_t spanIndexOne = 1;
313 const size_t spanIndexTwo = 2;
314 if (node->Type() == ir::AstNodeType::TEMPLATE_LITERAL) {
315 if (node->Start().index < position && position < node->End().index) {
316 return 0;
317 }
318 return spanIndex + spanIndexTwo;
319 }
320 return spanIndex + spanIndexOne;
321 }
322
GetImmediatelyContainingArgumentInfo(ir::AstNode * node,size_t position)323 std::optional<ArgumentListInfo> GetImmediatelyContainingArgumentInfo(ir::AstNode *node, size_t position)
324 {
325 if (position == 0) {
326 return std::nullopt;
327 }
328 if (node->Parent()->Type() == ir::AstNodeType::CALL_EXPRESSION ||
329 node->Parent()->Type() == ir::AstNodeType::NEW_EXPRESSION) {
330 auto const invocation = node->Parent();
331
332 auto const argument = GetArgumentOrParameterListInfo(node->Parent());
333 const auto &list = argument.GetList();
334 if (!list.empty()) {
335 auto const argumentIndex = argument.GetArgumentIndex();
336 auto const argumentCount = GetArgumentCount(node->Parent(), false);
337 auto const span = CreateTextSpanForNode(node->Parent());
338 ArgumentListInfo argumentList;
339 argumentList.SetInvocation(Invocation(CallInvocation {InvocationKind::CALL, invocation}));
340 argumentList.SetApplicableSpan(span);
341 argumentList.SetArgumentIndex(argumentIndex);
342 argumentList.SetArgumentCount(argumentCount);
343 return argumentList;
344 }
345 } else if (node->Parent()->Type() == ir::AstNodeType::METHOD_DEFINITION) {
346 auto const info = GetContextualSignatureLocationInfo(node->Parent());
347 if (!info) {
348 return std::nullopt;
349 }
350 auto const index = info->GetArgumentIndex();
351 auto const count = info->GetArgumentCount();
352 auto const span = info->GetArgumentsSpan();
353 std::optional<ArgumentListInfo> argumentList = ArgumentListInfo();
354 const ContextualInvocation invocation = ContextualInvocation {
355 InvocationKind::CONTEXTUAL, node->Parent()->AsMethodDefinition()->Function()->Signature(), node->Parent()};
356 argumentList->SetInvocation(invocation);
357 argumentList->SetApplicableSpan(span);
358 argumentList->SetArgumentIndex(index);
359 argumentList->SetArgumentCount(count);
360 return argumentList;
361 }
362 return std::nullopt;
363 }
364
GetContextualSignatureLocationInfo(ir::AstNode * node)365 std::optional<ContextualSignatureLocationInfo> GetContextualSignatureLocationInfo(ir::AstNode *node)
366 {
367 ContextualSignatureLocationInfo info;
368 switch (node->Type()) {
369 case ir::AstNodeType::METHOD_DEFINITION:
370 case ir::AstNodeType::FUNCTION_EXPRESSION:
371 case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION:
372 info = GetArgumentOrParameterListInfo(node);
373 return std::make_optional(info);
374 break;
375 default:
376 return std::nullopt;
377 break;
378 }
379 }
380 } // namespace ark::es2panda::lsp