Skip to content

Commit e8c76a1

Browse files
authored
Merge pull request #40471 from ahoppen/pr/split-context-free-codecompletion-result
[Code Completion] Split code completion results into context free part that can be cached and contextual part displayed to the user
2 parents e8830fc + fd72674 commit e8c76a1

13 files changed

+848
-615
lines changed

include/swift/IDE/CodeCompletion.h

Lines changed: 384 additions & 209 deletions
Large diffs are not rendered by default.

include/swift/IDE/CodeCompletionCache.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,14 @@ class CodeCompletionCache {
6161
};
6262

6363
struct Value : public llvm::ThreadSafeRefCountedBase<Value> {
64+
/// The allocator used to allocate the results stored in this cache.
65+
std::shared_ptr<llvm::BumpPtrAllocator> Allocator;
66+
6467
llvm::sys::TimePoint<> ModuleModificationTime;
65-
CodeCompletionResultSink Sink;
68+
69+
std::vector<const ContextFreeCodeCompletionResult *> Results;
70+
71+
Value() : Allocator(std::make_shared<llvm::BumpPtrAllocator>()) {}
6672
};
6773
using ValueRefCntPtr = llvm::IntrusiveRefCntPtr<Value>;
6874

lib/IDE/CodeCompletion.cpp

Lines changed: 283 additions & 269 deletions
Large diffs are not rendered by default.

lib/IDE/CodeCompletionCache.cpp

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace swift {
4141
struct CacheValueCostInfo<swift::ide::CodeCompletionCacheImpl::Value> {
4242
static size_t
4343
getCost(const swift::ide::CodeCompletionCacheImpl::Value &V) {
44-
return V.Sink.Allocator->getTotalMemory();
44+
return V.Allocator->getTotalMemory();
4545
}
4646
};
4747
} // namespace sys
@@ -102,7 +102,7 @@ CodeCompletionCache::~CodeCompletionCache() {}
102102
///
103103
/// This should be incremented any time we commit a change to the format of the
104104
/// cached results. This isn't expected to change very often.
105-
static constexpr uint32_t onDiskCompletionCacheVersion = 3; // Removed "source file path".
105+
static constexpr uint32_t onDiskCompletionCacheVersion = 4; // Store ContextFreeCodeCompletionResults in cache
106106

107107
/// Deserializes CodeCompletionResults from \p in and stores them in \p V.
108108
/// \see writeCacheModule.
@@ -166,7 +166,7 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
166166

167167
const char *p = strings + index;
168168
auto size = read32le(p);
169-
auto str = copyString(*V.Sink.Allocator, StringRef(p, size));
169+
auto str = copyString(*V.Allocator, StringRef(p, size));
170170
knownStrings[index] = str;
171171
return str;
172172
};
@@ -192,21 +192,19 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
192192
}
193193
}
194194

195-
return CodeCompletionString::create(*V.Sink.Allocator, chunkList);
195+
return CodeCompletionString::create(*V.Allocator, chunkList);
196196
};
197197

198198
// RESULTS
199199
while (cursor != resultEnd) {
200-
auto kind = static_cast<CodeCompletionResult::ResultKind>(*cursor++);
201-
auto declKind = static_cast<CodeCompletionDeclKind>(*cursor++);
200+
auto kind = static_cast<CodeCompletionResultKind>(*cursor++);
201+
auto associatedKind = static_cast<uint8_t>(*cursor++);
202202
auto opKind = static_cast<CodeCompletionOperatorKind>(*cursor++);
203-
auto context = static_cast<SemanticContextKind>(*cursor++);
204203
auto notRecommended =
205-
static_cast<CodeCompletionResult::NotRecommendedReason>(*cursor++);
204+
static_cast<ContextFreeNotRecommendedReason>(*cursor++);
206205
auto diagSeverity =
207206
static_cast<CodeCompletionDiagnosticSeverity>(*cursor++);
208207
auto isSystem = static_cast<bool>(*cursor++);
209-
auto numBytesToErase = static_cast<unsigned>(*cursor++);
210208
auto chunkIndex = read32le(cursor);
211209
auto moduleIndex = read32le(cursor);
212210
auto briefDocIndex = read32le(cursor);
@@ -223,21 +221,14 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
223221
auto briefDocComment = getString(briefDocIndex);
224222
auto diagMessage = getString(diagMessageIndex);
225223

226-
CodeCompletionResult *result = nullptr;
227-
if (kind == CodeCompletionResult::ResultKind::Declaration) {
228-
result = new (*V.Sink.Allocator) CodeCompletionResult(
229-
context, CodeCompletionFlair(), numBytesToErase, string, declKind,
230-
isSystem, moduleName, notRecommended, diagSeverity, diagMessage,
231-
briefDocComment,
232-
copyArray(*V.Sink.Allocator, ArrayRef<StringRef>(assocUSRs)),
233-
CodeCompletionResult::ExpectedTypeRelation::Unknown, opKind);
234-
} else {
235-
result = new (*V.Sink.Allocator) CodeCompletionResult(
236-
kind, context, CodeCompletionFlair(), numBytesToErase, string,
237-
CodeCompletionResult::ExpectedTypeRelation::NotApplicable, opKind);
238-
}
224+
ContextFreeCodeCompletionResult *result =
225+
new (*V.Allocator) ContextFreeCodeCompletionResult(
226+
kind, associatedKind, opKind, isSystem, string, moduleName,
227+
briefDocComment,
228+
copyArray(*V.Allocator, ArrayRef<StringRef>(assocUSRs)),
229+
notRecommended, diagSeverity, diagMessage);
239230

240-
V.Sink.Results.push_back(result);
231+
V.Results.push_back(result);
241232
}
242233

