[incrParse] Add validation of incremental parsing

If enabled using the environment variable
SOURCEKIT_INCREMENTAL_PARSE_VALIDATION, the incrementally parsed syntax
tree will be compared to the from-scratch parsing syntax tree. If they
differ a warning is emitted and log files showing the difference written
to a temporary directory.
This commit is contained in:
Alex Hoppen
2018-07-13 15:32:19 -07:00
parent 6635be10ea
commit 57ccdd89b6
3 changed files with 118 additions and 2 deletions

View File

@@ -27,6 +27,10 @@
namespace swift {
namespace json {
/// The associated value will be interpreted as \c bool. If \c true the node IDs
/// will not be included in the serialized JSON.
static void *DontSerializeNodeIdsUserInfoKey = &DontSerializeNodeIdsUserInfoKey;
/// Serialization traits for SourcePresence.
template <>
struct ScalarEnumerationTraits<syntax::SourcePresence> {
@@ -141,8 +145,12 @@ struct ObjectTraits<syntax::RawSyntax> {
}
auto presence = value.getPresence();
out.mapRequired("presence", presence);
auto nodeId = value.getId();
out.mapRequired("id", nodeId);
bool omitNodeId = (bool)out.getUserInfo()[DontSerializeNodeIdsUserInfoKey];
if (!omitNodeId) {
auto nodeId = value.getId();
out.mapRequired("id", nodeId);
}
}
};

View File

@@ -41,6 +41,7 @@
#include "swift/Syntax/SyntaxNodes.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mutex.h"
@@ -2007,10 +2008,16 @@ const SourceManager &SwiftEditorDocument::getSourceManager() const {
return Impl.SyntaxInfo->getSourceManager();
}
SourceManager &SwiftEditorDocument::getSourceManager() {
return Impl.SyntaxInfo->getSourceManager();
}
unsigned SwiftEditorDocument::getBufferID() const {
return Impl.SyntaxInfo->getBufferID();
}
std::string SwiftEditorDocument::getFilePath() const { return Impl.FilePath; }
void SwiftEditorDocument::formatText(unsigned Line, unsigned Length,
EditorConsumer &Consumer) {
auto SyntaxInfo = Impl.getSyntaxInfo();
@@ -2234,11 +2241,97 @@ void SwiftLangSupport::editorClose(StringRef Name, bool RemoveCache) {
// EditorReplaceText
//===----------------------------------------------------------------------===//
void verifyIncrementalParse(SwiftEditorDocumentRef EditorDoc,
unsigned EditOffset, unsigned EditLength,
StringRef PreEditText, StringRef ReplaceText) {
swift::json::Output::UserInfoMap JsonUserInfo;
JsonUserInfo[swift::json::DontSerializeNodeIdsUserInfoKey] =
reinterpret_cast<void *>(true);
// Dump the incremental syntax tree
std::string IncrTreeString;
llvm::raw_string_ostream IncrTreeStream(IncrTreeString);
swift::json::Output IncrTreeOutput(IncrTreeStream, JsonUserInfo);
IncrTreeOutput << *EditorDoc->getSyntaxTree()->getRaw();
// Reparse the file from scratch
CompilerInvocation Invocation;
Invocation.getLangOptions().BuildSyntaxTree = true;
std::vector<std::string> Args;
SwiftDocumentSyntaxInfo ScratchSyntaxInfo(Invocation,
EditorDoc->getLatestSnapshot(),
Args, EditorDoc->getFilePath());
ScratchSyntaxInfo.parse();
// Dump the from-scratch syntax tree
std::string FromScratchTreeString;
llvm::raw_string_ostream ScratchTreeStream(FromScratchTreeString);
swift::json::Output ScratchTreeOutput(ScratchTreeStream, JsonUserInfo);
auto SyntaxRoot = ScratchSyntaxInfo.getSourceFile().getSyntaxRoot();
ScratchTreeOutput << *SyntaxRoot.getRaw();
// If the serialized format of the two trees doesn't match incremental parsing
// we have found an error.
if (IncrTreeStream.str().compare(ScratchTreeStream.str())) {
LOG_SECTION("Incremental Parsing", Warning) {
Log->getOS() << "Incremental parsing different to from scratch parsing\n";
Log->getOS() << "Edit was " << EditOffset << "-"
<< (EditOffset + EditLength) << "='" << ReplaceText << "'"
<< " pre-edit-text: '" << PreEditText << "'\n";
SmallString<32> DirectoryName;
if (llvm::sys::fs::createUniqueDirectory(
"SourceKit-IncrementalParsing-Inconsistency", DirectoryName)) {
Log->getOS() << "Failed to create log directory\n";
}
std::error_code ErrorCode;
// Write the incremental syntax tree
auto IncrTreeFilename = DirectoryName + "/incrementalTree.json";
llvm::raw_fd_ostream IncrementalFilestream(
IncrTreeFilename.str(), ErrorCode, llvm::sys::fs::F_RW);
IncrementalFilestream << IncrTreeStream.str();
if (ErrorCode) {
Log->getOS() << "Failed to write incremental syntax tree to "
<< IncrTreeFilename << "(error code " << ErrorCode.value()
<< ": " << ErrorCode.message() << ")\n";
} else {
Log->getOS() << "Incremental syntax tree written to "
<< IncrTreeFilename << '\n';
}
// Write from-scratch syntax tree
auto ScratchTreeFilename = DirectoryName + "/fromScratchTree.json";
llvm::raw_fd_ostream ScratchTreeFilestream(
ScratchTreeFilename.str(), ErrorCode, llvm::sys::fs::F_RW);
ScratchTreeFilestream << ScratchTreeStream.str();
if (ErrorCode) {
Log->getOS() << "Failed to write from-scratch syntax tree to "
<< ScratchTreeFilename << "(error code "
<< ErrorCode.value() << ": " << ErrorCode.message()
<< ")\n";
} else {
Log->getOS() << "From-scratch syntax tree written to "
<< ScratchTreeFilename << '\n';
}
// Write source file
auto SourceFilename = DirectoryName + "/postEditSource.swift";
llvm::raw_fd_ostream SourceFilestream(SourceFilename.str(), ErrorCode,
llvm::sys::fs::F_RW);
auto FileBuffer = EditorDoc->getLatestSnapshot()->getBuffer();
SourceFilestream << FileBuffer->getText();
}
}
}
void SwiftLangSupport::editorReplaceText(StringRef Name,
llvm::MemoryBuffer *Buf,
unsigned Offset, unsigned Length,
EditorConsumer &Consumer) {
bool LogReuseRegions = ::getenv("SOURCEKIT_LOG_INCREMENTAL_REUSE_REGIONS");
bool ValidateSyntaxTree = ::getenv("SOURCEKIT_INCREMENTAL_PARSE_VALIDATION");
auto EditorDoc = EditorDocuments.getByUnresolvedName(Name);
if (!EditorDoc) {
@@ -2248,6 +2341,13 @@ void SwiftLangSupport::editorReplaceText(StringRef Name,
ImmutableTextSnapshotRef Snapshot;
if (Length != 0 || Buf->getBufferSize() != 0) {
std::string PreEditText;
if (ValidateSyntaxTree) {
auto CurBuffer = EditorDoc->getLatestSnapshot()->getBuffer();
auto BufferStart = CurBuffer->getInternalBuffer()->getBufferStart();
StringRef PreEditTextRef(BufferStart + Offset, Length);
PreEditText = PreEditTextRef.str();
}
Snapshot = EditorDoc->replaceText(Offset, Length, Buf,
Consumer.needsSemanticInfo());
assert(Snapshot);
@@ -2310,6 +2410,11 @@ void SwiftLangSupport::editorReplaceText(StringRef Name,
} else {
Consumer.handleSyntaxReuseRegions({});
}
if (ValidateSyntaxTree) {
verifyIncrementalParse(EditorDoc, Offset, Length, PreEditText,
Buf->getBuffer());
}
} else {
Snapshot = EditorDoc->getLatestSnapshot();
}

View File

@@ -111,9 +111,12 @@ public:
const llvm::Optional<swift::SourceFileSyntax> &getSyntaxTree() const;
const swift::SourceManager &getSourceManager() const;
swift::SourceManager &getSourceManager();
/// Get the buffer ID of this file in its source manager
unsigned getBufferID() const;
std::string getFilePath() const;
};
typedef IntrusiveRefCntPtr<SwiftEditorDocument> SwiftEditorDocumentRef;