mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user