Skip to content

Commit c60da65

Browse files
committed
[CursorInfo] Implement a few expression references as solver-based
This implements cursor info resolving for a few expression types using the constraint system. This allows us to detect ambiguous results – we cannot deliver them yet but that will be done in a follow-up PR.
1 parent 5ae7a66 commit c60da65

File tree

2 files changed

+161
-7
lines changed

2 files changed

+161
-7
lines changed

lib/IDE/CursorInfo.cpp

Lines changed: 150 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "swift/AST/GenericEnvironment.h"
1717
#include "swift/AST/NameLookup.h"
1818
#include "swift/AST/USRGeneration.h"
19+
#include "swift/IDE/SelectedOverloadInfo.h"
1920
#include "swift/IDE/TypeCheckCompletionCallback.h"
2021
#include "swift/Parse/IDEInspectionCallbacks.h"
2122
#include "swift/Sema/ConstraintSystem.h"
@@ -51,17 +52,19 @@ void typeCheckDeclAndParentClosures(ValueDecl *VD) {
5152
TypeCheckASTNodeAtLocContext::declContext(VD->getDeclContext()),
5253
VD->getLoc());
5354
if (auto VarD = dyn_cast<VarDecl>(VD)) {
54-
// Type check any attached property wrappers so the annotated declaration
55-
// can refer to their USRs.
56-
(void)VarD->getPropertyWrapperBackingPropertyType();
55+
if (VarD->hasAttachedPropertyWrapper()) {
56+
// Type check any attached property wrappers so the annotated declaration
57+
// can refer to their USRs.
58+
(void)VarD->getPropertyWrapperBackingPropertyType();
59+
}
5760
// Visit emitted accessors so we generated accessors from property wrappers.
5861
VarD->visitEmittedAccessors([&](AccessorDecl *accessor) {});
5962
}
6063
}
6164

6265
// MARK: - NodeFinderResults
6366

64-
enum class NodeFinderResultKind { Decl };
67+
enum class NodeFinderResultKind { Decl, Expr };
6568

6669
class NodeFinderResult {
6770
NodeFinderResultKind Kind;
@@ -87,6 +90,24 @@ class NodeFinderDeclResult : public NodeFinderResult {
8790
}
8891
};
8992

93+
class NodeFinderExprResult : public NodeFinderResult {
94+
Expr *E;
95+
/// The \c DeclContext in which \c E occurs.
96+
DeclContext *DC;
97+
98+
public:
99+
NodeFinderExprResult(Expr *E, DeclContext *DC)
100+
: NodeFinderResult(NodeFinderResultKind::Expr), E(E), DC(DC) {}
101+
102+
Expr *getExpr() const { return E; }
103+
104+
DeclContext *getDeclContext() const { return DC; }
105+
106+
static bool classof(const NodeFinderResult *Res) {
107+
return Res->getKind() == NodeFinderResultKind::Expr;
108+
}
109+
};
110+
90111
// MARK: - NodeFinder
91112

92113
/// Walks the AST, looking for a node at \c LocToResolve. While walking the
@@ -192,6 +213,23 @@ class NodeFinder : ASTWalker {
192213
}
193214
}
194215

216+
if (E->getLoc() != LocToResolve) {
217+
return Action::Continue(E);
218+
}
219+
220+
switch (E->getKind()) {
221+
case ExprKind::DeclRef:
222+
case ExprKind::UnresolvedDot:
223+
case ExprKind::UnresolvedDeclRef: {
224+
assert(Result == nullptr);
225+
Result =
226+
std::make_unique<NodeFinderExprResult>(E, getCurrentDeclContext());
227+
return Action::Stop();
228+
}
229+
default:
230+
break;
231+
}
232+
195233
return Action::Continue(E);
196234
}
197235

@@ -215,6 +253,57 @@ class NodeFinder : ASTWalker {
215253
}
216254
};
217255

