Skip to content

Commit d9382b1

Browse files
authored
Merge pull request #62362 from ahoppen/ahoppen/completion-like-cursor-info
[IDE] Implement completion-like cursor info for ValueDecls
2 parents e25d6a0 + 9a0a772 commit d9382b1

21 files changed

+689
-20
lines changed

include/swift/IDE/CursorInfo.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===--- CursorInfo.h --- ---------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_IDE_CURSORINFO_H
14+
#define SWIFT_IDE_CURSORINFO_H
15+
16+
#include "swift/AST/Type.h"
17+
#include "swift/Basic/LLVM.h"
18+
#include "swift/IDE/Utils.h"
19+
20+
namespace swift {
21+
class CodeCompletionCallbacksFactory;
22+
23+
namespace ide {
24+
25+
/// An abstract base class for consumers of context info results.
26+
class CursorInfoConsumer {
27+
public:
28+
virtual ~CursorInfoConsumer() {}
29+
virtual void handleResults(const ResolvedCursorInfo &) = 0;
30+
};
31+
32+
/// Create a factory for code completion callbacks.
33+
CodeCompletionCallbacksFactory *
34+
makeCursorInfoCallbacksFactory(CursorInfoConsumer &Consumer,
35+
SourceLoc RequestedLoc);
36+
37+
} // namespace ide
38+
} // namespace swift
39+
40+
#endif // SWIFT_IDE_CURSORINFO_H

include/swift/IDETool/CompletionInstance.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/IDE/CodeCompletionResult.h"
2020
#include "swift/IDE/CodeCompletionResultSink.h"
2121
#include "swift/IDE/ConformingMethodList.h"
22+
#include "swift/IDE/CursorInfo.h"
2223
#include "swift/IDE/ImportDepth.h"
2324
#include "swift/IDE/SwiftCompletionInfo.h"
2425
#include "swift/IDE/TypeContextInfo.h"
@@ -78,6 +79,14 @@ struct ConformingMethodListResults {
7879
bool DidReuseAST;
7980
};
8081

82+
/// The results returned from \c CompletionInstance::conformingMethodList.
83+
struct CursorInfoResults {
84+
/// The actual results. If \c nullptr, no results were found.
85+
const ResolvedCursorInfo *Result;
86+
/// Whether an AST was reused for the completion.
87+
bool DidReuseAST;
88+
};
89+
8190
/// Manages \c CompilerInstance for completion like operations.
8291
class CompletionInstance {
8392
struct Options {
@@ -152,7 +161,7 @@ class CompletionInstance {
152161
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
153162
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
154163
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
155-
DiagnosticConsumer *DiagC,
164+
DiagnosticConsumer *DiagC, bool IgnoreSwiftSourceInfo,
156165
std::shared_ptr<std::atomic<bool>> CancellationFlag,
157166
llvm::function_ref<void(CancellableResult<CompletionInstanceResult>)>
158167
Callback);
@@ -192,6 +201,14 @@ class CompletionInstance {
192201
std::shared_ptr<std::atomic<bool>> CancellationFlag,
193202
llvm::function_ref<void(CancellableResult<ConformingMethodListResults>)>
194203
Callback);
204+
205+
void cursorInfo(
206+
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
207+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
208+
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
209+
DiagnosticConsumer *DiagC,
210+
std::shared_ptr<std::atomic<bool>> CancellationFlag,
211+
llvm::function_ref<void(CancellableResult<CursorInfoResults>)> Callback);
195212
};
196213

197214
} // namespace ide

include/swift/Parse/Parser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ class Parser {
192192
}
193193

194194
bool isCodeCompletionFirstPass() const {
195-
return L->isCodeCompletion() && !CodeCompletion;
195+
return SourceMgr.hasCodeCompletionBuffer() && !CodeCompletion;
196196
}
197197

198198
bool allowTopLevelCode() const;

