Skip to content

Commit aabab5b

Browse files
committed
[IDE] Misc enhancements to signature help
1 parent bd2fdc0 commit aabab5b

File tree

6 files changed

+76
-65
lines changed

6 files changed

+76
-65
lines changed

lib/IDE/ArgumentCompletion.cpp

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -113,28 +113,44 @@ static bool hasParentCallLikeExpr(Expr *E, ConstraintSystem &CS) {
113113
/// if it turns out to be a double apply.
114114
static std::optional<std::pair<ValueDecl *, AnyFunctionType *>>
115115
tryResolveDoubleAppliedFunction(CallExpr *OuterCall, const Solution &S) {
116+
if (!OuterCall)
117+
return std::nullopt;
118+
116119
auto *InnerCall = dyn_cast<CallExpr>(OuterCall->getSemanticFn());
117120
if (!InnerCall)
118121
return std::nullopt;
119122

120123
auto &CS = S.getConstraintSystem();
121124
auto *InnerCallLocator = CS.getConstraintLocator(InnerCall);
122-
auto *InnerCalleeLocator = S.getCalleeLocator(InnerCallLocator);
125+
auto Overload = S.getCalleeOverloadChoiceIfAvailable(InnerCallLocator);
126+
if (!Overload)
127+
return std::nullopt;
123128

124-
if (auto Overload = S.getOverloadChoiceIfAvailable(InnerCalleeLocator);
125-
Overload->choice.isDecl()) {
126-
auto FuncRefInfo = Overload->choice.getFunctionRefInfo();
127-
if (!FuncRefInfo.isDoubleApply())
128-
return std::nullopt;
129+
if (!Overload->choice.isDecl())
130+
return std::nullopt;
129131

130-
auto CalleeTy = Overload->adjustedOpenedType->getAs<AnyFunctionType>();
131-
auto ResultTy = S.simplifyTypeForCodeCompletion(CalleeTy->getResult());
132+
auto FuncRefInfo = Overload->choice.getFunctionRefInfo();
133+
if (!FuncRefInfo.isDoubleApply())
134+
return std::nullopt;
132135

133-
if (auto *FuncTy = ResultTy->getAs<AnyFunctionType>())
134-
return std::make_pair(Overload->choice.getDecl(), FuncTy);
135-
}
136+
auto CalleeTy = Overload->adjustedOpenedType->getAs<AnyFunctionType>();
137+
auto ResultTy = S.simplifyTypeForCodeCompletion(CalleeTy->getResult());
136138

137-
return std::nullopt;
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);
138154
}
139155

140156
void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
@@ -486,12 +502,11 @@ void ArgumentTypeCheckCompletionCallback::getSignatures(
486502

487503
for (auto &Result : Results) {
488504
// Only show signature if the function isn't overridden.
489-
if (!ShadowedDecls.contains(Result.FuncD)) {
490-
if (!Result.FuncTy)
491-
continue;
492-
Signatures.push_back({Result.IsSubscript, Result.IsImplicitlyCurried,
493-
Result.IsSecondApply, Result.FuncD, Result.FuncTy,
494-
Result.ExpectedType, Result.ParamIdx});
495-
}
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});
496511
}
497512
}

lib/IDE/SignatureHelp.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2019 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -11,12 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/IDE/SignatureHelp.h"
14-
#include "ExprContextAnalysis.h"
15-
#include "swift/AST/GenericEnvironment.h"
16-
#include "swift/AST/NameLookup.h"
1714
#include "swift/IDE/ArgumentCompletion.h"
18-
#include "swift/IDE/TypeCheckCompletionCallback.h"
19-
#include "swift/Parse/IDEInspectionCallbacks.h"
2015
#include "swift/Sema/IDETypeChecking.h"
2116

2217
using namespace swift;

