juliehockett updated this revision to Diff 133726.
juliehockett added a comment.

Updating documentation


https://reviews.llvm.org/D41102

Files:
  CMakeLists.txt
  clang-doc/CMakeLists.txt
  clang-doc/ClangDoc.cpp
  clang-doc/ClangDoc.h
  clang-doc/ClangDocBinary.cpp
  clang-doc/ClangDocBinary.h
  clang-doc/ClangDocMapper.cpp
  clang-doc/ClangDocMapper.h
  clang-doc/ClangDocRepresentation.h
  clang-doc/tool/CMakeLists.txt
  clang-doc/tool/ClangDocMain.cpp
  docs/clang-doc.rst
  test/CMakeLists.txt
  test/clang-doc/mapper-namespace.cpp
  test/clang-doc/mapper-type.cpp

Index: test/clang-doc/mapper-type.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/mapper-type.cpp
@@ -0,0 +1,137 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-doc  --dump --omit-filenames -doxygen -p %t %t/test.cpp | FileCheck %s
+
+union A { int X; int Y; };
+// CHECK: ---
+// CHECK: KEY: A
+// CHECK: FullyQualifiedName: A
+// CHECK: Name: A
+// CHECK: TagType: 2
+// CHECK: ID: Member
+// CHECK: Type: int
+// CHECK: Name: A::X
+// CHECK: Access: 3
+// CHECK: ID: Member
+// CHECK: Type: int
+// CHECK: Name: A::Y
+// CHECK: Access: 3
+// CHECK: ---
+// CHECK: KEY: A::A
+// CHECK: FullyQualifiedName: A::A
+// CHECK: Name: A
+// CHECK: Namespace: A
+
+enum B { X, Y };
+// CHECK: ---
+// CHECK: KEY: B
+// CHECK: FullyQualifiedName: B
+// CHECK: Name: B
+// CHECK: ID: Member
+// CHECK: Type: X
+// CHECK: Access: 3
+// CHECK: ID: Member
+// CHECK: Type: Y
+// CHECK: Access: 3
+
+struct C { int i; };
+// CHECK: ---
+// CHECK: KEY: C
+// CHECK: FullyQualifiedName: C
+// CHECK: Name: C
+// CHECK: ID: Member
+// CHECK: Type: int
+// CHECK: Name: C::i
+// CHECK: Access: 3
+// CHECK: ---
+// CHECK: KEY: C::C
+// CHECK: FullyQualifiedName: C::C
+// CHECK: Name: C
+// CHECK: Namespace: C
+
+class D {};
+// CHECK: ---
+// CHECK: KEY: D
+// CHECK: FullyQualifiedName: D
+// CHECK: Name: D
+// CHECK: TagType: 3
+// CHECK: ---
+// CHECK: KEY: D::D
+// CHECK: FullyQualifiedName: D::D
+// CHECK: Name: D
+// CHECK: Namespace: D
+
+class E {
+// CHECK: ---
+// CHECK: KEY: E
+// CHECK: FullyQualifiedName: E
+// CHECK: Name: E
+// CHECK: TagType: 3
+// CHECK: ---
+// CHECK: KEY: E::E
+// CHECK: FullyQualifiedName: E::E
+// CHECK: Name: E
+// CHECK: Namespace: E
+
+public:
+	E() {}
+// CHECK: ---
+// CHECK: KEY: _ZN1EC1Ev
+// CHECK: FullyQualifiedName: E::E
+// CHECK: Name: E
+// CHECK: Namespace: E
+// CHECK: MangledName: _ZN1EC1Ev
+// CHECK: Parent: E
+// CHECK: ID: Return
+// CHECK: Type: void
+// CHECK: Access: 3
+
+	 ~E() {}
+// CHECK: ---
+// CHECK: KEY: _ZN1ED1Ev
+// CHECK: FullyQualifiedName: E::~E
+// CHECK: Name: ~E
+// CHECK: Namespace: E
+// CHECK: MangledName: _ZN1ED1Ev
+// CHECK: Parent: E
+// CHECK: ID: Return
+// CHECK: Type: void
+// CHECK: Access: 3
+
+protected:
+	void ProtectedMethod();
+// CHECK:  ---
+// CHECK: KEY: _ZN1E15ProtectedMethodEv
+// CHECK: FullyQualifiedName: _ZN1E15ProtectedMethodEv
+// CHECK: Name: ProtectedMethod
+// CHECK: Namespace: E
+};
+
+void E::ProtectedMethod() {}
+// CHECK: ---
+// CHECK: KEY: _ZN1E15ProtectedMethodEv
+// CHECK: FullyQualifiedName: E::ProtectedMethod
+// CHECK: Name: ProtectedMethod
+// CHECK: Namespace: E
+// CHECK: MangledName: _ZN1E15ProtectedMethodEv
+// CHECK: Parent: E
+// CHECK: ID: Return
+// CHECK: Type: void
+// CHECK: Access: 3
+// CHECK: Access: 1
+
+class F : virtual private D, public E {};
+// CHECK: ---
+// CHECK: KEY: F
+// CHECK: FullyQualifiedName: F
+// CHECK: Name: F
+// CHECK: TagType: 3
+// CHECK: Parent: class E
+// CHECK: VParent: class D
+// CHECK: ---
+// CHECK: KEY: F::F
+// CHECK: FullyQualifiedName: F::F
+// CHECK: Name: F
+// CHECK: Namespace: F
Index: test/clang-doc/mapper-namespace.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/mapper-namespace.cpp
@@ -0,0 +1,70 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp | FileCheck %s
+
+namespace A {
+// CHECK: ---
+// CHECK: KEY: A
+// CHECK: FullyQualifiedName: A
+// CHECK: Name: A
+
+void f() {};
+// CHECK: ---
+// CHECK: KEY: _ZN1A1fEv
+// CHECK: FullyQualifiedName: A::f
+// CHECK: Name: f
+// CHECK: Namespace: A
+// CHECK: MangledName: _ZN1A1fEv
+// CHECK: ID: Return
+// CHECK: Type: void
+// CHECK: Access: 3
+// CHECK: Access: 3
+
+} // A
+
+namespace A {
+// CHECK: ---
+// CHECK: KEY: A
+// CHECK: FullyQualifiedName: A
+// CHECK: Name: A
+
+namespace B {
+// CHECK: ---
+// CHECK: KEY: A::B
+// CHECK: FullyQualifiedName: A::B
+// CHECK: Name: B
+// CHECK: Namespace: A
+
+enum E { X };
+// CHECK: ---
+// CHECK: KEY: A::B::E
+// CHECK: FullyQualifiedName: A::B::E
+// CHECK: Name: E
+// CHECK: Namespace: A::B
+// CHECK: ID: Member
+// CHECK: Type: A::B::X
+// CHECK: Access: 3
+
+E func(int i) { 
+	return X;
+}
+
+// CHECK: ---
+// CHECK: KEY: _ZN1A1B4funcEi
+// CHECK: FullyQualifiedName: A::B::func
+// CHECK: Name: func
+// CHECK: Namespace: A::B
+// CHECK: MangledName: _ZN1A1B4funcEi
+// CHECK: ID: Return
+// CHECK: Type: enum A::B::E
+// CHECK: Access: 3
+// CHECK: ID: Param
+// CHECK: Type: int
+// CHECK: Name: i
+// CHECK: Access: 3
+// CHECK: Access: 3
+
+} // B
+} // C
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -41,6 +41,7 @@
   clang-apply-replacements
   clang-change-namespace
   clangd
+  clang-doc
   clang-include-fixer
   clang-move
   clang-query
