Skip to content

[Code Completion] Split code completion results into context free part that can be cached and contextual part displayed to the user #40471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
593 changes: 384 additions & 209 deletions include/swift/IDE/CodeCompletion.h

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion include/swift/IDE/CodeCompletionCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,14 @@ class CodeCompletionCache {
};

struct Value : public llvm::ThreadSafeRefCountedBase<Value> {
/// The allocator used to allocate the results stored in this cache.
std::shared_ptr<llvm::BumpPtrAllocator> Allocator;

llvm::sys::TimePoint<> ModuleModificationTime;
CodeCompletionResultSink Sink;

std::vector<const ContextFreeCodeCompletionResult *> Results;

Value() : Allocator(std::make_shared<llvm::BumpPtrAllocator>()) {}
};
using ValueRefCntPtr = llvm::IntrusiveRefCntPtr<Value>;

Expand Down
552 changes: 283 additions & 269 deletions lib/IDE/CodeCompletion.cpp

Large diffs are not rendered by default.

58 changes: 20 additions & 38 deletions lib/IDE/CodeCompletionCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace swift {
struct CacheValueCostInfo<swift::ide::CodeCompletionCacheImpl::Value> {
static size_t
getCost(const swift::ide::CodeCompletionCacheImpl::Value &V) {
return V.Sink.Allocator->getTotalMemory();
return V.Allocator->getTotalMemory();
}
};
} // namespace sys
Expand Down Expand Up @@ -102,7 +102,7 @@ CodeCompletionCache::~CodeCompletionCache() {}
///
/// This should be incremented any time we commit a change to the format of the
/// cached results. This isn't expected to change very often.
static constexpr uint32_t onDiskCompletionCacheVersion = 3; // Removed "source file path".
static constexpr uint32_t onDiskCompletionCacheVersion = 4; // Store ContextFreeCodeCompletionResults in cache

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

const char *p = strings + index;
auto size = read32le(p);
auto str = copyString(*V.Sink.Allocator, StringRef(p, size));
auto str = copyString(*V.Allocator, StringRef(p, size));
knownStrings[index] = str;
return str;
};
Expand All @@ -192,21 +192,19 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
}
}

return CodeCompletionString::create(*V.Sink.Allocator, chunkList);
return CodeCompletionString::create(*V.Allocator, chunkList);
};

