• 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 "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