Skip to content

Commit fd72674

Browse files
committed
[Code Completion] Split code completion results into context free part that can be cached and contextual part displayed to the user
This allows makes the distinction between cachable and non-cachable properties cleaner and allows us to more easily compute contextual information (like type relations) for cached items later.
1 parent bad9679 commit fd72674

11 files changed

+691
-431
lines changed

include/swift/IDE/CodeCompletion.h

Lines changed: 361 additions & 190 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: 182 additions & 145 deletions
Large diffs are not rendered by default.

lib/IDE/CodeCompletionCache.cpp

Lines changed: 20 additions & 37 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,20 +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) {
200200
auto kind = static_cast<CodeCompletionResultKind>(*cursor++);
201-
auto declKind = static_cast<CodeCompletionDeclKind>(*cursor++);
201+
auto associatedKind = static_cast<uint8_t>(*cursor++);
202202
auto opKind = static_cast<CodeCompletionOperatorKind>(*cursor++);
203-
auto context = static_cast<SemanticContextKind>(*cursor++);
204-
auto notRecommended = static_cast<NotRecommendedReason>(*cursor++);
203+
auto notRecommended =
204+
static_cast<ContextFreeNotRecommendedReason>(*cursor++);
205205
auto diagSeverity =
206206
static_cast<CodeCompletionDiagnosticSeverity>(*cursor++);
207207
auto isSystem = static_cast<bool>(*cursor++);
208-
auto numBytesToErase = static_cast<unsigned>(*cursor++);
209208
auto chunkIndex = read32le(cursor);
210209
auto moduleIndex = read32le(cursor);
211210
auto briefDocIndex = read32le(cursor);
@@ -222,21 +221,14 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
222221
auto briefDocComment = getString(briefDocIndex);
223222
auto diagMessage = getString(diagMessageIndex);
224223

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

239-
V.Sink.Results.push_back(result);
231+
V.Results.push_back(result);
240232
}
241233