lib/IDE/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ add_swift_host_library(swiftIDE STATIC
1818
CompletionLookup.cpp
1919
CompletionOverrideLookup.cpp
2020
ConformingMethodList.cpp
21+
CursorInfo.cpp
2122
ExprCompletion.cpp
2223
ExprContextAnalysis.cpp
2324
Formatting.cpp

lib/IDE/CursorInfo.cpp

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
//===--- CursorInfo.cpp ---------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/IDE/CursorInfo.h"
14+
#include "ExprContextAnalysis.h"
15+
#include "swift/AST/ASTDemangler.h"
16+
#include "swift/AST/GenericEnvironment.h"
17+
#include "swift/AST/NameLookup.h"
18+
#include "swift/AST/USRGeneration.h"
19+
#include "swift/IDE/TypeCheckCompletionCallback.h"
20+
#include "swift/Parse/CodeCompletionCallbacks.h"
21+
#include "swift/Sema/ConstraintSystem.h"
22+
#include "swift/Sema/IDETypeChecking.h"
23+
#include "clang/AST/Attr.h"
24+
#include "clang/AST/Decl.h"
25+
#include "clang/Basic/Module.h"
26+
27+
using namespace swift;
28+
using namespace swift::constraints;
29+
using namespace ide;
30+
31+
namespace {
32+
33+
// MARK: - Utilities
34+
35+
void typeCheckDeclAndParentClosures(ValueDecl *VD) {
36+
// We need to type check any parent closures because their types are
37+
// encoded in the USR of ParentContexts in the cursor info response.
38+
auto DC = VD->getDeclContext();
39+
while (DC->getParent()) {
40+
if (auto Closure = dyn_cast<AbstractClosureExpr>(DC)) {
41+
if (Closure->getType().isNull()) {
42+
typeCheckASTNodeAtLoc(
43+
TypeCheckASTNodeAtLocContext::declContext(DC->getParent()),
44+
Closure->getLoc());
45+
}
46+
}
47+
DC = DC->getParent();
48+
}
49+
50+
typeCheckASTNodeAtLoc(
51+
TypeCheckASTNodeAtLocContext::declContext(VD->getDeclContext()),
52+
VD->getLoc());
53+
}
54+
55+
// MARK: - NodeFinderResults
56+
57+
enum class NodeFinderResultKind { Decl };
58+
59+
class NodeFinderResult {
60+
NodeFinderResultKind Kind;
61+
62+
protected:
63+
NodeFinderResult(NodeFinderResultKind Kind) : Kind(Kind) {}
64+
65+
public:
66+
NodeFinderResultKind getKind() const { return Kind; }
67+
};
68+
69+
class NodeFinderDeclResult : public NodeFinderResult {
70+
ValueDecl *ValueD;
71+
72+
public:
73+
NodeFinderDeclResult(ValueDecl *ValueD)
74+
: NodeFinderResult(NodeFinderResultKind::Decl), ValueD(ValueD) {}
75+
76+
ValueDecl *getDecl() const { return ValueD; }
77+
78+
static bool classof(const NodeFinderResult *Res) {
79+
return Res->getKind() == NodeFinderResultKind::Decl;
80+
}
81+
};
82+
83+
// MARK: - NodeFinder
84+
85+
/// Walks the AST, looking for a node at \c LocToResolve. While walking the
86+
/// AST, also gathers information about shorthand shadows.
87+
class NodeFinder : ASTWalker {
88+
SourceFile &SrcFile;
89+
SourceLoc LocToResolve;
90+
91+
/// As we are walking the tree, this variable is updated to the last seen
92+
/// DeclContext.
93+
SmallVector<DeclContext *> DeclContextStack;
94+
95+
/// The found node.
96+
std::unique_ptr<NodeFinderResult> Result;
97+
98+
/// If a decl shadows another decl using shorthand syntax (`[foo]` or
99+
/// `if let foo {`), this maps the re-declared variable to the one that is
100+
/// being shadowed.
101+
/// The transitive closure of shorthand shadowed decls should be reported as
102+
/// additional results in cursor info.
103+
llvm::DenseMap<ValueDecl *, ValueDecl *> ShorthandShadowedDecls;
104+
105+
public:
106+
NodeFinder(SourceFile &SrcFile, SourceLoc LocToResolve)
107+
: SrcFile(SrcFile), LocToResolve(LocToResolve),
108+
DeclContextStack({&SrcFile}) {}
109+
110+
void resolve() { SrcFile.walk(*this); }
111+
112+
std::unique_ptr<NodeFinderResult> takeResult() { return std::move(Result); }
113+
114+
/// Get the declarations that \p ShadowingDecl shadows using shorthand shadow
115+
/// syntax.
116+
SmallVector<ValueDecl *, 2>
117+
getShorthandShadowedDecls(ValueDecl *ShadowingDecl) {
118+
SmallVector<ValueDecl *, 2> Result;
119+
auto ShorthandShadowedDecl = ShorthandShadowedDecls[ShadowingDecl];
120+
while (ShorthandShadowedDecl) {
121+
Result.push_back(ShorthandShadowedDecl);
122+
ShorthandShadowedDecl = ShorthandShadowedDecls[ShorthandShadowedDecl];
123+
}
124+
return Result;
125+
}
126+
127+
private:
128+
SourceManager &getSourceMgr() const {
129+
return SrcFile.getASTContext().SourceMgr;
130+
}
131+
132+
/// The decl context that is currently being walked.
133+
DeclContext *getCurrentDeclContext() { return DeclContextStack.back(); }
134+
135+
bool rangeContainsLocToResolve(SourceRange Range) const {
136+
return Range.contains(LocToResolve);
137+
}
138+
139+
PreWalkAction walkToDeclPre(Decl *D) override {
140+
if (!rangeContainsLocToResolve(D->getSourceRangeIncludingAttrs())) {
141+
return PreWalkAction::SkipChildren;
142+
}
143+
144+
if (auto *newDC = dyn_cast<DeclContext>(D)) {
145+
DeclContextStack.push_back(newDC);
146+
}
147+
148+
if (D->getLoc() != LocToResolve) {
149+
return Action::Continue();
150+
}
151+
152+
if (auto VD = dyn_cast<ValueDecl>(D)) {
153+
if (VD->hasName()) {
154+
assert(Result == nullptr);
155+
Result = std::make_unique<NodeFinderDeclResult>(VD);
156+
return Action::Stop();
157+
}
158+
}
159+
160+
return Action::Continue();
161+
}
162+
163+
PostWalkAction walkToDeclPost(Decl *D) override {
164+
if (auto *newDC = dyn_cast<DeclContext>(D)) {
165+
assert(DeclContextStack.back() == newDC);
166+
DeclContextStack.pop_back();
167+
}
168+
return Action::Continue();
169+
}
170+
171+
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
172+
if (auto closure = dyn_cast<ClosureExpr>(E)) {
173+
DeclContextStack.push_back(closure);
174+
}
175+
176+
if (auto CaptureList = dyn_cast<CaptureListExpr>(E)) {
177+
for (auto ShorthandShadows :
178+
getShorthandShadows(CaptureList, getCurrentDeclContext())) {
179+
assert(ShorthandShadowedDecls.count(ShorthandShadows.first) == 0);
180+
ShorthandShadowedDecls[ShorthandShadows.first] =
181+
ShorthandShadows.second;
182+
}
183+
}
184+
185+
return Action::Continue(E);
186+
}
187+
188+
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
189+
if (auto *closure = dyn_cast<ClosureExpr>(E)) {
190+
assert(DeclContextStack.back() == closure);
191+
DeclContextStack.pop_back();
192+
}
193+
return Action::Continue(E);
194+
}
195+
196+
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
197+
if (auto CondStmt = dyn_cast<LabeledConditionalStmt>(S)) {
198+
for (auto ShorthandShadow :
199+
getShorthandShadows(CondStmt, getCurrentDeclContext())) {
200+
assert(ShorthandShadowedDecls.count(ShorthandShadow.first) == 0);
201+
ShorthandShadowedDecls[ShorthandShadow.first] = ShorthandShadow.second;
202+
}
203+
}
204+
return Action::Continue(S);
205+
}
206+
};
207+
208+
// MARK: - CursorInfoDoneParsingCallback
209+
210+
class CursorInfoDoneParsingCallback : public CodeCompletionCallbacks {
211+
CursorInfoConsumer &Consumer;
212+
SourceLoc RequestedLoc;
213+
214+
public:
215+
CursorInfoDoneParsingCallback(Parser &P, CursorInfoConsumer &Consumer,
216+
SourceLoc RequestedLoc)
217+
: CodeCompletionCallbacks(P), Consumer(Consumer),
218+
RequestedLoc(RequestedLoc) {}
219+
220+
std::unique_ptr<ResolvedCursorInfo>
221+
getDeclResult(NodeFinderDeclResult *DeclResult, SourceFile *SrcFile,
222+
NodeFinder &Finder) const {
223+
typeCheckDeclAndParentClosures(DeclResult->getDecl());
224+
auto CursorInfo = std::make_unique<ResolvedValueRefCursorInfo>(
225+
ResolvedCursorInfo(SrcFile), DeclResult->getDecl(),
226+
/*CtorTyRef=*/nullptr,
227+
/*ExtTyRef=*/nullptr, /*IsRef=*/false, /*Ty=*/Type(),
228+
/*ContainerType=*/Type());
229+
CursorInfo->setLoc(RequestedLoc);
230+
CursorInfo->setShorthandShadowedDecls(
231+
Finder.getShorthandShadowedDecls(DeclResult->getDecl()));
232+
return CursorInfo;
233+
}
234+
235+
void doneParsing(SourceFile *SrcFile) override {
236+
if (!SrcFile) {
237+
return;
238+
}
239+
NodeFinder Finder(*SrcFile, RequestedLoc);
240+
Finder.resolve();
241+
auto Result = Finder.takeResult();
242+
if (!Result) {
243+
return;
244+
}
245+
std::unique_ptr<ResolvedCursorInfo> CursorInfo;
246+
switch (Result->getKind()) {
247+
case NodeFinderResultKind::Decl:
248+
CursorInfo = getDeclResult(cast<NodeFinderDeclResult>(Result.get()),
249+
SrcFile, Finder);
250+
break;
251+
}
252+
if (Result) {
253+
Consumer.handleResults(*CursorInfo);
254+
}
255+
}
256+
};
257+
258+
} // anonymous namespace.
259+
260+
CodeCompletionCallbacksFactory *
261+
swift::ide::makeCursorInfoCallbacksFactory(CursorInfoConsumer &Consumer,
262+
SourceLoc RequestedLoc) {
263+
class CursorInfoCallbacksFactoryImpl : public CodeCompletionCallbacksFactory {
264+
CursorInfoConsumer &Consumer;
265+
SourceLoc RequestedLoc;
266+
267+
public:
268+
CursorInfoCallbacksFactoryImpl(CursorInfoConsumer &Consumer,
269+
SourceLoc RequestedLoc)
270+
: Consumer(Consumer), RequestedLoc(RequestedLoc) {}
271+
272+
CodeCompletionCallbacks *createCodeCompletionCallbacks(Parser &P) override {
273+
return new CursorInfoDoneParsingCallback(P, Consumer, RequestedLoc);
274+
}
275+
};
276+
277+
return new CursorInfoCallbacksFactoryImpl(Consumer, RequestedLoc);
278+
}

0 commit comments

Comments
 (0)