Skip to content

Commit 5ae1834

Browse files
authored
Merge pull request #72370 from ahoppen/ahoppen/decl-cursor-info-from-solution
[SourceKit] Allow generation of cursor info for declarations from solutions that haven’t aren’t applied to the AST
2 parents 4b42302 + ef3e1c1 commit 5ae1834

File tree

8 files changed

+180
-64
lines changed

8 files changed

+180
-64
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: 98 additions & 42 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

@@ -302,24 +308,43 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
302308

303309
SmallVector<CursorInfoDeclReference, 1> Results;
304310

305-
Expr *getExprToResolve() {
311+
void sawSolutionImpl(const Solution &S) override {
306312
NodeFinder Finder(DC, ResolveLoc);
307313
Finder.resolve();
308314
auto Result = Finder.takeResult();
309-
if (!Result || Result->getKind() != NodeFinderResultKind::Expr) {
310-
return nullptr;
315+
if (!Result) {
316+
return;
317+
}
318+
switch (Result->getKind()) {
319+
case NodeFinderResultKind::Decl: {
320+
ValueDecl *DeclToResolve =
321+
cast<NodeFinderDeclResult>(Result.get())->getDecl();
322+
addCursorInfoResultForDecl(DeclToResolve, S);
323+
break;
324+
}
325+
case NodeFinderResultKind::Expr: {
326+
Expr *ExprToResolve = cast<NodeFinderExprResult>(Result.get())->getExpr();
327+
addCursorInfoResultForExpr(ExprToResolve, S);
328+
break;
329+
}
311330
}
312-
return cast<NodeFinderExprResult>(Result.get())->getExpr();
313331
}
314332

315-
void sawSolutionImpl(const Solution &S) override {
316-
auto &CS = S.getConstraintSystem();
317-
auto ResolveExpr = getExprToResolve();
318-
if (!ResolveExpr) {
333+
void addCursorInfoResultForDecl(ValueDecl *DeclToResolve, const Solution &S) {
334+
if (!S.hasType(DeclToResolve)) {
319335
return;
320336
}
337+
Type SolutionInterfaceTy =
338+
S.simplifyType(S.getType(DeclToResolve))->mapTypeOutOfContext();
339+
340+
addResult({/*BaseType=*/nullptr, /*IsDynamicRef=*/false, DeclToResolve,
341+
SolutionInterfaceTy});
342+
}
343+
344+
void addCursorInfoResultForExpr(Expr *ExprToResolve, const Solution &S) {
345+
auto &CS = S.getConstraintSystem();
321346

322-
auto Locator = CS.getConstraintLocator(ResolveExpr);
347+
auto Locator = CS.getConstraintLocator(ExprToResolve);
323348
auto CalleeLocator = S.getCalleeLocator(Locator);
324349
auto OverloadInfo = getSelectedOverloadInfo(S, CalleeLocator);
325350
if (!OverloadInfo.ValueRef) {
@@ -337,9 +362,11 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
337362
[&S](Expr *E) { return S.getResolvedType(E); });
338363
}
339364

340-
CursorInfoDeclReference NewResult = {OverloadInfo.BaseTy, IsDynamicRef,
341-
OverloadInfo.getValue()};
365+
addResult({OverloadInfo.BaseTy, IsDynamicRef, OverloadInfo.getValue(),
366+
/*SolutionSpecificInterfaceType=*/Type()});
367+
}
342368

369+
void addResult(const CursorInfoDeclReference &NewResult) {
343370
if (llvm::any_of(Results, [&](const CursorInfoDeclReference &R) {
344371
return R == NewResult;
345372
})) {
@@ -367,47 +394,38 @@ class CursorInfoDoneParsingCallback : public DoneParsingCallback {
367394
SourceLoc RequestedLoc)
368395
: DoneParsingCallback(), Consumer(Consumer), RequestedLoc(RequestedLoc) {}
369396

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-
397+
private:
398+
/// Shared core of `getExprResult` and `getDeclResult`.
399+
std::vector<ResolvedCursorInfoPtr> getResult(ASTNode Node, DeclContext *DC,
400+
SourceFile *SrcFile,
401+
NodeFinder &Finder) const {
393402
// Type check the statemnt containing E and listen for solutions.
394403
CursorInfoTypeCheckSolutionCallback Callback(*DC, RequestedLoc);
395404
{
396405
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
397406
DC->getASTContext().SolutionCallback, &Callback);
398-
typeCheckASTNodeAtLoc(TypeCheckASTNodeAtLocContext::declContext(DC),
399-
E->getLoc());
407+
if (ValueDecl *VD = getAsDecl<ValueDecl>(Node)) {
408+
typeCheckDeclAndParentClosures(VD);
409+
} else {
410+
typeCheckASTNodeAtLoc(TypeCheckASTNodeAtLocContext::declContext(DC),
411+
Node.getStartLoc());
412+
}
400413
}
401414

402415
if (Callback.getResults().empty()) {
403416
// No results.
404417
return {};
405418
}
406419

407-
for (auto Info : Callback.getResults()) {
408-
// Type check the referenced decls so that all their parent closures are
409-
// type-checked (see comment in typeCheckDeclAndParentClosures).
410-
typeCheckDeclAndParentClosures(Info.ReferencedDecl);
420+
if (Node.is<Expr *>()) {
421+
// If we are performing cursor info on an expression, type check the
422+
// referenced decls so that all their parent closures are type-checked
423+
// (see comment in typeCheckDeclAndParentClosures).
424+
// When doing cursor info on a declaration, we already type checked the
425+
// decl above while listening to the solution callbacks.
426+
for (auto Info : Callback.getResults()) {
427+
typeCheckDeclAndParentClosures(Info.ReferencedDecl);
428+
}
411429
}
412430

413431
// Deliver results
@@ -434,7 +452,8 @@ class CursorInfoDoneParsingCallback : public DoneParsingCallback {
434452
auto CursorInfo = new ResolvedValueRefCursorInfo(
435453
SrcFile, RequestedLoc, Res.ReferencedDecl,
436454
/*CtorTyRef=*/nullptr,
437-
/*ExtTyRef=*/nullptr, /*IsRef=*/true, /*Ty=*/Type(),
455+
/*ExtTyRef=*/nullptr, /*IsRef=*/true,
456+
Res.SolutionSpecificInterfaceType,
438457
/*ContainerType=*/Res.BaseType,
439458
/*CustomAttrRef=*/std::nullopt,
440459
/*IsKeywordArgument=*/false, Res.IsDynamicRef, ReceiverTypes,
@@ -444,6 +463,43 @@ class CursorInfoDoneParsingCallback : public DoneParsingCallback {
444463
return Results;
445464
}
446465

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

0 commit comments

Comments
 (0)