256+
// MARK: - Solver-based expression analysis
257+
258+
class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
259+
public:
260+
struct CursorInfoDeclReference {
261+
/// If the referenced declaration is a member reference, the type of the
262+
/// member's base, otherwise \c null.
263+
Type BaseType;
264+
/// Whether the reference is dynamic (see \c ide::isDynamicRef)
265+
bool IsDynamicRef;
266+
/// The declaration that is being referenced. Will never be \c nullptr.
267+
ValueDecl *ReferencedDecl;
268+
};
269+
270+
private:
271+
/// The expression for which we want to provide cursor info results.
272+
Expr *ResolveExpr;
273+
274+
SmallVector<CursorInfoDeclReference, 1> Results;
275+
276+
void sawSolutionImpl(const Solution &S) override {
277+
auto &CS = S.getConstraintSystem();
278+
279+
auto Locator = CS.getConstraintLocator(ResolveExpr);
280+
auto CalleeLocator = S.getCalleeLocator(Locator);
281+
auto OverloadInfo = getSelectedOverloadInfo(S, CalleeLocator);
282+
if (!OverloadInfo.Value) {
283+
// We could not resolve the referenced declaration. Skip the solution.
284+
return;
285+
}
286+
287+
bool IsDynamicRef = false;
288+
auto BaseLocator =
289+
CS.getConstraintLocator(Locator, ConstraintLocator::MemberRefBase);
290+
if (auto BaseExpr =
291+
simplifyLocatorToAnchor(BaseLocator).dyn_cast<Expr *>()) {
292+
IsDynamicRef =
293+
ide::isDynamicRef(BaseExpr, OverloadInfo.Value,
294+
[&S](Expr *E) { return S.getResolvedType(E); });
295+
}
296+
297+
Results.push_back({OverloadInfo.BaseTy, IsDynamicRef, OverloadInfo.Value});
298+
}
299+
300+
public:
301+
CursorInfoTypeCheckSolutionCallback(Expr *ResolveExpr)
302+
: ResolveExpr(ResolveExpr) {}
303+
304+
ArrayRef<CursorInfoDeclReference> getResults() const { return Results; }
305+
};
306+
218307
// MARK: - CursorInfoDoneParsingCallback
219308

