Skip to content

Commit ef3e1c1

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 ef3e1c1

File tree

8 files changed

+180
-64
lines changed

8 files changed

+180
-64
lines changed

include/swift/IDE/Utils.h

+8-4
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

+98-42
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

+2-1
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

+1-1
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)
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

+13-1
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)