test/SourceKit/SignatureHelp/signature_help_currying.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ func testCurryMemberFull() {
205205
key.active_parameter: 0
206206
},
207207
{
208-
key.name: "(first: Double?, second: Float, third: Int) -> Double",
208+
key.name: "(first: Double!, second: Float, third: Int) -> Double",
209209
key.parameters: [
210210
{
211211
key.nameoffset: 1,
@@ -251,7 +251,7 @@ func testCurryMemberFull() {
251251
key.active_parameter: 0
252252
},
253253
{
254-
key.name: "(x: Int, y: Int, with: (Int, Int) throws -> Int) throws -> Int?",
254+
key.name: "(x: Int, y: Int, with: (Int, Int) throws -> Int) throws -> Int!",
255255
key.parameters: [
256256
{
257257
key.nameoffset: 1,

tools/SourceKit/include/SourceKit/Core/LangSupport.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,17 +1012,17 @@ class ConformingMethodListConsumer {
10121012
virtual void cancelled() = 0;
10131013
};
10141014

1015-
struct SignatureHelpResult {
1015+
struct SignatureHelpResponse {
10161016
struct Parameter {
1017-
unsigned LabelBegin;
1018-
unsigned LabelLength;
1017+
unsigned Offset;
1018+
unsigned Length;
10191019
StringRef DocComment;
10201020

10211021
Parameter() {}
10221022
};
10231023

10241024
struct Signature {
1025-
StringRef Label;
1025+
StringRef Text;
10261026
StringRef Doc;
10271027
std::optional<unsigned> ActiveParam;
10281028
ArrayRef<Parameter> Params;
@@ -1038,7 +1038,7 @@ class SignatureHelpConsumer {
10381038
public:
10391039
virtual ~SignatureHelpConsumer() {}
10401040

1041-
virtual void handleResult(const SignatureHelpResult &Result) = 0;
1041+
virtual void handleResult(const SignatureHelpResponse &Result) = 0;
10421042
virtual void setReusingASTContext(bool flag) = 0;
10431043
virtual void failed(StringRef ErrDescription) = 0;
10441044
virtual void cancelled() = 0;

tools/SourceKit/lib/SwiftLang/SwiftSignatureHelp.cpp

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,16 @@ using namespace ide;
2828

2929
using ChunkKind = CodeCompletionString::Chunk::ChunkKind;
3030

31+
namespace {
3132
struct SignatureInfo {
32-
StringRef Label;
33+
StringRef Text;
3334
StringRef DocComment;
3435
std::optional<unsigned> ActiveParam;
35-
SmallVector<SourceKit::SignatureHelpResult::Parameter, 1> Params;
36+
SmallVector<SourceKit::SignatureHelpResponse::Parameter, 1> Params;
3637

3738
SignatureInfo() {}
3839
};
40+
} // namespace
3941

4042
/// \returns Array of parameters of \p VD accounting for implicitly curried
4143
/// instance methods.
@@ -59,26 +61,18 @@ getParameterArray(const ValueDecl *VD, bool IsImplicitlyCurried,
5961
return {};
6062
}
6163

62-
static CodeCompletionString *createSignatureLabel(
64+
static CodeCompletionString *createSignatureString(
6365
llvm::BumpPtrAllocator &Allocator, ValueDecl *FD, AnyFunctionType *AFT,
6466
const DeclContext *DC, GenericSignature GenericSig, bool IsSubscript,
6567
bool IsMember, bool IsImplicitlyCurried, bool IsSecondApply) {
66-
if (IsSecondApply) {
67-
// Don't use the function decl when creating the signature label
68-
// if this is the second apply of a double-applied function to
69-
// avoid showing incorrect information like IUOs, rethrows, or
70-
// reasync which can't be used in second apply.
71-
FD = nullptr;
72-
}
73-
7468
CodeCompletionStringBuilder StringBuilder(
7569
Allocator, /*AnnotateResults=*/false,
7670
/*UnderscoreEmptyArgumentLabel=*/!IsSubscript,
7771
/*FullParameterFlags=*/true);
7872

7973
DeclBaseName BaseName;
8074

81-
if (FD) {
75+
if (!IsSecondApply && FD) {
8276
BaseName = FD->getBaseName();
8377
} else if (IsSubscript) {
8478
BaseName = DeclBaseName::createSubscript();
@@ -98,11 +92,15 @@ static CodeCompletionString *createSignatureLabel(
9892
StringBuilder.addRightParen();
9993

10094
if (!IsImplicitlyCurried) {
95+
// For a second apply, we don't pass the declaration to avoid adding
96+
// incorrect rethrows and reasync which are only usable in a single apply.
10197
StringBuilder.addEffectsSpecifiers(
102-
AFT, dyn_cast_or_null<AbstractFunctionDecl>(FD));
98+
AFT,
99+
/*AFD=*/IsSecondApply ? nullptr
100+
: dyn_cast_or_null<AbstractFunctionDecl>(FD));
103101
}
104102

105-
if (!IsImplicitlyCurried && FD && FD->isImplicitlyUnwrappedOptional())
103+
if (FD && FD->isImplicitlyUnwrappedOptional())
106104
StringBuilder.addTypeAnnotationForImplicitlyUnwrappedOptional(
107105
AFT->getResult(), DC, GenericSig);
108106
else
@@ -129,21 +127,21 @@ static void getSignatureInfo(const DeclContext *DC, const Signature &Sig,
129127
if (FD) {
130128
IsConstructor = isa<ConstructorDecl>(FD);
131129

132-
if (auto *FDC = dyn_cast<DeclContext>(FD))
133-
genericSig = FDC->getGenericSignatureOfContext();
130+
if (auto *GC = FD->getAsGenericContext())
131+
genericSig = GC->getGenericSignature();
134132
}
135133

136-
auto *SignatureLabel =
137-
createSignatureLabel(Allocator, FD, AFT, DC, genericSig, Sig.IsSubscript,
138-
/*IsMember=*/bool(Sig.BaseType),
139-
Sig.IsImplicitlyCurried, Sig.IsSecondApply);
134+
auto *SignatureString =
135+
createSignatureString(Allocator, FD, AFT, DC, genericSig, Sig.IsSubscript,
136+
/*IsMember=*/bool(Sig.BaseType),
137+
Sig.IsImplicitlyCurried, Sig.IsSecondApply);
140138

141139
llvm::SmallString<512> SS;
142140
llvm::raw_svector_ostream OS(SS);
143141

144142
bool SkipResult = AFT->getResult()->isVoid() || IsConstructor;
145143

146-
auto Chunks = SignatureLabel->getChunks();
144+
auto Chunks = SignatureString->getChunks();
147145
auto C = Chunks.begin();
148146
while (C != Chunks.end()) {
149147
if (C->is(ChunkKind::TypeAnnotation) && SkipResult) {
@@ -159,7 +157,7 @@ static void getSignatureInfo(const DeclContext *DC, const Signature &Sig,
159157
++C;
160158

161159
auto &P = Info.Params.emplace_back();
162-
P.LabelBegin = SS.size();
160+
P.Offset = SS.size();
163161

164162
do {
165163
if (!C->is(ChunkKind::CallArgumentClosureType) && C->hasText())
@@ -168,7 +166,8 @@ static void getSignatureInfo(const DeclContext *DC, const Signature &Sig,
168166
++C;
169167
} while (C != Chunks.end() && !C->endsPreviousNestedGroup(NestingLevel));
170168

171-
P.LabelLength = SS.size() - P.LabelBegin;
169+
P.Length = SS.size() - P.Offset;
170+
continue;
172171
}
173172

174173
if (C->hasText())
@@ -177,7 +176,7 @@ static void getSignatureInfo(const DeclContext *DC, const Signature &Sig,
177176
++C;
178177
}
179178

180-
Info.Label = copyAndClearString(Allocator, SS);
179+
Info.Text = copyAndClearString(Allocator, SS);
181180
Info.ActiveParam = Sig.ParamIdx;
182181

183182
// Documentation.
@@ -211,12 +210,12 @@ static void deliverResults(SourceKit::SignatureHelpConsumer &SKConsumer,
211210
getSignatureInfo(Result->Result->DC, Sig, Info, Allocator);
212211
}
213212

214-
SourceKit::SignatureHelpResult SKResult;
215-
SmallVector<SourceKit::SignatureHelpResult::Signature, 8> SKSignatures;
213+
SourceKit::SignatureHelpResponse SKResult;
214+
SmallVector<SourceKit::SignatureHelpResponse::Signature, 8> SKSignatures;
216215

217216
for (auto &Info : Infos) {
218217
SKSignatures.push_back(
219-
{Info.Label, Info.DocComment, Info.ActiveParam, Info.Params});
218+
{Info.Text, Info.DocComment, Info.ActiveParam, Info.Params});
220219
}
221220

222221
SKResult.Signatures = SKSignatures;

tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,8 +1488,10 @@ handleRequestSignatureHelp(const RequestDict &Req,
14881488
if (!PrimaryFilePath)
14891489
return;
14901490
1491-
assert(getInputBufferNameForRequest(Req, Rec).empty() &&
1492-
"Input buffer name is different from primary file");
1491+
StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec);
1492+
if (!InputBufferName.empty())
1493+
return Rec(createErrorRequestInvalid(
1494+
"'key.primary_file' and 'key.sourcefile' can't be different"));
14931495
14941496
SmallVector<const char *, 8> Args;
14951497
if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec))
@@ -3643,15 +3645,15 @@ signatureHelp(StringRef PrimaryFilePath, int64_t Offset,
36433645
public:
36443646
Consumer(ResponseBuilder Builder) : SKResult(Builder.getDictionary()) {}
36453647
3646-
void handleResult(const SignatureHelpResult &Result) override {
3648+
void handleResult(const SignatureHelpResponse &Result) override {
36473649
SKResult.set(KeyActiveSignature, Result.ActiveSignature);
36483650
36493651
auto Signatures = SKResult.setArray(KeySignatures);
36503652
36513653
for (auto Signature : Result.Signatures) {
36523654
auto SignatureElem = Signatures.appendDictionary();
36533655
3654-
SignatureElem.set(KeyName, Signature.Label);
3656+
SignatureElem.set(KeyName, Signature.Text);
36553657
36563658
if (auto ActiveParam = Signature.ActiveParam)
36573659
SignatureElem.set(KeyActiveParameter, ActiveParam.value());
@@ -3664,8 +3666,8 @@ signatureHelp(StringRef PrimaryFilePath, int64_t Offset,
36643666
for (auto Param : Signature.Params) {
36653667
auto ParamElem = Params.appendDictionary();
36663668
3667-
ParamElem.set(KeyNameOffset, Param.LabelBegin);
3668-
ParamElem.set(KeyNameLength, Param.LabelLength);
3669+
ParamElem.set(KeyNameOffset, Param.Offset);
3670+
ParamElem.set(KeyNameLength, Param.Length);
36693671
36703672
if (!Param.DocComment.empty())
36713673
ParamElem.set(KeyDocComment, Param.DocComment);

0 commit comments

Comments
 (0)