Index: include/clang/Tooling/ASTDiff/ASTDiff.h =================================================================== --- include/clang/Tooling/ASTDiff/ASTDiff.h +++ include/clang/Tooling/ASTDiff/ASTDiff.h @@ -45,6 +45,8 @@ ast_type_traits::ASTNodeKind getType() const; StringRef getTypeLabel() const; bool isLeaf() const { return Children.empty(); } + llvm::Optional getIdentifier() const; + llvm::Optional getQualifiedIdentifier() const; }; class ASTDiff { @@ -66,15 +68,15 @@ class SyntaxTree { public: /// Constructs a tree from a translation unit. - SyntaxTree(const ASTContext &AST); + SyntaxTree(ASTContext &AST); /// Constructs a tree from any AST node. template - SyntaxTree(T *Node, const ASTContext &AST) + SyntaxTree(T *Node, ASTContext &AST) : TreeImpl(llvm::make_unique(this, Node, AST)) {} SyntaxTree(SyntaxTree &&Other) = default; ~SyntaxTree(); - const ASTContext &getASTContext() const; + ASTContext &getASTContext() const; StringRef getFilename() const; int getSize() const; Index: lib/Tooling/ASTDiff/ASTDiff.cpp =================================================================== --- lib/Tooling/ASTDiff/ASTDiff.cpp +++ lib/Tooling/ASTDiff/ASTDiff.cpp @@ -112,23 +112,23 @@ class SyntaxTree::Impl { public: /// Constructs a tree from the entire translation unit. - Impl(SyntaxTree *Parent, const ASTContext &AST); + Impl(SyntaxTree *Parent, ASTContext &AST); /// Constructs a tree from an AST node. - Impl(SyntaxTree *Parent, Decl *N, const ASTContext &AST); - Impl(SyntaxTree *Parent, Stmt *N, const ASTContext &AST); + Impl(SyntaxTree *Parent, Decl *N, ASTContext &AST); + Impl(SyntaxTree *Parent, Stmt *N, ASTContext &AST); template Impl(SyntaxTree *Parent, typename std::enable_if::value, T>::type *Node, - const ASTContext &AST) + ASTContext &AST) : Impl(Parent, dyn_cast(Node), AST) {} template Impl(SyntaxTree *Parent, typename std::enable_if::value, T>::type *Node, - const ASTContext &AST) + ASTContext &AST) : Impl(Parent, dyn_cast(Node), AST) {} SyntaxTree *Parent; - const ASTContext &AST; + ASTContext &AST; std::vector Leaves; // Maps preorder indices to postorder ones. std::vector PostorderIds; @@ -149,6 +149,8 @@ std::string getNodeValue(NodeId Id) const; std::string getNodeValue(const Node &Node) const; + std::string getDeclValue(const Decl *D) const; + std::string getStmtValue(const Stmt *S) const; private: /// Nodes in preorder. @@ -268,10 +270,10 @@ }; } // end anonymous namespace -SyntaxTree::Impl::Impl(SyntaxTree *Parent, const ASTContext &AST) +SyntaxTree::Impl::Impl(SyntaxTree *Parent, ASTContext &AST) : Impl(Parent, AST.getTranslationUnitDecl(), AST) {} -SyntaxTree::Impl::Impl(SyntaxTree *Parent, Decl *N, const ASTContext &AST) +SyntaxTree::Impl::Impl(SyntaxTree *Parent, Decl *N, ASTContext &AST) : Parent(Parent), AST(AST) { NodeCountVisitor NodeCounter(*this); NodeCounter.TraverseDecl(N); @@ -281,7 +283,7 @@ initTree(); } -SyntaxTree::Impl::Impl(SyntaxTree *Parent, Stmt *N, const ASTContext &AST) +SyntaxTree::Impl::Impl(SyntaxTree *Parent, Stmt *N, ASTContext &AST) : Parent(Parent), AST(AST) { NodeCountVisitor NodeCounter(*this); NodeCounter.TraverseStmt(N); @@ -366,55 +368,124 @@ llvm_unreachable("Node not found in parent's children."); } +// Returns the qualified name of ND. If it is subordinate to Context, +// then the prefix of the latter is removed from the returned value. +static std::string getRelativeName(const NamedDecl *ND, + const DeclContext *Context) { + std::string ContextPrefix; + if (auto *Namespace = dyn_cast(Context)) + ContextPrefix = Namespace->getQualifiedNameAsString(); + else if (auto *Tag = dyn_cast(Context)) + ContextPrefix = Tag->getQualifiedNameAsString(); + std::string Val = ND->getQualifiedNameAsString(); + // Strip the qualifier, if Val refers to somthing in the current scope. + // But leave one leading ':' in place, so that we know that this is a + // relative path. + if (!ContextPrefix.empty() && + Val.substr(0, ContextPrefix.size()) == ContextPrefix) + Val = Val.substr(ContextPrefix.size() + 1); + return Val; +} + +static std::string getRelativeName(const NamedDecl *ND) { + return getRelativeName(ND, ND->getDeclContext()); +} + +static const DeclContext *getEnclosingDeclContext(ASTContext &AST, + const Stmt *S) { + while (S) { + const auto &Parents = AST.getParents(*S); + if (Parents.empty()) + return nullptr; + const auto &P = Parents[0]; + if (const auto *D = P.get()) + return D->getDeclContext(); + S = P.get(); + } + llvm_unreachable("Could not find Decl ancestor."); +} + std::string SyntaxTree::Impl::getNodeValue(NodeId Id) const { return getNodeValue(getNode(Id)); } std::string SyntaxTree::Impl::getNodeValue(const Node &N) const { const DynTypedNode &DTN = N.ASTNode; - if (auto *X = DTN.get()) - return X->getOpcodeStr(); - if (auto *X = DTN.get()) { - CharSourceRange Range(X->getSourceRange(), false); + if (auto *S = DTN.get()) + return getStmtValue(S); + if (auto *D = DTN.get()) + return getDeclValue(D); + llvm_unreachable("Fatal: unhandled AST node.\n"); +} + +std::string SyntaxTree::Impl::getDeclValue(const Decl *D) const { + std::string Value; + PrintingPolicy TypePP(AST.getLangOpts()); + TypePP.AnonymousTagLocations = false; + + if (auto *V = dyn_cast(D)) { + Value += getRelativeName(V) + "(" + V->getType().getAsString(TypePP) + ")"; + if (auto *C = dyn_cast(D)) { + for (auto *Init : C->inits()) { + if (!Init->isWritten()) + continue; + if (Init->isBaseInitializer()) { + Value += Init->getBaseClass()->getCanonicalTypeInternal().getAsString( + TypePP) + + ","; + } else if (Init->isDelegatingInitializer()) { + Value += C->getNameAsString() + ","; + } else { + assert(Init->isAnyMemberInitializer()); + Value += getRelativeName(Init->getMember()) + ","; + } + } + } + return Value; + } + if (auto *N = dyn_cast(D)) + Value += getRelativeName(N) + ";"; + if (auto *T = dyn_cast(D)) + return Value + T->getUnderlyingType().getAsString(TypePP) + ";"; + if (auto *T = dyn_cast(D)) + if (T->getTypeForDecl()) + Value += + T->getTypeForDecl()->getCanonicalTypeInternal().getAsString(TypePP) + + ";"; + if (auto *U = dyn_cast(D)) + return U->getNominatedNamespace()->getName(); + if (auto *A = dyn_cast(D)) { + CharSourceRange Range(A->getSourceRange(), false); return Lexer::getSourceText(Range, AST.getSourceManager(), AST.getLangOpts()); } - if (auto *X = DTN.get()) { + return Value; +} + +std::string SyntaxTree::Impl::getStmtValue(const Stmt *S) const { + if (auto *U = dyn_cast(S)) + return UnaryOperator::getOpcodeStr(U->getOpcode()); + if (auto *B = dyn_cast(S)) + return B->getOpcodeStr(); + if (auto *M = dyn_cast(S)) + return getRelativeName(M->getMemberDecl()); + if (auto *I = dyn_cast(S)) { SmallString<256> Str; - X->getValue().toString(Str, /*Radix=*/10, /*Signed=*/false); + I->getValue().toString(Str, /*Radix=*/10, /*Signed=*/false); return Str.str(); } - if (auto *X = DTN.get()) - return X->getString(); - if (auto *X = DTN.get()) - return X->getNameAsString() + "(" + X->getType().getAsString() + ")"; - if (DTN.get() || DTN.get()) - return ""; - std::string Value; - if (auto *X = DTN.get()) { - if (X->hasQualifier()) { - llvm::raw_string_ostream OS(Value); - PrintingPolicy PP(AST.getLangOpts()); - X->getQualifier()->print(OS, PP); - } - Value += X->getDecl()->getNameAsString(); - return Value; + if (auto *F = dyn_cast(S)) { + SmallString<256> Str; + F->getValue().toString(Str); + return Str.str(); } - if (auto *X = DTN.get()) - Value += X->getNameAsString() + ";"; - if (auto *X = DTN.get()) - return Value + X->getUnderlyingType().getAsString() + ";"; - if (DTN.get()) - return Value; - if (auto *X = DTN.get()) - if (X->getTypeForDecl()) - Value += - X->getTypeForDecl()->getCanonicalTypeInternal().getAsString() + ";"; - if (DTN.get()) - return Value; - if (DTN.get()) - return ""; - llvm_unreachable("Fatal: unhandled AST node.\n"); + if (auto *D = dyn_cast(S)) + return getRelativeName(D->getDecl(), getEnclosingDeclContext(AST, S)); + if (auto *String = dyn_cast(S)) + return String->getString(); + if (auto *B = dyn_cast(S)) + return B->getValue() ? "true" : "false"; + return ""; } /// Identifies a node in a subtree by its postorder offset, starting at 1. @@ -637,6 +708,22 @@ StringRef Node::getTypeLabel() const { return getType().asStringRef(); } +llvm::Optional Node::getQualifiedIdentifier() const { + if (auto *ND = ASTNode.get()) { + if (ND->getDeclName().isIdentifier()) + return ND->getQualifiedNameAsString(); + } + return llvm::None; +} + +llvm::Optional Node::getIdentifier() const { + if (auto *ND = ASTNode.get()) { + if (ND->getDeclName().isIdentifier()) + return ND->getName(); + } + return llvm::None; +} + namespace { // Compares nodes by their depth. struct HeightLess { @@ -894,13 +981,13 @@ return DiffImpl->getMapped(SourceTree.TreeImpl, Id); } -SyntaxTree::SyntaxTree(const ASTContext &AST) +SyntaxTree::SyntaxTree(ASTContext &AST) : TreeImpl(llvm::make_unique( this, AST.getTranslationUnitDecl(), AST)) {} SyntaxTree::~SyntaxTree() = default; -const ASTContext &SyntaxTree::getASTContext() const { return TreeImpl->AST; } +ASTContext &SyntaxTree::getASTContext() const { return TreeImpl->AST; } const Node &SyntaxTree::getNode(NodeId Id) const { return TreeImpl->getNode(Id); @@ -917,7 +1004,8 @@ return TreeImpl->findPositionInParent(Id); } -std::pair SyntaxTree::getSourceRangeOffsets(const Node &N) const { +std::pair +SyntaxTree::getSourceRangeOffsets(const Node &N) const { const SourceManager &SrcMgr = TreeImpl->AST.getSourceManager(); SourceRange Range = N.ASTNode.getSourceRange(); SourceLocation BeginLoc = Range.getBegin(); Index: test/Tooling/clang-diff-ast.cpp =================================================================== --- test/Tooling/clang-diff-ast.cpp +++ test/Tooling/clang-diff-ast.cpp @@ -5,22 +5,31 @@ // CHECK: {{^}} NamespaceDecl: test;( namespace test { -// CHECK: {{^}} FunctionDecl: f( +// CHECK: {{^}} FunctionDecl: :f( // CHECK: CompoundStmt( void f() { // CHECK: VarDecl: i(int)( // CHECK: IntegerLiteral: 1 auto i = 1; + // CHECK: FloatingLiteral: 1.5( + auto r = 1.5; + // CHECK: CXXBoolLiteralExpr: true( + auto b = true; // CHECK: CallExpr( // CHECK-NOT: ImplicitCastExpr - // CHECK-NEXT: DeclRefExpr: f( + // CHECK: DeclRefExpr: :f( f(); + // CHECK: UnaryOperator: ++( + ++i; // CHECK: BinaryOperator: =( i = i; } } // end namespace test +// CHECK: UsingDirectiveDecl: test( +using namespace test; + // CHECK: TypedefDecl: nat;unsigned int;( typedef unsigned nat; // CHECK: TypeAliasDecl: real;double;( @@ -29,10 +38,10 @@ class Base { }; -// CHECK: CXXRecordDecl: X;class X;( +// CHECK: CXXRecordDecl: X;X;( class X : Base { int m; - // CHECK: CXXMethodDecl: foo(const char *(int))( + // CHECK: CXXMethodDecl: :foo(const char *(int))( // CHECK: ParmVarDecl: i(int)( const char *foo(int i) { if (i == 0) @@ -44,9 +53,9 @@ // CHECK: AccessSpecDecl: public( public: - // CHECK: CXXConstructorDecl: X(void (char, int))( + // CHECK: CXXConstructorDecl: :X(void (char, int))Base,:m,( X(char, int) : Base(), m(0) { - // CHECK: MemberExpr( + // CHECK: MemberExpr: :m( int x = m; } }; Index: test/Tooling/clang-diff-basic.cpp =================================================================== --- test/Tooling/clang-diff-basic.cpp +++ test/Tooling/clang-diff-basic.cpp @@ -11,18 +11,18 @@ } } -// CHECK: Match DeclRefExpr: foo{{.*}} to DeclRefExpr: inner::foo +// CHECK: Match DeclRefExpr: :foo{{.*}} to DeclRefExpr: :inner::foo void main() { inner::foo(); } // CHECK: Match StringLiteral: foo{{.*}} to StringLiteral: foo const char *b = "f" "o" "o"; // unsigned is canonicalized to unsigned int -// CHECK: Match TypedefDecl: nat;unsigned int;{{.*}} to TypedefDecl: nat;unsigned int; +// CHECK: Match TypedefDecl: :nat;unsigned int;{{.*}} to TypedefDecl: :nat;unsigned int; typedef unsigned nat; -// CHECK: Match VarDecl: p(int){{.*}} to VarDecl: prod(double) -// CHECK: Update VarDecl: p(int){{.*}} to prod(double) +// CHECK: Match VarDecl: :p(int){{.*}} to VarDecl: :prod(double) +// CHECK: Update VarDecl: :p(int){{.*}} to :prod(double) // CHECK: Match BinaryOperator: *{{.*}} to BinaryOperator: * double prod = 1 * 2 * 10; // CHECK: Update DeclRefExpr @@ -44,7 +44,7 @@ namespace { // match with parents of different type -// CHECK: Match FunctionDecl: f1{{.*}} to FunctionDecl: f1 +// CHECK: Match FunctionDecl: f1{{.*}} to FunctionDecl: (anonymous namespace)::f1 void f1() {{ (void) __func__;;; }} } Index: test/Tooling/clang-diff-bottomup.cpp =================================================================== --- test/Tooling/clang-diff-bottomup.cpp +++ test/Tooling/clang-diff-bottomup.cpp @@ -16,7 +16,7 @@ void f1() { // CompoundStmt: 3 matched descendants, subtree sizes 4 and 5 // Jaccard similarity = 3 / (4 + 5 - 3) = 3 / 6 >= 0.5 -// CHECK: Match FunctionDecl: f1(void (void))(1) to FunctionDecl: f1(void (void))(1) +// CHECK: Match FunctionDecl: f1(void ())(1) to FunctionDecl: f1(void ())(1) // CHECK: Match CompoundStmt(2) to CompoundStmt(2) // CHECK: Match CompoundStmt(4) to CompoundStmt(3) // CHECK: Match CompoundStmt(5) to CompoundStmt(4) Index: test/Tooling/clang-diff-opt.cpp =================================================================== --- test/Tooling/clang-diff-opt.cpp +++ test/Tooling/clang-diff-opt.cpp @@ -25,7 +25,7 @@ // CHECK-NEXT: Match NullStmt(4) to NullStmt(3) ; {{;}} } - + void f2() { // Jaccard similarity = 7 / (10 + 10 - 7) >= 0.5 // As none of the subtrees is bigger than 10 nodes, the optimal algorithm @@ -41,4 +41,5 @@ // CHECK: Delete NullStmt(22) ;; {{;;;;;;}} } + #endif Index: test/Tooling/clang-diff-topdown.cpp =================================================================== --- test/Tooling/clang-diff-topdown.cpp +++ test/Tooling/clang-diff-topdown.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -E %s > %t.src.cpp // RUN: %clang_cc1 -E %s > %t.dst.cpp -DDEST -// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- | FileCheck %s +// RUN: clang-diff -dump-matches -stop-after=topdown %t.src.cpp %t.dst.cpp -- -std=c++11 | FileCheck %s // // Test the top-down matching of identical subtrees only. @@ -27,8 +27,19 @@ {{;;}} } +int x; + +namespace src { + int x; + int x1 = x + 1; + int x2 = ::x + 1; +} + +class A { int x = 1 + 1; void f() { int x1 = x; } }; + #else + void f1() { {{;}} @@ -45,4 +56,28 @@ ; } +int x; + +namespace dst { + int x; + // CHECK: Match DeclRefExpr: :x(17) to DeclRefExpr: :x(22) + int x1 = x + 1; + // CHECK: Match DeclRefExpr: x(21) to DeclRefExpr: x(26) + int x2 = ::x + 1; +} + +class B { + // Only the class name changed; it is not included in the field value, + // therefore there is no update. + // CHECK: Match FieldDecl: :x(int)(24) to FieldDecl: :x(int)(29) + // CHECK-NOT: Update FieldDecl: :x(int)(24) + int x = 1+1; + void f() { + // CHECK: Match MemberExpr: :x(32) to MemberExpr: :x(37) + // CHECK-NOT: Update MemberExpr: :x(32) + int x1 = B::x; + } + +}; + #endif Index: tools/clang-diff/ClangDiff.cpp =================================================================== --- tools/clang-diff/ClangDiff.cpp +++ tools/clang-diff/ClangDiff.cpp @@ -315,6 +315,18 @@ const diff::Node &N = Tree.getNode(Id); OS << "{"; printNodeAttributes(OS, Tree, Id); + auto Identifier = N.getIdentifier(); + auto QualifiedIdentifier = N.getQualifiedIdentifier(); + if (Identifier) { + OS << R"(,"identifier":")"; + printJsonString(OS, *Identifier); + OS << R"(")"; + if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) { + OS << R"(,"qualified_identifier":")"; + printJsonString(OS, *QualifiedIdentifier); + OS << R"(")"; + } + } OS << R"(,"children":[)"; if (N.Children.size() > 0) { printNodeAsJson(OS, Tree, N.Children[0]);