Skip to content

Commit 3ef6543

Browse files
committed
[SourceKit] Allow generation of cursor info for declarations from solutions that haven’t aren’t applied to the AST ye
This has two benefits: 1. We can now report ambiguous variable types 2. We are more robust in the generation of results for declarations inside closures. If the closure has an error, we won’t apply the solution to the AST and thus any cursor info that tried to get types out of the AST would fail. rdar://123845208
1 parent 4c5558d commit 3ef6543

File tree

7 files changed

+169
-52
lines changed

7 files changed

+169
-52
lines changed

include/swift/IDE/Utils.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ struct ResolvedValueRefCursorInfo : public ResolvedCursorInfo {
171171
TypeDecl *CtorTyRef = nullptr;
172172
ExtensionDecl *ExtTyRef = nullptr;
173173
bool IsRef = true;
174-
Type Ty;
174+
Type SolutionSpecificInterfaceType;
175175
Type ContainerType;
176176
std::optional<std::pair<const CustomAttr *, Decl *>> CustomAttrRef =
177177
std::nullopt;
@@ -196,13 +196,15 @@ struct ResolvedValueRefCursorInfo : public ResolvedCursorInfo {
196196
ResolvedValueRefCursorInfo() = default;
197197
explicit ResolvedValueRefCursorInfo(
198198
SourceFile *SF, SourceLoc Loc, ValueDecl *ValueD, TypeDecl *CtorTyRef,
199-
ExtensionDecl *ExtTyRef, bool IsRef, Type Ty, Type ContainerType,
199+
ExtensionDecl *ExtTyRef, bool IsRef, Type SolutionSpecificInterfaceType,
200+
Type ContainerType,
200201
std::optional<std::pair<const CustomAttr *, Decl *>> CustomAttrRef,
201202
bool IsKeywordArgument, bool IsDynamic,
202203
SmallVector<NominalTypeDecl *> ReceiverTypes,
203204
SmallVector<ValueDecl *> ShorthandShadowedDecls)
204205
: ResolvedCursorInfo(CursorInfoKind::ValueRef, SF, Loc), ValueD(ValueD),
205-
CtorTyRef(CtorTyRef), ExtTyRef(ExtTyRef), IsRef(IsRef), Ty(Ty),
206+
CtorTyRef(CtorTyRef), ExtTyRef(ExtTyRef), IsRef(IsRef),
207+
SolutionSpecificInterfaceType(SolutionSpecificInterfaceType),
206208
ContainerType(ContainerType), CustomAttrRef(CustomAttrRef),
207209
IsKeywordArgument(IsKeywordArgument), IsDynamic(IsDynamic),
208210
ReceiverTypes(ReceiverTypes),
@@ -216,7 +218,9 @@ struct ResolvedValueRefCursorInfo : public ResolvedCursorInfo {
216218

217219
bool isRef() const { return IsRef; }
218220

219-
Type getType() const { return Ty; }
221+
Type getSolutionSpecificInterfaceType() const {
222+
return SolutionSpecificInterfaceType;
223+
}
220224

221225
Type getContainerType() const { return ContainerType; }
222226

lib/IDE/CursorInfo.cpp

Lines changed: 94 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -282,11 +282,17 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
282282
bool IsDynamicRef;
283283
/// The declaration that is being referenced. Will never be \c nullptr.
284284
ValueDecl *ReferencedDecl;
285+
/// The interface type of the referenced declaration. This might not be
286+
/// stored in `ReferencedDecl->getInterfaceType()` if the declaration's
287+
/// type hasn't been applied to the AST.
288+
Type SolutionSpecificInterfaceType;
285289

286290
bool operator==(const CursorInfoDeclReference &Other) const {
287291
return nullableTypesEqual(BaseType, Other.BaseType) &&
288292
IsDynamicRef == Other.IsDynamicRef &&
289-
ReferencedDecl == Other.ReferencedDecl;
293+
ReferencedDecl == Other.ReferencedDecl &&
294+
nullableTypesEqual(SolutionSpecificInterfaceType,
295+
Other.SolutionSpecificInterfaceType);
290296
}
291297
};
292298

@@ -313,13 +319,42 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
313319
}
314320

315321
void sawSolutionImpl(const Solution &S) override {
316-
auto &CS = S.getConstraintSystem();
317-
auto ResolveExpr = getExprToResolve();
318-
if (!ResolveExpr) {
322+
NodeFinder Finder(DC, ResolveLoc);
323+
Finder.resolve();
324+
auto Result = Finder.takeResult();
325+
if (!Result) {
319326
return;
320327
}
328+
switch (Result->getKind()) {
329+
case NodeFinderResultKind::Decl: {
330+
ValueDecl *DeclToResolve =
331+
cast<NodeFinderDeclResult>(Result.get())->getDecl();
332+
addCursorInfoResultForDecl(DeclToResolve, S);
333+
break;
334+
}
335+
case NodeFinderResultKind::Expr: {
336+
Expr *ExprToResolve = cast<NodeFinderExprResult>(Result.get())->getExpr();
337+
addCursorInfoResultForExpr(ExprToResolve, S);
338+
break;
339+
}
340+
}
341+
}
342+
343+
void addCursorInfoResultForDecl(ValueDecl *DeclToResolve, const Solution &S) {
344+
if (!S.hasType(DeclToResolve)) {
345+
return;
346+
}
347+
Type SolutionInterfaceTy =
348+
S.simplifyType(S.getType(DeclToResolve))->mapTypeOutOfContext();
349+
350+
addResult({/*BaseType=*/nullptr, /*IsDynamicRef=*/false, DeclToResolve,
351+
SolutionInterfaceTy});
352+
}
321353

322-
auto Locator = CS.getConstraintLocator(ResolveExpr);
354+
void addCursorInfoResultForExpr(Expr *ExprToResolve, const Solution &S) {
355+
auto &CS = S.getConstraintSystem();
356+
357+
auto Locator = CS.getConstraintLocator(ExprToResolve);
323358
auto CalleeLocator = S.getCalleeLocator(Locator);
324359
auto OverloadInfo = getSelectedOverloadInfo(S, CalleeLocator);
325360
if (!OverloadInfo.ValueRef) {
@@ -337,9 +372,11 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
337372
[&S](Expr *E) { return S.getResolvedType(E); });
338373
}
339374

340-
CursorInfoDeclReference NewResult = {OverloadInfo.BaseTy, IsDynamicRef,
341-
OverloadInfo.getValue()};
375+
addResult({OverloadInfo.BaseTy, IsDynamicRef, OverloadInfo.getValue(),
376+
/*SolutionSpecificInterfaceType=*/Type()});
377+
}
342378

379+
void addResult(const CursorInfoDeclReference &NewResult) {
343380
if (llvm::any_of(Results, [&](const CursorInfoDeclReference &R) {
344381
return R == NewResult;
345382
})) {
@@ -367,36 +404,22 @@ class CursorInfoDoneParsingCallback : public DoneParsingCallback {
367404
SourceLoc RequestedLoc)
368405
: DoneParsingCallback(), Consumer(Consumer), RequestedLoc(RequestedLoc) {}
369406

370-
std::vector<ResolvedCursorInfoPtr>
371-
getDeclResult(NodeFinderDeclResult *DeclResult, SourceFile *SrcFile,
372-
NodeFinder &Finder) const {
373-
typeCheckDeclAndParentClosures(DeclResult->getDecl());
374-
auto CursorInfo = new ResolvedValueRefCursorInfo(
375-
SrcFile, RequestedLoc, DeclResult->getDecl(),
376-
/*CtorTyRef=*/nullptr,
377-
/*ExtTyRef=*/nullptr, /*IsRef=*/false, /*Ty=*/Type(),
378-
/*ContainerType=*/Type(),
379-
/*CustomAttrRef=*/std::nullopt,
380-
/*IsKeywordArgument=*/false,
381-
/*IsDynamic=*/false,
382-
/*ReceiverTypes=*/{},
383-
Finder.getShorthandShadowedDecls(DeclResult->getDecl()));
384-
return {CursorInfo};
385-
}
386-
387-
std::vector<ResolvedCursorInfoPtr>
388-
getExprResult(NodeFinderExprResult *ExprResult, SourceFile *SrcFile,
389-
NodeFinder &Finder) const {
390-
Expr *E = ExprResult->getExpr();
391-
DeclContext *DC = ExprResult->getDeclContext();
392-
407+
private:
408+
/// Shared core of `getExprResult` and `getDeclResult`.
409+
std::vector<ResolvedCursorInfoPtr> getResult(ASTNode Node, DeclContext *DC,
410+
SourceFile *SrcFile,
411+
NodeFinder &Finder) const {
393412
// Type check the statemnt containing E and listen for solutions.
394413
CursorInfoTypeCheckSolutionCallback Callback(*DC, RequestedLoc);
395414
{
396415
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
397416
DC->getASTContext().SolutionCallback, &Callback);
398-
typeCheckASTNodeAtLoc(TypeCheckASTNodeAtLocContext::declContext(DC),
399-
E->getLoc());
417+
if (ValueDecl *VD = getAsDecl<ValueDecl>(Node)) {
418+
typeCheckDeclAndParentClosures(VD);
419+
} else {
420+
typeCheckASTNodeAtLoc(TypeCheckASTNodeAtLocContext::declContext(DC),
421+
Node.getStartLoc());
422+
}
400423
}
401424

402425
if (Callback.getResults().empty()) {
@@ -434,7 +457,8 @@ class CursorInfoDoneParsingCallback : public DoneParsingCallback {
434457
auto CursorInfo = new ResolvedValueRefCursorInfo(
435458
SrcFile, RequestedLoc, Res.ReferencedDecl,
436459
/*CtorTyRef=*/nullptr,
437-
/*ExtTyRef=*/nullptr, /*IsRef=*/true, /*Ty=*/Type(),
460+
/*ExtTyRef=*/nullptr, /*IsRef=*/true,
461+
Res.SolutionSpecificInterfaceType,
438462
/*ContainerType=*/Res.BaseType,
439463
/*CustomAttrRef=*/std::nullopt,
440464
/*IsKeywordArgument=*/false, Res.IsDynamicRef, ReceiverTypes,
@@ -444,6 +468,43 @@ class CursorInfoDoneParsingCallback : public DoneParsingCallback {
444468
return Results;
445469
}
446470

471+
public:
472+
std::vector<ResolvedCursorInfoPtr>
473+
getDeclResult(NodeFinderDeclResult *DeclResult, SourceFile *SrcFile,
474+
NodeFinder &Finder) const {
475+
std::vector<ResolvedCursorInfoPtr> Results =
476+
getResult(DeclResult->getDecl(),
477+
DeclResult->getDecl()->getDeclContext(), SrcFile, Finder);
478+
479+
if (!Results.empty()) {
480+
return Results;
481+
}
482+
483+
// If we didn't get any solution from the constraint system, try getting the
484+
// type from the decl itself. This may happen if the decl is in an inactive
485+
// branch of a `#if` clause.
486+
auto CursorInfo = new ResolvedValueRefCursorInfo(
487+
SrcFile, RequestedLoc, DeclResult->getDecl(),
488+
/*CtorTyRef=*/nullptr,
489+
/*ExtTyRef=*/nullptr,
490+
/*IsRef=*/false,
491+
/*SolutionSpecificInterfaceType=*/Type(),
492+
/*ContainerType=*/Type(),
493+
/*CustomAttrRef=*/std::nullopt,
494+
/*IsKeywordArgument=*/false,
495+
/*IsDynamic=*/false,
496+
/*ReceiverTypes=*/{},
497+
Finder.getShorthandShadowedDecls(DeclResult->getDecl()));
498+
return {CursorInfo};
499+
}
500+
501+
std::vector<ResolvedCursorInfoPtr>
502+
getExprResult(NodeFinderExprResult *ExprResult, SourceFile *SrcFile,
503+
NodeFinder &Finder) const {
504+
return getResult(ExprResult->getExpr(), ExprResult->getDeclContext(),
505+
SrcFile, Finder);
506+
}
507+
447508
void doneParsing(SourceFile *SrcFile) override {
448509
if (!SrcFile) {
449510
return;

lib/IDE/IDERequests.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ bool CursorInfoResolver::tryResolve(ValueDecl *D, TypeDecl *CtorTyRef,
165165

166166
CursorInfo = new ResolvedValueRefCursorInfo(
167167
CursorInfo->getSourceFile(), CursorInfo->getLoc(), D, CtorTyRef, ExtTyRef,
168-
IsRef, Ty, ContainerType, CustomAttrRef,
168+
IsRef, /*SolutionSpecificInterfaceType=*/Type(), ContainerType,
169+
CustomAttrRef,
169170
/*IsKeywordArgument=*/false, IsDynamic, ReceiverTypes,
170171
/*ShorthandShadowedDecls=*/{});
171172

lib/IDE/SourceEntityWalker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ ASTWalker::PreWalkAction SemaAnnotator::walkToDeclPreProper(Decl *D) {
178178
SourceLoc loc = parsedName.second;
179179
if (auto assocTypeDecl = proto->getAssociatedType(name)) {
180180
auto Continue = passReference(
181-
assocTypeDecl, assocTypeDecl->getDeclaredInterfaceType(),
181+
assocTypeDecl, assocTypeDecl->getInterfaceType(),
182182
DeclNameLoc(loc),
183183
ReferenceMetaData(SemaReferenceKind::TypeRef, std::nullopt));
184184
if (!Continue)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
func withString(body: (String) -> Void) {}
2+
3+
func test(array: [String]) {
4+
withString { element in
5+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):9 %s -- %s | %FileCheck %s
6+
let refToElement = element
7+
8+
_ = invalid
9+
}
10+
}
11+
12+
// CHECK: <Declaration>let refToElement: <Type usr="s:SS">String</Type></Declaration>

test/SourceKit/CursorInfo/cursor_ambiguous.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,19 @@ func testAmbiguousFunctionReference() {
1616
// LOCAL_FUNC: SECONDARY SYMBOLS END
1717
}
1818

19-
19+
func testAmbiguousFunctionResult() {
20+
func foo() -> Int {}
21+
func foo() -> String {}
22+
23+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):7 %s -- %s | %FileCheck %s --check-prefix AMBIGUOUS_FUNC_RESULT
24+
let value = foo()
25+
// AMBIGUOUS_FUNC_RESULT: source.lang.swift.ref.var.local
26+
// AMBIGUOUS_FUNC_RESULT: <Declaration>let value: <Type usr="s:Si">Int</Type></Declaration>
27+
// AMBIGUOUS_FUNC_RESULT: SECONDARY SYMBOLS BEGIN
28+
// AMBIGUOUS_FUNC_RESULT: source.lang.swift.ref.var.local
29+
// AMBIGUOUS_FUNC_RESULT: <Declaration>let value: <Type usr="s:SS">String</Type></Declaration>
30+
// AMBIGUOUS_FUNC_RESULT: SECONDARY SYMBOLS END
31+
}
2032

2133
struct TestDeduplicateResults {
2234
// The constraints system produces multiple solutions here for the argument type but

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,13 @@ static StringRef getModuleName(const ValueDecl *VD,
770770
}
771771

772772
struct DeclInfo {
773-
const ValueDecl *VD;
773+
ValueDecl *VD;
774+
/// If not null, a solution-specific interface type of `VD`. This allows
775+
/// us to produce results for declarations where the soluion has not been
776+
/// applied to the AST, eg. because the declaration has an ambiguous type
777+
/// or because it occurs inside a closure that has an error, which prevents
778+
/// the constraint system solution to be applied to the AST.
779+
Type SolutionSpecificInterfaceType;
774780
Type ContainerType;
775781
bool IsRef;
776782
bool IsDynamic;
@@ -785,11 +791,13 @@ struct DeclInfo {
785791
/// Whether the \c VD is in a synthesized extension of \c BaseType
786792
bool InSynthesizedExtension = false;
787793

788-
DeclInfo(const ValueDecl *VD, Type ContainerType, bool IsRef, bool IsDynamic,
794+
DeclInfo(ValueDecl *VD, Type SolutionSpecificInterfaceType,
795+
Type ContainerType, bool IsRef, bool IsDynamic,
789796
ArrayRef<NominalTypeDecl *> ReceiverTypes,
790797
const CompilerInvocation &Invoc)
791-
: VD(VD), ContainerType(ContainerType), IsRef(IsRef),
792-
IsDynamic(IsDynamic), ReceiverTypes(ReceiverTypes) {
798+
: VD(VD), SolutionSpecificInterfaceType(SolutionSpecificInterfaceType),
799+
ContainerType(ContainerType), IsRef(IsRef), IsDynamic(IsDynamic),
800+
ReceiverTypes(ReceiverTypes) {
793801
if (VD == nullptr)
794802
return;
795803

@@ -939,6 +947,18 @@ fillSymbolInfo(CursorSymbolInfo &Symbol, const DeclInfo &DInfo,
939947
const CompilerInvocation &Invoc,
940948
ArrayRef<ImmutableTextSnapshotRef> PreviousSnaps,
941949
llvm::BumpPtrAllocator &Allocator) {
950+
// Override the type of `DInfo.VD` while retrieving symbol information.
951+
// Ideall, we would pass a customizable `getInterfaceType` function to all
952+
// functions that operate on VD but doing so is not viable - USR generation,
953+
// AST printing, symbol graph and probably more have been designed with the
954+
// assumption that VD->getInterfaceType() returns the fixed interface type
955+
// of the declaration.
956+
Type OriginalInterfaceType = DInfo.VD->getInterfaceType();
957+
if (DInfo.SolutionSpecificInterfaceType) {
958+
DInfo.VD->setInterfaceType(DInfo.SolutionSpecificInterfaceType);
959+
}
960+
SWIFT_DEFER { DInfo.VD->setInterfaceType(OriginalInterfaceType); };
961+
942962
SmallString<256> Buffer;
943963
SmallVector<StringRef, 4> Strings;
944964
llvm::raw_svector_ostream OS(Buffer);
@@ -1199,7 +1219,7 @@ static bool addCursorInfoForLiteral(
11991219
}
12001220

12011221
auto &Symbol = Data.Symbols.emplace_back();
1202-
DeclInfo Info(Decl, nullptr, true, false, {}, CompInvoc);
1222+
DeclInfo Info(Decl, nullptr, nullptr, true, false, {}, CompInvoc);
12031223
auto Err = fillSymbolInfo(Symbol, Info, CursorLoc, false, Lang, CompInvoc,
12041224
PreviousSnaps, Data.Allocator);
12051225

@@ -1218,9 +1238,10 @@ addCursorInfoForDecl(CursorInfoData &Data, ResolvedValueRefCursorInfoPtr Info,
12181238
SwiftLangSupport &Lang, const CompilerInvocation &Invoc,
12191239
std::string &Diagnostic,
12201240
ArrayRef<ImmutableTextSnapshotRef> PreviousSnaps) {
1221-
DeclInfo OrigInfo(Info->getValueD(), Info->getContainerType(), Info->isRef(),
1222-
Info->isDynamic(), Info->getReceiverTypes(), Invoc);
1223-
DeclInfo CtorTypeInfo(Info->getCtorTyRef(), Type(), true, false,
1241+
DeclInfo OrigInfo(Info->getValueD(), Info->getSolutionSpecificInterfaceType(),
1242+
Info->getContainerType(), Info->isRef(), Info->isDynamic(),
1243+
Info->getReceiverTypes(), Invoc);
1244+
DeclInfo CtorTypeInfo(Info->getCtorTyRef(), Type(), Type(), true, false,
12241245
ArrayRef<NominalTypeDecl *>(), Invoc);
12251246
DeclInfo &MainInfo = CtorTypeInfo.VD ? CtorTypeInfo : OrigInfo;
12261247
if (MainInfo.Unavailable) {
@@ -1257,7 +1278,7 @@ addCursorInfoForDecl(CursorInfoData &Data, ResolvedValueRefCursorInfoPtr Info,
12571278
if (!Info->isRef()) {
12581279
for (auto D : Info->getShorthandShadowedDecls()) {
12591280
CursorSymbolInfo &SymbolInfo = Data.Symbols.emplace_back();
1260-
DeclInfo DInfo(D, Type(), /*IsRef=*/true, /*IsDynamic=*/false,
1281+
DeclInfo DInfo(D, Type(), Type(), /*IsRef=*/true, /*IsDynamic=*/false,
12611282
ArrayRef<NominalTypeDecl *>(), Invoc);
12621283
if (auto Err =
12631284
fillSymbolInfo(SymbolInfo, DInfo, Info->getLoc(), AddSymbolGraph,
@@ -2082,7 +2103,7 @@ void SwiftLangSupport::getCursorInfo(
20822103
const_cast<ValueDecl *>(Entity.Dcl),
20832104
/*CtorTyRef=*/nullptr,
20842105
/*ExtTyRef=*/nullptr, Entity.IsRef,
2085-
/*Ty=*/Type(),
2106+
/*SolutionSpecificInterfaceType=*/Type(),
20862107
/*ContainerType=*/Type(),
20872108
/*CustomAttrRef=*/std::nullopt,
20882109
/*IsKeywordArgument=*/false,
@@ -2143,7 +2164,13 @@ void SwiftLangSupport::getCursorInfo(
21432164
// AST based completion *always* produces a result
21442165
bool NoResults = Res.isError();
21452166
if (Res.isValue()) {
2146-
NoResults = Res.value().Symbols.empty();
2167+
NoResults |= Res.value().Symbols.empty();
2168+
bool AllResultsHaveErrorType =
2169+
llvm::all_of(Res.value().Symbols, [](const CursorSymbolInfo &Symbol) {
2170+
// USR of the AST's error type if no type could be resolved.
2171+
return Symbol.TypeUSR == "$sXeD";
2172+
});
2173+
NoResults |= AllResultsHaveErrorType;
21472174
}
21482175
if (!NoResults || !InputBuffer) {
21492176
Receiver(Res);
@@ -2383,7 +2410,7 @@ static void resolveCursorFromUSR(
23832410
/*CtorTyRef=*/nullptr,
23842411
/*ExtTyRef=*/nullptr,
23852412
/*IsRef=*/false,
2386-
/*Ty=*/Type(), ContainerType,
2413+
/*SolutionSpecificInterfaceType=*/Type(), ContainerType,
23872414
/*CustomAttrRef=*/std::nullopt,
23882415
/*IsKeywordArgument=*/false,
23892416
/*IsDynamic=*/false,

0 commit comments

Comments
 (0)