242234
return true;
@@ -342,27 +334,18 @@ static void writeCachedModule(llvm::raw_ostream &out,
342334
// RESULTS
343335
{
344336
endian::Writer LE(results, little);
345-
for (CodeCompletionResult *R : V.Sink.Results) {
346-
assert(!R->getFlair().toRaw() && "Any flairs should not be cached");
347-
assert(R->getNotRecommendedReason() !=
348-
NotRecommendedReason::InvalidAsyncContext &&
349-
"InvalidAsyncContext is decl context specific, cannot be cached");
350-
337+
for (const ContextFreeCodeCompletionResult *R : V.Results) {
351338
// FIXME: compress bitfield
352339
LE.write(static_cast<uint8_t>(R->getKind()));
353-
if (R->getKind() == CodeCompletionResultKind::Declaration)
354-
LE.write(static_cast<uint8_t>(R->getAssociatedDeclKind()));
355-
else
356-
LE.write(static_cast<uint8_t>(~0u));
357-
if (R->isOperator())
358-
LE.write(static_cast<uint8_t>(R->getOperatorKind()));
359-
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 {
360344
LE.write(static_cast<uint8_t>(CodeCompletionOperatorKind::None));
361-
LE.write(static_cast<uint8_t>(R->getSemanticContext()));
345+
}
362346
LE.write(static_cast<uint8_t>(R->getNotRecommendedReason()));
363347
LE.write(static_cast<uint8_t>(R->getDiagnosticSeverity()));
364348
LE.write(static_cast<uint8_t>(R->isSystem()));
365-
LE.write(static_cast<uint8_t>(R->getNumBytesToErase()));
366349
LE.write(
367350
static_cast<uint32_t>(addCompletionString(R->getCompletionString())));
368351
LE.write(addString(R->getModuleName())); // index into strings

lib/IDE/CodeCompletionDiagnostics.cpp

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -144,33 +144,43 @@ bool CodeCompletionDiagnostics::getDiagnosticForDeprecated(
144144

145145
} // namespace
146146

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

lib/IDE/CodeCompletionDiagnostics.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +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(NotRecommendedReason reason, const ValueDecl *D,
27-
CodeCompletionDiagnosticSeverity &severity,
28-
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);
2939

3040
} // namespace ide
3141
} // namespace swift

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@ class CodeCompletionResultBuilder {
8989
CodeCompletionResult::ExpectedTypeRelation ExpectedTypeRelation =
9090
CodeCompletionResult::ExpectedTypeRelation::Unknown;
9191
bool Cancelled = false;
92-
NotRecommendedReason NotRecReason = NotRecommendedReason::None;
92+
ContextFreeNotRecommendedReason ContextFreeNotRecReason =
93+
ContextFreeNotRecommendedReason::None;
94+
ContextualNotRecommendedReason ContextualNotRecReason =
95+
ContextualNotRecommendedReason::None;
9396
StringRef BriefDocComment;
9497

9598
void addChunkWithText(CodeCompletionString::Chunk::ChunkKind Kind,
@@ -145,10 +148,11 @@ class CodeCompletionResultBuilder {
145148

146149
void setLiteralKind(CodeCompletionLiteralKind kind) { LiteralKind = kind; }
147150
void setKeywordKind(CodeCompletionKeywordKind kind) { KeywordKind = kind; }
148-
void setNotRecommended(NotRecommendedReason Reason) { NotRecReason = Reason; }
149-
150-
void setSemanticContext(SemanticContextKind Kind) {
151-
SemanticContext = Kind;
151+
void setContextFreeNotRecommended(ContextFreeNotRecommendedReason Reason) {
152+
ContextFreeNotRecReason = Reason;
153+
}
154+
void setContextualNotRecommended(ContextualNotRecommendedReason Reason) {
155+
ContextualNotRecReason = Reason;
152156
}
153157

154158
void addFlair(CodeCompletionFlair Options) {

tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,19 @@ bool SourceKit::CodeCompletion::addCustomCompletions(
154154
auto chunk = Chunk::createWithText(Chunk::ChunkKind::Text, 0, nameCopy);
155155
auto *completionString =
156156
CodeCompletionString::create(sink.allocator, chunk);
157+
auto *contextFreeResult =
158+
new (sink.allocator) ContextFreeCodeCompletionResult(
159+
CodeCompletionResultKind::Pattern, completionString,
160+
CodeCompletionOperatorKind::None, /*BriefDocComment=*/"",
161+
ContextFreeNotRecommendedReason::None,
162+
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"");
157163
auto *swiftResult = new (sink.allocator) CodeCompletion::SwiftResult(
158-
CodeCompletionResultKind::Pattern, SemanticContextKind::Local,
164+
*contextFreeResult, SemanticContextKind::Local,
159165
CodeCompletionFlairBit::ExpressionSpecific,
160-
/*NumBytesToErase=*/0, completionString,
161-
CodeCompletionResult::ExpectedTypeRelation::Unknown);
166+
/*NumBytesToErase=*/0,
167+
CodeCompletionResult::ExpectedTypeRelation::Unknown,
168+
ContextualNotRecommendedReason::None,
169+
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"");
162170

163171
CompletionBuilder builder(sink, *swiftResult);
164172
builder.setCustomKind(customCompletion.Kind);
@@ -1176,18 +1184,26 @@ Completion *CompletionBuilder::finish() {
11761184
if (base.isOperator())
11771185
opKind = base.getOperatorKind();
11781186

1179-
if (base.getKind() == CodeCompletionResultKind::Declaration) {
1180-
newBase = new (sink.allocator) SwiftResult(
1181-
semanticContext, flair, base.getNumBytesToErase(), completionString,
1182-
base.getAssociatedDeclKind(), base.isSystem(), base.getModuleName(),
1183-
base.getNotRecommendedReason(), base.getDiagnosticSeverity(),
1184-
base.getDiagnosticMessage(), base.getBriefDocComment(),
1185-
base.getAssociatedUSRs(), typeRelation, opKind);
1186-
} else {
1187-
newBase = new (sink.allocator) SwiftResult(
1188-
base.getKind(), semanticContext, flair, base.getNumBytesToErase(),
1189-
completionString, typeRelation, opKind);
1190-
}
1187+
const ContextFreeCodeCompletionResult &contextFreeBase =
1188+
base.getContextFreeResult();
1189+
1190+
ContextFreeCodeCompletionResult *contextFreeResult =
1191+
new (sink.allocator) ContextFreeCodeCompletionResult(
1192+
contextFreeBase.getKind(),
1193+
contextFreeBase.getOpaqueAssociatedKind(), opKind,
1194+
contextFreeBase.isSystem(), completionString,
1195+
contextFreeBase.getModuleName(),
1196+
contextFreeBase.getBriefDocComment(),
1197+
contextFreeBase.getAssociatedUSRs(),
1198+
contextFreeBase.getNotRecommendedReason(),
1199+
contextFreeBase.getDiagnosticSeverity(),
1200+
contextFreeBase.getDiagnosticMessage());
1201+
1202+
newBase = new (sink.allocator) SwiftResult(
1203+
*contextFreeResult, semanticContext, flair, base.getNumBytesToErase(),
1204+
typeRelation, base.getContextualNotRecommendedReason(),
1205+
base.getContextualDiagnosticSeverity(),
1206+
base.getContextualDiagnosticMessage());
11911207

11921208
llvm::raw_svector_ostream OSS(nameStorage);
11931209
ide::printCodeCompletionResultFilterName(*newBase, OSS);

tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -919,12 +919,20 @@ static void transformAndForwardResults(
919919
auto buildInnerResult = [&](ArrayRef<CodeCompletionString::Chunk> chunks) {
920920
auto *completionString =
921921
CodeCompletionString::create(innerSink.allocator, chunks);
922+
ContextFreeCodeCompletionResult *contextFreeResult =
923+
new (innerSink.allocator) ContextFreeCodeCompletionResult(
924+
CodeCompletionResultKind::BuiltinOperator, completionString,
925+
CodeCompletionOperatorKind::None,
926+
/*BriefDocComment=*/"", ContextFreeNotRecommendedReason::None,
927+
CodeCompletionDiagnosticSeverity::None,
928+
/*DiagnosticMessage=*/"");
922929
auto *paren = new (innerSink.allocator) CodeCompletion::SwiftResult(
923-
CodeCompletionResultKind::BuiltinOperator,
924-
SemanticContextKind::CurrentNominal,
930+
*contextFreeResult, SemanticContextKind::CurrentNominal,
925931
CodeCompletionFlairBit::ExpressionSpecific,
926-
exactMatch ? exactMatch->getNumBytesToErase() : 0, completionString,
927-
CodeCompletionResult::ExpectedTypeRelation::NotApplicable);
932+
exactMatch ? exactMatch->getNumBytesToErase() : 0,
933+
CodeCompletionResult::ExpectedTypeRelation::NotApplicable,
934+
ContextualNotRecommendedReason::None,
935+
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"");
928936

929937
SwiftCompletionInfo info;
930938
std::vector<Completion *> extended = extendCompletions(

tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ class SemanticAnnotator : public SourceEntityWalker {
983983

984984
unsigned ByteOffset = SM.getLocOffsetInBuffer(Range.getStart(), BufferID);
985985
unsigned Length = Range.getByteLength();
986-
auto Kind = CodeCompletionResult::getCodeCompletionDeclKind(D);
986+
auto Kind = ContextFreeCodeCompletionResult::getCodeCompletionDeclKind(D);
987987
bool IsSystem = D->getModuleContext()->isSystemModule();
988988
SemaToks.emplace_back(Kind, ByteOffset, Length, IsRef, IsSystem);
989989
}

tools/swift-ide-test/swift-ide-test.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4179,10 +4179,25 @@ int main(int argc, char *argv[]) {
41794179
// FIXME: error?
41804180
continue;
41814181
}
4182+
// Make contextual CodeCompletionResults from the
4183+
// ContextFreeCodeCompletionResults so we can print them.
4184+
std::vector<CodeCompletionResult *> contextualResults;
4185+
contextualResults.reserve(resultsOpt->get()->Results.size());
4186+
for (auto contextFreeResult : resultsOpt->get()->Results) {
4187+
// We are leaking these results but it doesn't matter since the process
4188+
// just terminates afterwards anyway.
4189+
auto contextualResult = new CodeCompletionResult(
4190+
*contextFreeResult, SemanticContextKind::OtherModule,
4191+
CodeCompletionFlair(),
4192+
/*numBytesToErase=*/0,
4193+
CodeCompletionResult::ExpectedTypeRelation::Unknown,
4194+
ContextualNotRecommendedReason::None,
4195+
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"");
4196+
contextualResults.push_back(contextualResult);
4197+
}
41824198
printCodeCompletionResultsImpl(
4183-
resultsOpt->get()->Sink.Results, llvm::outs(),
4184-
options::CodeCompletionKeywords, options::CodeCompletionComments,
4185-
options::CodeCompletionSourceText,
4199+
contextualResults, llvm::outs(), options::CodeCompletionKeywords,
4200+
options::CodeCompletionComments, options::CodeCompletionSourceText,
41864201
options::CodeCompletionAnnotateResults);
41874202
}
41884203

0 commit comments

Comments
 (0)