Index: docs/clang-doc.rst
===================================================================
--- /dev/null
+++ docs/clang-doc.rst
@@ -0,0 +1,62 @@
+===================
+Clang-Doc
+===================
+
+.. contents::
+
+:program:`clang-doc` is a tool for generating C and C++ documenation from 
+source code and comments. 
+
+The tool is in a very early development stage, so you might encounter bugs and
+crashes. Submitting reports with information about how to reproduce the issue
+to `the LLVM bugtracker <https://llvm.org/bugs>`_ will definitely help the
+project. If you have any ideas or suggestions, please to put a feature request
+there.
+
+Use
+=====
+
+:program:`clang-doc` is a `LibTooling
+<http://clang.llvm.org/docs/LibTooling.html>`_-based tool, and so requires a
+compile command database for your project (for an example of how to do this 
+see `How To Setup Tooling For LLVM
+<http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html>`_).
+
+The tool can be used on a single file or multiple files as defined in 
+the compile commands database:
+
+.. code-block:: console
+
+  $ clang-doc file.cpp -p /path/to/compile/commands
+
+This generates an intermediate representation of the declarations and their
+associated information in the specified TUs, serialized to LLVM 
+bitcode.
+
+As currently implemented, the tool is only able to parse TUs that can be 
+stored in-memory. Future additions will extend the current framewwork to use
+map-reduce frameworks to allow for use with large codebases.
+
+:program:`clang-doc` offers the following options:
+
+.. code-block:: console
+
+	$ clang-doc --help
+	USAGE: clang-doc [options] <source0> [... <sourceN>]
+
+	OPTIONS:
+
+	Generic Options:
+
+	  -help                      - Display available options (-help-hidden for more)
+	  -help-list                 - Display list of available options (-help-list-hidden for more)
+	  -version                   - Display the version of this program
+
+	clang-doc options:
+
+	  -doxygen                   - Use only doxygen-style comments to generate docs.
+	  -dump                      - Dump results to stdout.
+	  -extra-arg=<string>        - Additional argument to append to the compiler command line
+	  -extra-arg-before=<string> - Additional argument to prepend to the compiler command line
+	  -omit-filenames            - Omit filenames in output.
+	  -p=<string>                - Build path
Index: clang-doc/tool/ClangDocMain.cpp
===================================================================
--- /dev/null
+++ clang-doc/tool/ClangDocMain.cpp
@@ -0,0 +1,100 @@
+//===-- ClangDocMain.cpp - Clangdoc -----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <string>
+#include "ClangDoc.h"
+#include "ClangDocBinary.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Execution.h"
+#include "clang/Tooling/StandaloneExecution.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tooling;
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+static cl::OptionCategory ClangDocCategory("clang-doc options");
+
+static cl::opt<bool> DumpResult("dump", cl::desc("Dump results to stdout."),
+                                cl::init(false), cl::cat(ClangDocCategory));
+
+static cl::opt<bool> OmitFilenames("omit-filenames",
+                                   cl::desc("Omit filenames in output."),
+                                   cl::init(false), cl::cat(ClangDocCategory));
+
+static cl::opt<bool> DoxygenOnly(
+    "doxygen", cl::desc("Use only doxygen-style comments to generate docs."),
+    cl::init(false), cl::cat(ClangDocCategory));
+
+}  // namespace
+
+int main(int argc, const char **argv) {
+  sys::PrintStackTraceOnErrorSignal(argv[0]);
+
+  auto Exec = clang::tooling::createExecutorFromCommandLineArgs(
+      argc, argv, ClangDocCategory);
+
+  if (!Exec) {
+    errs() << toString(Exec.takeError()) << "\n";
+    return 1;
+  }
+
+  MatchFinder Finder;
+  ExecutionContext *ECtx = Exec->get()->getExecutionContext();
+  doc::ClangDocBinaryWriter Writer(OmitFilenames);
+
+  doc::ClangDocCallback NCallback("namespace", *ECtx, Writer);
+  Finder.addMatcher(namespaceDecl().bind("namespace"), &NCallback);
+  doc::ClangDocCallback RCallback("record", *ECtx, Writer);
+  Finder.addMatcher(recordDecl().bind("record"), &RCallback);
+  doc::ClangDocCallback ECallback("enum", *ECtx, Writer);
+  Finder.addMatcher(enumDecl().bind("enum"), &ECallback);
+  doc::ClangDocCallback MCallback("method", *ECtx, Writer);
+  Finder.addMatcher(cxxMethodDecl(isUserProvided()).bind("method"), &MCallback);
+  doc::ClangDocCallback FCallback("function", *ECtx, Writer);
+  Finder.addMatcher(functionDecl(unless(cxxMethodDecl())).bind("function"),
+                    &FCallback);
+
+  ArgumentsAdjuster ArgAdjuster;
+  if (!DoxygenOnly)
+    ArgAdjuster = combineAdjusters(
+        getInsertArgumentAdjuster("-fparse-all-comments",
+                                  tooling::ArgumentInsertPosition::BEGIN),
+        ArgAdjuster);
+  auto Err =
+      Exec->get()->execute(newFrontendActionFactory(&Finder), ArgAdjuster);
+  if (Err) errs() << toString(std::move(Err)) << "\n";
+
+  if (DumpResult) {
+    doc::ClangDocBinaryReader Reader(outs());
+    Exec->get()->getToolResults()->forEachResult(
+        [&Reader](StringRef Key, SmallString<2048> Value) {
+          outs() << "---\n"
+                 << "KEY: " << Key.str() << "\n";
+          Reader.readBitstream(Value);
+        });
+  }
+
+  return 0;
+}
Index: clang-doc/tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-doc/tool/CMakeLists.txt
@@ -0,0 +1,18 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(clang-doc
+  ClangDocMain.cpp
+  )
+
+target_link_libraries(clang-doc
+  PRIVATE
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangDoc
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
Index: clang-doc/ClangDocRepresentation.h
===================================================================
--- /dev/null
+++ clang-doc/ClangDocRepresentation.h
@@ -0,0 +1,99 @@
+///===-- ClangDocRepresentation.h - ClangDocRepresenation -------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPRESENTATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPRESENTATION_H
+
+#include <string>
+#include "clang/AST/Type.h"
+#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+// A representation of a parsed comment.
+struct CommentInfo {
+  StringRef Kind;
+  StringRef Text;
+  StringRef Name;
+  StringRef Direction;
+  StringRef ParamName;
+  StringRef CloseName;
+  bool SelfClosing = false;
+  bool Explicit = false;
+  llvm::SmallVector<StringRef, 4> AttrKeys;
+  llvm::SmallVector<StringRef, 4> AttrValues;
+  llvm::SmallVector<StringRef, 4> Args;
+  llvm::SmallVector<StringRef, 4> Position;
+  std::vector<std::shared_ptr<CommentInfo>> Children;
+};
+
+// TODO: Pull the CommentInfo for a parameter or member out of the record or
+// function's CommentInfo.
+// Info for named types (parameters, members).
+struct NamedType {
+  std::string Type;
+  std::string Name;
+  AccessSpecifier Access = clang::AccessSpecifier::AS_none;
+  CommentInfo Description;
+};
+
+/// A base struct for Infos.
+struct Info {
+  std::string FullyQualifiedName;
+  std::string SimpleName;
+  std::string Namespace;
+  CommentInfo Description;
+};
+
+struct NamespaceInfo : public Info {};
+
+struct SymbolInfo : public Info {
+  int LineNumber;
+  StringRef Filename;
+};
+
+struct NonDefInfo : public SymbolInfo {};
+
+// TODO: Expand to allow for documenting templating.
+// Info for functions.
+struct FunctionInfo : public SymbolInfo {
+  std::string MangledName;
+  std::string Parent;
+  NamedType ReturnType;
+  llvm::SmallVector<NamedType, 4> Params;
+  AccessSpecifier Access;
+};
+
+// TODO: Expand to allow for documenting templating, inheritance access,
+// friend classes
+// Info for types.
+struct RecordInfo : public SymbolInfo {
+  TagTypeKind TagType;
+  llvm::SmallVector<NamedType, 4> Members;
+  llvm::SmallVector<std::string, 4> Parents;
+  llvm::SmallVector<std::string, 4> VirtualParents;
+};
+
+// TODO: Expand to allow for documenting templating.
+// Info for types.
+struct EnumInfo : public SymbolInfo {
+  bool Scoped;
+  llvm::SmallVector<NamedType, 4> Members;
+};
+
+// TODO: Add functionality to include separate markdown pages.
+
+}  // namespace doc
+}  // namespace clang
+
+#endif  // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPRESENTATION_H
\ No newline at end of file
Index: clang-doc/ClangDocMapper.h
===================================================================
--- /dev/null
+++ clang-doc/ClangDocMapper.h
@@ -0,0 +1,95 @@
+//===-- ClangDocMapper.h - ClangDocMapper -----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_MAPPER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_MAPPER_H
+
+#include <memory>
+#include <string>
+#include <vector>
+#include "ClangDocBinary.h"
+#include "ClangDocRepresentation.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CommentVisitor.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/Execution.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::comments;
+using namespace clang::tooling;
+
+namespace clang {
+namespace doc {
+
+class ClangDocCommentVisitor
+    : public ConstCommentVisitor<ClangDocCommentVisitor> {
+ public:
+  ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
+
+  void parseComment(const comments::Comment *C);
+
+  void visitTextComment(const TextComment *C);
+  void visitInlineCommandComment(const InlineCommandComment *C);
+  void visitHTMLStartTagComment(const HTMLStartTagComment *C);
+  void visitHTMLEndTagComment(const HTMLEndTagComment *C);
+  void visitBlockCommandComment(const BlockCommandComment *C);
+  void visitParamCommandComment(const ParamCommandComment *C);
+  void visitTParamCommandComment(const TParamCommandComment *C);
+  void visitVerbatimBlockComment(const VerbatimBlockComment *C);
+  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
+  void visitVerbatimLineComment(const VerbatimLineComment *C);
+
+ private:
+  StringRef getCommandName(unsigned CommandID) const;
+  bool isWhitespaceOnly(StringRef S) const;
+
+  CommentInfo &CurrentCI;
+};
+
+class ClangDocMapper {
+ public:
+  ClangDocMapper(ClangDocBinaryWriter &Writer) : Writer(Writer) {}
+
+  template <class C>
+  StringRef emitInfo(const C *D, const FullComment *FC, StringRef Key,
+                     int LineNumber, StringRef File);
+
+ private:
+  template <typename T>
+  SmallString<2048> serialize(T &I) const;
+  void populateSymbolInfo(SymbolInfo &I, StringRef Name, StringRef SimpleName,
+                          StringRef Namespace, const FullComment *C,
+                          int LineNumber, StringRef File);
+  void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
+                            StringRef Name, const FullComment *C,
+                            int LineNumber, StringRef File);
+  template <class C>
+  StringRef serializeNonDefInfo(const C *D, StringRef Name,
+                                const FullComment *FC, int LineNumber,
+                                StringRef File);
+  void parseFields(RecordInfo &I, const RecordDecl *D) const;
+  void parseEnumerators(EnumInfo &I, const EnumDecl *D) const;
+  void parseBases(RecordInfo &I, const CXXRecordDecl *D) const;
+  void parseParameters(FunctionInfo &I, const FunctionDecl *D) const;
+  void parseFullComment(const FullComment *C, CommentInfo &CI);
+  std::string getParentNamespace(const DeclContext *D) const;
+
+  ClangDocBinaryWriter &Writer;
+};
+
+}  // namespace doc
+}  // namespace clang
+
+#endif  // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_MAPPER_H
Index: clang-doc/ClangDocMapper.cpp
===================================================================
--- /dev/null
+++ clang-doc/ClangDocMapper.cpp
@@ -0,0 +1,283 @@
+//===-- ClangDocMapper.cpp - ClangDoc Mapper ----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangDocMapper.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using clang::comments::FullComment;
+
+namespace clang {
+namespace doc {
+
+// ClangDocMapper
+
+template <>
+StringRef ClangDocMapper::emitInfo(const NamespaceDecl *D,
+                                   const FullComment *FC, StringRef Name,
+                                   int LineNumber, StringRef File) {
+  NamespaceInfo I;
+  I.FullyQualifiedName = Name;
+  I.SimpleName = D->getNameAsString();
+  I.Namespace = getParentNamespace(D);
+  if (FC) parseFullComment(FC, I.Description);
+  return serialize(I);
+}
+
+template <>
+StringRef ClangDocMapper::emitInfo(const RecordDecl *D, const FullComment *FC,
+                                   StringRef Name, int LineNumber,
+                                   StringRef File) {
+  if (!D->isThisDeclarationADefinition())
+    return serializeNonDefInfo(D, Name, FC, LineNumber, File);
+  RecordInfo I;
+  populateSymbolInfo(I, Name, D->getNameAsString(), getParentNamespace(D), FC,
+                     LineNumber, File);
+  I.TagType = D->getTagKind();
+  if (const auto *CXXR = dyn_cast<CXXRecordDecl>(D)) parseBases(I, CXXR);
+  parseFields(I, D);
+  return serialize(I);
+}
+
+template <>
+StringRef ClangDocMapper::emitInfo(const FunctionDecl *D, const FullComment *FC,
+                                   StringRef Name, int LineNumber,
+                                   StringRef File) {
+  if (!D->isThisDeclarationADefinition())
+    return serializeNonDefInfo(D, Name, FC, LineNumber, File);
+  FunctionInfo I;
+  populateFunctionInfo(I, D, Name, FC, LineNumber, File);
+  I.Access = clang::AccessSpecifier::AS_none;
+  return serialize(I);
+}
+
+template <>
+StringRef ClangDocMapper::emitInfo(const CXXMethodDecl *D,
+                                   const FullComment *FC, StringRef Name,
+                                   int LineNumber, StringRef File) {
+  if (!D->isThisDeclarationADefinition())
+    return serializeNonDefInfo(D, Name, FC, LineNumber, File);
+  FunctionInfo I;
+  populateFunctionInfo(I, D, Name, FC, LineNumber, File);
+  I.Parent = D->getParent()->getQualifiedNameAsString();
+  I.Access = D->getAccess();
+  return serialize(I);
+}
+
+template <>
+StringRef ClangDocMapper::emitInfo(const EnumDecl *D, const FullComment *FC,
+                                   StringRef Name, int LineNumber,
+                                   StringRef File) {
+  if (!D->isThisDeclarationADefinition())
+    return serializeNonDefInfo(D, Name, FC, LineNumber, File);
+  EnumInfo I;
+  populateSymbolInfo(I, Name, D->getNameAsString(), getParentNamespace(D), FC,
+                     LineNumber, File);
+  I.Scoped = D->isScoped();
+  parseEnumerators(I, D);
+  return serialize(I);
+}
+
+template <typename T>
+SmallString<2048> ClangDocMapper::serialize(T &I) const {
+  SmallString<2048> Buffer;
+  llvm::BitstreamWriter Stream(Buffer);
+  Writer.writeBitstream(I, Stream);
+  return Buffer;
+}
+
+void ClangDocMapper::parseFullComment(const FullComment *C, CommentInfo &CI) {
+  ClangDocCommentVisitor Visitor(CI);
+  Visitor.parseComment(C);
+}
+
+void ClangDocMapper::populateSymbolInfo(SymbolInfo &I, StringRef Name,
+                                        StringRef SimpleName,
+                                        StringRef Namespace,
+                                        const FullComment *C, int LineNumber,
+                                        StringRef File) {
+  I.FullyQualifiedName = Name;
+  I.SimpleName = SimpleName;
+  I.Namespace = Namespace;
+  I.LineNumber = LineNumber;
+  I.Filename = File;
+  if (C) parseFullComment(C, I.Description);
+}
+
+void ClangDocMapper::populateFunctionInfo(FunctionInfo &I,
+                                          const FunctionDecl *D, StringRef Name,
+                                          const FullComment *C, int LineNumber,
+                                          StringRef File) {
+  populateSymbolInfo(I, D->getQualifiedNameAsString(), D->getNameAsString(),
+                     getParentNamespace(D), C, LineNumber, File);
+  I.MangledName = Name;
+  NamedType N;
+  N.Type = D->getReturnType().getAsString();
+  I.ReturnType = N;
+  parseParameters(I, D);
+}
+
+template <class C>
+StringRef ClangDocMapper::serializeNonDefInfo(const C *D, StringRef Name,
+                                              const FullComment *FC,
+                                              int LineNumber, StringRef File) {
+  NonDefInfo I;
+  I.FullyQualifiedName = Name;
+  I.SimpleName = D->getNameAsString();
+  I.Namespace = getParentNamespace(D);
+  I.LineNumber = LineNumber;
+  I.Filename = File;
+  if (FC) parseFullComment(FC, I.Description);
+  return serialize(I);
+}
+
+void ClangDocMapper::parseFields(RecordInfo &I, const RecordDecl *D) const {
+  for (const FieldDecl *F : D->fields()) {
+    NamedType N;
+    N.Type = F->getTypeSourceInfo()->getType().getAsString();
+    N.Name = F->getQualifiedNameAsString();
+    // FIXME: Set Access to the appropriate value.
+    I.Members.emplace_back(N);
+  }
+}
+
+void ClangDocMapper::parseEnumerators(EnumInfo &I, const EnumDecl *D) const {
+  for (const EnumConstantDecl *E : D->enumerators()) {
+    NamedType N;
+    N.Type = E->getQualifiedNameAsString();
+    // FIXME: Set Access to the appropriate value.
+    I.Members.emplace_back(N);
+  }
+}
+
+void ClangDocMapper::parseParameters(FunctionInfo &I,
+                                     const FunctionDecl *D) const {
+  for (const ParmVarDecl *P : D->parameters()) {
+    NamedType N;
+    N.Type = P->getOriginalType().getAsString();
+    N.Name = P->getQualifiedNameAsString();
+    // FIXME: Set Access to the appropriate value.
+    I.Params.emplace_back(N);
+  }
+}
+
+void ClangDocMapper::parseBases(RecordInfo &I, const CXXRecordDecl *D) const {
+  for (const CXXBaseSpecifier &B : D->bases()) {
+    if (!B.isVirtual()) I.Parents.push_back(B.getType().getAsString());
+  }
+  for (const CXXBaseSpecifier &B : D->vbases())
+    I.VirtualParents.push_back(B.getType().getAsString());
+}
+
+std::string ClangDocMapper::getParentNamespace(const DeclContext *D) const {
+  if (const auto *N = dyn_cast<NamedDecl>(D->getParent())) {
+    return N->getQualifiedNameAsString();
+  }
+  return "";
+}
+
+// ClangDocCommentVisitor
+
+void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
+  CurrentCI.Kind = C->getCommentKindName();
+  ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
+  for (comments::Comment *Child :
+       make_range(C->child_begin(), C->child_end())) {
+    CurrentCI.Children.emplace_back(std::make_shared<CommentInfo>());
+    ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
+    Visitor.parseComment(Child);
+  }
+}
+
+void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
+  if (!isWhitespaceOnly(C->getText())) CurrentCI.Text = C->getText();
+}
+
+void ClangDocCommentVisitor::visitInlineCommandComment(
+    const InlineCommandComment *C) {
+  CurrentCI.Name = getCommandName(C->getCommandID());
+  for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i)
+    CurrentCI.Args.push_back(C->getArgText(i));
+}
+
+void ClangDocCommentVisitor::visitHTMLStartTagComment(
+    const HTMLStartTagComment *C) {
+  CurrentCI.Name = C->getTagName();
+  CurrentCI.SelfClosing = C->isSelfClosing();
+  for (unsigned i = 0, e = C->getNumAttrs(); i < e; ++i) {
+    const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
+    CurrentCI.AttrKeys.push_back(Attr.Name);
+    CurrentCI.AttrValues.push_back(Attr.Value);
+  }
+}
+
+void ClangDocCommentVisitor::visitHTMLEndTagComment(
+    const HTMLEndTagComment *C) {
+  CurrentCI.Name = C->getTagName();
+  CurrentCI.SelfClosing = true;
+}
+
+void ClangDocCommentVisitor::visitBlockCommandComment(
+    const BlockCommandComment *C) {
+  CurrentCI.Name = getCommandName(C->getCommandID());
+  for (unsigned i = 0, e = C->getNumArgs(); i < e; ++i)
+    CurrentCI.Args.push_back(C->getArgText(i));
+}
+
+void ClangDocCommentVisitor::visitParamCommandComment(
+    const ParamCommandComment *C) {
+  CurrentCI.Direction =
+      ParamCommandComment::getDirectionAsString(C->getDirection());
+  CurrentCI.Explicit = C->isDirectionExplicit();
+  if (C->hasParamName() && C->isParamIndexValid())
+    CurrentCI.ParamName = C->getParamNameAsWritten();
+}
+
+void ClangDocCommentVisitor::visitTParamCommandComment(
+    const TParamCommandComment *C) {
+  if (C->hasParamName() && C->isPositionValid())
+    CurrentCI.ParamName = C->getParamNameAsWritten();
+
+  if (C->isPositionValid()) {
+    for (unsigned i = 0, e = C->getDepth(); i < e; ++i)
+      CurrentCI.Position.push_back(std::to_string(C->getIndex(i)));
+  }
+}
+
+void ClangDocCommentVisitor::visitVerbatimBlockComment(
+    const VerbatimBlockComment *C) {
+  CurrentCI.Name = getCommandName(C->getCommandID());
+  CurrentCI.CloseName = C->getCloseName();
+}
+
+void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
+    const VerbatimBlockLineComment *C) {
+  if (!isWhitespaceOnly(C->getText())) CurrentCI.Text = C->getText();
+}
+
+void ClangDocCommentVisitor::visitVerbatimLineComment(
+    const VerbatimLineComment *C) {
+  if (!isWhitespaceOnly(C->getText())) CurrentCI.Text = C->getText();
+}
+
+StringRef ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
+  const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
+  if (Info) return Info->Name;
+  // TODO: Add parsing for \file command.
+  return "<not a builtin command>";
+}
+
+bool ClangDocCommentVisitor::isWhitespaceOnly(StringRef S) const {
+  return std::all_of(S.begin(), S.end(), isspace);
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/ClangDocBinary.h
===================================================================
--- /dev/null
+++ clang-doc/ClangDocBinary.h
@@ -0,0 +1,90 @@
+//===--  ClangDocBinary.h - ClangDoc Binary ---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BINARY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BINARY_H
+
+#include <vector>
+#include "ClangDocRepresentation.h"
+#include "clang/AST/AST.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Bitcode/BitstreamReader.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+class AbbreviationMap {
+  llvm::DenseMap<unsigned, unsigned> Abbrevs;
+
+ public:
+  AbbreviationMap() {}
+  void set(unsigned recordID, unsigned abbrevID);
+  unsigned get(unsigned recordID);
+  void clear();
+};
+
+class ClangDocBinaryWriter {
+ public:
+  ClangDocBinaryWriter(bool OmitFilenames = false)
+      : OmitFilenames(OmitFilenames){};
+
+  using RecordData = SmallVector<uint64_t, 128>;
+
+  template <typename T>
+  void writeBitstream(const T &I, BitstreamWriter &Stream);
+
+ private:
+  void emitBlockInfoBlock(BitstreamWriter &Stream);
+  void emitHeader(BitstreamWriter &Stream);
+  void emitStringRecord(StringRef Str, unsigned RecordId,
+                        BitstreamWriter &Stream);
+  void emitLocationRecord(int LineNumber, StringRef File, unsigned RecordId,
+                          BitstreamWriter &Stream);
+  void emitIntRecord(int Value, unsigned RecordId, BitstreamWriter &Stream);
+  void emitNamedTypeBlock(const NamedType &N, StringRef ID,
+                          BitstreamWriter &Stream);
+  void emitCommentBlock(const CommentInfo *I, BitstreamWriter &Stream);
+
+  void emitRecordID(unsigned ID, const char *Name, BitstreamWriter &Stream);
+  void emitBlockID(unsigned ID, const char *Name, BitstreamWriter &Stream);
+  void emitStringAbbrev(unsigned D, unsigned Block, BitstreamWriter &Stream);
+  void emitLocationAbbrev(unsigned D, unsigned Block, BitstreamWriter &Stream);
+  void emitIntAbbrev(unsigned D, unsigned Block, BitstreamWriter &Stream);
+
+  RecordData Record;
+  bool OmitFilenames;
+  AbbreviationMap Abbrevs;
+};
+
+class ClangDocBinaryReader {
+ public:
+  ClangDocBinaryReader(raw_ostream &OS) : OS(OS) {}
+
+  using RecordData = SmallVector<uint64_t, 128>;
+
+  bool readBitstream(SmallString<2048> Bits);
+
+ private:
+  enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin };
+  bool readBlock(llvm::BitstreamCursor &Stream, unsigned ID);
+  Cursor skipUntilRecordOrBlock(llvm::BitstreamCursor &Stream,
+                                unsigned &BlockOrRecordID);
+
+  Optional<llvm::BitstreamBlockInfo> BlockInfo;
+  std::map<unsigned, StringRef> RecordNames;
+  raw_ostream &OS;
+};
+
+}  // namespace doc
+}  // namespace clang
+
+#endif  // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BINARY_H
Index: clang-doc/ClangDocBinary.cpp
===================================================================
--- /dev/null
+++ clang-doc/ClangDocBinary.cpp
@@ -0,0 +1,559 @@
+//===--  ClangDocBinary.cpp - ClangDoc Binary -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangDocBinary.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+enum BlockIds {
+  NAMESPACE_BLOCK_ID = bitc::FIRST_APPLICATION_BLOCKID,
+  NONDEF_BLOCK_ID,
+  ENUM_BLOCK_ID,
+  NAMED_TYPE_BLOCK_ID,
+  RECORD_BLOCK_ID,
+  FUNCTION_BLOCK_ID,
+  COMMENT_BLOCK_ID,
+  BI_FIRST = NAMESPACE_BLOCK_ID,
+  BI_LAST = COMMENT_BLOCK_ID
+};
+
+#define INFODATATYPES(X) X##_FULLY_QUALIFIED_NAME, X##_NAME, X##_NAMESPACE,
+
+enum DataTypes {
+  COMMENT_KIND = 1,
+  COMMENT_TEXT,
+  COMMENT_NAME,
+  COMMENT_POSITION,
+  COMMENT_DIRECTION,
+  COMMENT_PARAMNAME,
+  COMMENT_CLOSENAME,
+  COMMENT_SELFCLOSING,
+  COMMENT_EXPLICIT,
+  COMMENT_ATTRKEY,
+  COMMENT_ATTRVAL,
+  COMMENT_ARG,
+  NAMED_TYPE_ID,
+  NAMED_TYPE_TYPE,
+  NAMED_TYPE_NAME,
+  NAMED_TYPE_ACCESS,
+  INFODATATYPES(NAMESPACE) 
+  INFODATATYPES(NONDEF) 
+  NONDEF_LOCATION,
+  INFODATATYPES(ENUM) 
+  ENUM_LOCATION,
+  ENUM_SCOPED,
+  ENUM_NAMED_TYPE,
+  ENUM_MEMBER,
+  INFODATATYPES(RECORD) 
+  RECORD_LOCATION,
+  RECORD_TAG_TYPE,
+  RECORD_MEMBER,
+  RECORD_PARENT,
+  RECORD_VPARENT,
+  INFODATATYPES(FUNCTION) 
+  FUNCTION_LOCATION,
+  FUNCTION_MANGLED_NAME,
+  FUNCTION_PARENT,
+  FUNCTION_ACCESS,
+  DT_FIRST = COMMENT_KIND,
+  DT_LAST = FUNCTION_ACCESS
+};
+
+#undef INFODATATYPES
+
+void AbbreviationMap::set(unsigned recordID, unsigned abbrevID) {
+  assert(Abbrevs.find(recordID) == Abbrevs.end() &&
+         "Abbreviation already set.");
+  Abbrevs[recordID] = abbrevID;
+}
+
+unsigned AbbreviationMap::get(unsigned recordID) {
+  assert(Abbrevs.find(recordID) != Abbrevs.end() && "Abbreviation not set.");
+  return Abbrevs[recordID];
+}
+
+void AbbreviationMap::clear() { Abbrevs.clear(); }
+
+void ClangDocBinaryWriter::emitHeader(BitstreamWriter &Stream) {
+  // Emit the file header.
+  Stream.Emit((unsigned)'D', 8);
+  Stream.Emit((unsigned)'O', 8);
+  Stream.Emit((unsigned)'C', 8);
+  Stream.Emit((unsigned)'S', 8);
+}
+
+/// \brief Emits a block ID in the BLOCKINFO block.
+void ClangDocBinaryWriter::emitBlockID(unsigned ID, const char *Name,
+                                       BitstreamWriter &Stream) {
+  Record.clear();
+  Record.push_back(ID);
+  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);
+
+  // Emit the block name if present.
+  if (!Name || Name[0] == 0) return;
+  Record.clear();
+  while (*Name) Record.push_back(*Name++);
+  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
+}
+
+/// \brief Emits a record ID in the BLOCKINFO block.
+void ClangDocBinaryWriter::emitRecordID(unsigned ID, const char *Name,
+                                        BitstreamWriter &Stream) {
+  Record.clear();
+  Record.push_back(ID);
+  while (*Name) Record.push_back(*Name++);
+  Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
+}
+
+// Common Abbreviations
+
+void ClangDocBinaryWriter::emitStringAbbrev(unsigned D, unsigned Block,
+                                            BitstreamWriter &Stream) {
+  auto Abbrev = std::make_shared<BitCodeAbbrev>();
+  Abbrev->Add(BitCodeAbbrevOp(D));
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16));  // String size
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));       // String
+  Abbrevs.set(D, Stream.EmitBlockInfoAbbrev(Block, Abbrev));
+}
+
+void ClangDocBinaryWriter::emitLocationAbbrev(unsigned D, unsigned Block,
+                                              BitstreamWriter &Stream) {
+  auto Abbrev = std::make_shared<BitCodeAbbrev>();
+  Abbrev->Add(BitCodeAbbrevOp(D));
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16));  // Line number
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16));  // Filename size
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));       // Filename
+  Abbrevs.set(D, Stream.EmitBlockInfoAbbrev(Block, Abbrev));
+}
+
+void ClangDocBinaryWriter::emitIntAbbrev(unsigned D, unsigned Block,
+                                         BitstreamWriter &Stream) {
+  auto Abbrev = std::make_shared<BitCodeAbbrev>();
+  Abbrev->Add(BitCodeAbbrevOp(D));
+  Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16));  // Integer
+  Abbrevs.set(D, Stream.EmitBlockInfoAbbrev(Block, Abbrev));
+}
+
+// Common Records
+
+void ClangDocBinaryWriter::emitStringRecord(StringRef Str, unsigned RecordId,
+                                            BitstreamWriter &Stream) {
+  if (Str.empty()) return;
+  Record.clear();
+  Record.push_back(RecordId);
+  Record.push_back(Str.size());
+  Stream.EmitRecordWithBlob(Abbrevs.get(RecordId), Record, Str);
+}
+
+void ClangDocBinaryWriter::emitLocationRecord(int LineNumber, StringRef File,
+                                              unsigned RecordId,
+                                              BitstreamWriter &Stream) {
+  if (OmitFilenames) return;
+  Record.clear();
+  Record.push_back(RecordId);
+  Record.push_back(LineNumber);
+  Record.push_back(File.size());
+  Stream.EmitRecordWithBlob(Abbrevs.get(RecordId), Record, File);
+}
+
+void ClangDocBinaryWriter::emitIntRecord(int Value, unsigned RecordId,
+                                         BitstreamWriter &Stream) {
+  if (!Value) return;
+  Record.clear();
+  Record.push_back(RecordId);
+  Record.push_back(Value);
+  Stream.EmitRecordWithAbbrev(Abbrevs.get(RecordId), Record);
+}
+
+// Common Blocks
+
+void ClangDocBinaryWriter::emitNamedTypeBlock(const NamedType &N, StringRef ID,
+                                              BitstreamWriter &Stream) {
+  Stream.EnterSubblock(NAMED_TYPE_BLOCK_ID, 5);
+  emitStringRecord(ID, NAMED_TYPE_ID, Stream);
+  emitStringRecord(N.Type, NAMED_TYPE_TYPE, Stream);
+  emitStringRecord(N.Name, NAMED_TYPE_NAME, Stream);
+  emitIntRecord(N.Access, NAMED_TYPE_ACCESS, Stream);
+  Stream.ExitBlock();
+}
+
+void ClangDocBinaryWriter::emitCommentBlock(const CommentInfo *I,
+                                            BitstreamWriter &Stream) {
+  Stream.EnterSubblock(COMMENT_BLOCK_ID, 5);
+  emitStringRecord(I->Kind, COMMENT_KIND, Stream);
+  emitStringRecord(I->Text, COMMENT_TEXT, Stream);
+  emitStringRecord(I->Name, COMMENT_NAME, Stream);
+  emitStringRecord(I->Direction, COMMENT_DIRECTION, Stream);
+  emitStringRecord(I->ParamName, COMMENT_PARAMNAME, Stream);
+  emitStringRecord(I->CloseName, COMMENT_CLOSENAME, Stream);
+  emitIntRecord(I->SelfClosing, COMMENT_SELFCLOSING, Stream);
+  emitIntRecord(I->Explicit, COMMENT_EXPLICIT, Stream);
+  for (const auto &A : I->AttrKeys)
+    emitStringRecord(A, COMMENT_ATTRKEY, Stream);
+  for (const auto &A : I->AttrValues)
+    emitStringRecord(A, COMMENT_ATTRVAL, Stream);
+  for (const auto &A : I->Args) emitStringRecord(A, COMMENT_ARG, Stream);
+  for (const auto &P : I->Position)
+    emitStringRecord(P, COMMENT_POSITION, Stream);
+  for (const auto &C : I->Children) emitCommentBlock(C.get(), Stream);
+  Stream.ExitBlock();
+}
+
+void ClangDocBinaryWriter::emitBlockInfoBlock(BitstreamWriter &Stream) {
+  Abbrevs.clear();
+  emitHeader(Stream);
+  Stream.EnterBlockInfoBlock();
+
+  // Comment Block
+  emitBlockID(COMMENT_BLOCK_ID, "CommentBlock", Stream);
+  emitRecordID(COMMENT_KIND, "Kind", Stream);
+  emitRecordID(COMMENT_TEXT, "Text", Stream);
+  emitRecordID(COMMENT_NAME, "Name", Stream);
+  emitRecordID(COMMENT_DIRECTION, "Direction", Stream);
+  emitRecordID(COMMENT_PARAMNAME, "ParamName", Stream);
+  emitRecordID(COMMENT_CLOSENAME, "CloseName", Stream);
+  emitRecordID(COMMENT_SELFCLOSING, "SelfClosing", Stream);
+  emitRecordID(COMMENT_EXPLICIT, "Explicit", Stream);
+  emitRecordID(COMMENT_ATTRKEY, "AtrrKey", Stream);
+  emitRecordID(COMMENT_ATTRVAL, "AttrVal", Stream);
+  emitRecordID(COMMENT_ARG, "Arg", Stream);
+  emitRecordID(COMMENT_POSITION, "Position", Stream);
+  emitStringAbbrev(COMMENT_KIND, COMMENT_BLOCK_ID, Stream);
+  emitStringAbbrev(COMMENT_TEXT, COMMENT_BLOCK_ID, Stream);
+  emitStringAbbrev(COMMENT_NAME, COMMENT_BLOCK_ID, Stream);
+  emitStringAbbrev(COMMENT_POSITION, COMMENT_BLOCK_ID, Stream);
+  emitStringAbbrev(COMMENT_DIRECTION, COMMENT_BLOCK_ID, Stream);
+  emitStringAbbrev(COMMENT_PARAMNAME, COMMENT_BLOCK_ID, Stream);
+  emitStringAbbrev(COMMENT_CLOSENAME, COMMENT_BLOCK_ID, Stream);
+  emitIntAbbrev(COMMENT_SELFCLOSING, COMMENT_BLOCK_ID, Stream);
+  emitIntAbbrev(COMMENT_EXPLICIT, COMMENT_BLOCK_ID, Stream);
+  emitStringAbbrev(COMMENT_ATTRKEY, COMMENT_BLOCK_ID, Stream);
+  emitStringAbbrev(COMMENT_ATTRVAL, COMMENT_BLOCK_ID, Stream);
+  emitStringAbbrev(COMMENT_ARG, COMMENT_BLOCK_ID, Stream);
+
+  // NamedType Block
+  emitBlockID(NAMED_TYPE_BLOCK_ID, "NamedTypeBlock", Stream);
+  emitRecordID(NAMED_TYPE_ID, "ID", Stream);
+  emitRecordID(NAMED_TYPE_TYPE, "Type", Stream);
+  emitRecordID(NAMED_TYPE_NAME, "Name", Stream);
+  emitRecordID(NAMED_TYPE_ACCESS, "Access", Stream);
+  emitStringAbbrev(NAMED_TYPE_ID, NAMED_TYPE_BLOCK_ID, Stream);
+  emitStringAbbrev(NAMED_TYPE_TYPE, NAMED_TYPE_BLOCK_ID, Stream);
+  emitStringAbbrev(NAMED_TYPE_NAME, NAMED_TYPE_BLOCK_ID, Stream);
+  emitIntAbbrev(NAMED_TYPE_ACCESS, NAMED_TYPE_BLOCK_ID, Stream);
+
+#define INFORECORD(X)                                                   \
+  emitRecordID(X##_FULLY_QUALIFIED_NAME, "FullyQualifiedName", Stream); \
+  emitRecordID(X##_NAME, "Name", Stream);                               \
+  emitRecordID(X##_NAMESPACE, "Namespace", Stream);
+
+#define INFOABBREV(X)                                               \
+  emitStringAbbrev(X##_FULLY_QUALIFIED_NAME, X##_BLOCK_ID, Stream); \
+  emitStringAbbrev(X##_NAME, X##_BLOCK_ID, Stream);                 \
+  emitStringAbbrev(X##_NAMESPACE, X##_BLOCK_ID, Stream);
+
+  // Namespace Block
+  emitBlockID(NAMESPACE_BLOCK_ID, "NamespaceBlock", Stream);
+  INFORECORD(NAMESPACE)
+  INFOABBREV(NAMESPACE)
+
+  // NonDef Block
+  emitBlockID(NONDEF_BLOCK_ID, "NonDefBlock", Stream);
+  INFORECORD(NONDEF)
+  emitRecordID(NONDEF_LOCATION, "Location", Stream);
+  INFOABBREV(NONDEF)
+  emitLocationAbbrev(NONDEF_LOCATION, NONDEF_BLOCK_ID, Stream);
+
+  // Enum Block
+  emitBlockID(ENUM_BLOCK_ID, "EnumBlock", Stream);
+  INFORECORD(ENUM)
+  emitRecordID(ENUM_LOCATION, "Location", Stream);
+  emitRecordID(ENUM_SCOPED, "Scoped", Stream);
+  INFOABBREV(ENUM)
+  emitLocationAbbrev(ENUM_LOCATION, ENUM_BLOCK_ID, Stream);
+  emitIntAbbrev(ENUM_SCOPED, ENUM_BLOCK_ID, Stream);
+
+  // Record Block
+  emitBlockID(RECORD_BLOCK_ID, "RecordBlock", Stream);
+  INFORECORD(RECORD)
+  emitRecordID(RECORD_LOCATION, "Location", Stream);
+  emitRecordID(RECORD_TAG_TYPE, "TagType", Stream);
+  emitRecordID(RECORD_PARENT, "Parent", Stream);
+  emitRecordID(RECORD_VPARENT, "VParent", Stream);
+  INFOABBREV(RECORD)
+  emitLocationAbbrev(RECORD_LOCATION, RECORD_BLOCK_ID, Stream);
+  emitIntAbbrev(RECORD_TAG_TYPE, RECORD_BLOCK_ID, Stream);
+  emitStringAbbrev(RECORD_PARENT, RECORD_BLOCK_ID, Stream);
+  emitStringAbbrev(RECORD_VPARENT, RECORD_BLOCK_ID, Stream);
+
+  // Function Block
+  emitBlockID(FUNCTION_BLOCK_ID, "FunctionBlock", Stream);
+  INFORECORD(FUNCTION)
+  emitRecordID(FUNCTION_LOCATION, "Location", Stream);
+  emitRecordID(FUNCTION_MANGLED_NAME, "MangledName", Stream);
+  emitRecordID(FUNCTION_PARENT, "Parent", Stream);
+  emitRecordID(FUNCTION_ACCESS, "Access", Stream);
+  INFOABBREV(FUNCTION)
+  emitLocationAbbrev(FUNCTION_LOCATION, FUNCTION_BLOCK_ID, Stream);
+  emitStringAbbrev(FUNCTION_MANGLED_NAME, FUNCTION_BLOCK_ID, Stream);
+  emitStringAbbrev(FUNCTION_PARENT, FUNCTION_BLOCK_ID, Stream);
+  emitIntAbbrev(FUNCTION_ACCESS, FUNCTION_BLOCK_ID, Stream);
+
+#undef INFORECORDS
+#undef INFOABBREV
+
+  Stream.ExitBlock();
+}
+
+// Info emission
+
+#define EMITINFO(X)                                                         \
+  emitStringRecord(I.FullyQualifiedName, X##_FULLY_QUALIFIED_NAME, Stream); \
+  emitStringRecord(I.SimpleName, X##_NAME, Stream);                         \
+  emitStringRecord(I.Namespace, X##_NAMESPACE, Stream);                     \
+  emitCommentBlock(&I.Description, Stream);
+
+template <>
+void ClangDocBinaryWriter::writeBitstream(const NamespaceInfo &I,
+                                          BitstreamWriter &Stream) {
+  emitBlockInfoBlock(Stream);
+  Stream.EnterSubblock(NAMESPACE_BLOCK_ID, 5);
+  EMITINFO(NAMESPACE)
+  Stream.ExitBlock();
+}
+
+template <>
+void ClangDocBinaryWriter::writeBitstream(const NonDefInfo &I,
+                                          BitstreamWriter &Stream) {
+  emitBlockInfoBlock(Stream);
+  Stream.EnterSubblock(NONDEF_BLOCK_ID, 5);
+  EMITINFO(NONDEF)
+  emitLocationRecord(I.LineNumber, I.Filename, NONDEF_LOCATION, Stream);
+  Stream.ExitBlock();
+}
+
+template <>
+void ClangDocBinaryWriter::writeBitstream(const EnumInfo &I,
+                                          BitstreamWriter &Stream) {
+  emitBlockInfoBlock(Stream);
+  Stream.EnterSubblock(ENUM_BLOCK_ID, 5);
+  EMITINFO(ENUM)
+  emitLocationRecord(I.LineNumber, I.Filename, ENUM_LOCATION, Stream);
+  emitIntRecord(I.Scoped, ENUM_SCOPED, Stream);
+  for (const auto &N : I.Members) emitNamedTypeBlock(N, "Member", Stream);
+  Stream.ExitBlock();
+}
+
+template <>
+void ClangDocBinaryWriter::writeBitstream(const RecordInfo &I,
+                                          BitstreamWriter &Stream) {
+  emitBlockInfoBlock(Stream);
+  Stream.EnterSubblock(RECORD_BLOCK_ID, 5);
+  EMITINFO(RECORD)
+  emitLocationRecord(I.LineNumber, I.Filename, RECORD_LOCATION, Stream);
+  emitIntRecord(I.TagType, RECORD_TAG_TYPE, Stream);
+  for (const auto &N : I.Members) emitNamedTypeBlock(N, "Member", Stream);
+  for (const auto &P : I.Parents) emitStringRecord(P, RECORD_PARENT, Stream);
+  for (const auto &P : I.VirtualParents)
+    emitStringRecord(P, RECORD_VPARENT, Stream);
+  Stream.ExitBlock();
+}
+
+template <>
+void ClangDocBinaryWriter::writeBitstream(const FunctionInfo &I,
+                                          BitstreamWriter &Stream) {
+  emitBlockInfoBlock(Stream);
+  Stream.EnterSubblock(FUNCTION_BLOCK_ID, 5);
+  EMITINFO(FUNCTION)
+  emitLocationRecord(I.LineNumber, I.Filename, FUNCTION_LOCATION, Stream);
+  emitStringRecord(I.MangledName, FUNCTION_MANGLED_NAME, Stream);
+  emitStringRecord(I.Parent, FUNCTION_PARENT, Stream);
+  emitNamedTypeBlock(I.ReturnType, "Return", Stream);
+  for (const auto &N : I.Params) emitNamedTypeBlock(N, "Param", Stream);
+  emitIntRecord(I.Access, FUNCTION_ACCESS, Stream);
+  Stream.ExitBlock();
+}
+
+#undef EMITINFO
+
+// Reader
+
+bool ClangDocBinaryReader::readBitstream(SmallString<2048> Bits) {
+  BitstreamCursor Stream(Bits);
+
+  if (Stream.AtEndOfStream()) return false;
+
+  // Sniff for the signature.
+  if (Stream.Read(8) != 'D' || Stream.Read(8) != 'O' || Stream.Read(8) != 'C' ||
+      Stream.Read(8) != 'S')
+    return false;
+
+  // Read the top level blocks.
+  while (!Stream.AtEndOfStream()) {
+    unsigned Code = Stream.ReadCode();
+    if (Code != bitc::ENTER_SUBBLOCK) return false;
+
+    switch (Stream.ReadSubBlockID()) {
+      case llvm::bitc::BLOCKINFO_BLOCK_ID: {
+        BlockInfo = Stream.ReadBlockInfoBlock(/*ReadBlockInfoNames=*/true);
+        if (!BlockInfo) return false;
+        Stream.setBlockInfo(&*BlockInfo);
+        // Extract the record names associated with each field
+        for (unsigned i = BI_FIRST; i <= BI_LAST; ++i) {
+          for (const auto &N : (*BlockInfo).getBlockInfo(i)->RecordNames)
+            RecordNames[N.first] = N.second;
+        }
+        continue;
+      }
+      case NAMESPACE_BLOCK_ID:
+        if (readBlock(Stream, NAMESPACE_BLOCK_ID)) return true;
+        continue;
+      case NONDEF_BLOCK_ID:
+        if (readBlock(Stream, NONDEF_BLOCK_ID)) return true;
+        continue;
+      case NAMED_TYPE_BLOCK_ID:
+        if (readBlock(Stream, NAMED_TYPE_BLOCK_ID)) return true;
+        continue;
+      case COMMENT_BLOCK_ID:
+        if (readBlock(Stream, COMMENT_BLOCK_ID)) return true;
+        continue;
+      case RECORD_BLOCK_ID:
+        if (readBlock(Stream, RECORD_BLOCK_ID)) return true;
+        continue;
+      case ENUM_BLOCK_ID:
+        if (readBlock(Stream, ENUM_BLOCK_ID)) return true;
+        continue;
+      case FUNCTION_BLOCK_ID:
+        if (readBlock(Stream, FUNCTION_BLOCK_ID)) return true;
+        continue;
+      default:
+        if (!Stream.SkipBlock()) return false;
+        continue;
+    }
+  }
+  return true;
+}
+
+bool ClangDocBinaryReader::readBlock(llvm::BitstreamCursor &Stream,
+                                     unsigned ID) {
+  if (Stream.EnterSubBlock(ID)) return false;
+
+  SmallVector<uint64_t, 1024> Record;
+  while (true) {
+    unsigned BlockOrCode = 0;
+    Cursor Res = skipUntilRecordOrBlock(Stream, BlockOrCode);
+
+    switch (Res) {
+      case Cursor::BadBlock:
+        return false;
+      case Cursor::BlockEnd:
+        return true;
+      case Cursor::BlockBegin:
+        if (readBlock(Stream, BlockOrCode)) continue;
+        if (!Stream.SkipBlock()) return false;
+        continue;
+      case Cursor::Record:
+        break;
+    }
+
+    // Read the record.
+    Record.clear();
+    StringRef Blob;
+    unsigned RecID = Stream.readRecord(BlockOrCode, Record, &Blob);
+    if (RecID < DT_FIRST || RecID > DT_LAST) continue;
+
+#define INFOCASES(X)             \
+  case X##_FULLY_QUALIFIED_NAME: \
+  case X##_NAME:                 \
+  case X##_NAMESPACE:
+
+    switch ((DataTypes)RecID) {
+      // Locations
+      case ENUM_LOCATION:
+      case RECORD_LOCATION:
+      case FUNCTION_LOCATION:
+      case NONDEF_LOCATION:
+        OS << RecordNames[RecID] << ": " << Blob << ":" << Record[0] << "\n";
+        continue;
+
+      // Strings
+      INFOCASES(NAMESPACE)
+      INFOCASES(NONDEF)
+      INFOCASES(ENUM)
+      INFOCASES(RECORD)
+      INFOCASES(FUNCTION)
+      case NAMED_TYPE_ID:
+      case NAMED_TYPE_TYPE:
+      case NAMED_TYPE_NAME:
+      case RECORD_PARENT:
+      case RECORD_VPARENT:
+      case FUNCTION_PARENT:
+      case FUNCTION_MANGLED_NAME:
+      case COMMENT_KIND:
+      case COMMENT_TEXT:
+      case COMMENT_NAME:
+      case COMMENT_DIRECTION:
+      case COMMENT_PARAMNAME:
+      case COMMENT_CLOSENAME:
+      case COMMENT_ATTRKEY:
+      case COMMENT_ATTRVAL:
+      case COMMENT_ARG:
+      case COMMENT_POSITION:
+        OS << RecordNames[RecID] << ": " << Blob << "\n";
+        continue;
+
+      // Ints
+      case ENUM_SCOPED:
+      case RECORD_TAG_TYPE:
+      case NAMED_TYPE_ACCESS:
+      case FUNCTION_ACCESS:
+      case COMMENT_SELFCLOSING:
+      case COMMENT_EXPLICIT:
+        OS << RecordNames[RecID] << ": " << Record[0] << "\n";
+      default:
+        continue;
+    }
+  }
+}
+
+#undef INFOCASES
+
+ClangDocBinaryReader::Cursor ClangDocBinaryReader::skipUntilRecordOrBlock(
+    llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) {
+  BlockOrRecordID = 0;
+
+  while (!Stream.AtEndOfStream()) {
+    unsigned Code = Stream.ReadCode();
+
+    switch ((llvm::bitc::FixedAbbrevIDs)Code) {
+      case llvm::bitc::ENTER_SUBBLOCK:
+        BlockOrRecordID = Stream.ReadSubBlockID();
+        return Cursor::BlockBegin;
+      case llvm::bitc::END_BLOCK:
+        if (Stream.ReadBlockEnd()) return Cursor::BadBlock;
+        return Cursor::BlockEnd;
+      case llvm::bitc::DEFINE_ABBREV:
+        Stream.ReadAbbrevRecord();
+        continue;
+      case llvm::bitc::UNABBREV_RECORD:
+        return Cursor::BadBlock;
+      default:
+        // We found a record.
+        BlockOrRecordID = Code;
+        return Cursor::Record;
+    }
+  }
+  llvm_unreachable("Premature stream end.");
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/ClangDoc.h
===================================================================
--- /dev/null
+++ clang-doc/ClangDoc.h
@@ -0,0 +1,60 @@
+//===-- ClangDoc.h - ClangDoc -----------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
+
+#include <string>
+#include <vector>
+#include "ClangDocBinary.h"
+#include "ClangDocMapper.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Comment.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/Tooling.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace doc {
+
+/// Callback class for matchers.
+/// Parses each match and sends it along to the reporter for serialization.
+class ClangDocCallback : public MatchFinder::MatchCallback {
+ public:
+  ClangDocCallback(StringRef BoundName, ExecutionContext &ECtx,
+                   ClangDocBinaryWriter &Writer)
+      : ECtx(ECtx), Mapper(Writer), BoundName(BoundName) {}
+  virtual void run(const MatchFinder::MatchResult &Result) final;
+
+ private:
+  template <class T>
+  void processMatchedDecl(const T *D);
+  int getLine(const NamedDecl *D) const;
+  StringRef getFile(const NamedDecl *D) const;
+  comments::FullComment *getComment(const NamedDecl *D) const;
+  std::string getName(const NamedDecl *D) const;
+
+  ASTContext *Context;
+  ExecutionContext &ECtx;
+  ClangDocMapper Mapper;
+  StringRef BoundName;
+};
+
+}  // namespace doc
+}  // namespace clang
+
+#endif  // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
Index: clang-doc/ClangDoc.cpp
===================================================================
--- /dev/null
+++ clang-doc/ClangDoc.cpp
@@ -0,0 +1,86 @@
+//===-- ClangDoc.cpp - ClangDoc ---------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangDoc.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/Comment.h"
+#include "clang/AST/Mangle.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::tooling;
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+template <typename T>
+void ClangDocCallback::processMatchedDecl(const T *D) {
+  if (!Context->getSourceManager().isWrittenInMainFile(D->getLocation()))
+    return;
+  std::string Name = getName(D);
+  ECtx.reportResult(
+      Name, Mapper.emitInfo(D, getComment(D), Name, getLine(D), getFile(D)));
+}
+
+void ClangDocCallback::run(const MatchFinder::MatchResult &Result) {
+  Context = Result.Context;
+  if (const auto *M = Result.Nodes.getNodeAs<NamespaceDecl>(BoundName))
+    processMatchedDecl(M);
+  else if (const auto *M = Result.Nodes.getNodeAs<RecordDecl>(BoundName))
+    processMatchedDecl(M);
+  else if (const auto *M = Result.Nodes.getNodeAs<EnumDecl>(BoundName))
+    processMatchedDecl(M);
+  else if (const auto *M = Result.Nodes.getNodeAs<CXXMethodDecl>(BoundName))
+    processMatchedDecl(M);
+  else if (const auto *M = Result.Nodes.getNodeAs<FunctionDecl>(BoundName))
+    processMatchedDecl(M);
+}
+
+comments::FullComment *ClangDocCallback::getComment(const NamedDecl *D) const {
+  RawComment *Comment = Context->getRawCommentForDeclNoCache(D);
+  // FIXME: Move setAttached to the initial comment parsing.
+  if (Comment) {
+    Comment->setAttached();
+    return Comment->parse(*Context, nullptr, D);
+  }
+  return nullptr;
+}
+
+int ClangDocCallback::getLine(const NamedDecl *D) const {
+  return Context->getSourceManager().getPresumedLoc(D->getLocStart()).getLine();
+}
+
+StringRef ClangDocCallback::getFile(const NamedDecl *D) const {
+  return Context->getSourceManager()
+      .getPresumedLoc(D->getLocStart())
+      .getFilename();
+}
+
+std::string ClangDocCallback::getName(const NamedDecl *D) const {
+  if (const auto *F = dyn_cast<FunctionDecl>(D)) {
+    MangleContext *MC = Context->createMangleContext();
+    std::string S;
+    llvm::raw_string_ostream MangledName(S);
+    if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(F))
+      MC->mangleCXXCtor(Ctor, CXXCtorType::Ctor_Complete, MangledName);
+    else if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(F))
+      MC->mangleCXXDtor(Dtor, CXXDtorType::Dtor_Complete, MangledName);
+    else
+      MC->mangleName(F, MangledName);
+    return MangledName.str();
+  }
+  return D->getQualifiedNameAsString();
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-doc/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(clangDoc
+  ClangDoc.cpp
+  ClangDocMapper.cpp
+  ClangDocBinary.cpp
+
+  LINK_LIBS
+  clangAnalysis
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -7,6 +7,7 @@
 endif()
 
 add_subdirectory(change-namespace)
+add_subdirectory(clang-doc)
 add_subdirectory(clang-query)
 add_subdirectory(clang-move)
 add_subdirectory(clangd)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to