Skip to content

Commit fd039f4

Browse files
authored
Merge pull request swiftlang#62478 from ahoppen/ahoppen/solver-based-cursor-info
[CursorInfo] Implement a few expression references as solver-based
2 parents 9985807 + a9cba54 commit fd039f4

File tree

19 files changed

+338
-176
lines changed

19 files changed

+338
-176
lines changed

lib/AST/ASTPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ const std::function<bool(const ExtensionDecl *)>
8181
void PrintOptions::setBaseType(Type T) {
8282
if (T->is<ErrorType>())
8383
return;
84+
if (auto DynamicSelf = T->getAs<DynamicSelfType>()) {
85+
// TypeTransformContext requires `T` to have members. Look through dynamic
86+
// Self.
87+
T = DynamicSelf->getSelfType();
88+
}
8489
TransformContext = TypeTransformContext(T);
8590
}
8691

lib/IDE/CursorInfo.cpp

Lines changed: 158 additions & 7 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"
@@ -47,21 +48,28 @@ void typeCheckDeclAndParentClosures(ValueDecl *VD) {
4748
DC = DC->getParent();
4849
}
4950

50-
typeCheckASTNodeAtLoc(
51-
TypeCheckASTNodeAtLocContext::declContext(VD->getDeclContext()),
52-
VD->getLoc());
51+
if (!VD->getInterfaceType()) {
52+
// The decl has an interface time if it came from another module. In that
53+
// case, there's nothing to do. Otherwise, type check the decl to get its
54+
// type.
55+
typeCheckASTNodeAtLoc(
56+
TypeCheckASTNodeAtLocContext::declContext(VD->getDeclContext()),
57+
VD->getLoc());
58+
}
5359
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();
60+
if (VarD->hasAttachedPropertyWrapper()) {
61+
// Type check any attached property wrappers so the annotated declaration
62+
// can refer to their USRs.
63+
(void)VarD->getPropertyWrapperBackingPropertyType();
64+
}
5765
// Visit emitted accessors so we generated accessors from property wrappers.
5866
VarD->visitEmittedAccessors([&](AccessorDecl *accessor) {});
5967
}
6068
}
6169

6270
// MARK: - NodeFinderResults
6371

64-
enum class NodeFinderResultKind { Decl };
72+
enum class NodeFinderResultKind { Decl, Expr };
6573

6674
class NodeFinderResult {
6775
NodeFinderResultKind Kind;
@@ -87,6 +95,24 @@ class NodeFinderDeclResult : public NodeFinderResult {
8795
}
8896
};
8997

98+
class NodeFinderExprResult : public NodeFinderResult {
99+
Expr *E;
100+
/// The \c DeclContext in which \c E occurs.
101+
DeclContext *DC;
102+
103+
public:
104+
NodeFinderExprResult(Expr *E, DeclContext *DC)
105+
: NodeFinderResult(NodeFinderResultKind::Expr), E(E), DC(DC) {}
106+
107+
Expr *getExpr() const { return E; }
108+
109+
DeclContext *getDeclContext() const { return DC; }
110+
111+
static bool classof(const NodeFinderResult *Res) {
112+
return Res->getKind() == NodeFinderResultKind::Expr;
113+
}
114+
};
115+
90116
// MARK: - NodeFinder
91117

92118
/// Walks the AST, looking for a node at \c LocToResolve. While walking the
@@ -192,6 +218,23 @@ class NodeFinder : ASTWalker {
192218
}
193219
}
194220

221+
if (E->getLoc() != LocToResolve) {
222+
return Action::Continue(E);
223+
}
224+
225+
switch (E->getKind()) {
226+
case ExprKind::DeclRef:
227+
case ExprKind::UnresolvedDot:
228+
case ExprKind::UnresolvedDeclRef: {
229+
assert(Result == nullptr);
230+
Result =
231+
std::make_unique<NodeFinderExprResult>(E, getCurrentDeclContext());
232+
return Action::Stop();
233+
}
234+
default:
235+
break;
236+
}
237+
195238
return Action::Continue(E);
196239
}
197240