// RESULTS
while (cursor != resultEnd) {
auto kind = static_cast<CodeCompletionResult::ResultKind>(*cursor++);
auto declKind = static_cast<CodeCompletionDeclKind>(*cursor++);
auto kind = static_cast<CodeCompletionResultKind>(*cursor++);
auto associatedKind = static_cast<uint8_t>(*cursor++);
auto opKind = static_cast<CodeCompletionOperatorKind>(*cursor++);
auto context = static_cast<SemanticContextKind>(*cursor++);
auto notRecommended =
static_cast<CodeCompletionResult::NotRecommendedReason>(*cursor++);
static_cast<ContextFreeNotRecommendedReason>(*cursor++);
auto diagSeverity =
static_cast<CodeCompletionDiagnosticSeverity>(*cursor++);
auto isSystem = static_cast<bool>(*cursor++);
auto numBytesToErase = static_cast<unsigned>(*cursor++);
auto chunkIndex = read32le(cursor);
auto moduleIndex = read32le(cursor);
auto briefDocIndex = read32le(cursor);
Expand All @@ -223,21 +221,14 @@ static bool readCachedModule(llvm::MemoryBuffer *in,
auto briefDocComment = getString(briefDocIndex);
auto diagMessage = getString(diagMessageIndex);

CodeCompletionResult *result = nullptr;
if (kind == CodeCompletionResult::ResultKind::Declaration) {
result = new (*V.Sink.Allocator) CodeCompletionResult(
context, CodeCompletionFlair(), numBytesToErase, string, declKind,
isSystem, moduleName, notRecommended, diagSeverity, diagMessage,
briefDocComment,
copyArray(*V.Sink.Allocator, ArrayRef<StringRef>(assocUSRs)),
CodeCompletionResult::ExpectedTypeRelation::Unknown, opKind);
} else {
result = new (*V.Sink.Allocator) CodeCompletionResult(
kind, context, CodeCompletionFlair(), numBytesToErase, string,
CodeCompletionResult::ExpectedTypeRelation::NotApplicable, opKind);
}
ContextFreeCodeCompletionResult *result =
new (*V.Allocator) ContextFreeCodeCompletionResult(
kind, associatedKind, opKind, isSystem, string, moduleName,
briefDocComment,
copyArray(*V.Allocator, ArrayRef<StringRef>(assocUSRs)),
notRecommended, diagSeverity, diagMessage);

V.Sink.Results.push_back(result);
V.Results.push_back(result);
}

return true;
Expand Down Expand Up @@ -343,27 +334,18 @@ static void writeCachedModule(llvm::raw_ostream &out,
// RESULTS
{
endian::Writer LE(results, little);
for (CodeCompletionResult *R : V.Sink.Results) {
assert(!R->getFlair().toRaw() && "Any flairs should not be cached");
assert(R->getNotRecommendedReason() !=
CodeCompletionResult::NotRecommendedReason::InvalidAsyncContext &&
"InvalidAsyncContext is decl context specific, cannot be cached");

for (const ContextFreeCodeCompletionResult *R : V.Results) {
// FIXME: compress bitfield
LE.write(static_cast<uint8_t>(R->getKind()));
if (R->getKind() == CodeCompletionResult::ResultKind::Declaration)
LE.write(static_cast<uint8_t>(R->getAssociatedDeclKind()));
else
LE.write(static_cast<uint8_t>(~0u));
if (R->isOperator())
LE.write(static_cast<uint8_t>(R->getOperatorKind()));
else
LE.write(static_cast<uint8_t>(R->getOpaqueAssociatedKind()));
if (R->isOperator()) {
LE.write(static_cast<uint8_t>(R->getKnownOperatorKind()));
} else {
LE.write(static_cast<uint8_t>(CodeCompletionOperatorKind::None));
LE.write(static_cast<uint8_t>(R->getSemanticContext()));
}
LE.write(static_cast<uint8_t>(R->getNotRecommendedReason()));
LE.write(static_cast<uint8_t>(R->getDiagnosticSeverity()));
LE.write(static_cast<uint8_t>(R->isSystem()));
LE.write(static_cast<uint8_t>(R->getNumBytesToErase()));
LE.write(
static_cast<uint32_t>(addCompletionString(R->getCompletionString())));
LE.write(addString(R->getModuleName())); // index into strings
Expand Down
65 changes: 36 additions & 29 deletions lib/IDE/CodeCompletionDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,37 +144,44 @@ bool CodeCompletionDiagnostics::getDiagnosticForDeprecated(

} // namespace

bool swift::ide::getCompletionDiagnostics(
CodeCompletionResult::NotRecommendedReason reason, const ValueDecl *D,
CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out) {
using NotRecommendedReason = CodeCompletionResult::NotRecommendedReason;

ASTContext &ctx = D->getASTContext();

CodeCompletionDiagnostics Diag(ctx);
switch (reason) {
case NotRecommendedReason::Deprecated:
case NotRecommendedReason::SoftDeprecated:
return Diag.getDiagnosticForDeprecated(D, severity, Out);
case NotRecommendedReason::InvalidAsyncContext:
// FIXME: Could we use 'diag::async_in_nonasync_function'?
return Diag.getDiagnostics(severity, Out, diag::ide_async_in_nonasync_context,
D->getName());
case NotRecommendedReason::CrossActorReference:
return Diag.getDiagnostics(severity, Out, diag::ide_cross_actor_reference_swift5,
D->getName());
case NotRecommendedReason::RedundantImport:
return Diag.getDiagnostics(severity, Out, diag::ide_redundant_import,
D->getName());
case NotRecommendedReason::RedundantImportIndirect:
return Diag.getDiagnostics(severity, Out, diag::ide_redundant_import_indirect,
D->getName());
case NotRecommendedReason::VariableUsedInOwnDefinition:
return Diag.getDiagnostics(severity, Out, diag::recursive_accessor_reference,
D->getName().getBaseIdentifier(), /*"getter"*/ 0);
case NotRecommendedReason::None:
bool swift::ide::getContextFreeCompletionDiagnostics(
ContextFreeNotRecommendedReason Reason, const ValueDecl *D,
CodeCompletionDiagnosticSeverity &Severity, llvm::raw_ostream &Out) {
CodeCompletionDiagnostics Diag(D->getASTContext());
switch (Reason) {
case ContextFreeNotRecommendedReason::Deprecated:
case ContextFreeNotRecommendedReason::SoftDeprecated:
return Diag.getDiagnosticForDeprecated(D, Severity, Out);
case ContextFreeNotRecommendedReason::None:
llvm_unreachable("invalid not recommended reason");
}
return true;
}

bool swift::ide::getContextualCompletionDiagnostics(
ContextualNotRecommendedReason Reason, const ValueDecl *D,
CodeCompletionDiagnosticSeverity &Severity, llvm::raw_ostream &Out) {
CodeCompletionDiagnostics Diag(D->getASTContext());
switch (Reason) {
case ContextualNotRecommendedReason::InvalidAsyncContext:
// FIXME: Could we use 'diag::async_in_nonasync_function'?
return Diag.getDiagnostics(
Severity, Out, diag::ide_async_in_nonasync_context, D->getName());
case ContextualNotRecommendedReason::CrossActorReference:
return Diag.getDiagnostics(
Severity, Out, diag::ide_cross_actor_reference_swift5, D->getName());
case ContextualNotRecommendedReason::RedundantImport:
return Diag.getDiagnostics(Severity, Out, diag::ide_redundant_import,
D->getName());
case ContextualNotRecommendedReason::RedundantImportIndirect:
return Diag.getDiagnostics(
Severity, Out, diag::ide_redundant_import_indirect, D->getName());
case ContextualNotRecommendedReason::VariableUsedInOwnDefinition:
return Diag.getDiagnostics(
Severity, Out, diag::recursive_accessor_reference,
D->getName().getBaseIdentifier(), /*"getter"*/ 0);
case ContextualNotRecommendedReason::None:
llvm_unreachable("invalid not recommended reason");
}
return true;
}
19 changes: 14 additions & 5 deletions lib/IDE/CodeCompletionDiagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,21 @@ class ValueDecl;

namespace ide {

/// Populate \p severity and \p Out with the diagnostics for \p D.
/// Populate \p severity and \p Out with the context-free diagnostics for \p D.
/// See \c NotRecommendedReason for an explaination of context-free vs.
/// contextual diagnostics.
/// Returns \c true if it fails to generate the diagnostics.
bool getCompletionDiagnostics(CodeCompletionResult::NotRecommendedReason reason,
const ValueDecl *D,
CodeCompletionDiagnosticSeverity &severity,
llvm::raw_ostream &Out);
bool getContextFreeCompletionDiagnostics(
ContextFreeNotRecommendedReason reason, const ValueDecl *D,
CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out);

/// Populate \p severity and \p Out with the contextual diagnostics for \p D.
/// See \c NotRecommendedReason for an explaination of context-free vs.
/// contextual diagnostics.
/// Returns \c true if it fails to generate the diagnostics.
bool getContextualCompletionDiagnostics(
ContextualNotRecommendedReason reason, const ValueDecl *D,
CodeCompletionDiagnosticSeverity &severity, llvm::raw_ostream &Out);

} // namespace ide
} // namespace swift
Expand Down
19 changes: 10 additions & 9 deletions lib/IDE/CodeCompletionResultBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class CodeCompletionResultBuilder {
friend CodeCompletionStringPrinter;

CodeCompletionResultSink &Sink;
CodeCompletionResult::ResultKind Kind;
CodeCompletionResultKind Kind;
SemanticContextKind SemanticContext;
CodeCompletionFlair Flair;
unsigned NumBytesToErase = 0;
Expand All @@ -89,8 +89,10 @@ class CodeCompletionResultBuilder {
CodeCompletionResult::ExpectedTypeRelation ExpectedTypeRelation =
CodeCompletionResult::ExpectedTypeRelation::Unknown;
bool Cancelled = false;
CodeCompletionResult::NotRecommendedReason NotRecReason =
CodeCompletionResult::NotRecommendedReason::None;
ContextFreeNotRecommendedReason ContextFreeNotRecReason =
ContextFreeNotRecommendedReason::None;
ContextualNotRecommendedReason ContextualNotRecReason =
ContextualNotRecommendedReason::None;
StringRef BriefDocComment;

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

public:
CodeCompletionResultBuilder(CodeCompletionResultSink &Sink,
CodeCompletionResult::ResultKind Kind,
CodeCompletionResultKind Kind,
SemanticContextKind SemanticContext,
const ExpectedTypeContext &declTypeContext)
: Sink(Sink), Kind(Kind), SemanticContext(SemanticContext),
Expand Down Expand Up @@ -146,12 +148,11 @@ class CodeCompletionResultBuilder {

void setLiteralKind(CodeCompletionLiteralKind kind) { LiteralKind = kind; }
void setKeywordKind(CodeCompletionKeywordKind kind) { KeywordKind = kind; }
void setNotRecommended(CodeCompletionResult::NotRecommendedReason Reason) {
NotRecReason = Reason;
void setContextFreeNotRecommended(ContextFreeNotRecommendedReason Reason) {
ContextFreeNotRecReason = Reason;
}

void setSemanticContext(SemanticContextKind Kind) {
SemanticContext = Kind;
void setContextualNotRecommended(ContextualNotRecommendedReason Reason) {
ContextualNotRecReason = Reason;
}

void addFlair(CodeCompletionFlair Options) {
Expand Down
2 changes: 1 addition & 1 deletion lib/IDE/REPLCodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ static void toDisplayString(CodeCompletionResult *Result,
}
if (C.is(CodeCompletionString::Chunk::ChunkKind::TypeAnnotation) ||
C.is(CodeCompletionString::Chunk::ChunkKind::TypeAnnotationBegin)) {
if (Result->getKind() == CodeCompletionResult::ResultKind::Declaration) {
if (Result->getKind() == CodeCompletionResultKind::Declaration) {
switch (Result->getAssociatedDeclKind()) {
case CodeCompletionDeclKind::Module:
case CodeCompletionDeclKind::PrecedenceGroup:
Expand Down
5 changes: 4 additions & 1 deletion tools/SourceKit/lib/SwiftLang/CodeCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ using swift::ide::CodeCompletionFlair;
using swift::ide::CodeCompletionKeywordKind;
using swift::ide::CodeCompletionLiteralKind;
using swift::ide::CodeCompletionOperatorKind;
using swift::ide::CodeCompletionResultKind;
using swift::ide::CodeCompletionString;
using swift::ide::SemanticContextKind;
using SwiftResult = swift::ide::CodeCompletionResult;
Expand Down Expand Up @@ -110,7 +111,9 @@ class Completion {

// MARK: Methods that forward to the SwiftResult

SwiftResult::ResultKind getKind() const { return getSwiftResult().getKind(); }
CodeCompletionResultKind getKind() const {
return getSwiftResult().getKind();
}

CodeCompletionDeclKind getAssociatedDeclKind() const {
return getSwiftResult().getAssociatedDeclKind();
Expand Down
Loading