mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[incrParse] Reparse a node if the next leaf node has been modified
This commit is contained in:
@@ -15,6 +15,32 @@
|
|||||||
|
|
||||||
#include "swift/Syntax/SyntaxNodes.h"
|
#include "swift/Syntax/SyntaxNodes.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// A single edit to the original source file in which a continuous range of
|
||||||
|
/// characters have been replaced by a new string
|
||||||
|
struct SourceEdit {
|
||||||
|
/// The byte offset from which on characters were replaced.
|
||||||
|
size_t Start;
|
||||||
|
|
||||||
|
/// The byte offset to which on characters were replaced.
|
||||||
|
size_t End;
|
||||||
|
|
||||||
|
/// The length of the string that replaced the range described above.
|
||||||
|
size_t ReplacementLength;
|
||||||
|
|
||||||
|
/// The length of the range that has been replaced
|
||||||
|
size_t originalLength() { return End - Start; }
|
||||||
|
|
||||||
|
/// Check if the characters replaced by this edit fall into the given range
|
||||||
|
/// or are directly adjacent to it
|
||||||
|
bool intersectsOrTouchesRange(size_t RangeStart, size_t RangeEnd) {
|
||||||
|
return !(End <= RangeStart || Start >= RangeEnd);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
namespace swift {
|
namespace swift {
|
||||||
|
|
||||||
using namespace swift::syntax;
|
using namespace swift::syntax;
|
||||||
@@ -23,11 +49,29 @@ class SyntaxParsingCache {
|
|||||||
/// The syntax tree prior to the edit
|
/// The syntax tree prior to the edit
|
||||||
SourceFileSyntax OldSyntaxTree;
|
SourceFileSyntax OldSyntaxTree;
|
||||||
|
|
||||||
|
/// The edits that were made from the source file that created this cache to
|
||||||
|
/// the source file that is now parsed incrementally
|
||||||
|
llvm::SmallVector<SourceEdit, 4> Edits;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SyntaxParsingCache(SourceFileSyntax OldSyntaxTree)
|
SyntaxParsingCache(SourceFileSyntax OldSyntaxTree)
|
||||||
: OldSyntaxTree(OldSyntaxTree) {}
|
: OldSyntaxTree(OldSyntaxTree) {}
|
||||||
|
|
||||||
|
/// Add an edit that transformed the source file which created this cache into
|
||||||
|
/// the source file that is now being parsed incrementally. The order in which
|
||||||
|
/// the edits are added using this method needs to be the same order in which
|
||||||
|
/// the edits were applied to the source file.
|
||||||
|
void addEdit(size_t Start, size_t End, size_t ReplacementLength) {
|
||||||
|
Edits.push_back({Start, End, ReplacementLength});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a syntax node of the given kind at the given position can be
|
||||||
|
/// reused for a new syntax tree.
|
||||||
llvm::Optional<Syntax> lookUp(size_t NewPosition, SyntaxKind Kind) const;
|
llvm::Optional<Syntax> lookUp(size_t NewPosition, SyntaxKind Kind) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
llvm::Optional<Syntax> lookUpFrom(Syntax Node, size_t Position,
|
||||||
|
SyntaxKind Kind) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace swift
|
} // namespace swift
|
||||||
|
|||||||
@@ -108,4 +108,8 @@ struct ScalarEnumerationTraits<swift::syntax::SyntaxKind> {
|
|||||||
|
|
||||||
} // end namespace yaml
|
} // end namespace yaml
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
raw_ostream &operator<<(raw_ostream &OS, swift::syntax::SyntaxKind Kind);
|
||||||
|
} // end namespace llvm
|
||||||
#endif // SWIFT_SYNTAX_KIND_H
|
#endif // SWIFT_SYNTAX_KIND_H
|
||||||
|
|||||||
@@ -15,11 +15,28 @@
|
|||||||
using namespace swift;
|
using namespace swift;
|
||||||
using namespace swift::syntax;
|
using namespace swift::syntax;
|
||||||
|
|
||||||
llvm::Optional<Syntax> lookUpFrom(Syntax Node, size_t Position,
|
llvm::Optional<Syntax> SyntaxParsingCache::lookUpFrom(Syntax Node,
|
||||||
SyntaxKind Kind) {
|
size_t Position,
|
||||||
|
SyntaxKind Kind) const {
|
||||||
if (Node.getAbsolutePosition().getOffset() == Position &&
|
if (Node.getAbsolutePosition().getOffset() == Position &&
|
||||||
Node.getKind() == Kind) {
|
Node.getKind() == Kind) {
|
||||||
return Node;
|
// Check if this node has been edited. If it has, we cannot reuse it.
|
||||||
|
bool NodeEdited = false;
|
||||||
|
|
||||||
|
auto NodeStart = Node.getAbsolutePosition().getOffset();
|
||||||
|
auto NodeEnd = NodeStart + Node.getTextLength();
|
||||||
|
for (auto Edit : Edits) {
|
||||||
|
if (Edit.intersectsOrTouchesRange(NodeStart, NodeEnd)) {
|
||||||
|
NodeEdited = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Node can also not be reused if an edit has been made in the next
|
||||||
|
// token's leading trivia
|
||||||
|
|
||||||
|
if (!NodeEdited) {
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t I = 0, E = Node.getNumChildren(); I < E; ++I) {
|
for (size_t I = 0, E = Node.getNumChildren(); I < E; ++I) {
|
||||||
@@ -38,6 +55,15 @@ llvm::Optional<Syntax> lookUpFrom(Syntax Node, size_t Position,
|
|||||||
|
|
||||||
llvm::Optional<Syntax> SyntaxParsingCache::lookUp(size_t NewPosition,
|
llvm::Optional<Syntax> SyntaxParsingCache::lookUp(size_t NewPosition,
|
||||||
SyntaxKind Kind) const {
|
SyntaxKind Kind) const {
|
||||||
// FIXME: Transform the new position into the position in the old file
|
// Undo the edits in reverse order
|
||||||
return lookUpFrom(OldSyntaxTree, NewPosition, Kind);
|
size_t OldPosition = NewPosition;
|
||||||
|
for (auto I = Edits.rbegin(), E = Edits.rend(); I != E; ++I) {
|
||||||
|
auto Edit = *I;
|
||||||
|
if (Edit.End < OldPosition) {
|
||||||
|
OldPosition =
|
||||||
|
OldPosition - Edit.ReplacementLength + Edit.originalLength();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookUpFrom(OldSyntaxTree, OldPosition, Kind);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "swift/Syntax/SyntaxKind.h"
|
#include "swift/Syntax/SyntaxKind.h"
|
||||||
#include "swift/Syntax/TokenKinds.h"
|
#include "swift/Syntax/TokenKinds.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
namespace swift {
|
namespace swift {
|
||||||
|
|
||||||
@@ -123,3 +124,21 @@ SyntaxKind getUnknownKind(SyntaxKind Kind) {
|
|||||||
}
|
}
|
||||||
} // end namespace syntax
|
} // end namespace syntax
|
||||||
} // end namespace swift
|
} // end namespace swift
|
||||||
|
|
||||||
|
llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &OS,
|
||||||
|
swift::syntax::SyntaxKind Kind) {
|
||||||
|
switch (Kind) {
|
||||||
|
% for node in SYNTAX_NODES:
|
||||||
|
case swift::syntax::SyntaxKind::${node.syntax_kind}:
|
||||||
|
OS << "${node.syntax_kind}";
|
||||||
|
break;
|
||||||
|
% end
|
||||||
|
case swift::syntax::SyntaxKind::Token:
|
||||||
|
OS << "TokenSyntax";
|
||||||
|
break;
|
||||||
|
case swift::syntax::SyntaxKind::Unknown:
|
||||||
|
OS << "UnknownSyntax";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return OS;
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include "llvm/Support/Regex.h"
|
||||||
|
|
||||||
using namespace swift;
|
using namespace swift;
|
||||||
using namespace swift::syntax;
|
using namespace swift::syntax;
|
||||||
@@ -111,6 +112,17 @@ OldSyntaxTreeFilename("old-syntax-tree-filename",
|
|||||||
llvm::cl::desc("Path to the serialized syntax tree of "
|
llvm::cl::desc("Path to the serialized syntax tree of "
|
||||||
"the pre-edit file"));
|
"the pre-edit file"));
|
||||||
|
|
||||||
|
static llvm::cl::list<std::string>
|
||||||
|
IncrementalEdits("incremental-edit",
|
||||||
|
llvm::cl::desc("An edit that was applied to reach the input "
|
||||||
|
"file from the source file that generated the "
|
||||||
|
"old syntax tree in the format "
|
||||||
|
"<start>:<end>=<replacement> where <start> and "
|
||||||
|
"<end> are byte offsets in the original file "
|
||||||
|
"and <replacement> is the string that shall "
|
||||||
|
"replace the selected range. "
|
||||||
|
"Can be passed multiple times."));
|
||||||
|
|
||||||
static llvm::cl::opt<std::string>
|
static llvm::cl::opt<std::string>
|
||||||
OutputFilename("output-filename",
|
OutputFilename("output-filename",
|
||||||
llvm::cl::desc("Path to the output file"));
|
llvm::cl::desc("Path to the output file"));
|
||||||
@@ -321,6 +333,25 @@ int doIncrementalParse(const char *MainExecutablePath,
|
|||||||
}
|
}
|
||||||
SyntaxParsingCache *Cache = new SyntaxParsingCache(OldSyntaxTree.getValue());
|
SyntaxParsingCache *Cache = new SyntaxParsingCache(OldSyntaxTree.getValue());
|
||||||
|
|
||||||
|
for (auto EditPattern : options::IncrementalEdits) {
|
||||||
|
llvm::Regex MatchRegex("([0-9]+):([0-9]+)=(.*)");
|
||||||
|
SmallVector<StringRef, 4> Matches;
|
||||||
|
if (!MatchRegex.match(EditPattern, &Matches)) {
|
||||||
|
llvm::errs() << "Invalid edit pattern: " << EditPattern;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
int EditStart, EditEnd;
|
||||||
|
if (Matches[1].getAsInteger(10, EditStart)) {
|
||||||
|
llvm::errs() << "Could not parse edit start as integer: " << EditStart;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (Matches[2].getAsInteger(10, EditEnd)) {
|
||||||
|
llvm::errs() << "Could not parse edit end as integer: " << EditStart;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
Cache->addEdit(EditStart, EditEnd, /*ReplacmentLength=*/Matches[3].size());
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the new libSyntax tree incrementally
|
// Parse the new libSyntax tree incrementally
|
||||||
CompilerInstance Instance;
|
CompilerInstance Instance;
|
||||||
SourceFile *SF =
|
SourceFile *SF =
|
||||||
|
|||||||
Reference in New Issue
Block a user