@@ -215,6 +258,57 @@ class NodeFinder : ASTWalker {
215258
}
216259
};
217260

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

220314
class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks {
@@ -242,6 +336,59 @@ class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks {
242336
return CursorInfo;
243337
}
244338

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

lib/IDE/SelectedOverloadInfo.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ swift::ide::getSelectedOverloadInfo(const Solution &S,
3636
if (Result.BaseTy) {
3737
Result.BaseTy = S.simplifyType(Result.BaseTy)->getRValueType();
3838
}
39+
if (Result.BaseTy && Result.BaseTy->is<ModuleType>()) {
40+
Result.BaseTy = nullptr;
41+
}
3942

4043
Result.Value = SelectedOverload->choice.getDeclOrNull();
4144
Result.ValueTy =

lib/IDE/Utils.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,9 @@ Expr *swift::ide::getBase(ArrayRef<Expr *> ExprStack) {
860860

861861
Expr *CurrentE = ExprStack.back();
862862
Expr *ParentE = getContainingExpr(ExprStack, 1);
863+
if (ParentE && isa<FunctionConversionExpr>(ParentE)) {
864+
ParentE = getContainingExpr(ExprStack, 2);
865+
}
863866
Expr *Base = nullptr;
864867

865868
if (auto DSE = dyn_cast_or_null<DotSyntaxCallExpr>(ParentE))
@@ -925,6 +928,8 @@ bool swift::ide::isDynamicRef(Expr *Base, ValueDecl *D, llvm::function_ref<Type(
925928
if (!isDeclOverridable(D))
926929
return false;
927930

931+
Base = Base->getSemanticsProvidingExpr();
932+
928933
// super.method()
929934
// TODO: Should be dynamic if `D` is marked as dynamic and @objc, but in
930935
// that case we really need to change the role the index outputs as
@@ -956,11 +961,9 @@ void swift::ide::getReceiverType(Expr *Base,
956961
if (!ReceiverTy)
957962
return;
958963

959-
if (auto LVT = ReceiverTy->getAs<LValueType>())
960-
ReceiverTy = LVT->getObjectType();
961-
else if (auto MetaT = ReceiverTy->getAs<MetatypeType>())
962-
ReceiverTy = MetaT->getInstanceType();
963-
else if (auto SelfT = ReceiverTy->getAs<DynamicSelfType>())
964+
ReceiverTy = ReceiverTy->getWithoutSpecifierType();
965+
ReceiverTy = ReceiverTy->getMetatypeInstanceType();
966+
if (auto SelfT = ReceiverTy->getAs<DynamicSelfType>())
964967
ReceiverTy = SelfT->getSelfType();
965968

966969
// TODO: Handle generics and composed protocols

test/SourceKit/CompileNotifications/cursor-info.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// rdar://103369449
2-
// REQUIRES: asserts
3-
41
// RUN: %sourcekitd-test -req=track-compiles == -req=cursor %s -offset=0 -- %s | %FileCheck %s -check-prefix=COMPILE_1 --enable-yaml-compatibility
52
// COMPILE_1: <empty cursor info; internal diagnostic: "Unable to resolve cursor info.">
63
// COMPILE_1: {
@@ -12,7 +9,7 @@
129
// COMPILE_1: key.notification: source.notification.compile-did-finish,
1310
// COMPILE_1: key.compileid: [[CID1]]
1411
// COMPILE_1: }
15-
// FIXME: Once we switch to only run solver-based cursor info, we should only receive a single compile notification
12+
// FIXME: Once all cursor info kinds are migrated to solver-based and we remove the fallback path to AST-based cursor info, we should only receive a single compile notification
1613
// COMPILE_1: {
1714
// COMPILE_1: key.notification: source.notification.compile-will-start,
1815
// COMPILE_1: key.filepath: "SOURCE_DIR{{.*}}cursor-info.swift",

test/SourceKit/CursorInfo/cursor_in_pound_if.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,5 @@ func foo() {
88
let xxx = "hello"
99
#endif
1010
}
11-
// TODO: Once we switch to use the solver-based cursor info implementation, we also receive results for the int case
12-
// CHECK-INT: Unable to resolve cursor info
11+
// CHECK-INT: <Declaration>let xxx: <Type usr="s:Si">Int</Type></Declaration>
1312
// CHECK-STR: <Declaration>let xxx: <Type usr="s:SS">String</Type></Declaration>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
func test() {
2+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):9 %s -- %s | %FileCheck %s
3+
Swift.min(1, 2)
4+
}
5+
6+
// CHECK: source.lang.swift.ref.function.free ()
7+
// CHECK-NEXT: min(_:_:)
8+
// CHECK-NEXT: s:s3minyxx_xtSLRzlF
9+
// CHECK-NEXT: source.lang.swift
10+
// CHECK-NEXT: <T where T : Comparable> (T, T) -> T
11+
// CHECK-NEXT: $syxx_xtcSLRzluD
12+
// CHECK-NEXT: Swift
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
protocol Publisher<Output> {
2+
associatedtype Output
3+
}
4+
5+
extension Publisher {
6+
func stage2() -> MyGenerictype<Self.Output> {
7+
fatalError()
8+
}
9+
10+
func stage3() -> Self {
11+
fatalError()
12+
}
13+
}
14+
15+
struct MyGenerictype<Output> : Publisher {
16+
init() {}
17+
18+
func stage1(with output: Self.Output) -> HasTypeAccessInTypealias<Self> {
19+
fatalError()
20+
}
21+
}
22+
23+
struct HasTypeAccessInTypealias<Upstream> : Publisher where Upstream : Publisher {
24+
typealias Output = Upstream.Output
25+
}
26+
27+
func test() {
28+
MyGenerictype()
29+
.stage1(with: 0)
30+
.stage2()
31+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):6 %s -- %s
32+
.stage3()
33+
}
34+
35+
// CHECK: <Declaration>func stage3() -&gt; <Type usr="s:4test13MyGenerictypeV">MyGenerictype</Type>&lt;<Type usr="s:4test24HasTypeAccessInTypealiasV">HasTypeAccessInTypealias</Type>&lt;<Type usr="s:4test13MyGenerictypeV">MyGenerictype</Type>&lt;<Type usr="s:Si">Int</Type>&gt;&gt;.<Type usr="s:4test24HasTypeAccessInTypealiasV6Outputa">Output</Type>&gt;</Declaration>

test/SourceKit/CursorInfo/discriminator.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ func testNestedClosures() {
3535
}
3636

3737
func testReuseAST(bytes: Int) {
38-
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 2):7 -req-opts=verifysolverbasedcursorinfo=1 %s -- %s == \
39-
// RUN: -req=cursor -pos=%(line + 2):7 -req-opts=verifysolverbasedcursorinfo=1 %s -- %s | %FileCheck %s --check-prefix=REUSE_AST
38+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 2):7 %s -- %s == \
39+
// RUN: -req=cursor -pos=%(line + 2):7 %s -- %s | %FileCheck %s --check-prefix=REUSE_AST
4040
let size = 3
4141
var bytes = 6
4242
// REUSE_AST: source.lang.swift.decl.var.local (40:7-40:11)
4343
// REUSE_AST: s:13discriminator12testReuseAST5bytesySi_tF4sizeL_Sivp
4444
// REUSE_AST: source.lang.swift.decl.var.local (41:7-41:12)
4545
// REUSE_AST: s:13discriminator12testReuseAST5bytesySi_tFACL0_Sivp
46-
}
46+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class UserCollection {
2+
static let staticMember = "ABC"
3+
func test() {
4+
// RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):10 %s -- %s
5+
Self.staticMember
6+
}
7+
}

0 commit comments

Comments
 (0)