Merge remote-tracking branch 'origin/main' into rebranch

This commit is contained in:
swift-ci
2021-01-15 10:12:20 -08:00
19 changed files with 251 additions and 46 deletions

View File

@@ -402,6 +402,9 @@ public:
return false;
}
virtual void collectBasicSourceFileInfo(
llvm::function_ref<void(const BasicSourceFileInfo &)> callback) const {}
static bool classof(const FileUnit *file) {
return file->getKind() == FileUnitKind::SerializedAST ||
file->getKind() == FileUnitKind::ClangModule ||

View File

@@ -728,6 +728,10 @@ public:
return ReverseFullNameIterator(this);
}
/// Calls \p callback for each source file of the module.
void collectBasicSourceFileInfo(
llvm::function_ref<void(const BasicSourceFileInfo &)> callback);
SourceRange getSourceRange() const { return SourceRange(); }
static bool classof(const DeclContext *DC) {

View File

@@ -13,11 +13,15 @@
#ifndef SWIFT_AST_RAW_COMMENT_H
#define SWIFT_AST_RAW_COMMENT_H
#include "swift/Basic/Fingerprint.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/Basic/SourceManager.h"
namespace swift {
class SourceFile;
struct SingleRawComment {
enum class CommentKind {
OrdinaryLine, ///< Any normal // comments
@@ -92,6 +96,17 @@ struct BasicDeclLocs {
LineColumn EndLoc;
};
struct BasicSourceFileInfo {
StringRef FilePath;
Fingerprint InterfaceHash = Fingerprint::ZERO();
llvm::sys::TimePoint<> LastModified = {};
uint64_t FileSize = 0;
BasicSourceFileInfo() {}
bool populate(SourceFile *SF);
};
} // namespace swift
#endif // LLVM_SWIFT_AST_RAW_COMMENT_H

View File

@@ -531,6 +531,9 @@ public:
out << getInterfaceHash() << '\n';
}
/// Get this file's interface hash including the type members in the file.
Fingerprint getInterfaceHashIncludingTypeMembers() const;
/// If this source file has been told to collect its parsed tokens, retrieve
/// those tokens.
ArrayRef<Token> getAllTokens() const;

View File

@@ -446,6 +446,9 @@ public:
StringRef getTargetTriple() const;
virtual void collectBasicSourceFileInfo(
llvm::function_ref<void(const BasicSourceFileInfo &)>) const override;
static bool classof(const FileUnit *file) {
return file->getKind() == FileUnitKind::SerializedAST;
}

View File

@@ -1099,6 +1099,30 @@ Fingerprint SourceFile::getInterfaceHash() const {
return Fingerprint{std::move(result)};
}
Fingerprint SourceFile::getInterfaceHashIncludingTypeMembers() const {
/// FIXME: Gross. Hashing multiple "hash" values.
llvm::MD5 hash;
hash.update(getInterfaceHash().getRawValue());
std::function<void(IterableDeclContext *)> hashTypeBodyFingerprints =
[&](IterableDeclContext *IDC) {
if (auto fp = IDC->getBodyFingerprint())
hash.update(fp->getRawValue());
for (auto *member : IDC->getParsedMembers())
if (auto *childIDC = dyn_cast<IterableDeclContext>(member))
hashTypeBodyFingerprints(childIDC);
};
for (auto *D : getTopLevelDecls()) {
if (auto IDC = dyn_cast<IterableDeclContext>(D))
hashTypeBodyFingerprints(IDC);
}
llvm::MD5::MD5Result result;
hash.final(result);
return Fingerprint{std::move(result)};
}
syntax::SourceFileSyntax SourceFile::getSyntaxRoot() const {
assert(shouldBuildSyntaxTree() && "Syntax tree disabled");
auto &eval = getASTContext().evaluator;
@@ -1514,6 +1538,20 @@ const clang::Module *ModuleDecl::findUnderlyingClangModule() const {
return nullptr;
}
void ModuleDecl::collectBasicSourceFileInfo(
llvm::function_ref<void(const BasicSourceFileInfo &)> callback) {
for (FileUnit *fileUnit : getFiles()) {
if (SourceFile *SF = dyn_cast<SourceFile>(fileUnit)) {
BasicSourceFileInfo info;
if (info.populate(SF))
continue;
callback(info);
} else if (auto *serialized = dyn_cast<LoadedFile>(fileUnit)) {
serialized->collectBasicSourceFileInfo(callback);
}
}
}
//===----------------------------------------------------------------------===//
// Cross-Import Overlays
//===----------------------------------------------------------------------===//

View File

@@ -22,6 +22,7 @@
#include "swift/AST/FileUnit.h"
#include "swift/AST/Module.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Types.h"
#include "swift/Basic/PrimitiveParsing.h"
#include "swift/Basic/SourceManager.h"
@@ -238,3 +239,27 @@ CharSourceRange RawComment::getCharSourceRange() {
static_cast<const char *>(Start.getOpaquePointerValue());
return CharSourceRange(Start, Length);
}
bool BasicSourceFileInfo::populate(SourceFile *SF) {
SourceManager &SM = SF->getASTContext().SourceMgr;
auto filename = SF->getFilename();
if (filename.empty())
return true;
auto stat = SM.getFileSystem()->status(filename);
if (!stat)
return true;
FilePath = filename;
LastModified = stat->getLastModificationTime();
FileSize = stat->getSize();
if (SF->hasInterfaceHash()) {
InterfaceHash = SF->getInterfaceHashIncludingTypeMembers();
} else {
// FIXME: Parse the file with EnableInterfaceHash option.
InterfaceHash = Fingerprint::ZERO();
}
return false;
}

View File

@@ -277,49 +277,6 @@ static bool areAnyDependentFilesInvalidated(
});
}
/// Get interface hash of \p SF including the type members in the file.
///
/// See if the inteface of the function and types visible from a function body
/// has changed since the last completion. If they haven't changed, completion
/// can reuse the existing AST of the source file. \c SF->getInterfaceHash() is
/// not enough because it doesn't take the interface of the type members into
/// account. For example:
///
/// struct S {
/// func foo() {}
/// }
/// func main(val: S) {
/// val.<HERE>
/// }
///
/// In this case, we need to ensure that the interface of \c S hasn't changed.
/// Note that we don't care about local types (i.e. type declarations inside
/// function bodies, closures, or top level statement bodies) because they are
/// not visible from other functions where the completion is happening.
static Fingerprint getInterfaceHashIncludingTypeMembers(const SourceFile *SF) {
/// FIXME: Gross. Hashing multiple "hash" values.
llvm::MD5 hash;
hash.update(SF->getInterfaceHash().getRawValue());
std::function<void(IterableDeclContext *)> hashTypeBodyFingerprints =
[&](IterableDeclContext *IDC) {
if (auto fp = IDC->getBodyFingerprint())
hash.update(fp->getRawValue());
for (auto *member : IDC->getParsedMembers())
if (auto *childIDC = dyn_cast<IterableDeclContext>(member))
hashTypeBodyFingerprints(childIDC);
};
for (auto *D : SF->getTopLevelDecls()) {
if (auto IDC = dyn_cast<IterableDeclContext>(D))
hashTypeBodyFingerprints(IDC);
}
llvm::MD5::MD5Result result;
hash.final(result);
return Fingerprint{std::move(result)};
}
} // namespace
bool CompletionInstance::performCachedOperationIfPossible(
@@ -396,8 +353,26 @@ bool CompletionInstance::performCachedOperationIfPossible(
switch (newInfo.Kind) {
case CodeCompletionDelayedDeclKind::FunctionBody: {
// If the interface has changed, AST must be refreshed.
const auto oldInterfaceHash = getInterfaceHashIncludingTypeMembers(oldSF);
const auto newInterfaceHash = getInterfaceHashIncludingTypeMembers(tmpSF);
// See if the inteface of the function and types visible from a function
// body has changed since the last completion. If they haven't changed,
// completion can reuse the existing AST of the source file.
// \c getInterfaceHash() is not enough because it doesn't take the interface
// of the type members into account. For example:
//
// struct S {
// func foo() {}
// }
// func main(val: S) {
// val.<HERE>
// }
//
// In this case, we need to ensure that the interface of \c S hasn't
// changed. Note that we don't care about local types (i.e. type
// declarations inside function bodies, closures, or top level statement
// bodies) because they are not visible from other functions where the
// completion is happening.
const auto oldInterfaceHash = oldSF->getInterfaceHashIncludingTypeMembers();
const auto newInterfaceHash = tmpSF->getInterfaceHashIncludingTypeMembers();
if (oldInterfaceHash != newInterfaceHash)
return false;

View File

@@ -959,6 +959,41 @@ Optional<CommentInfo> ModuleFile::getCommentForDecl(const Decl *D) const {
return getCommentForDeclByUSR(USRBuffer.str());
}
void ModuleFile::collectBasicSourceFileInfo(
llvm::function_ref<void(const BasicSourceFileInfo &)> callback) const {
if (Core->SourceFileListData.empty())
return;
assert(!Core->SourceLocsTextData.empty());
auto *Cursor = Core->SourceFileListData.bytes_begin();
auto *End = Core->SourceFileListData.bytes_end();
while (Cursor < End) {
// FilePath (byte offset in 'SourceLocsTextData').
auto fileID = endian::readNext<uint32_t, little, unaligned>(Cursor);
// InterfaceHash (fixed length string).
auto fpStr = StringRef{reinterpret_cast<const char *>(Cursor),
Fingerprint::DIGEST_LENGTH};
Cursor += Fingerprint::DIGEST_LENGTH;
// LastModified (nanoseconds since epoch).
auto timestamp = endian::readNext<uint64_t, little, unaligned>(Cursor);
// FileSize (num of bytes).
auto fileSize = endian::readNext<uint64_t, little, unaligned>(Cursor);
assert(fileID < Core->SourceLocsTextData.size());
auto filePath = Core->SourceLocsTextData.substr(fileID);
size_t terminatorOffset = filePath.find('\0');
filePath = filePath.slice(0, terminatorOffset);
BasicSourceFileInfo info;
info.FilePath = filePath;
info.InterfaceHash = Fingerprint::fromString(fpStr);
info.LastModified =
llvm::sys::TimePoint<>(std::chrono::nanoseconds(timestamp));
info.FileSize = fileSize;
callback(info);
}
}
Optional<BasicDeclLocs>
ModuleFile::getBasicDeclLocsForDecl(const Decl *D) const {
assert(D);

View File

@@ -694,6 +694,8 @@ public:
Optional<BasicDeclLocs> getBasicDeclLocsForDecl(const Decl *D) const;
Identifier getDiscriminatorForPrivateValue(const ValueDecl *D);
Optional<Fingerprint> loadFingerprint(const IterableDeclContext *IDC) const;
void collectBasicSourceFileInfo(
llvm::function_ref<void(const BasicSourceFileInfo &)> callback) const;
// MARK: Deserialization interface

View File

@@ -974,6 +974,9 @@ bool ModuleFileSharedCore::readDeclLocsBlock(llvm::BitstreamCursor &cursor) {
return false;
}
switch (*kind) {
case decl_locs_block::SOURCE_FILE_LIST:
SourceFileListData = blobData;
break;
case decl_locs_block::BASIC_DECL_LOCS:
BasicDeclLocsData = blobData;
break;

View File

@@ -289,6 +289,9 @@ private:
/// A blob of 0 terminated string segments referenced in \c SourceLocsTextData
StringRef SourceLocsTextData;
/// A blob of source file list.
StringRef SourceFileListData;
/// An array of fixed size source location data for each USR appearing in
/// \c DeclUSRsTable.
StringRef BasicDeclLocsData;

View File

@@ -783,6 +783,66 @@ static void emitBasicLocsRecord(llvm::BitstreamWriter &Out,
DeclLocsList.emit(scratch, Writer.Buffer);
}
static void emitFileListRecord(llvm::BitstreamWriter &Out,
ModuleOrSourceFile MSF, StringWriter &FWriter) {
assert(MSF);
struct SourceFileListWriter {
StringWriter &FWriter;
llvm::SmallString<0> Buffer;
llvm::StringSet<> seenFilenames;
void emitSourceFileInfo(const BasicSourceFileInfo &info) {
// Make 'FilePath' absolute for serialization;
SmallString<128> absolutePath = info.FilePath;
llvm::sys::fs::make_absolute(absolutePath);
// Don't emit duplicated files.
if (!seenFilenames.insert(info.FilePath).second)
return;
auto fileID = FWriter.getTextOffset(absolutePath);
auto fingerprintStr = info.InterfaceHash.getRawValue();
auto timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(
info.LastModified.time_since_epoch())
.count();
llvm::raw_svector_ostream out(Buffer);
endian::Writer writer(out, little);
// FilePath.
writer.write<uint32_t>(fileID);
// InterfaceHash (fixed length string).
assert(fingerprintStr.size() == Fingerprint::DIGEST_LENGTH);
out << fingerprintStr;
// LastModified (nanoseconds since epoch).
writer.write<uint64_t>(timestamp);
// FileSize (num of bytes).
writer.write<uint64_t>(info.FileSize);
}
SourceFileListWriter(StringWriter &FWriter) : FWriter(FWriter) {
Buffer.reserve(1024);
}
} writer(FWriter);
if (SourceFile *SF = MSF.dyn_cast<SourceFile *>()) {
BasicSourceFileInfo info;
if (info.populate(SF))
return;
writer.emitSourceFileInfo(info);
} else {
auto *M = MSF.get<ModuleDecl *>();
M->collectBasicSourceFileInfo([&](const BasicSourceFileInfo &info) {
writer.emitSourceFileInfo(info);
});
}
const decl_locs_block::SourceFileListLayout FileList(Out);
SmallVector<uint64_t, 8> scratch;
FileList.emit(scratch, writer.Buffer);
}
class SourceInfoSerializer : public SerializerBase {
public:
using SerializerBase::SerializerBase;
@@ -807,6 +867,7 @@ public:
BLOCK_RECORD(control_block, TARGET);
BLOCK(DECL_LOCS_BLOCK);
BLOCK_RECORD(decl_locs_block, SOURCE_FILE_LIST);
BLOCK_RECORD(decl_locs_block, BASIC_DECL_LOCS);
BLOCK_RECORD(decl_locs_block, DECL_USRS);
BLOCK_RECORD(decl_locs_block, TEXT_DATA);
@@ -849,6 +910,7 @@ void serialization::writeSourceInfoToStream(raw_ostream &os,
DeclUSRsTableWriter USRWriter;
StringWriter FPWriter;
DocRangeWriter DocWriter;
emitFileListRecord(S.Out, DC, FPWriter);
emitBasicLocsRecord(S.Out, DC, USRWriter, FPWriter, DocWriter);
// Emit USR table mapping from a USR to USR Id.
// The basic locs record uses USR Id instead of actual USR, so that we

View File

@@ -1327,3 +1327,8 @@ SerializedASTFile::getDiscriminatorForPrivateValue(const ValueDecl *D) const {
assert(!discriminator.empty() && "no discriminator found for value");
return discriminator;
}
void SerializedASTFile::collectBasicSourceFileInfo(
llvm::function_ref<void(const BasicSourceFileInfo &)> callback) const {
File.collectBasicSourceFileInfo(callback);
}

View File

@@ -49,7 +49,7 @@ const uint16_t SWIFTSOURCEINFO_VERSION_MAJOR = 2;
/// interesting to test for. A backwards-compatible change is one where an \e
/// old compiler can read the new format without any problems (usually by
/// ignoring new information).
const uint16_t SWIFTSOURCEINFO_VERSION_MINOR = 0; // Last change: add doc comment ranges
const uint16_t SWIFTSOURCEINFO_VERSION_MINOR = 1; // Last change: add source file list
/// The hash seed used for the string hashes(llvm::djbHash) in a .swiftsourceinfo file.
const uint32_t SWIFTSOURCEINFO_HASH_SEED = 5387;
@@ -72,8 +72,14 @@ namespace decl_locs_block {
DECL_USRS,
TEXT_DATA,
DOC_RANGES,
SOURCE_FILE_LIST,
};
using SourceFileListLayout = BCRecordLayout<
SOURCE_FILE_LIST, // record ID
BCBlob // An array of fixed size 'BasicSourceFileInfo' data.
>;
using BasicDeclLocsLayout = BCRecordLayout<
BASIC_DECL_LOCS, // record ID
BCBlob // an array of fixed size location data

View File

@@ -0,0 +1,2 @@
public func foo(val: MyStruct) {
}

View File

@@ -0,0 +1,4 @@
public struct MyStruct {
var x: Int
var y: Int
}

View File

@@ -0,0 +1,10 @@
import MyModule
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/Modules)
// RUN: %target-swiftc_driver -emit-module -module-name MyModule -o %t/Modules/MyModule.swiftmodule %S/Inputs/SourceInfo/File1.swift %S/Inputs/SourceInfo/File2.swift
// RUN: %target-swift-ide-test -print-module-metadata -module-to-print MyModule -enable-swiftsourceinfo -I %t/Modules -source-filename %s | %FileCheck %s
// CHECK: filepath=SOURCE_DIR{{[/\\]}}test{{[/\\]}}Serialization{{[/\\]}}Inputs{{[/\\]}}SourceInfo{{[/\\]}}File1.swift; hash=b44bab617797a7239a9fa948f11eb90b; mtime={{[0-9]{4}-[0-9]{2}-[0-9]{2} .*}}; size=35
// CHECK: filepath=SOURCE_DIR{{[/\\]}}test{{[/\\]}}Serialization{{[/\\]}}Inputs{{[/\\]}}SourceInfo{{[/\\]}}File2.swift; hash=c989d6b98d505a1f52749d43ea0569a1; mtime={{[0-9]{4}-[0-9]{2}-[0-9]{2} .*}}; size=57

View File

@@ -2519,6 +2519,13 @@ static void printModuleMetadata(ModuleDecl *MD) {
OS << "link library: " << lib.getName()
<< ", force load: " << (lib.shouldForceLoad() ? "true" : "false") << "\n";
});
MD->collectBasicSourceFileInfo([&](const BasicSourceFileInfo &info) {
OS << "filepath=" << info.FilePath << "; ";
OS << "hash=" << info.InterfaceHash.getRawValue() << "; ";
OS << "mtime=" << info.LastModified << "; ";
OS << "size=" << info.FileSize;
OS << "\n";
});
}
static int doPrintModuleMetaData(const CompilerInvocation &InitInvok,