[SourceKit] Reuse compiler instance between multiple completion

- Introduce ide::CompletionInstance to manage CompilerInstance
- `CompletionInstance` vends the cached CompilerInstance when:
-- The compiler arguments (i.e. CompilerInvocation) has has not changed
-- The primary file is the same
-- The completion happens inside function bodies in both previous and
   current completion
-- The interface hash of the primary file has not changed
- Otherwise, it vends a fresh CompilerInstance and cache it for the next
  completion

rdar://problem/20787086
This commit is contained in:
Rintaro Ishizaki
2019-11-18 18:02:41 +09:00
parent ff97c06e8d
commit 62c44126b6
23 changed files with 589 additions and 318 deletions

View File

@@ -16,6 +16,7 @@
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/ConformingMethodList.h"
#include "swift/IDE/CompletionInstance.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Comment.h"
#include "clang/AST/Decl.h"
@@ -32,25 +33,30 @@ static bool swiftConformingMethodListImpl(
ide::ConformingMethodListConsumer &Consumer,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
std::string &Error) {
auto bufferIdentifier =
Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier());
// Resolve symlinks for the input file.
llvm::SmallString<128> bufferIdentifier;
if (auto err = FileSystem->getRealPath(
UnresolvedInputFile->getBufferIdentifier(), bufferIdentifier))
bufferIdentifier = UnresolvedInputFile->getBufferIdentifier();
auto origOffset = Offset;
auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer(
auto newBuffer = ide::makeCodeCompletionMemoryBuffer(
UnresolvedInputFile, Offset, bufferIdentifier);
CompilerInstance CI;
SourceManager SM;
DiagnosticEngine Diags(SM);
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
EditorDiagConsumer TraceDiags;
trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion);
trace::TracedOperation TracedOp{trace::OperationKind::CodeCompletion};
Diags.addConsumer(PrintDiags);
if (TracedOp.enabled()) {
CI.addDiagnosticConsumer(&TraceDiags);
Diags.addConsumer(TraceDiags);
trace::SwiftInvocation SwiftArgs;
trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args);
TracedOp.setDiagnosticProvider(
[&TraceDiags](SmallVectorImpl<DiagnosticEntryInfo> &diags) {
[&](SmallVectorImpl<DiagnosticEntryInfo> &diags) {
TraceDiags.getAllDiagnostics(diags);
});
TracedOp.start(
@@ -58,22 +64,27 @@ static bool swiftConformingMethodListImpl(
{std::make_pair("OriginalOffset", std::to_string(origOffset)),
std::make_pair("Offset", std::to_string(Offset))});
}
ForwardingDiagnosticConsumer CIDiags(Diags);
CompilerInvocation Invocation;
bool Failed = Lang.getASTManager()->initCompilerInvocation(
Invocation, Args, CI.getDiags(), bufferIdentifier, FileSystem, Error);
Invocation, Args, Diags, newBuffer->getBufferIdentifier(), FileSystem,
Error);
if (Failed)
return false;
if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) {
Error = "no input filenames specified";
return false;
}
CompilerInstance *CI = Lang.getCompletionInstance().getCompilerInstance(
Invocation, FileSystem, newBuffer.get(), Offset, Error, &CIDiags);
if (!CI)
return false;
SWIFT_DEFER { CI->removeDiagnosticConsumer(&CIDiags); };
// Always disable source location resolutions from .swiftsourceinfo file
// because they're somewhat heavy operations and aren't needed for completion.
Invocation.getFrontendOptions().IgnoreSwiftSourceInfo = true;
Invocation.setCodeCompletionPoint(newBuffer.get(), Offset);
// Perform the parsing and completion.
if (!CI->hasPersistentParserState())
CI->performParseAndResolveImportsOnly();
// Create a factory for code completion callbacks that will feed the
// Consumer.
@@ -81,18 +92,8 @@ static bool swiftConformingMethodListImpl(
ide::makeConformingMethodListCallbacksFactory(ExpectedTypeNames,
Consumer));
Invocation.setCodeCompletionFactory(callbacksFactory.get());
if (FileSystem != llvm::vfs::getRealFileSystem()) {
CI.getSourceMgr().setFileSystem(FileSystem);
}
if (CI.setup(Invocation)) {
// FIXME: error?
return true;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performParseAndResolveImportsOnly();
performCodeCompletionSecondPass(CI->getPersistentParserState(),
*callbacksFactory);
return true;
}