18#include "llvm/ADT/PriorityQueue.h"
23#include <unordered_set>
36 Mapping(Mapping &&
Other) =
default;
37 Mapping &operator=(Mapping &&
Other) =
default;
39 Mapping(
size_t Size) {
40 SrcToDst = std::make_unique<NodeId[]>(Size);
41 DstToSrc = std::make_unique<NodeId[]>(Size);
44 void link(NodeId Src, NodeId Dst) {
45 SrcToDst[Src] = Dst, DstToSrc[Dst] = Src;
48 NodeId getDst(NodeId Src)
const {
return SrcToDst[Src]; }
49 NodeId getSrc(NodeId Dst)
const {
return DstToSrc[Dst]; }
50 bool hasSrc(NodeId Src)
const {
return getDst(Src).isValid(); }
51 bool hasDst(NodeId Dst)
const {
return getSrc(Dst).isValid(); }
54 std::unique_ptr<NodeId[]> SrcToDst, DstToSrc;
76 assert(&*
Tree == &
T2 &&
"Invalid tree.");
88 bool haveSameParents(
const Mapping &M,
NodeId Id1,
NodeId Id2)
const;
92 void addOptimalMapping(Mapping &M,
NodeId Id1,
NodeId Id2)
const;
96 double getJaccardSimilarity(
const Mapping &M,
NodeId Id1,
NodeId Id2)
const;
99 NodeId findCandidate(
const Mapping &M,
NodeId Id1)
const;
102 Mapping matchTopDown()
const;
105 void matchBottomUp(Mapping &M)
const;
162 void setLeftMostDescendants();
190 int Id = 0, Depth = 0;
194 PreorderVisitor(SyntaxTree::Impl &Tree) :
Tree(
Tree) {}
196 template <
class T> std::tuple<NodeId, NodeId> PreTraverse(
T *ASTNode) {
198 Tree.Nodes.emplace_back();
199 Node &N =
Tree.getMutableNode(MyId);
203 assert(!N.ASTNode.getNodeKind().isNone() &&
204 "Expected nodes to have a valid kind.");
207 P.Children.push_back(MyId);
212 return std::make_tuple(MyId,
Tree.getNode(MyId).Parent);
214 void PostTraverse(std::tuple<NodeId, NodeId> State) {
215 NodeId MyId, PreviousParent;
216 std::tie(MyId, PreviousParent) = State;
217 assert(MyId.isValid() &&
"Expecting to only traverse valid nodes.");
220 Node &N =
Tree.getMutableNode(MyId);
221 N.RightMostDescendant =
Id - 1;
222 assert(N.RightMostDescendant >= 0 &&
223 N.RightMostDescendant <
Tree.getSize() &&
224 "Rightmost descendant must be a valid tree node.");
226 Tree.Leaves.push_back(MyId);
228 for (NodeId Child : N.Children)
229 N.Height = std::max(N.Height, 1 +
Tree.getNode(Child).Height);
231 bool TraverseDecl(
Decl *
D) {
234 auto SavedState = PreTraverse(
D);
236 PostTraverse(SavedState);
239 bool TraverseStmt(
Stmt *S) {
240 if (
auto *
E = dyn_cast_or_null<Expr>(S))
244 auto SavedState = PreTraverse(S);
246 PostTraverse(SavedState);
249 bool TraverseType(
QualType T) {
return true; }
253 auto SavedState = PreTraverse(
Init);
255 PostTraverse(SavedState);
268 PreorderVisitor PreorderWalker(*
this);
269 PreorderWalker.TraverseDecl(N);
275 PreorderVisitor PreorderWalker(*
this);
276 PreorderWalker.TraverseStmt(N);
282 std::vector<NodeId> Postorder;
287 Postorder.push_back(
Id);
295 std::vector<NodeId> Ids;
298 while (Expanded < Ids.size())
299 for (
NodeId Child :
Tree.getNode(Ids[Expanded++]).Children)
300 Ids.push_back(Child);
304void SyntaxTree::Impl::initTree() {
305 setLeftMostDescendants();
307 PostorderIds.resize(
getSize());
308 std::function<void(NodeId)> PostorderTraverse = [&](NodeId
Id) {
310 PostorderTraverse(Child);
311 PostorderIds[
Id] = PostorderId;
318void SyntaxTree::Impl::setLeftMostDescendants() {
319 for (NodeId Leaf : Leaves) {
320 getMutableNode(Leaf).LeftMostDescendant = Leaf;
321 NodeId
Parent, Cur = Leaf;
325 getMutableNode(Cur).LeftMostDescendant = Leaf;
344 for (
size_t I = 0,
E = Siblings.size(); I <
E; ++I) {
347 if (Siblings[I] ==
Id) {
352 llvm_unreachable(
"Node not found in parent's children.");
361 std::string ContextPrefix;
364 if (
auto *Namespace = dyn_cast<NamespaceDecl>(Context))
365 ContextPrefix = Namespace->getQualifiedNameAsString();
366 else if (
auto *
Record = dyn_cast<RecordDecl>(Context))
367 ContextPrefix =
Record->getQualifiedNameAsString();
368 else if (AST.getLangOpts().CPlusPlus11)
369 if (
auto *Tag = dyn_cast<TagDecl>(Context))
370 ContextPrefix = Tag->getQualifiedNameAsString();
374 if (!ContextPrefix.empty() && StringRef(Val).starts_with(ContextPrefix))
375 Val = Val.substr(ContextPrefix.size() + 1);
389 const auto &
P = Parents[0];
390 if (
const auto *
D =
P.get<
Decl>())
399 if (
Init->isAnyMemberInitializer())
400 return std::string(
Init->getAnyMember()->getName());
401 if (
Init->isBaseInitializer())
403 if (
Init->isDelegatingInitializer())
404 return Init->getTypeSourceInfo()->getType().getAsString(TypePP);
405 llvm_unreachable(
"Unknown initializer type");
415 return getStmtValue(S);
417 return getDeclValue(
D);
420 llvm_unreachable(
"Fatal: unhandled AST node.\n");
425 if (
auto *
V = dyn_cast<ValueDecl>(
D))
426 return getRelativeName(
V) +
"(" +
V->getType().getAsString(TypePP) +
")";
427 if (
auto *N = dyn_cast<NamedDecl>(
D))
428 Value += getRelativeName(N) +
";";
429 if (
auto *
T = dyn_cast<TypedefNameDecl>(
D))
430 return Value +
T->getUnderlyingType().getAsString(TypePP) +
";";
431 if (
auto *
T = dyn_cast<TypeDecl>(
D))
432 if (
T->getTypeForDecl())
436 if (
auto *
U = dyn_cast<UsingDirectiveDecl>(
D))
437 return std::string(
U->getNominatedNamespace()->getName());
438 if (
auto *A = dyn_cast<AccessSpecDecl>(
D)) {
447 if (
auto *
U = dyn_cast<UnaryOperator>(S))
449 if (
auto *B = dyn_cast<BinaryOperator>(S))
450 return std::string(B->getOpcodeStr());
451 if (
auto *M = dyn_cast<MemberExpr>(S))
452 return getRelativeName(M->getMemberDecl());
453 if (
auto *I = dyn_cast<IntegerLiteral>(S)) {
455 I->getValue().toString(Str, 10,
false);
456 return std::string(Str);
458 if (
auto *F = dyn_cast<FloatingLiteral>(S)) {
460 F->getValue().toString(Str);
461 return std::string(Str);
463 if (
auto *
D = dyn_cast<DeclRefExpr>(S))
465 if (
auto *String = dyn_cast<StringLiteral>(S))
466 return std::string(String->getString());
467 if (
auto *B = dyn_cast<CXXBoolLiteralExpr>(S))
468 return B->getValue() ?
"true" :
"false";
490 std::vector<NodeId> RootIds;
492 std::vector<SNodeId> LeftMostDescendants;
499 int NumLeaves = setLeftMostDescendants();
500 computeKeyRoots(NumLeaves);
502 int getSize()
const {
return RootIds.size(); }
504 assert(
Id > 0 &&
Id <= getSize() &&
"Invalid subtree node index.");
505 return RootIds[
Id - 1];
508 return Tree.getNode(getIdInRoot(
Id));
511 assert(
Id > 0 &&
Id <= getSize() &&
"Invalid subtree node index.");
512 return LeftMostDescendants[
Id - 1];
519 return Tree.getNodeValue(getIdInRoot(
Id));
524 int setLeftMostDescendants() {
526 LeftMostDescendants.resize(getSize());
527 for (
int I = 0; I < getSize(); ++I) {
531 assert(I ==
Tree.PostorderIds[getIdInRoot(SI)] - getPostorderOffset() &&
532 "Postorder traversal in subtree should correspond to traversal in "
533 "the root tree by a constant offset.");
535 getPostorderOffset());
539 void computeKeyRoots(
int Leaves) {
540 KeyRoots.resize(Leaves);
541 std::unordered_set<int>
Visited;
543 for (SNodeId I(getSize()); I > 0; --I) {
544 SNodeId LeftDesc = getLeftMostDescendant(I);
547 assert(K >= 0 &&
"K should be non-negative");
562 std::unique_ptr<std::unique_ptr<double[]>[]> TreeDist, ForestDist;
567 : DiffImpl(DiffImpl), S1(T1, Id1), S2(T2, Id2) {
568 TreeDist = std::make_unique<std::unique_ptr<double[]>[]>(
570 ForestDist = std::make_unique<std::unique_ptr<double[]>[]>(
572 for (
int I = 0,
E = S1.
getSize() + 1; I <
E; ++I) {
573 TreeDist[I] = std::make_unique<double[]>(
size_t(S2.
getSize()) + 1);
574 ForestDist[I] = std::make_unique<double[]>(
size_t(S2.
getSize()) + 1);
579 std::vector<std::pair<NodeId, NodeId>> Matches;
580 std::vector<std::pair<SNodeId, SNodeId>> TreePairs;
584 bool RootNodePair =
true;
588 while (!TreePairs.empty()) {
589 SNodeId LastRow, LastCol, FirstRow, FirstCol, Row, Col;
590 std::tie(LastRow, LastCol) = TreePairs.back();
591 TreePairs.pop_back();
594 computeForestDist(LastRow, LastCol);
597 RootNodePair =
false;
605 while (Row > FirstRow || Col > FirstCol) {
606 if (Row > FirstRow &&
607 ForestDist[Row - 1][Col] + 1 == ForestDist[Row][Col]) {
609 }
else if (Col > FirstCol &&
610 ForestDist[Row][Col - 1] + 1 == ForestDist[Row][Col]) {
619 assert(DiffImpl.isMatchingPossible(Id1, Id2) &&
620 "These nodes must not be matched.");
621 Matches.emplace_back(Id1, Id2);
625 TreePairs.emplace_back(Row, Col);
641 static constexpr double DeletionCost = 1;
642 static constexpr double InsertionCost = 1;
646 return std::numeric_limits<double>::max();
650 void computeTreeDist() {
653 computeForestDist(Id1, Id2);
656 void computeForestDist(SNodeId Id1, SNodeId Id2) {
657 assert(Id1 > 0 && Id2 > 0 &&
"Expecting offsets greater than 0.");
661 ForestDist[LMD1][LMD2] = 0;
662 for (SNodeId D1 = LMD1 + 1; D1 <= Id1; ++D1) {
663 ForestDist[D1][LMD2] = ForestDist[D1 - 1][LMD2] + DeletionCost;
664 for (SNodeId D2 = LMD2 + 1; D2 <= Id2; ++D2) {
665 ForestDist[LMD1][D2] = ForestDist[LMD1][D2 - 1] + InsertionCost;
668 if (DLMD1 == LMD1 && DLMD2 == LMD2) {
669 double UpdateCost = getUpdateCost(D1, D2);
671 std::min({ForestDist[D1 - 1][D2] + DeletionCost,
672 ForestDist[D1][D2 - 1] + InsertionCost,
673 ForestDist[D1 - 1][D2 - 1] + UpdateCost});
674 TreeDist[D1][D2] = ForestDist[D1][D2];
677 std::min({ForestDist[D1 - 1][D2] + DeletionCost,
678 ForestDist[D1][D2 - 1] + InsertionCost,
679 ForestDist[DLMD1][DLMD2] + TreeDist[D1][D2]});
691 if (
auto *ND = ASTNode.get<
NamedDecl>()) {
692 if (ND->getDeclName().isIdentifier())
693 return ND->getQualifiedNameAsString();
699 if (
auto *ND = ASTNode.get<
NamedDecl>()) {
700 if (ND->getDeclName().isIdentifier())
701 return ND->getName();
711 bool operator()(NodeId Id1, NodeId Id2)
const {
712 return Tree.getNode(Id1).Height <
Tree.getNode(Id2).Height;
720 const SyntaxTree::Impl &
Tree;
722 std::vector<NodeId> Container;
723 PriorityQueue<NodeId, std::vector<NodeId>, HeightLess> List;
726 PriorityList(
const SyntaxTree::Impl &
Tree)
729 void push(NodeId
id) { List.push(
id); }
731 std::vector<NodeId> pop() {
733 std::vector<NodeId> Result;
736 while (peekMax() ==
Max) {
737 Result.push_back(List.top());
744 int peekMax()
const {
747 return Tree.getNode(List.top()).Height;
749 void open(NodeId
Id) {
750 for (NodeId Child :
Tree.getNode(
Id).Children)
756bool ASTDiff::Impl::identical(NodeId Id1, NodeId Id2)
const {
757 const Node &N1 = T1.getNode(Id1);
758 const Node &N2 = T2.getNode(Id2);
759 if (N1.Children.size() != N2.Children.size() ||
760 !isMatchingPossible(Id1, Id2) ||
761 T1.getNodeValue(Id1) != T2.getNodeValue(Id2))
763 for (
size_t Id = 0,
E = N1.Children.size();
Id <
E; ++
Id)
764 if (!identical(N1.Children[
Id], N2.Children[
Id]))
769bool ASTDiff::Impl::isMatchingPossible(NodeId Id1, NodeId Id2)
const {
770 return Options.isMatchingAllowed(T1.getNode(Id1), T2.getNode(Id2));
773bool ASTDiff::Impl::haveSameParents(
const Mapping &M, NodeId Id1,
775 NodeId P1 = T1.getNode(Id1).Parent;
776 NodeId P2 = T2.getNode(Id2).Parent;
777 return (P1.isInvalid() && P2.isInvalid()) ||
778 (P1.isValid() && P2.isValid() && M.getDst(P1) == P2);
781void ASTDiff::Impl::addOptimalMapping(Mapping &M, NodeId Id1,
783 if (std::max(T1.getNumberOfDescendants(Id1), T2.getNumberOfDescendants(Id2)) >
786 ZhangShashaMatcher Matcher(*
this, T1, T2, Id1, Id2);
787 std::vector<std::pair<NodeId, NodeId>> R = Matcher.getMatchingNodes();
788 for (
const auto &Tuple : R) {
789 NodeId Src = Tuple.first;
790 NodeId Dst = Tuple.second;
791 if (!M.hasSrc(Src) && !M.hasDst(Dst))
796double ASTDiff::Impl::getJaccardSimilarity(
const Mapping &M, NodeId Id1,
798 int CommonDescendants = 0;
799 const Node &N1 = T1.getNode(Id1);
801 for (NodeId Src = Id1 + 1; Src <= N1.RightMostDescendant; ++Src) {
802 NodeId Dst = M.getDst(Src);
803 CommonDescendants +=
int(Dst.isValid() && T2.isInSubtree(Dst, Id2));
806 double Denominator = T1.getNumberOfDescendants(Id1) - 1 +
807 T2.getNumberOfDescendants(Id2) - 1 - CommonDescendants;
809 assert(Denominator >= 0 &&
"Expected non-negative denominator.");
810 if (Denominator == 0)
812 return CommonDescendants / Denominator;
815NodeId ASTDiff::Impl::findCandidate(
const Mapping &M, NodeId Id1)
const {
817 double HighestSimilarity = 0.0;
818 for (NodeId Id2 : T2) {
819 if (!isMatchingPossible(Id1, Id2))
823 double Similarity = getJaccardSimilarity(M, Id1, Id2);
824 if (Similarity >= Options.MinSimilarity && Similarity > HighestSimilarity) {
825 HighestSimilarity = Similarity;
832void ASTDiff::Impl::matchBottomUp(Mapping &M)
const {
834 for (NodeId Id1 : Postorder) {
835 if (Id1 == T1.getRootId() && !M.hasSrc(T1.getRootId()) &&
836 !M.hasDst(T2.getRootId())) {
837 if (isMatchingPossible(T1.getRootId(), T2.getRootId())) {
838 M.link(T1.getRootId(), T2.getRootId());
839 addOptimalMapping(M, T1.getRootId(), T2.getRootId());
843 bool Matched = M.hasSrc(Id1);
844 const Node &N1 = T1.getNode(Id1);
845 bool MatchedChildren = llvm::any_of(
846 N1.Children, [&](NodeId Child) { return M.hasSrc(Child); });
847 if (Matched || !MatchedChildren)
849 NodeId Id2 = findCandidate(M, Id1);
852 addOptimalMapping(M, Id1, Id2);
857Mapping ASTDiff::Impl::matchTopDown()
const {
861 Mapping M(T1.getSize() + T2.getSize());
863 L1.push(T1.getRootId());
864 L2.push(T2.getRootId());
867 while (std::min(Max1 = L1.peekMax(), Max2 = L2.peekMax()) >
870 for (NodeId
Id : L1.pop())
875 for (NodeId
Id : L2.pop())
879 std::vector<NodeId> H1, H2;
882 for (NodeId Id1 : H1) {
883 for (NodeId Id2 : H2) {
884 if (identical(Id1, Id2) && !M.hasSrc(Id1) && !M.hasDst(Id2)) {
885 for (
int I = 0,
E = T1.getNumberOfDescendants(Id1); I <
E; ++I)
886 M.link(Id1 + I, Id2 + I);
890 for (NodeId Id1 : H1) {
894 for (NodeId Id2 : H2) {
904 : T1(T1), T2(T2), Options(Options) {
910 TheMapping = matchTopDown();
911 if (Options.StopAfterTopDown)
913 matchBottomUp(TheMapping);
918 if (!M.hasSrc(Id1)) {
919 T1.getMutableNode(Id1).Change =
Delete;
920 T1.getMutableNode(Id1).Shift -= 1;
924 if (!M.hasDst(Id2)) {
925 T2.getMutableNode(Id2).Change =
Insert;
926 T2.getMutableNode(Id2).Shift -= 1;
929 for (
NodeId Id1 : T1.NodesBfs) {
930 NodeId Id2 = M.getDst(Id1);
933 if (!haveSameParents(M, Id1, Id2) ||
934 T1.findPositionInParent(Id1,
true) !=
935 T2.findPositionInParent(Id2,
true)) {
936 T1.getMutableNode(Id1).Shift -= 1;
937 T2.getMutableNode(Id2).Shift -= 1;
940 for (
NodeId Id2 : T2.NodesBfs) {
941 NodeId Id1 = M.getSrc(Id2);
944 Node &N1 = T1.getMutableNode(Id1);
945 Node &N2 = T2.getMutableNode(Id2);
948 if (!haveSameParents(M, Id1, Id2) ||
949 T1.findPositionInParent(Id1,
true) !=
950 T2.findPositionInParent(Id2,
true)) {
953 if (T1.getNodeValue(Id1) != T2.getNodeValue(Id2)) {
961 : DiffImpl(
std::make_unique<
Impl>(*T1.TreeImpl, *T2.TreeImpl, Options)) {}
971 this, AST.getTranslationUnitDecl(), AST)) {}
992std::pair<unsigned, unsigned>
1000 if (ThisExpr->isImplicit())
1005 return {
Begin, End};
llvm::DenseSet< const void * > Visited
llvm::MachO::Record Record
static Expected< DynTypedNode > getNode(const ast_matchers::BoundNodes &Nodes, StringRef ID)
Defines the SourceManager interface.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
Represents a C++ base or member initializer.
bool isWritten() const
Determine whether this initializer is explicitly written in the source code.
Represents the this expression in C++.
Represents a character-granular source range.
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Decl - This represents one declaration (or definition), e.g.
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
DeclContext * getDeclContext()
const T * get() const
Retrieve the stored node as type T.
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
Expr * IgnoreImplicit() LLVM_READONLY
Skip past any implicit AST nodes which might surround this expression until reaching a fixed point.
static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)
Returns a string for the source that the range encompasses.
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
This represents a decl that may have a name.
std::string getQualifiedNameAsString() const
A (possibly-)qualified type.
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue=nullptr)
Recursively visit a statement or expression, by dispatching to Traverse*() based on the argument's dy...
bool TraverseDecl(Decl *D)
Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...
bool TraverseConstructorInitializer(CXXCtorInitializer *Init)
Recursively visit a constructor initializer.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
This class handles loading and caching of source files into memory.
unsigned getFileOffset(SourceLocation SpellingLoc) const
Returns the offset from the start of the file that the specified SourceLocation represents.
bool isInMainFile(SourceLocation Loc) const
Returns whether the PresumedLoc for a given SourceLocation is in the main file.
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID.
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
Stmt - This represents one statement.
QualType getCanonicalTypeInternal() const
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
void computeChangeKinds(Mapping &M)
NodeId getMapped(const std::unique_ptr< SyntaxTree::Impl > &Tree, NodeId Id) const
Impl(SyntaxTree::Impl &T1, SyntaxTree::Impl &T2, const ComparisonOptions &Options)
void computeMapping()
Matches nodes one-by-one based on their similarity.
ASTDiff(SyntaxTree &Src, SyntaxTree &Dst, const ComparisonOptions &Options)
NodeId getMapped(const SyntaxTree &SourceTree, NodeId Id) const
const Node & getNode(SNodeId Id) const
SNodeId getLeftMostDescendant(SNodeId Id) const
Subtree(const SyntaxTree::Impl &Tree, NodeId SubtreeRoot)
std::vector< SNodeId > KeyRoots
NodeId getPostorderOffset() const
Returns the postorder index of the leftmost descendant in the subtree.
NodeId getIdInRoot(SNodeId Id) const
std::string getNodeValue(SNodeId Id) const
Represents the AST of a TranslationUnit.
std::string getRelativeName(const NamedDecl *ND, const DeclContext *Context) const
int getNumberOfDescendants(NodeId Id) const
bool isValidNodeId(NodeId Id) const
std::vector< NodeId > Leaves
bool isInSubtree(NodeId Id, NodeId SubtreeRoot) const
PreorderIterator begin() const
std::vector< Node > Nodes
Nodes in preorder.
PreorderIterator end() const
int findPositionInParent(NodeId Id, bool Shifted=false) const
const Node & getNode(NodeId Id) const
std::string getStmtValue(const Stmt *S) const
std::vector< int > PostorderIds
std::string getNodeValue(NodeId Id) const
Impl(SyntaxTree *Parent, std::enable_if_t< std::is_base_of_v< Decl, T >, T > *Node, ASTContext &AST)
Impl(SyntaxTree *Parent, std::enable_if_t< std::is_base_of_v< Stmt, T >, T > *Node, ASTContext &AST)
std::string getDeclValue(const Decl *D) const
std::vector< NodeId > NodesBfs
Impl(SyntaxTree *Parent, ASTContext &AST)
Node & getMutableNode(NodeId Id)
SyntaxTree objects represent subtrees of the AST.
const Node & getNode(NodeId Id) const
int findPositionInParent(NodeId Id) const
const ASTContext & getASTContext() const
PreorderIterator begin() const
std::pair< unsigned, unsigned > getSourceRangeOffsets(const Node &N) const
SyntaxTree(ASTContext &AST)
Constructs a tree from a translation unit.
std::unique_ptr< Impl > TreeImpl
PreorderIterator end() const
std::string getNodeValue(NodeId Id) const
Serialize the node attributes to a string representation.
Implementation of Zhang and Shasha's Algorithm for tree edit distance.
ZhangShashaMatcher(const ASTDiff::Impl &DiffImpl, const SyntaxTree::Impl &T1, const SyntaxTree::Impl &T2, NodeId Id1, NodeId Id2)
std::vector< std::pair< NodeId, NodeId > > getMatchingNodes()
static bool isSpecializedNodeExcluded(const Decl *D)
static const DeclContext * getEnclosingDeclContext(ASTContext &AST, const Stmt *S)
DynTypedNode DynTypedNode
static std::vector< NodeId > getSubtreePostorder(const SyntaxTree::Impl &Tree, NodeId Root)
static std::vector< NodeId > getSubtreeBfs(const SyntaxTree::Impl &Tree, NodeId Root)
static bool isNodeExcluded(const SourceManager &SrcMgr, T *N)
static std::string getInitializerValue(const CXXCtorInitializer *Init, const PrintingPolicy &TypePP)
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
@ Other
Other implicit parameter.
bool link(llvm::ArrayRef< const char * > args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput)
Diagnostic wrappers for TextAPI types for error reporting.
Describes how types, statements, expressions, and declarations should be printed.
unsigned AnonymousTagLocations
When printing an anonymous tag name, also print the location of that entity (e.g.,...
Within a tree, this identifies a node by its preorder offset.
Represents a Clang AST node, alongside some additional information.
NodeId RightMostDescendant
std::optional< std::string > getQualifiedIdentifier() const
std::optional< StringRef > getIdentifier() const
ASTNodeKind getType() const
NodeId LeftMostDescendant
StringRef getTypeLabel() const
SmallVector< NodeId, 4 > Children
Identifies a node in a subtree by its postorder offset, starting at 1.
SNodeId operator+(int Other) const