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 { Index: lib/Tooling/ASTDiff/ASTDiff.cpp =================================================================== --- lib/Tooling/ASTDiff/ASTDiff.cpp +++ lib/Tooling/ASTDiff/ASTDiff.cpp @@ -148,6 +148,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; struct SubtreeIterator { NodeId Root, End; @@ -378,49 +380,82 @@ 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()) { + 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 *X = dyn_cast(D)) { + Value += X->getQualifiedNameAsString() + "(" + + X->getType().getAsString(TypePP) + ")"; + if (auto *X = dyn_cast(D)) { + for (auto *Init : X->inits()) { + if (!Init->isWritten()) + continue; + if (Init->isBaseInitializer()) { + Value += Init->getBaseClass()->getCanonicalTypeInternal().getAsString( + TypePP) + + ","; + } else if (Init->isDelegatingInitializer()) { + Value += X->getNameAsString() + ","; + } else { + assert(Init->isAnyMemberInitializer()); + Value += Init->getMember()->getQualifiedNameAsString() + ","; + } + } + } + return Value; + } + if (auto *X = dyn_cast(D)) + Value += X->getQualifiedNameAsString() + ";"; + if (auto *X = dyn_cast(D)) + return Value + X->getUnderlyingType().getAsString(TypePP) + ";"; + if (auto *X = dyn_cast(D)) + if (X->getTypeForDecl()) + Value += + X->getTypeForDecl()->getCanonicalTypeInternal().getAsString(TypePP) + + ";"; + if (auto *X = dyn_cast(D)) + return X->getNominatedNamespace()->getName(); + if (auto *X = dyn_cast(D)) { CharSourceRange Range(X->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 *X = dyn_cast(S)) + return UnaryOperator::getOpcodeStr(X->getOpcode()); + if (auto *X = dyn_cast(S)) + return X->getOpcodeStr(); + if (auto *X = dyn_cast(S)) + return X->getMemberDecl()->getQualifiedNameAsString(); + if (auto *X = dyn_cast(S)) { SmallString<256> Str; X->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 *X = dyn_cast(S)) { + SmallString<256> Str; + X->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 *X = dyn_cast(S)) + return X->getDecl()->getQualifiedNameAsString(); + if (auto *X = dyn_cast(S)) + return X->getString(); + if (auto *X = dyn_cast(S)) + return X->getValue() ? "true" : "false"; + return ""; } /// Identifies a node in a subtree by its postorder offset, starting at 1. @@ -643,6 +678,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 { Index: test/Tooling/clang-diff-ast.cpp =================================================================== --- /dev/null +++ test/Tooling/clang-diff-ast.cpp @@ -0,0 +1,59 @@ +// RUN: clang-diff -no-compilation-database -ast-dump %s -extra-arg='-std=c++11' | FileCheck %s +// +// This tests the getNodeValue function from Tooling/ASTDiff/ASTDiff.h + +// CHECK: NamespaceDecl: test;( +namespace test { + +// CHECK: FunctionDecl: test::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: DeclRefExpr: test::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;( +using real = double; + +class Base { +}; + +// CHECK: CXXRecordDecl: X;X;( +class X : Base { + int m; + // CHECK: CXXMethodDecl: X::foo(const char *(int))( + // CHECK: ParmVarDecl: i(int)( + const char *foo(int i) { + if (i == 0) + // CHECK: StringLiteral: foo( + return "foo"; + return 0; + } + + // CHECK: AccessSpecDecl: public( +public: + // CHECK: CXXConstructorDecl: X::X(void (char, int))Base,X::m,( + X(char, int) : Base(), m(0) { + // CHECK: MemberExpr: X::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: src::foo{{.*}} to DeclRefExpr: dst::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: src::nat;unsigned int;{{.*}} to TypedefDecl: dst::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: src::p(int){{.*}} to VarDecl: dst::prod(double) +// CHECK: Update VarDecl: src::p(int){{.*}} to dst::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: tools/clang-diff/ClangDiff.cpp =================================================================== --- tools/clang-diff/ClangDiff.cpp +++ tools/clang-diff/ClangDiff.cpp @@ -312,6 +312,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]);