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:
@@ -27,6 +27,10 @@
|
|||||||
namespace swift {
|
namespace swift {
|
||||||
namespace json {
|
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.
|
/// Serialization traits for SourcePresence.
|
||||||
template <>
|
template <>
|
||||||
struct ScalarEnumerationTraits<syntax::SourcePresence> {
|
struct ScalarEnumerationTraits<syntax::SourcePresence> {
|
||||||
@@ -141,8 +145,12 @@ struct ObjectTraits<syntax::RawSyntax> {
|
|||||||
}
|
}
|
||||||
auto presence = value.getPresence();
|
auto presence = value.getPresence();
|
||||||
out.mapRequired("presence", presence);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include "swift/Syntax/SyntaxNodes.h"
|
#include "swift/Syntax/SyntaxNodes.h"
|
||||||
|
|
||||||
#include "llvm/Support/ErrorHandling.h"
|
#include "llvm/Support/ErrorHandling.h"
|
||||||
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/MemoryBuffer.h"
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
#include "llvm/Support/Mutex.h"
|
#include "llvm/Support/Mutex.h"
|
||||||
|
|
||||||
@@ -2007,10 +2008,16 @@ const SourceManager &SwiftEditorDocument::getSourceManager() const {
|
|||||||
return Impl.SyntaxInfo->getSourceManager();
|
return Impl.SyntaxInfo->getSourceManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SourceManager &SwiftEditorDocument::getSourceManager() {
|
||||||
|
return Impl.SyntaxInfo->getSourceManager();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned SwiftEditorDocument::getBufferID() const {
|
unsigned SwiftEditorDocument::getBufferID() const {
|
||||||
return Impl.SyntaxInfo->getBufferID();
|
return Impl.SyntaxInfo->getBufferID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string SwiftEditorDocument::getFilePath() const { return Impl.FilePath; }
|
||||||
|
|
||||||
void SwiftEditorDocument::formatText(unsigned Line, unsigned Length,
|
void SwiftEditorDocument::formatText(unsigned Line, unsigned Length,
|
||||||
EditorConsumer &Consumer) {
|
EditorConsumer &Consumer) {
|
||||||
auto SyntaxInfo = Impl.getSyntaxInfo();
|
auto SyntaxInfo = Impl.getSyntaxInfo();
|
||||||
@@ -2234,11 +2241,97 @@ void SwiftLangSupport::editorClose(StringRef Name, bool RemoveCache) {
|
|||||||
// EditorReplaceText
|
// 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,
|
void SwiftLangSupport::editorReplaceText(StringRef Name,
|
||||||
llvm::MemoryBuffer *Buf,
|
llvm::MemoryBuffer *Buf,
|
||||||
unsigned Offset, unsigned Length,
|
unsigned Offset, unsigned Length,
|
||||||
EditorConsumer &Consumer) {
|
EditorConsumer &Consumer) {
|
||||||
bool LogReuseRegions = ::getenv("SOURCEKIT_LOG_INCREMENTAL_REUSE_REGIONS");
|
bool LogReuseRegions = ::getenv("SOURCEKIT_LOG_INCREMENTAL_REUSE_REGIONS");
|
||||||
|
bool ValidateSyntaxTree = ::getenv("SOURCEKIT_INCREMENTAL_PARSE_VALIDATION");
|
||||||
|
|
||||||
auto EditorDoc = EditorDocuments.getByUnresolvedName(Name);
|
auto EditorDoc = EditorDocuments.getByUnresolvedName(Name);
|
||||||
if (!EditorDoc) {
|
if (!EditorDoc) {
|
||||||
@@ -2248,6 +2341,13 @@ void SwiftLangSupport::editorReplaceText(StringRef Name,
|
|||||||
|
|
||||||
ImmutableTextSnapshotRef Snapshot;
|
ImmutableTextSnapshotRef Snapshot;
|
||||||
if (Length != 0 || Buf->getBufferSize() != 0) {
|
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,
|
Snapshot = EditorDoc->replaceText(Offset, Length, Buf,
|
||||||
Consumer.needsSemanticInfo());
|
Consumer.needsSemanticInfo());
|
||||||
assert(Snapshot);
|
assert(Snapshot);
|
||||||
@@ -2310,6 +2410,11 @@ void SwiftLangSupport::editorReplaceText(StringRef Name,
|
|||||||
} else {
|
} else {
|
||||||
Consumer.handleSyntaxReuseRegions({});
|
Consumer.handleSyntaxReuseRegions({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ValidateSyntaxTree) {
|
||||||
|
verifyIncrementalParse(EditorDoc, Offset, Length, PreEditText,
|
||||||
|
Buf->getBuffer());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Snapshot = EditorDoc->getLatestSnapshot();
|
Snapshot = EditorDoc->getLatestSnapshot();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,9 +111,12 @@ public:
|
|||||||
const llvm::Optional<swift::SourceFileSyntax> &getSyntaxTree() const;
|
const llvm::Optional<swift::SourceFileSyntax> &getSyntaxTree() const;
|
||||||
|
|
||||||
const swift::SourceManager &getSourceManager() const;
|
const swift::SourceManager &getSourceManager() const;
|
||||||
|
swift::SourceManager &getSourceManager();
|
||||||
|
|
||||||
/// Get the buffer ID of this file in its source manager
|
/// Get the buffer ID of this file in its source manager
|
||||||
unsigned getBufferID() const;
|
unsigned getBufferID() const;
|
||||||
|
|
||||||
|
std::string getFilePath() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef IntrusiveRefCntPtr<SwiftEditorDocument> SwiftEditorDocumentRef;
|
typedef IntrusiveRefCntPtr<SwiftEditorDocument> SwiftEditorDocumentRef;
|
||||||
|
|||||||
Reference in New Issue
Block a user