243234
return true;
@@ -343,27 +334,18 @@ static void writeCachedModule(llvm::raw_ostream &out,
343334
// RESULTS
344335
{
345336
endian::Writer LE(results, little);
346-
for (CodeCompletionResult *R : V.Sink.Results) {
347-
assert(!R->getFlair().toRaw() && "Any flairs should not be cached");
348-
assert(R->getNotRecommendedReason() !=
349-
CodeCompletionResult::NotRecommendedReason::InvalidAsyncContext &&
350-
"InvalidAsyncContext is decl context specific, cannot be cached");
351-
337+
for (const ContextFreeCodeCompletionResult *R : V.Results) {
352338
// FIXME: compress bitfield
353339
LE.write(static_cast<uint8_t>(R->getKind()));
354-
if (R->getKind() == CodeCompletionResult::ResultKind::Declaration)
355-
LE.write(static_cast<uint8_t>(R->getAssociatedDeclKind()));
356-
else
357-
LE.write(static_cast<uint8_t>(~0u));
358-
if (R->isOperator())
359-
LE.write(static_cast<uint8_t>(R->getOperatorKind()));
360-
else
340+
LE.write(static_cast<uint8_t>(R->getOpaqueAssociatedKind()));
341+
if (R->isOperator()) {
342+
LE.write(static_cast<uint8_t>(R->getKnownOperatorKind()));
343+
} else {
361344
LE.write(static_cast<uint8_t>(CodeCompletionOperatorKind::None));
362-
LE.write(static_cast<uint8_t>(R->getSemanticContext()));
345+
}
363346
LE.write(static_cast<uint8_t>(R->getNotRecommendedReason()));
364347
LE.write(static_cast<uint8_t>(R->getDiagnosticSeverity()));
365348
LE.write(static_cast<uint8_t>(R->isSystem()));
366-
LE.write(static_cast<uint8_t>(R->getNumBytesToErase()));
367349
LE.write(
368350
static_cast<uint32_t>(addCompletionString(R->getCompletionString())));
369351
LE.write(addString(R->getModuleName())); // index into strings

lib/IDE/CodeCompletionDiagnostics.cpp

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -144,37 +144,44 @@ bool CodeCompletionDiagnostics::getDiagnosticForDeprecated(
144144

145145
} // namespace
146146

147-
bool swift::ide::getCompletionDiagnostics(
148-
CodeCompletionResult::NotRecommendedReason reason, const ValueDecl *D,
149-
CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out) {
150-
using NotRecommendedReason = CodeCompletionResult::NotRecommendedReason;
151-
152-
ASTContext &ctx = D->getASTContext();
153-
154-
CodeCompletionDiagnostics Diag(ctx);
155-
switch (reason) {
156-
case NotRecommendedReason::Deprecated:
157-
case NotRecommendedReason::SoftDeprecated:
158-
return Diag.getDiagnosticForDeprecated(D, severity, Out);
159-
case NotRecommendedReason::InvalidAsyncContext:
160-
// FIXME: Could we use 'diag::async_in_nonasync_function'?
161-
return Diag.getDiagnostics(severity, Out, diag::ide_async_in_nonasync_context,
162-
D->getName());
163-
case NotRecommendedReason::CrossActorReference:
164-
return Diag.getDiagnostics(severity, Out, diag::ide_cross_actor_reference_swift5,
165-
D->getName());
166-
case NotRecommendedReason::RedundantImport:
167-
return Diag.getDiagnostics(severity, Out, diag::ide_redundant_import,
168-
D->getName());
169-
case NotRecommendedReason::RedundantImportIndirect:
170-
return Diag.getDiagnostics(severity, Out, diag::ide_redundant_import_indirect,
171-
D->getName());
172-
case NotRecommendedReason::VariableUsedInOwnDefinition:
173-
return Diag.getDiagnostics(severity, Out, diag::recursive_accessor_reference,
174-
D->getName().getBaseIdentifier(), /*"getter"*/ 0);
175-
case NotRecommendedReason::None:
147+
bool swift::ide::getContextFreeCompletionDiagnostics(
148+
ContextFreeNotRecommendedReason Reason, const ValueDecl *D,
149+
CodeCompletionDiagnosticSeverity &Severity, llvm::raw_ostream &Out) {
150+
CodeCompletionDiagnostics Diag(D->getASTContext());
151+
switch (Reason) {
152+
case ContextFreeNotRecommendedReason::Deprecated:
153+
case ContextFreeNotRecommendedReason::SoftDeprecated:
154+
return Diag.getDiagnosticForDeprecated(D, Severity, Out);
155+
case ContextFreeNotRecommendedReason::None:
176156
llvm_unreachable("invalid not recommended reason");
177157
}
178158
return true;
179159
}
180160

161+
bool swift::ide::getContextualCompletionDiagnostics(
162+
ContextualNotRecommendedReason Reason, const ValueDecl *D,
163+
CodeCompletionDiagnosticSeverity &Severity, llvm::raw_ostream &Out) {
164+
CodeCompletionDiagnostics Diag(D->getASTContext());
165+
switch (Reason) {
166+
case ContextualNotRecommendedReason::InvalidAsyncContext:
167+
// FIXME: Could we use 'diag::async_in_nonasync_function'?
168+
return Diag.getDiagnostics(
169+
Severity, Out, diag::ide_async_in_nonasync_context, D->getName());
170+
case ContextualNotRecommendedReason::CrossActorReference:
171+
return Diag.getDiagnostics(
172+
Severity, Out, diag::ide_cross_actor_reference_swift5, D->getName());
173+
case ContextualNotRecommendedReason::RedundantImport:
174+
return Diag.getDiagnostics(Severity, Out, diag::ide_redundant_import,
175+
D->getName());
176+
case ContextualNotRecommendedReason::RedundantImportIndirect:
177+
return Diag.getDiagnostics(
178+
Severity, Out, diag::ide_redundant_import_indirect, D->getName());
179+
case ContextualNotRecommendedReason::VariableUsedInOwnDefinition:
180+
return Diag.getDiagnostics(
181+
Severity, Out, diag::recursive_accessor_reference,
182+
D->getName().getBaseIdentifier(), /*"getter"*/ 0);
183+
case ContextualNotRecommendedReason::None:
184+
llvm_unreachable("invalid not recommended reason");
185+
}
186+
return true;
187+
}

lib/IDE/CodeCompletionDiagnostics.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,21 @@ class ValueDecl;
2121

2222
namespace ide {
2323

24-
/// Populate \p severity and \p Out with the diagnostics for \p D.
24+
/// Populate \p severity and \p Out with the context-free diagnostics for \p D.
25+
/// See \c NotRecommendedReason for an explaination of context-free vs.
26+
/// contextual diagnostics.
2527
/// Returns \c true if it fails to generate the diagnostics.
26-
bool getCompletionDiagnostics(CodeCompletionResult::NotRecommendedReason reason,
27-
const ValueDecl *D,
28-
CodeCompletionDiagnosticSeverity &severity,
29-
llvm::raw_ostream &Out);
28+
bool getContextFreeCompletionDiagnostics(
29+
ContextFreeNotRecommendedReason reason, const ValueDecl *D,
30+
CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out);
31+
32+
/// Populate \p severity and \p Out with the contextual diagnostics for \p D.
33+
/// See \c NotRecommendedReason for an explaination of context-free vs.
34+
/// contextual diagnostics.
35+
/// Returns \c true if it fails to generate the diagnostics.
36+
bool getContextualCompletionDiagnostics(
37+
ContextualNotRecommendedReason reason, const ValueDecl *D,
38+
CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out);
3039

3140
} // namespace ide
3241
} // namespace swift

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class CodeCompletionResultBuilder {
7474
friend CodeCompletionStringPrinter;
7575

7676
CodeCompletionResultSink &Sink;
77-
CodeCompletionResult::ResultKind Kind;
77+
CodeCompletionResultKind Kind;
7878
SemanticContextKind SemanticContext;
7979
CodeCompletionFlair Flair;
8080
unsigned NumBytesToErase = 0;
@@ -89,8 +89,10 @@ class CodeCompletionResultBuilder {
8989
CodeCompletionResult::ExpectedTypeRelation ExpectedTypeRelation =
9090
CodeCompletionResult::ExpectedTypeRelation::Unknown;
9191
bool Cancelled = false;
92-
CodeCompletionResult::NotRecommendedReason NotRecReason =
93-
CodeCompletionResult::NotRecommendedReason::None;
92+
ContextFreeNotRecommendedReason ContextFreeNotRecReason =
93+
ContextFreeNotRecommendedReason::None;
94+
ContextualNotRecommendedReason ContextualNotRecReason =
95+
ContextualNotRecommendedReason::None;
9496
StringRef BriefDocComment;
9597

9698
void addChunkWithText(CodeCompletionString::Chunk::ChunkKind Kind,
@@ -117,7 +119,7 @@ class CodeCompletionResultBuilder {
117119

118120
public:
119121
CodeCompletionResultBuilder(CodeCompletionResultSink &Sink,
120-
CodeCompletionResult::ResultKind Kind,
122+
CodeCompletionResultKind Kind,
121123
SemanticContextKind SemanticContext,
122124
const ExpectedTypeContext &declTypeContext)
123125
: Sink(Sink), Kind(Kind), SemanticContext(SemanticContext),
@@ -146,12 +148,11 @@ class CodeCompletionResultBuilder {
146148

147149
void setLiteralKind(CodeCompletionLiteralKind kind) { LiteralKind = kind; }
148150
void setKeywordKind(CodeCompletionKeywordKind kind) { KeywordKind = kind; }
149-
void setNotRecommended(CodeCompletionResult::NotRecommendedReason Reason) {
150-
NotRecReason = Reason;
151+
void setContextFreeNotRecommended(ContextFreeNotRecommendedReason Reason) {
152+
ContextFreeNotRecReason = Reason;
151153
}
152-
153-
void setSemanticContext(SemanticContextKind Kind) {
154-
SemanticContext = Kind;
154+
void setContextualNotRecommended(ContextualNotRecommendedReason Reason) {
155+
ContextualNotRecReason = Reason;
155156
}
156157

157158
void addFlair(CodeCompletionFlair Options) {

lib/IDE/REPLCodeCompletion.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ static void toDisplayString(CodeCompletionResult *Result,
122122
}
123123
if (C.is(CodeCompletionString::Chunk::ChunkKind::TypeAnnotation) ||
124124
C.is(CodeCompletionString::Chunk::ChunkKind::TypeAnnotationBegin)) {
125-
if (Result->getKind() == CodeCompletionResult::ResultKind::Declaration) {
125+
if (Result->getKind() == CodeCompletionResultKind::Declaration) {
126126
switch (Result->getAssociatedDeclKind()) {
127127
case CodeCompletionDeclKind::Module:
128128
case CodeCompletionDeclKind::PrecedenceGroup:

tools/SourceKit/lib/SwiftLang/CodeCompletion.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ using swift::ide::CodeCompletionFlair;
2727
using swift::ide::CodeCompletionKeywordKind;
2828
using swift::ide::CodeCompletionLiteralKind;
2929
using swift::ide::CodeCompletionOperatorKind;
30+
using swift::ide::CodeCompletionResultKind;
3031
using swift::ide::CodeCompletionString;
3132
using swift::ide::SemanticContextKind;
3233
using SwiftResult = swift::ide::CodeCompletionResult;
@@ -110,7 +111,9 @@ class Completion {
110111

111112
// MARK: Methods that forward to the SwiftResult
112113

113-
SwiftResult::ResultKind getKind() const { return getSwiftResult().getKind(); }
114+
CodeCompletionResultKind getKind() const {
115+
return getSwiftResult().getKind();
116+
}
114117

115118
CodeCompletionDeclKind getAssociatedDeclKind() const {
116119
return getSwiftResult().getAssociatedDeclKind();

0 commit comments

Comments
 (0)