mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
581 lines
20 KiB
C++
581 lines
20 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/IDE/CodeCompletionCache.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/Cache.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/Hashing.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/Support/EndianStream.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
using namespace swift;
|
|
using namespace ide;
|
|
|
|
namespace swift {
|
|
namespace ide {
|
|
struct CodeCompletionCacheImpl {
|
|
using Key = CodeCompletionCache::Key;
|
|
using Value = CodeCompletionCache::Value;
|
|
using ValueRefCntPtr = CodeCompletionCache::ValueRefCntPtr;
|
|
sys::Cache<Key, ValueRefCntPtr> TheCache{"swift.libIDE.CodeCompletionCache"};
|
|
};
|
|
} // end namespace ide
|
|
} // end namespace swift
|
|
|
|
namespace swift {
|
|
namespace sys {
|
|
template<>
|
|
struct CacheValueCostInfo<swift::ide::CodeCompletionCacheImpl::Value> {
|
|
static size_t
|
|
getCost(const swift::ide::CodeCompletionCacheImpl::Value &V) {
|
|
return V.Allocator->getTotalMemory();
|
|
}
|
|
};
|
|
} // namespace sys
|
|
} // namespace swift
|
|
|
|
CodeCompletionCache::ValueRefCntPtr CodeCompletionCache::createValue() {
|
|
return ValueRefCntPtr(new Value);
|
|
}
|
|
|
|
std::optional<CodeCompletionCache::ValueRefCntPtr>
|
|
CodeCompletionCache::get(const Key &K) {
|
|
auto &TheCache = Impl->TheCache;
|
|
std::optional<ValueRefCntPtr> V = TheCache.get(K);
|
|
if (V) {
|
|
// Check whether V is up to date.
|
|
llvm::sys::fs::file_status ModuleStatus;
|
|
if (llvm::sys::fs::status(K.ModuleFilename, ModuleStatus) ||
|
|
V.value()->ModuleModificationTime !=
|
|
ModuleStatus.getLastModificationTime()) {
|
|
// Cache is stale.
|
|
V = std::nullopt;
|
|
TheCache.remove(K);
|
|
}
|
|
} else if (nextCache && (V = nextCache->get(K))) {
|
|
// Hit the chained cache. Update our own cache to match.
|
|
setImpl(K, *V, /*setChain*/ false);
|
|
}
|
|
return V;
|
|
}
|
|
|
|
void CodeCompletionCache::setImpl(const Key &K, ValueRefCntPtr V,
|
|
bool setChain) {
|
|
{
|
|
assert(!K.ModuleFilename.empty());
|
|
|
|
llvm::sys::fs::file_status ModuleStatus;
|
|
if (llvm::sys::fs::status(K.ModuleFilename, ModuleStatus)) {
|
|
V->ModuleModificationTime = std::chrono::system_clock::now();
|
|
return;
|
|
} else {
|
|
V->ModuleModificationTime = ModuleStatus.getLastModificationTime();
|
|
}
|
|
}
|
|
Impl->TheCache.set(K, V);
|
|
|
|
// FIXME: we could write the results to disk in the background, since they're
|
|
// immutable at this point.
|
|
if (nextCache && setChain)
|
|
nextCache->set(K, V);
|
|
}
|
|
|
|
CodeCompletionCache::CodeCompletionCache(OnDiskCodeCompletionCache *nextCache)
|
|
: Impl(new CodeCompletionCacheImpl()), nextCache(nextCache) {}
|
|
|
|
CodeCompletionCache::~CodeCompletionCache() {}
|
|
|
|
/// A version number for the format of the serialized code completion results.
|
|
///
|
|
/// 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 =
|
|
12; // Removed 'IsAsync'.
|
|
|
|
/// Deserializes CodeCompletionResults from \p in and stores them in \p V.
|
|
/// \see writeCacheModule.
|
|
static bool readCachedModule(llvm::MemoryBuffer *in,
|
|
const CodeCompletionCache::Key &K,
|
|
CodeCompletionCache::Value &V,
|
|
bool allowOutOfDate = false) {
|
|
const char *cursor = in->getBufferStart();
|
|
const char *end = in->getBufferEnd();
|
|
|
|
auto read32le = [end](const char *&cursor) {
|
|
auto result = llvm::support::endian::read32le(cursor);
|
|
cursor += sizeof(result);
|
|
assert(cursor <= end);
|
|
(void)end;
|
|
return result;
|
|
};
|
|
|
|
// HEADER
|
|
{
|
|
auto version = read32le(cursor);
|
|
if (version != onDiskCompletionCacheVersion)
|
|
return false; // File written with different format.
|
|
|
|
auto mtime = llvm::support::endian::read64le(cursor);
|
|
cursor += sizeof(mtime);
|
|
|
|
// Check the module file's last modification time.
|
|
if (!allowOutOfDate) {
|
|
llvm::sys::fs::file_status status;
|
|
if (llvm::sys::fs::status(K.ModuleFilename, status) ||
|
|
status.getLastModificationTime().time_since_epoch().count() !=
|
|
std::chrono::nanoseconds(mtime).count()) {
|
|
return false; // Out of date, or doesn't exist.
|
|
}
|
|
}
|
|
}
|
|
|
|
// DEBUG INFO
|
|
cursor += read32le(cursor); // Skip the whole debug section.
|
|
|
|
// Get the size of the various sections.
|
|
auto resultSize = read32le(cursor);
|
|
const char *resultEnd = cursor + resultSize;
|
|
const char *chunks = resultEnd;
|
|
auto chunkSize = read32le(chunks);
|
|
const char *strings = chunks + chunkSize;
|
|
auto stringsSize = read32le(strings);
|
|
const char *types = strings + stringsSize;
|
|
auto typesSize = read32le(types);
|
|
assert(types + typesSize == end && "incorrect file size");
|
|
(void)typesSize; // so it is not seen as "unused" in release builds.
|
|
|
|
// STRINGS
|
|
llvm::DenseMap<uint32_t, NullTerminatedStringRef> knownStrings;
|
|
auto getString = [&](uint32_t index) -> NullTerminatedStringRef {
|
|
if (index == ~0u)
|
|
return "";
|
|
auto found = knownStrings.find(index);
|
|
if (found != knownStrings.end()) {
|
|
return found->second;
|
|
}
|
|
|
|
const char *p = strings + index;
|
|
size_t size = read32le(p);
|
|
auto str = NullTerminatedStringRef(StringRef(p, size), *V.Allocator);
|
|
knownStrings[index] = str;
|
|
return str;
|
|
};
|
|
|
|
// TYPES
|
|
llvm::DenseMap<uint32_t, const USRBasedType *> knownTypes;
|
|
std::function<const USRBasedType *(uint32_t)> getType =
|
|
[&](uint32_t index) -> const USRBasedType * {
|
|
auto found = knownTypes.find(index);
|
|
if (found != knownTypes.end()) {
|
|
return found->second;
|
|
}
|
|
const char *p = types + index;
|
|
auto usrLength = read32le(p);
|
|
auto usr = StringRef(p, usrLength);
|
|
p += usrLength;
|
|
auto supertypesCount = read32le(p);
|
|
std::vector<const USRBasedType *> supertypes;
|
|
supertypes.reserve(supertypesCount);
|
|
for (unsigned i = 0; i < supertypesCount; i++) {
|
|
auto supertypeIndex = read32le(p);
|
|
supertypes.push_back(getType(supertypeIndex));
|
|
}
|
|
auto customAttributeKinds = OptionSet<CustomAttributeKind, uint8_t>(*p++);
|
|
const USRBasedType *res = USRBasedType::fromUSR(
|
|
usr, supertypes, customAttributeKinds, V.USRTypeArena);
|
|
knownTypes[index] = res;
|
|
return res;
|
|
};
|
|
|
|
// CHUNKS
|
|
auto getCompletionString = [&](uint32_t chunkIndex) {
|
|
const char *p = chunks + chunkIndex;
|
|
auto len = read32le(p);
|
|
using Chunk = CodeCompletionString::Chunk;
|
|
SmallVector<Chunk, 32> chunkList;
|
|
for (unsigned j = 0; j < len; ++j) {
|
|
auto kind = static_cast<Chunk::ChunkKind>(*p++);
|
|
auto nest = *p++;
|
|
auto isAnnotation = static_cast<bool>(*p++);
|
|
auto textIndex = read32le(p);
|
|
auto text = getString(textIndex);
|
|
|
|
if (Chunk::chunkHasText(kind)) {
|
|
chunkList.push_back(
|
|
Chunk::createWithText(kind, nest, text, isAnnotation));
|
|
} else {
|
|
chunkList.push_back(Chunk::createSimple(kind, nest, isAnnotation));
|
|
}
|
|
}
|
|
|
|
return CodeCompletionString::create(*V.Allocator, chunkList);
|
|
};
|
|
|
|
// RESULTS
|
|
while (cursor != resultEnd) {
|
|
auto kind = static_cast<CodeCompletionResultKind>(*cursor++);
|
|
auto associatedKind = static_cast<uint8_t>(*cursor++);
|
|
auto opKind = static_cast<CodeCompletionOperatorKind>(*cursor++);
|
|
auto roles = CodeCompletionMacroRoles(*cursor++);
|
|
auto notRecommended =
|
|
static_cast<ContextFreeNotRecommendedReason>(*cursor++);
|
|
auto diagSeverity =
|
|
static_cast<CodeCompletionDiagnosticSeverity>(*cursor++);
|
|
auto isSystem = static_cast<bool>(*cursor++);
|
|
auto hasAsyncAlternative = static_cast<bool>(*cursor++);
|
|
auto chunkIndex = read32le(cursor);
|
|
auto moduleIndex = read32le(cursor);
|
|
auto briefDocIndex = read32le(cursor);
|
|
auto diagMessageIndex = read32le(cursor);
|
|
auto filterNameIndex = read32le(cursor);
|
|
auto nameForDiagnosticsIndex = read32le(cursor);
|
|
|
|
auto assocUSRCount = read32le(cursor);
|
|
SmallVector<NullTerminatedStringRef, 4> assocUSRs;
|
|
for (unsigned i = 0; i < assocUSRCount; ++i) {
|
|
assocUSRs.push_back(getString(read32le(cursor)));
|
|
}
|
|
|
|
auto resultTypesCount = read32le(cursor);
|
|
SmallVector<const USRBasedType *, 1> resultTypes;
|
|
resultTypes.reserve(resultTypesCount);
|
|
for (size_t i = 0; i < resultTypesCount; i++) {
|
|
resultTypes.push_back(getType(read32le(cursor)));
|
|
}
|
|
|
|
CodeCompletionString *string = getCompletionString(chunkIndex);
|
|
auto moduleName = getString(moduleIndex);
|
|
auto briefDocComment = getString(briefDocIndex);
|
|
auto diagMessage = getString(diagMessageIndex);
|
|
auto filterName = getString(filterNameIndex);
|
|
auto nameForDiagnostics = getString(nameForDiagnosticsIndex);
|
|
|
|
ContextFreeCodeCompletionResult *result =
|
|
new (*V.Allocator) ContextFreeCodeCompletionResult(
|
|
kind, associatedKind, opKind, roles, isSystem,
|
|
hasAsyncAlternative, string, moduleName, briefDocComment,
|
|
llvm::ArrayRef(assocUSRs).copy(*V.Allocator),
|
|
CodeCompletionResultType(resultTypes), notRecommended, diagSeverity,
|
|
diagMessage, filterName, nameForDiagnostics);
|
|
|
|
V.Results.push_back(result);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Writes the code completion results from the sink for \p V to \p out.
|
|
///
|
|
/// The high-level format is:
|
|
///
|
|
/// HEADER
|
|
/// * version, which **must be bumped** if we change the format!
|
|
/// * mtime for the module file
|
|
///
|
|
/// KEY
|
|
/// * the original CodeCompletionCache::Key, used for debugging the cache.
|
|
///
|
|
/// RESULTS
|
|
/// * A length-prefixed array of fixed size CodeCompletionResults.
|
|
/// * Contains offsets into CHUNKS and STRINGS.
|
|
///
|
|
/// CHUNKS
|
|
/// * A length-prefixed array of CodeCompletionStrings.
|
|
/// * Each CodeCompletionString is a length-prefixed array of fixed size
|
|
/// CodeCompletionString::Chunks.
|
|
///
|
|
/// STRINGS
|
|
/// * A blob of length-prefixed strings referred to in CHUNKS or RESULTS.
|
|
static void writeCachedModule(llvm::raw_ostream &out,
|
|
const CodeCompletionCache::Key &K,
|
|
CodeCompletionCache::Value &V) {
|
|
llvm::support::endian::Writer LE(out, llvm::endianness::little);
|
|
|
|
// HEADER
|
|
// Metadata required for reading the completions.
|
|
LE.write(onDiskCompletionCacheVersion); // Version
|
|
auto mtime = V.ModuleModificationTime.time_since_epoch().count();
|
|
LE.write(mtime); // Mtime for module file
|
|
|
|
// KEY
|
|
// We don't need the stored key to load the results, but it is useful if we
|
|
// want to debug the cache itself.
|
|
{
|
|
SmallString<256> scratch;
|
|
llvm::raw_svector_ostream OSS(scratch);
|
|
OSS << K.ModuleFilename << "\0";
|
|
OSS << K.ModuleName << "\0";
|
|
llvm::support::endian::Writer OSSLE(OSS, llvm::endianness::little);
|
|
OSSLE.write(K.AccessPath.size());
|
|
for (StringRef p : K.AccessPath)
|
|
OSS << p << "\0";
|
|
OSSLE.write(K.ResultsHaveLeadingDot);
|
|
OSSLE.write(K.ForTestableLookup);
|
|
OSSLE.write(K.ForPrivateImportLookup);
|
|
OSSLE.write(K.AddInitsInToplevel);
|
|
OSSLE.write(K.AddCallWithNoDefaultArgs);
|
|
OSSLE.write(K.Annotated);
|
|
LE.write(static_cast<uint32_t>(OSS.tell())); // Size of debug info
|
|
out.write(OSS.str().data(), OSS.str().size()); // Debug info blob
|
|
}
|
|
|
|
// String streams for writing to the CHUNKS and STRINGS sections.
|
|
std::string results_;
|
|
llvm::raw_string_ostream results(results_);
|
|
std::string chunks_;
|
|
llvm::raw_string_ostream chunks(chunks_);
|
|
llvm::support::endian::Writer chunksLE(chunks, llvm::endianness::little);
|
|
std::string strings_;
|
|
llvm::raw_string_ostream strings(strings_);
|
|
llvm::StringMap<uint32_t> knownStrings;
|
|
std::string types_;
|
|
llvm::raw_string_ostream types(types_);
|
|
llvm::DenseMap<const USRBasedType *, uint32_t> knownTypes;
|
|
|
|
auto addString = [&strings, &knownStrings](StringRef str) {
|
|
if (str.empty())
|
|
return ~0u;
|
|
auto found = knownStrings.find(str);
|
|
if (found != knownStrings.end()) {
|
|
return found->second;
|
|
}
|
|
auto size = strings.tell();
|
|
llvm::support::endian::Writer LE(strings, llvm::endianness::little);
|
|
LE.write(static_cast<uint32_t>(str.size()));
|
|
strings << str;
|
|
knownStrings[str] = size;
|
|
return static_cast<uint32_t>(size);
|
|
};
|
|
|
|
std::function<uint32_t(const USRBasedType *)> addType =
|
|
[&types, &knownTypes, &addType](const USRBasedType *type) -> uint32_t {
|
|
auto found = knownTypes.find(type);
|
|
if (found != knownTypes.end()) {
|
|
return found->second;
|
|
}
|
|
std::vector<uint32_t> supertypeIndicies;
|
|
// IMPORTANT: To compute the supertype indicies, we might need to add
|
|
// entries to the type table by calling addType recursively. Thus, we must
|
|
// perform this calculation before writing any bytes of this type to the
|
|
// types table.
|
|
auto supertypes = type->getSupertypes();
|
|
supertypeIndicies.reserve(supertypes.size());
|
|
for (auto supertype : supertypes) {
|
|
supertypeIndicies.push_back(addType(supertype));
|
|
}
|
|
|
|
auto size = types.tell();
|
|
llvm::support::endian::Writer LE(types, llvm::endianness::little);
|
|
StringRef USR = type->getUSR();
|
|
LE.write(static_cast<uint32_t>(USR.size()));
|
|
types << USR;
|
|
LE.write(static_cast<uint32_t>(supertypeIndicies.size()));
|
|
for (auto supertypeIndex : supertypeIndicies) {
|
|
LE.write(static_cast<uint32_t>(supertypeIndex));
|
|
}
|
|
OptionSet<CustomAttributeKind, uint8_t> customAttributeKinds =
|
|
type->getCustomAttributeKinds();
|
|
LE.write(static_cast<uint8_t>(customAttributeKinds.toRaw()));
|
|
knownTypes[type] = size;
|
|
return static_cast<uint32_t>(size);
|
|
};
|
|
|
|
auto addCompletionString = [&](const CodeCompletionString *str) {
|
|
auto size = chunks.tell();
|
|
chunksLE.write(static_cast<uint32_t>(str->getChunks().size()));
|
|
for (auto chunk : str->getChunks()) {
|
|
chunksLE.write(static_cast<uint8_t>(chunk.getKind()));
|
|
chunksLE.write(static_cast<uint8_t>(chunk.getNestingLevel()));
|
|
chunksLE.write(static_cast<uint8_t>(chunk.isAnnotation()));
|
|
if (chunk.hasText()) {
|
|
chunksLE.write(addString(chunk.getText()));
|
|
} else {
|
|
chunksLE.write(static_cast<uint32_t>(~0u));
|
|
}
|
|
}
|
|
return static_cast<uint32_t>(size);
|
|
};
|
|
|
|
// RESULTS
|
|
{
|
|
llvm::support::endian::Writer LE(results, llvm::endianness::little);
|
|
for (const ContextFreeCodeCompletionResult *R : V.Results) {
|
|
// FIXME: compress bitfield
|
|
LE.write(static_cast<uint8_t>(R->getKind()));
|
|
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->getMacroRoles().toRaw()));
|
|
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->hasAsyncAlternative()));
|
|
LE.write(
|
|
static_cast<uint32_t>(addCompletionString(R->getCompletionString())));
|
|
LE.write(addString(R->getModuleName())); // index into strings
|
|
LE.write(addString(R->getBriefDocComment())); // index into strings
|
|
LE.write(addString(R->getDiagnosticMessage())); // index into strings
|
|
LE.write(addString(R->getFilterName())); // index into strings
|
|
LE.write(addString(R->getNameForDiagnostics())); // index into strings
|
|
|
|
LE.write(static_cast<uint32_t>(R->getAssociatedUSRs().size()));
|
|
for (unsigned i = 0; i < R->getAssociatedUSRs().size(); ++i) {
|
|
LE.write(addString(R->getAssociatedUSRs()[i]));
|
|
}
|
|
|
|
auto resultTypes =
|
|
R->getResultType().getUSRBasedResultTypes(V.USRTypeArena);
|
|
LE.write(static_cast<uint32_t>(resultTypes.size()));
|
|
for (auto resultType : resultTypes) {
|
|
LE.write(addType(resultType)); // index into types
|
|
}
|
|
}
|
|
}
|
|
LE.write(static_cast<uint32_t>(results.tell()));
|
|
out << results.str();
|
|
|
|
// CHUNKS
|
|
LE.write(static_cast<uint32_t>(chunks.tell()));
|
|
out << chunks.str();
|
|
|
|
// STRINGS
|
|
LE.write(static_cast<uint32_t>(strings.tell()));
|
|
out << strings.str();
|
|
|
|
// TYPES
|
|
LE.write(static_cast<uint32_t>(types.tell()));
|
|
out << types.str();
|
|
}
|
|
|
|
/// Get the name for the cached code completion results for a given key \p K in
|
|
/// \p cacheDirectory.
|
|
///
|
|
/// This name is unique (modulo hash collisions) to the key \p K.
|
|
static std::string getName(StringRef cacheDirectory,
|
|
const CodeCompletionCache::Key &K) {
|
|
SmallString<128> name(cacheDirectory);
|
|
|
|
// cacheDirectory/ModuleName
|
|
llvm::sys::path::append(name, K.ModuleName);
|
|
llvm::raw_svector_ostream OSS(name);
|
|
|
|
// name[-with-enabled-options]
|
|
OSS << (K.ResultsHaveLeadingDot ? "-dot" : "")
|
|
<< (K.ForTestableLookup ? "-testable" : "")
|
|
<< (K.ForPrivateImportLookup ? "-private" : "")
|
|
<< (K.AddInitsInToplevel ? "-inits" : "")
|
|
<< (K.AddCallWithNoDefaultArgs ? "-nodefaults" : "")
|
|
<< (K.Annotated ? "-annotated" : "");
|
|
if (K.SpiGroups.size() > 0) {
|
|
OSS << "-spi";
|
|
for (auto SpiGroup : K.SpiGroups) {
|
|
OSS << "-" << SpiGroup;
|
|
}
|
|
}
|
|
|
|
// name[-access-path-components]
|
|
for (StringRef component : K.AccessPath)
|
|
OSS << "-" << component;
|
|
|
|
// name-<hash of module filename>
|
|
auto hash = llvm::hash_value(K.ModuleFilename);
|
|
SmallString<16> hashStr;
|
|
llvm::APInt(64, uint64_t(hash)).toStringUnsigned(hashStr, /*Radix*/ 36);
|
|
OSS << "-" << hashStr << ".completions";
|
|
|
|
return std::string(name.str());
|
|
}
|
|
|
|
std::optional<CodeCompletionCache::ValueRefCntPtr>
|
|
OnDiskCodeCompletionCache::get(const Key &K) {
|
|
// Try to find the cached file.
|
|
auto bufferOrErr = llvm::MemoryBuffer::getFile(getName(cacheDirectory, K));
|
|
if (!bufferOrErr)
|
|
return std::nullopt;
|
|
|
|
// Read the cached results, failing if they are out of date.
|
|
auto V = CodeCompletionCache::createValue();
|
|
if (!readCachedModule(bufferOrErr.get().get(), K, *V))
|
|
return std::nullopt;
|
|
|
|
return V;
|
|
}
|
|
|
|
std::error_code OnDiskCodeCompletionCache::set(const Key &K, ValueRefCntPtr V) {
|
|
if (K.ModuleFilename.empty())
|
|
return std::make_error_code(std::errc::no_such_file_or_directory);
|
|
|
|
// Create the cache directory if it doesn't exist.
|
|
if (auto err = llvm::sys::fs::create_directories(cacheDirectory))
|
|
return err;
|
|
|
|
std::string name = getName(cacheDirectory, K);
|
|
|
|
// Create a temporary file to write the results into.
|
|
SmallString<128> tmpName(name + "-%%%%%%");
|
|
int tmpFD;
|
|
if (auto err = llvm::sys::fs::createUniqueFile(tmpName.str(), tmpFD, tmpName))
|
|
return err;
|
|
|
|
// Write the contents of the buffer.
|
|
llvm::raw_fd_ostream out(tmpFD, /*shouldClose=*/true);
|
|
writeCachedModule(out, K, *V);
|
|
out.flush();
|
|
if (out.has_error())
|
|
return std::make_error_code(std::errc::io_error);
|
|
|
|
// Atomically rename the file into its final location.
|
|
return llvm::sys::fs::rename(tmpName.str(), name);
|
|
}
|
|
|
|
std::optional<CodeCompletionCache::ValueRefCntPtr>
|
|
OnDiskCodeCompletionCache::getFromFile(StringRef filename) {
|
|
// Try to find the cached file.
|
|
auto bufferOrErr = llvm::MemoryBuffer::getFile(filename);
|
|
if (!bufferOrErr)
|
|
return std::nullopt;
|
|
|
|
// Make up a key for readCachedModule.
|
|
CodeCompletionCache::Key K{/*ModuleFilename=*/filename.str(),
|
|
/*ModuleName=*/"<module-name>",
|
|
/*AccessPath=*/{},
|
|
/*ResultsHaveLeadingDot=*/false,
|
|
/*ForTestableLookup=*/false,
|
|
/*ForPrivateImportLookup=*/false,
|
|
/*SpiGroups=*/{},
|
|
/*AddInitsInToplevel=*/false,
|
|
/*AddCallWithNoDefaultArgs=*/false,
|
|
/*Annotated=*/false};
|
|
|
|
// Read the cached results.
|
|
auto V = CodeCompletionCache::createValue();
|
|
if (!readCachedModule(bufferOrErr.get().get(), K, *V,
|
|
/*allowOutOfDate*/ true))
|
|
return std::nullopt;
|
|
|
|
return V;
|
|
}
|
|
|
|
OnDiskCodeCompletionCache::OnDiskCodeCompletionCache(Twine cacheDirectory)
|
|
: cacheDirectory(cacheDirectory.str()) {}
|
|
|
|
OnDiskCodeCompletionCache::~OnDiskCodeCompletionCache() {}
|