220309
class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks {
@@ -242,6 +331,59 @@ class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks {
242331
return CursorInfo;
243332
}
244333

334+
std::unique_ptr<ResolvedCursorInfo>
335+
getExprResult(NodeFinderExprResult *ExprResult, SourceFile *SrcFile,
336+
NodeFinder &Finder) const {
337+
Expr *E = ExprResult->getExpr();
338+
DeclContext *DC = ExprResult->getDeclContext();
339+
340+
// Type check the statemnt containing E and listen for solutions.
341+
CursorInfoTypeCheckSolutionCallback Callback(E);
342+
llvm::SaveAndRestore<TypeCheckCompletionCallback *> CompletionCollector(
343+
DC->getASTContext().SolutionCallback, &Callback);
344+
typeCheckASTNodeAtLoc(TypeCheckASTNodeAtLocContext::declContext(DC),
345+
E->getLoc());
346+
347+
if (Callback.getResults().empty()) {
348+
// No results.
349+
return nullptr;
350+
}
351+
352+
for (auto Info : Callback.getResults()) {
353+
// Type check the referenced decls so that all their parent closures are
354+
// type-checked (see comment in typeCheckDeclAndParentClosures).
355+
typeCheckDeclAndParentClosures(Info.ReferencedDecl);
356+
}
357+
358+
if (Callback.getResults().size() != 1) {
359+
// FIXME: We need to be able to report multiple results.
360+
return nullptr;
361+
}
362+
363+
// Deliver results
364+
365+
auto Res = Callback.getResults()[0];
366+
auto CursorInfo = std::make_unique<ResolvedValueRefCursorInfo>(
367+
ResolvedCursorInfo(SrcFile), Res.ReferencedDecl, /*CtorTyRef=*/nullptr,
368+
/*ExtTyRef=*/nullptr, /*IsRef=*/true, /*Ty=*/Type(),
369+
/*ContainerType=*/Res.BaseType);
370+
CursorInfo->setLoc(RequestedLoc);
371+
CursorInfo->setIsDynamic(Res.IsDynamicRef);
372+
if (Res.IsDynamicRef && Res.BaseType) {
373+
if (auto ReceiverType = Res.BaseType->getAnyNominal()) {
374+
CursorInfo->setReceiverTypes({ReceiverType});
375+
} else if (auto MT = Res.BaseType->getAs<AnyMetatypeType>()) {
376+
// Look through metatypes to get the nominal type decl.
377+
if (auto ReceiverType = MT->getInstanceType()->getAnyNominal()) {
378+
CursorInfo->setReceiverTypes({ReceiverType});
379+
}
380+
}
381+
}
382+
CursorInfo->setShorthandShadowedDecls(
383+
Finder.getShorthandShadowedDecls(Res.ReferencedDecl));
384+
return CursorInfo;
385+
}
386+
245387
void doneParsing(SourceFile *SrcFile) override {
246388
if (!SrcFile) {
247389
return;
@@ -258,6 +400,10 @@ class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks {
258400
CursorInfo = getDeclResult(cast<NodeFinderDeclResult>(Result.get()),
259401
SrcFile, Finder);
260402
break;
403+
case NodeFinderResultKind::Expr:
404+
CursorInfo = getExprResult(cast<NodeFinderExprResult>(Result.get()),
405+
SrcFile, Finder);
406+
break;
261407
}
262408
if (Result) {
263409
Consumer.handleResults(*CursorInfo);

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,8 @@ struct CursorSymbolInfo {
519519

520520
llvm::Optional<unsigned> ParentNameOffset;
521521

522-
void print(llvm::raw_ostream &OS, std::string Indentation) const {
522+
void print(llvm::raw_ostream &OS, std::string Indentation,
523+
bool ForSolverBasedCursorInfoVerification = false) const {
523524
OS << Indentation << "CursorSymbolInfo" << '\n';
524525
OS << Indentation << " Kind: " << Kind.getName() << '\n';
525526
OS << Indentation << " DeclarationLang: " << DeclarationLang.getName()
@@ -528,7 +529,14 @@ struct CursorSymbolInfo {
528529
OS << Indentation << " USR: " << USR << '\n';
529530
OS << Indentation << " TypeName: " << TypeName << '\n';
530531
OS << Indentation << " TypeUSR: " << TypeUSR << '\n';
531-
OS << Indentation << " ContainerTypeUSR: " << ContainerTypeUSR << '\n';
532+
// The ContainerTypeUSR varies too much between the solver-based and
533+
// AST-based implementation. A few manual inspections showed that the
534+
// solver-based container is usually more correct than the old. Instead of
535+
// fixing the AST-based container type computation, exclude the container
536+
// type from the verification.
537+
if (!ForSolverBasedCursorInfoVerification) {
538+
OS << Indentation << " ContainerTypeUSR: " << ContainerTypeUSR << '\n';
539+
}
532540
OS << Indentation << " DocComment: " << DocComment << '\n';
533541
OS << Indentation << " GroupName: " << GroupName << '\n';
534542
OS << Indentation << " LocalizationKey: " << LocalizationKey << '\n';
@@ -600,7 +608,7 @@ struct CursorInfoData {
600608
OS << Indentation << "CursorInfoData" << '\n';
601609
OS << Indentation << " Symbols:" << '\n';
602610
for (auto Symbol : Symbols) {
603-
Symbol.print(OS, Indentation + " ");
611+
Symbol.print(OS, Indentation + " ", ForSolverBasedCursorInfoVerification);
604612
}
605613
OS << Indentation << " AvailableActions:" << '\n';
606614
for (auto AvailableAction : AvailableActions) {

0 commit comments

Comments
 (0)