Index: cfe/trunk/include/clang/Tooling/ASTDiff/ASTDiff.h =================================================================== --- cfe/trunk/include/clang/Tooling/ASTDiff/ASTDiff.h +++ cfe/trunk/include/clang/Tooling/ASTDiff/ASTDiff.h @@ -57,8 +57,8 @@ ast_type_traits::DynTypedNode ASTNode; SmallVector Children; - ast_type_traits::ASTNodeKind getType() const { return ASTNode.getNodeKind(); } - const StringRef getTypeLabel() const { return getType().asStringRef(); } + ast_type_traits::ASTNodeKind getType() const; + StringRef getTypeLabel() const; bool isLeaf() const { return Children.empty(); } }; @@ -96,15 +96,20 @@ SyntaxTree(SyntaxTree &&Other) = default; ~SyntaxTree(); + const ASTContext &getASTContext() const; + StringRef getFilename() const; + const Node &getNode(NodeId Id) const; + NodeId getRootId() const; + + // Returns the starting and ending offset of the node in its source file. + std::pair getSourceRangeOffsets(const Node &N) const; /// Serialize the node attributes to a string representation. This should /// uniquely distinguish nodes of the same kind. Note that this function just /// returns a representation of the node value, not considering descendants. std::string getNodeValue(const DynTypedNode &DTN) const; - void printAsJson(raw_ostream &OS); - class Impl; std::unique_ptr TreeImpl; }; Index: cfe/trunk/lib/Tooling/ASTDiff/ASTDiff.cpp =================================================================== --- cfe/trunk/lib/Tooling/ASTDiff/ASTDiff.cpp +++ cfe/trunk/lib/Tooling/ASTDiff/ASTDiff.cpp @@ -176,9 +176,6 @@ void printTree(NodeId Root) const; void printTree(raw_ostream &OS, NodeId Root) const; - void printAsJsonImpl(raw_ostream &OS) const; - void printNodeAsJson(raw_ostream &OS, NodeId Id) const; - private: /// Nodes in preorder. std::vector Nodes; @@ -438,28 +435,6 @@ OS << "(" << PostorderIds[Id] << ")"; } -void SyntaxTree::Impl::printNodeAsJson(raw_ostream &OS, NodeId Id) const { - auto N = getNode(Id); - OS << R"({"type":")" << N.getTypeLabel() << R"(")"; - if (getNodeValue(Id) != "") - OS << R"(,"value":")" << getNodeValue(Id) << R"(")"; - OS << R"(,"children":[)"; - if (N.Children.size() > 0) { - printNodeAsJson(OS, N.Children[0]); - for (size_t I = 1, E = N.Children.size(); I < E; ++I) { - OS << ","; - printNodeAsJson(OS, N.Children[I]); - } - } - OS << "]}"; -} - -void SyntaxTree::Impl::printAsJsonImpl(raw_ostream &OS) const { - OS << R"({"root":)"; - printNodeAsJson(OS, getRootId()); - OS << "}\n"; -} - /// Identifies a node in a subtree by its postorder offset, starting at 1. struct SNodeId { int Id = 0; @@ -674,6 +649,12 @@ } }; +ast_type_traits::ASTNodeKind Node::getType() const { + return ASTNode.getNodeKind(); +} + +StringRef Node::getTypeLabel() const { return getType().asStringRef(); } + namespace { // Compares nodes by their depth. struct HeightLess { @@ -999,7 +980,28 @@ SyntaxTree::~SyntaxTree() = default; -void SyntaxTree::printAsJson(raw_ostream &OS) { TreeImpl->printAsJsonImpl(OS); } +const ASTContext &SyntaxTree::getASTContext() const { return TreeImpl->AST; } + +const Node &SyntaxTree::getNode(NodeId Id) const { + return TreeImpl->getNode(Id); +} + +NodeId SyntaxTree::getRootId() const { return TreeImpl->getRootId(); } + +std::pair SyntaxTree::getSourceRangeOffsets(const Node &N) const { + const SourceManager &SrcMgr = TreeImpl->AST.getSourceManager(); + SourceRange Range = N.ASTNode.getSourceRange(); + SourceLocation BeginLoc = Range.getBegin(); + SourceLocation EndLoc = Lexer::getLocForEndOfToken( + Range.getEnd(), /*Offset=*/0, SrcMgr, TreeImpl->AST.getLangOpts()); + if (auto *ThisExpr = N.ASTNode.get()) { + if (ThisExpr->isImplicit()) + EndLoc = BeginLoc; + } + unsigned Begin = SrcMgr.getFileOffset(SrcMgr.getExpansionLoc(BeginLoc)); + unsigned End = SrcMgr.getFileOffset(SrcMgr.getExpansionLoc(EndLoc)); + return {Begin, End}; +} std::string SyntaxTree::getNodeValue(const DynTypedNode &DTN) const { return TreeImpl->getNodeValue(DTN); Index: cfe/trunk/test/Tooling/clang-diff-json.cpp =================================================================== --- cfe/trunk/test/Tooling/clang-diff-json.cpp +++ cfe/trunk/test/Tooling/clang-diff-json.cpp @@ -0,0 +1,27 @@ +// RUN: clang-diff -ast-dump %s -- \ +// RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \ +// RUN: | FileCheck %s + +// CHECK: "begin": 294, +// CHECK: "type": "CXXRecordDecl", +// CHECK: "type": "FieldDecl", +// CHECK: "end": 314, +class A { + int x; +}; + +// CHECK: "children": [ +// CHECK-NEXT: { +// CHECK-NEXT: "begin": +// CHECK-NEXT: "children": [] +// CHECK-NEXT: "end": +// CHECK-NEXT: "id": +// CHECK-NEXT: "type": "CharacterLiteral" +// CHECK-NEXT: } +// CHECK: ] +// CHECK: "type": "VarDecl", +char nl = '\n'; + +// CHECK: "value": "abc \n\t\u0000\u001f\u0123 \ub370\ubc15" +char s[] = "abc \n\t\0\x1f\u0123 데박"; + Index: cfe/trunk/test/lit.cfg =================================================================== --- cfe/trunk/test/lit.cfg +++ cfe/trunk/test/lit.cfg @@ -276,6 +276,7 @@ config.substitutions.append( ('%itanium_abi_triple', makeItaniumABITriple(config.target_triple)) ) config.substitutions.append( ('%ms_abi_triple', makeMSABITriple(config.target_triple)) ) config.substitutions.append( ('%resource_dir', getClangBuiltinIncludeDir(config.clang)) ) +config.substitutions.append( ('%python', config.python_executable) ) # The host triple might not be set, at least if we're compiling clang from # an already installed llvm. Index: cfe/trunk/test/lit.site.cfg.in =================================================================== --- cfe/trunk/test/lit.site.cfg.in +++ cfe/trunk/test/lit.site.cfg.in @@ -25,6 +25,7 @@ config.enable_backtrace = @ENABLE_BACKTRACES@ config.host_arch = "@HOST_ARCH@" config.enable_abi_breaking_checks = "@LLVM_ENABLE_ABI_BREAKING_CHECKS@" +config.python_executable = "@PYTHON_EXECUTABLE@" # Support substitution of the tools and libs dirs with user parameters. This is # used when we can't determine the tool dir at configuration time. Index: cfe/trunk/tools/clang-diff/ClangDiff.cpp =================================================================== --- cfe/trunk/tools/clang-diff/ClangDiff.cpp +++ cfe/trunk/tools/clang-diff/ClangDiff.cpp @@ -94,6 +94,65 @@ return std::move(ASTs[0]); } +static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); } + +static void printJsonString(raw_ostream &OS, const StringRef Str) { + for (char C : Str) { + switch (C) { + case '"': + OS << R"(\")"; + break; + case '\\': + OS << R"(\\)"; + break; + case '\n': + OS << R"(\n)"; + break; + case '\t': + OS << R"(\t)"; + break; + default: + if ('\x00' <= C && C <= '\x1f') { + OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C); + } else { + OS << C; + } + } + } +} + +static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree, + diff::NodeId Id) { + const diff::Node &N = Tree.getNode(Id); + OS << R"("id":)" << int(Id); + OS << R"(,"type":")" << N.getTypeLabel() << '"'; + auto Offsets = Tree.getSourceRangeOffsets(N); + OS << R"(,"begin":)" << Offsets.first; + OS << R"(,"end":)" << Offsets.second; + std::string Value = Tree.getNodeValue(N.ASTNode); + if (!Value.empty()) { + OS << R"(,"value":")"; + printJsonString(OS, Value); + OS << '"'; + } +} + +static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree, + diff::NodeId Id) { + const diff::Node &N = Tree.getNode(Id); + OS << "{"; + printNodeAttributes(OS, Tree, Id); + OS << R"(,"children":[)"; + if (N.Children.size() > 0) { + printNodeAsJson(OS, Tree, N.Children[0]); + for (size_t I = 1, E = N.Children.size(); I < E; ++I) { + OS << ","; + printNodeAsJson(OS, Tree, N.Children[I]); + } + } + OS << "]}"; +} + int main(int argc, const char **argv) { std::string ErrorMessage; std::unique_ptr CommonCompilations = @@ -117,7 +176,11 @@ if (!AST) return 1; diff::SyntaxTree Tree(AST->getASTContext()); - Tree.printAsJson(llvm::outs()); + llvm::outs() << R"({"filename":")"; + printJsonString(llvm::outs(), SourcePath); + llvm::outs() << R"(","root":)"; + printNodeAsJson(llvm::outs(), Tree, Tree.getRootId()); + llvm::outs() << "}\n"; return 0; }