Skip to content

Commit 9d1436c

Browse files
authored
[IDE] [Signature Help] Add basic signature help request to SourceKit (#83378)
1 parent 1bc9685 commit 9d1436c

33 files changed

+2025
-51
lines changed

include/swift/IDE/ArgumentCompletion.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
namespace swift {
2222
namespace ide {
2323

24+
struct Signature;
25+
2426
class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
2527
struct Result {
2628
/// The type associated with the code completion expression itself.
@@ -75,6 +77,12 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
7577
/// functions is supported.
7678
bool IsInAsyncContext;
7779

80+
/// True if the function is an implicitly curried instance method.
81+
bool IsImplicitlyCurried;
82+
83+
/// True if the call is the second apply of a double-applied function.
84+
bool IsSecondApply;
85+
7886
/// A bitfield to mark whether the parameter at a given index is optional.
7987
/// Parameters can be optional if they have a default argument or belong to
8088
/// a parameter pack.
@@ -117,9 +125,12 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
117125
/// \param IsLabeledTrailingClosure Whether we are completing the label of a
118126
/// labeled trailing closure, ie. if the code completion location is outside
119127
/// the call after the first trailing closure of the call.
120-
void collectResults(bool IsLabeledTrailingClosure,
121-
SourceLoc Loc, DeclContext *DC,
122-
CodeCompletionContext &CompletionCtx);
128+
void collectResults(bool IsLabeledTrailingClosure, SourceLoc Loc,
129+
DeclContext *DC, CodeCompletionContext &CompletionCtx);
130+
131+
/// Collects non-shadowed signature results into \p Signatures
132+
void getSignatures(SourceLoc Loc, DeclContext *DC,
133+
SmallVectorImpl<Signature> &Signatures);
123134
};
124135

125136
} // end namespace ide

include/swift/IDE/CodeCompletionStringBuilder.h

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ Type eraseArchetypes(Type type, GenericSignature genericSig);
3131

3232
bool hasInterestingDefaultValue(const ParamDecl *param);
3333

34+
/// Modes for printing arguments with default values in
35+
/// \c CodeCompletionStringBuilder::addCallArgumentPatterns
36+
enum class DefaultArgumentOutputMode {
37+
/// Don't output any argument that has a default value.
38+
None,
39+
/// Output arguments with interesting default values only.
40+
/// \sa hasInterestingDefaultValue
41+
Interesting,
42+
/// Output all arguments with default values (even non-interesting ones).
43+
All
44+
};
45+
3446
class CodeCompletionStringBuilder {
3547
friend CodeCompletionStringPrinter;
3648

@@ -39,11 +51,17 @@ class CodeCompletionStringBuilder {
3951
SmallVector<CodeCompletionString::Chunk, 4> Chunks;
4052

4153
bool AnnotateResults;
54+
bool UnderscoreEmptyArgumentLabel;
55+
bool FullParameterFlags;
4256

4357
public:
4458
CodeCompletionStringBuilder(llvm::BumpPtrAllocator &Allocator,
45-
bool AnnotateResults)
46-
: Allocator(Allocator), AnnotateResults(AnnotateResults) {}
59+
bool AnnotateResults = false,
60+
bool UnderscoreEmptyArgumentLabel = false,
61+
bool FullParameterFlags = false)
62+
: Allocator(Allocator), AnnotateResults(AnnotateResults),
63+
UnderscoreEmptyArgumentLabel(UnderscoreEmptyArgumentLabel),
64+
FullParameterFlags(FullParameterFlags) {}
4765

4866
private:
4967
void addChunkWithText(CodeCompletionString::Chunk::ChunkKind Kind,
@@ -316,7 +334,8 @@ class CodeCompletionStringBuilder {
316334
void addCallArgument(Identifier Name, Identifier LocalName, Type Ty,
317335
Type ContextTy, bool IsVarArg, bool IsInOut, bool IsIUO,
318336
bool IsAutoClosure, bool IsLabeledTrailingClosure,
319-
bool IsForOperator, bool HasDefault);
337+
bool IsForOperator, bool HasDefault,
338+
StringRef DefaultValue = {});
320339

321340
void addCallArgument(Identifier Name, Type Ty, Type ContextTy = Type(),
322341
bool IsForOperator = false) {
@@ -383,15 +402,19 @@ class CodeCompletionStringBuilder {
383402
ArrayRef<const ParamDecl *> declParams,
384403
const DeclContext *DC,
385404
GenericSignature genericSig,
386-
bool includeDefaultArgs = true);
405+
DefaultArgumentOutputMode defaultArgsMode =
406+
DefaultArgumentOutputMode::Interesting,
407+
bool includeDefaultValues = false);
387408

388409
/// Build argument patterns for calling. Returns \c true if any content was
389410
/// added to \p Builder. If \p Params is non-nullptr, \F
390411
bool addCallArgumentPatterns(const AnyFunctionType *AFT,
391412
const ParameterList *Params,
392413
const DeclContext *DC,
393414
GenericSignature genericSig,
394-
bool includeDefaultArgs = true);
415+
DefaultArgumentOutputMode defaultArgsMode =
416+
DefaultArgumentOutputMode::Interesting,
417+
bool includeDefaultValues = false);
395418

396419
void addTypeAnnotation(Type T, const DeclContext *DC,
397420
GenericSignature genericSig = GenericSignature());
@@ -408,6 +431,8 @@ class CodeCompletionStringBuilder {
408431
CodeCompletionString *createCompletionString() {
409432
return CodeCompletionString::create(Allocator, Chunks);
410433
}
434+
435+
ArrayRef<CodeCompletionString::Chunk> getChunks() { return Chunks; }
411436
};
412437

413438
} // end namespace ide

include/swift/IDE/SignatureHelp.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//===--- SignatureHelp.h --- ------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_IDE_SIGNATURE_HELP_H
14+
#define SWIFT_IDE_SIGNATURE_HELP_H
15+
16+
#include "swift/AST/Type.h"
17+
#include "swift/Basic/LLVM.h"
18+
#include "swift/IDE/TypeCheckCompletionCallback.h"
19+
20+
namespace swift {
21+
class IDEInspectionCallbacksFactory;
22+
23+
namespace ide {
24+
25+
struct Signature {
26+
/// True if this is a subscript rather than a function call.
27+
bool IsSubscript;
28+
29+
/// True if the function is an implicitly curried instance method.
30+
bool IsImplicitlyCurried;
31+
32+
/// True if the call is the second apply of a double-applied function.
33+
bool IsSecondApply;
34+
35+
/// The FuncDecl or SubscriptDecl associated with the call.
36+
ValueDecl *FuncD;
37+
38+
/// The type of the function being called.
39+
AnyFunctionType *FuncTy;
40+
41+
/// The base type of the call/subscript (null for free functions).
42+
Type BaseType;
43+
44+
/// The index of the parameter corresponding to the completion argument.
45+
std::optional<unsigned> ParamIdx;
46+
};
47+
48+
struct SignatureHelpResult {
49+
/// The decl context of the parsed expression.
50+
DeclContext *DC;
51+
52+
/// Suggested signatures.
53+
SmallVector<Signature, 2> Signatures;
54+
55+
SignatureHelpResult(DeclContext *DC) : DC(DC) {}
56+
};
57+
58+
/// An abstract base class for consumers of signatures results.
59+
class SignatureHelpConsumer {
60+
public:
61+
virtual ~SignatureHelpConsumer() {}
62+
virtual void handleResult(const SignatureHelpResult &result) = 0;
63+
};
64+
65+
/// Create a factory for code completion callbacks.
66+
IDEInspectionCallbacksFactory *
67+
makeSignatureHelpCallbacksFactory(SignatureHelpConsumer &Consumer);
68+
69+
} // namespace ide
70+
} // namespace swift
71+
72+
#endif // SWIFT_IDE_SIGNATURE_HELP_H

include/swift/IDETool/IDEInspectionInstance.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/IDE/ConformingMethodList.h"
2222
#include "swift/IDE/CursorInfo.h"
2323
#include "swift/IDE/ImportDepth.h"
24+
#include "swift/IDE/SignatureHelp.h"
2425
#include "swift/IDE/SwiftCompletionInfo.h"
2526
#include "swift/IDE/TypeContextInfo.h"
2627
#include "llvm/ADT/Hashing.h"
@@ -80,6 +81,14 @@ struct ConformingMethodListResults {
8081
bool DidReuseAST;
8182
};
8283

84+
/// The results returned from \c IDEInspectionInstance::signatures.
85+
struct SignatureHelpResults {
86+
/// The actual results. If \c nullptr, no results were found.
87+
const SignatureHelpResult *Result;
88+
/// Whether an AST was reused to produce the results.
89+
bool DidReuseAST;
90+
};
91+
8392
/// The results returned from \c IDEInspectionInstance::cursorInfo.
8493
struct CursorInfoResults {
8594
/// The actual results.
@@ -206,6 +215,15 @@ class IDEInspectionInstance {
206215
llvm::function_ref<void(CancellableResult<ConformingMethodListResults>)>
207216
Callback);
208217

218+
void signatureHelp(
219+
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
220+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
221+
llvm::MemoryBuffer *ideInspectionTargetBuffer, unsigned int Offset,
222+
DiagnosticConsumer *DiagC,
223+
std::shared_ptr<std::atomic<bool>> CancellationFlag,
224+
llvm::function_ref<void(CancellableResult<SignatureHelpResults>)>
225+
Callback);
226+
209227
void cursorInfo(
210228
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
211229
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,

lib/IDE/ArgumentCompletion.cpp

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#include "swift/Basic/Assertions.h"
1413
#include "swift/IDE/ArgumentCompletion.h"
14+
#include "swift/AST/Types.h"
15+
#include "swift/Basic/Assertions.h"
1516
#include "swift/IDE/CodeCompletion.h"
1617
#include "swift/IDE/CompletionLookup.h"
1718
#include "swift/IDE/SelectedOverloadInfo.h"
19+
#include "swift/IDE/SignatureHelp.h"
1820
#include "swift/Sema/ConstraintSystem.h"
1921
#include "swift/Sema/IDETypeChecking.h"
2022

@@ -60,9 +62,9 @@ bool ArgumentTypeCheckCompletionCallback::addPossibleParams(
6062
// a multitple trailing closure label but the parameter is not a function
6163
// type. Since we only allow labeled trailing closures after the first
6264
// trailing closure, we cannot pass an argument for this parameter.
63-
// If the parameter is required, stop here since we cannot pass an argument
64-
// for the parameter. If it's optional, keep looking for more trailing
65-
// closures that can be passed.
65+
// If the parameter is required, stop here since we cannot pass an
66+
// argument for the parameter. If it's optional, keep looking for more
67+
// trailing closures that can be passed.
6668
if (Required) {
6769
break;
6870
} else {
@@ -95,14 +97,62 @@ bool ArgumentTypeCheckCompletionCallback::addPossibleParams(
9597
static bool hasParentCallLikeExpr(Expr *E, ConstraintSystem &CS) {
9698
E = CS.getParentExpr(E);
9799
while (E) {
98-
if (E->getArgs() || isa<ParenExpr>(E) || isa<TupleExpr>(E) || isa<CollectionExpr>(E)) {
100+
if (E->getArgs() || isa<ParenExpr>(E) || isa<TupleExpr>(E) ||
101+
isa<CollectionExpr>(E)) {
99102
return true;
100103
}
101104
E = CS.getParentExpr(E);
102105
}
103106
return false;
104107
}
105108

109+
/// The callee can be a double-applied function in the second apply (e.g.
110+
/// `f()(|)`). In that case, the normal callee locator will not be able to find
111+
/// a selected overload since an overload has been selected for the first apply
112+
/// but not the second. We try to find the function's declaration and type
113+
/// if it turns out to be a double apply.
114+
static std::optional<std::pair<ValueDecl *, AnyFunctionType *>>
115+
tryResolveDoubleAppliedFunction(CallExpr *OuterCall, const Solution &S) {
116+
if (!OuterCall)
117+
return std::nullopt;
118+
119+
auto *InnerCall = dyn_cast<CallExpr>(OuterCall->getSemanticFn());
120+
if (!InnerCall)
121+
return std::nullopt;
122+
123+
auto &CS = S.getConstraintSystem();
124+
auto *InnerCallLocator = CS.getConstraintLocator(InnerCall);
125+
auto Overload = S.getCalleeOverloadChoiceIfAvailable(InnerCallLocator);
126+
if (!Overload)
127+
return std::nullopt;
128+
129+
if (!Overload->choice.isDecl())
130+
return std::nullopt;
131+
132+
auto FuncRefInfo = Overload->choice.getFunctionRefInfo();
133+
if (!FuncRefInfo.isDoubleApply())
134+
return std::nullopt;
135+
136+
auto CalleeTy = Overload->adjustedOpenedType->getAs<AnyFunctionType>();
137+
auto ResultTy = S.simplifyTypeForCodeCompletion(CalleeTy->getResult());
138+
139+
auto *FuncTy = ResultTy->getAs<AnyFunctionType>();
140+
if (!FuncTy)
141+
return std::nullopt;
142+
143+
auto *VD = Overload->choice.getDecl();
144+
auto BaseTy = Overload->choice.getBaseType();
145+
bool IsOuterCallImplicitlyCurried =
146+
VD->isInstanceMember() && !doesMemberRefApplyCurriedSelf(BaseTy, VD);
147+
148+
// The function declaration is only relevant if the function is an implicitly
149+
// curried instance method.
150+
if (IsOuterCallImplicitlyCurried)
151+
return std::make_pair(Overload->choice.getDecl(), FuncTy);
152+
153+
return std::make_pair(nullptr, FuncTy);
154+
}
155+
106156
void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
107157
Type ExpectedTy = getTypeForCompletion(S, CompletionExpr);
108158

@@ -230,11 +280,24 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
230280
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;
231281
getSolutionSpecificVarTypes(S, SolutionSpecificVarTypes);
232282

283+
ValueDecl *FuncD = nullptr;
233284
AnyFunctionType *FuncTy = nullptr;
285+
bool IsSecondApply = false;
234286
if (Info.ValueTy) {
235-
FuncTy = Info.ValueTy->lookThroughAllOptionalTypes()->getAs<AnyFunctionType>();
287+
FuncD = Info.getValue();
288+
FuncTy =
289+
Info.ValueTy->lookThroughAllOptionalTypes()->getAs<AnyFunctionType>();
290+
} else if (auto Result = tryResolveDoubleAppliedFunction(
291+
dyn_cast<CallExpr>(ParentCall), S)) {
292+
FuncD = Result->first;
293+
FuncTy = Result->second;
294+
IsSecondApply = true;
236295
}
237296

297+
bool IsImplicitlyCurried =
298+
Info.ValueRef && Info.ValueRef.getDecl()->isInstanceMember() &&
299+
!doesMemberRefApplyCurriedSelf(Info.BaseTy, Info.ValueRef.getDecl());
300+
238301
// Determine which parameters are optional. We need to do this in
239302
// `sawSolutionImpl` because it accesses the substitution map in
240303
// `Info.ValueRef`. This substitution map might contain type variables that
@@ -246,9 +309,7 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
246309
for (auto Idx : range(0, ParamsToPass.size())) {
247310
bool Optional = false;
248311
if (Info.ValueRef) {
249-
if (Info.ValueRef.getDecl()->isInstanceMember() &&
250-
!doesMemberRefApplyCurriedSelf(Info.BaseTy,
251-
Info.ValueRef.getDecl())) {
312+
if (IsImplicitlyCurried) {
252313
// We are completing in an unapplied instance function, eg.
253314
// struct TestStatic {
254315
// func method() -> Void {}
@@ -286,10 +347,11 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
286347
}
287348

288349
Results.push_back(
289-
{ExpectedTy, ExpectedCallType, isa<SubscriptExpr>(ParentCall),
290-
Info.getValue(), FuncTy, ArgIdx, ParamIdx, std::move(ClaimedParams),
291-
IsNoninitialVariadic, IncludeSignature, Info.BaseTy, HasLabel, FirstTrailingClosureIndex,
292-
IsAsync, DeclParamIsOptional, SolutionSpecificVarTypes});
350+
{ExpectedTy, ExpectedCallType, isa<SubscriptExpr>(ParentCall), FuncD,
351+
FuncTy, ArgIdx, ParamIdx, std::move(ClaimedParams), IsNoninitialVariadic,
352+
IncludeSignature, Info.BaseTy, HasLabel, FirstTrailingClosureIndex,
353+
IsAsync, IsImplicitlyCurried, IsSecondApply, DeclParamIsOptional,
354+
SolutionSpecificVarTypes});
293355
}
294356

295357
void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
@@ -432,3 +494,19 @@ void ArgumentTypeCheckCompletionCallback::collectResults(
432494
*Lookup.getExpectedTypeContext(),
433495
Lookup.canCurrDeclContextHandleAsync());
434496
}
497+
498+
void ArgumentTypeCheckCompletionCallback::getSignatures(
499+
SourceLoc Loc, DeclContext *DC, SmallVectorImpl<Signature> &Signatures) {
500+
SmallPtrSet<ValueDecl *, 4> ShadowedDecls;
501+
computeShadowedDecls(ShadowedDecls);
502+
503+
for (auto &Result : Results) {
504+
// Only show signature if the function isn't overridden.
505+
if (!Result.FuncTy || ShadowedDecls.contains(Result.FuncD))
506+
continue;
507+
508+
Signatures.push_back({Result.IsSubscript, Result.IsImplicitlyCurried,
509+
Result.IsSecondApply, Result.FuncD, Result.FuncTy,
510+
Result.ExpectedType, Result.ParamIdx});
511+
}
512+
}

0 commit comments

Comments
 (0)