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

1. Adding in a basic test setup for the framework
2. Pulling the YAML specs out into their own file
3. Expanding the representation to consider different types of declarations 
(namespace, tag, and function) and store the appropriate information for output.


https://reviews.llvm.org/D41102

Files:
  test/CMakeLists.txt
  test/Tooling/clang-doc-basic.cpp
  test/lit.cfg.py
  tools/CMakeLists.txt
  tools/clang-doc/CMakeLists.txt
  tools/clang-doc/ClangDoc.cpp
  tools/clang-doc/ClangDoc.h
  tools/clang-doc/ClangDocReporter.cpp
  tools/clang-doc/ClangDocReporter.h
  tools/clang-doc/ClangDocYAML.h
  tools/clang-doc/tool/CMakeLists.txt
  tools/clang-doc/tool/ClangDocMain.cpp

Index: tools/clang-doc/tool/ClangDocMain.cpp
===================================================================
--- /dev/null
+++ tools/clang-doc/tool/ClangDocMain.cpp
@@ -0,0 +1,68 @@
+//===-- 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 "ClangDoc.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include <string>
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+cl::OptionCategory ClangDocCategory("clang-doc options");
+
+cl::opt<bool>
+    EmitLLVM("emit-llvm",
+             cl::desc("Output in LLVM bitstream format (default is YAML)."),
+             cl::init(false), cl::cat(ClangDocCategory));
+
+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]);
+  tooling::CommonOptionsParser OptionsParser(argc, argv, ClangDocCategory);
+
+  clang::doc::OutFormat EmitFormat =
+      EmitLLVM ? doc::OutFormat::LLVM : doc::OutFormat::YAML;
+
+  // TODO: Update the source path list to only consider changed files for
+  // incremental doc updates.
+  doc::ClangDocReporter Reporter(OptionsParser.getSourcePathList());
+  doc::ClangDocContext Context{EmitFormat};
+
+  tooling::ClangTool Tool(OptionsParser.getCompilations(),
+                          OptionsParser.getSourcePathList());
+
+  if (!DoxygenOnly)
+    Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
+        "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
+
+  doc::ClangDocActionFactory Factory(Context, Reporter);
+
+  outs() << "Parsing codebase...\n";
+  int Status = Tool.run(&Factory);
+  if (Status)
+    return Status;
+
+  outs() << "Writing docs...\n";
+  Reporter.serialize(EmitFormat, outs());
+
+  return 0;
+}
Index: tools/clang-doc/tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ tools/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: tools/clang-doc/ClangDocYAML.h
===================================================================
--- /dev/null
+++ tools/clang-doc/ClangDocYAML.h
@@ -0,0 +1,193 @@
+//===--  ClangDocYAML.h - ClangDoc YAML -------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/YAMLTraits.h"
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::CommentInfo)
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Link)
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::NamedType)
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair<std::string>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair<clang::doc::File>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair<clang::doc::Info>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair<clang::doc::TypeInfo>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair<clang::doc::FunctionInfo>)
+
+namespace llvm {
+namespace yaml {
+
+template <typename T> struct NormalizedMap {
+  NormalizedMap(IO &) {}
+  NormalizedMap(IO &, const StringMap<T> &Map) {
+    for (const auto &Entry : Map) {
+      clang::doc::Pair<T> Pair{Entry.getKeyData(), Entry.getValue()};
+      VectorMap.push_back(Pair);
+    }
+  }
+
+  StringMap<T> denormalize(IO &) {
+    StringMap<T> Map;
+    for (const auto &Pair : VectorMap)
+      Map[Pair.Key] = Pair.Value;
+    return Map;
+  }
+
+  std::vector<clang::doc::Pair<T>> VectorMap;
+};
+
+template <> struct ScalarEnumerationTraits<clang::doc::DeclType> {
+  static void enumeration(IO &io, clang::doc::DeclType &value) {
+    io.enumCase(value, "File", clang::doc::DeclType::FILE);
+    io.enumCase(value, "Type", clang::doc::DeclType::TYPE);
+    io.enumCase(value, "Namespace", clang::doc::DeclType::NAMESPACE);
+    io.enumCase(value, "Function", clang::doc::DeclType::FUNCTION);
+  }
+};
+
+template <> struct ScalarEnumerationTraits<clang::AccessSpecifier> {
+  static void enumeration(IO &io, clang::AccessSpecifier &value) {
+    io.enumCase(value, "Public", clang::AccessSpecifier::AS_public);
+    io.enumCase(value, "Protected", clang::AccessSpecifier::AS_protected);
+    io.enumCase(value, "Private", clang::AccessSpecifier::AS_private);
+    io.enumCase(value, "None", clang::AccessSpecifier::AS_none);
+  }
+};
+
+template <> struct ScalarEnumerationTraits<clang::TagTypeKind> {
+  static void enumeration(IO &io, clang::TagTypeKind &value) {
+    io.enumCase(value, "Struct", clang::TagTypeKind::TTK_Struct);
+    io.enumCase(value, "Interface", clang::TagTypeKind::TTK_Interface);
+    io.enumCase(value, "Union", clang::TagTypeKind::TTK_Union);
+    io.enumCase(value, "Class", clang::TagTypeKind::TTK_Class);
+    io.enumCase(value, "Enum", clang::TagTypeKind::TTK_Enum);
+  }
+};
+
+template <> struct MappingTraits<clang::doc::Pair<std::string>> {
+  static void mapping(IO &IO, clang::doc::Pair<std::string> &Pair) {
+    IO.mapOptional("Value", Pair.Value);
+  }
+};
+
+// TODO: uncomment description attrs
+template <> struct MappingTraits<clang::doc::Pair<clang::doc::Info>> {
+  static void mapping(IO &IO, clang::doc::Pair<clang::doc::Info> &Pair) {
+    IO.mapOptional("Qualified Name", Pair.Value.FullyQualifiedName);
+    IO.mapOptional("Name", Pair.Value.SimpleName);
+    IO.mapOptional("Namespace", Pair.Value.Namespace);
+    IO.mapOptional("Descriptions", Pair.Value.Descriptions);
+    IO.mapOptional("Locations", Pair.Value.Locations);
+  }
+};
+
+template <> struct MappingTraits<clang::doc::Pair<clang::doc::TypeInfo>> {
+  static void mapping(IO &IO, clang::doc::Pair<clang::doc::TypeInfo> &Pair) {
+    IO.mapOptional("Qualified Name", Pair.Value.FullyQualifiedName);
+    IO.mapOptional("Name", Pair.Value.SimpleName);
+    IO.mapOptional("Namespace", Pair.Value.Namespace);
+    IO.mapOptional("Descriptions", Pair.Value.Descriptions);
+    IO.mapOptional("TagType", Pair.Value.TagType);
+    IO.mapOptional("Locations", Pair.Value.Locations);
+    IO.mapOptional("DefinitionFile", Pair.Value.DefinitionFile);
+    IO.mapOptional("Members", Pair.Value.Members);
+    IO.mapOptional("Parents", Pair.Value.Parents);
+    IO.mapOptional("VirtualParents", Pair.Value.VirtualParents);
+  }
+};
+
+template <> struct MappingTraits<clang::doc::NamedType> {
+  static void mapping(IO &IO, clang::doc::NamedType &NamedType) {
+    IO.mapOptional("Type", NamedType.Type);
+    IO.mapOptional("Name", NamedType.Name);
+    IO.mapOptional("Access", NamedType.Access);
+  }
+};
+
+template <> struct MappingTraits<clang::doc::Pair<clang::doc::FunctionInfo>> {
+  static void mapping(IO &IO,
+                      clang::doc::Pair<clang::doc::FunctionInfo> &Pair) {
+    IO.mapOptional("Qualified Name", Pair.Value.FullyQualifiedName);
+    IO.mapOptional("Name", Pair.Value.SimpleName);
+    IO.mapOptional("Namespace", Pair.Value.Namespace);
+    IO.mapOptional("Descriptions", Pair.Value.Descriptions);
+    IO.mapOptional("Locations", Pair.Value.Locations);
+    IO.mapOptional("DefinitionFile", Pair.Value.DefinitionFile);
+    IO.mapOptional("Params", Pair.Value.Params);
+    IO.mapOptional("ReturnType", Pair.Value.ReturnType);
+    IO.mapOptional("Parent", Pair.Value.Parent);
+    IO.mapOptional("Access", Pair.Value.Access);
+  }
+};
+
+template <> struct MappingTraits<clang::doc::Pair<clang::doc::File>> {
+  static void mapping(IO &IO, clang::doc::Pair<clang::doc::File> &Pair) {
+    IO.mapOptional("Filename", Pair.Value.Filename);
+    IO.mapOptional("Description", Pair.Value.Description);
+  }
+};
+
+template <> struct MappingTraits<clang::doc::Link> {
+  static void mapping(IO &IO, clang::doc::Link &Link) {
+    StringRef Type;
+    IO.mapOptional("Type", Link.InfoType);
+    IO.mapOptional("Name", Link.FullyQualifiedName);
+  }
+};
+
+template <> struct MappingTraits<clang::doc::Documentation> {
+  static void mapping(IO &IO, clang::doc::Documentation &Docs) {
+    MappingNormalization<NormalizedMap<clang::doc::File>,
+                         StringMap<clang::doc::File>>
+        files(IO, Docs.Files);
+    MappingNormalization<NormalizedMap<clang::doc::Info>,
+                         StringMap<clang::doc::Info>>
+        namespaces(IO, Docs.Namespaces);
+    MappingNormalization<NormalizedMap<clang::doc::TypeInfo>,
+                         StringMap<clang::doc::TypeInfo>>
+        types(IO, Docs.Types);
+    MappingNormalization<NormalizedMap<clang::doc::FunctionInfo>,
+                         StringMap<clang::doc::FunctionInfo>>
+        functions(IO, Docs.Functions);
+
+    IO.mapOptional("Files", files->VectorMap);
+    IO.mapOptional("Namespaces", namespaces->VectorMap);
+    IO.mapOptional("Types", types->VectorMap);
+    IO.mapOptional("Functions", functions->VectorMap);
+  }
+};
+
+template <> struct MappingTraits<clang::doc::CommentInfo> {
+
+  static void mapping(IO &IO, clang::doc::CommentInfo &Info) {
+    MappingNormalization<NormalizedMap<std::string>, StringMap<std::string>>
+        keys(IO, Info.Attrs);
+
+    IO.mapOptional("Kind", Info.Kind);
+    if (!Info.Text.empty())
+      IO.mapOptional("Text", Info.Text);
+    if (!Info.Name.empty())
+      IO.mapOptional("Name", Info.Name);
+    if (!Info.Direction.empty())
+      IO.mapOptional("Direction", Info.Direction);
+    if (!Info.ParamName.empty())
+      IO.mapOptional("ParamName", Info.ParamName);
+    if (!Info.CloseName.empty())
+      IO.mapOptional("CloseName", Info.CloseName);
+    if (Info.SelfClosing)
+      IO.mapOptional("SelfClosing", Info.SelfClosing);
+    if (Info.Explicit)
+      IO.mapOptional("Explicit", Info.Explicit);
+    IO.mapOptional("Args", Info.Args);
+    IO.mapOptional("Attrs", keys->VectorMap);
+    IO.mapOptional("Position", Info.Position);
+    IO.mapOptional("Children", Info.Children);
+  }
+};
+
+} // end namespace yaml
+} // end namespace llvm
Index: tools/clang-doc/ClangDocReporter.h
===================================================================
--- /dev/null
+++ tools/clang-doc/ClangDocReporter.h
@@ -0,0 +1,171 @@
+//===-- Doc.cpp - 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_CLANG_DOC_REPORTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPORTER_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/Tooling.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/raw_ostream.h"
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace clang::comments;
+
+namespace clang {
+namespace doc {
+
+enum class OutFormat { YAML, LLVM };
+enum class DeclType { FILE, TYPE, NAMESPACE, FUNCTION };
+
+/// A representation of a parsed comment.
+struct CommentInfo {
+  std::string Kind;
+  std::string Text;
+  std::string Name;
+  std::string Direction;
+  std::string ParamName;
+  std::string CloseName;
+  bool SelfClosing = false;
+  bool Explicit = false;
+  llvm::StringMap<std::string> Attrs;
+  llvm::SmallVector<std::string, 8> Args;
+  llvm::SmallVector<int, 8> Position;
+  std::vector<CommentInfo> Children;
+};
+
+/// A link to a different Info, providing details to get the appropriate Info.
+struct Link {
+  DeclType InfoType;
+  std::string FullyQualifiedName;
+};
+
+/// A source file in the project.
+struct File {
+  std::string Filename;
+  CommentInfo Description;
+  llvm::SmallVector<CommentInfo, 8> UnattachedComments;
+};
+
+// Info for named types (parameters, members).
+struct NamedType {
+  Link Type;
+  std::string Name;
+  AccessSpecifier Access;
+};
+
+/// A base struct for Infos.
+struct Info {
+  std::string FullyQualifiedName;
+  std::string SimpleName;
+  Link Namespace;
+  std::vector<CommentInfo> Descriptions;
+  llvm::SmallVector<Link, 8> Locations;
+};
+
+// TODO: Expand to allow for documenting templating.
+// Info for types.
+struct TypeInfo : public Info {
+  Link DefinitionFile;
+  TagTypeKind TagType;
+  llvm::SmallVector<NamedType, 8> Members;
+  llvm::SmallVector<Link, 8> Parents;
+  llvm::SmallVector<Link, 8> VirtualParents;
+};
+
+// TODO: Expand to allow for documenting templating.
+// Info for functions.
+struct FunctionInfo : public Info {
+  Link DefinitionFile;
+  Link ReturnType;
+  Link Parent;
+  llvm::SmallVector<NamedType, 8> Params;
+  AccessSpecifier Access;
+};
+
+/// A struct encapsulating all documentation information for the project.
+struct Documentation {
+  llvm::StringMap<File> Files;
+  llvm::StringMap<Info> Namespaces;
+  llvm::StringMap<TypeInfo> Types;
+  llvm::StringMap<FunctionInfo> Functions;
+  // TODO: Add functionality to include separate markdown pages.
+};
+
+/// Struct with a key and a value to allow for serialization.
+template <typename T> struct Pair {
+  std::string Key;
+  T Value;
+};
+
+class ClangDocReporter : public ConstCommentVisitor<ClangDocReporter> {
+public:
+  ClangDocReporter() {}
+  ClangDocReporter(const std::vector<std::string> &Sources);
+
+  void addUnattachedComment(StringRef Filename, const CommentInfo &CI);
+  void addFile(StringRef Filename);
+
+  void createNamespaceInfo(const NamespaceDecl *D, const FullComment *C,
+                             StringRef Loc);
+  void createTypeInfo(const TagDecl *D, const FullComment *C, StringRef Loc,
+                        StringRef DefFile);
+  void createFunctionInfo(const FunctionDecl *D, const FullComment *C,
+                            StringRef Loc, StringRef DefFile,
+                            StringRef MangledName);
+
+  void parseFullComment(const FullComment *C, CommentInfo &CI);
+  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);
+
+  bool hasFile(StringRef Filename) const;
+  void serialize(clang::doc::OutFormat Format, llvm::raw_ostream &OS) const;
+
+private:
+  void addComment(Info &I, const FullComment *C);
+  void addLocation(Info &I, StringRef Location) const;
+
+  void parseComment(CommentInfo *CI, const comments::Comment *C);
+  void parseFields(TypeInfo &I, const RecordDecl *D) const;
+  void parseEnumerators(TypeInfo &I, const EnumDecl *D) const;
+  void parseBases(TypeInfo &I, const CXXRecordDecl *D) const;
+  void parseParameters(FunctionInfo &I, const FunctionDecl *D) const;
+
+  void serializeYAML(llvm::raw_ostream &OS) const;
+  void serializeLLVM(llvm::raw_ostream &OS) const;
+
+  const char *getCommandName(unsigned CommandID) const;
+  bool isWhitespaceOnly(StringRef S) const;
+  std::string getParentNamespace(const DeclContext *D) const;
+  Link makeLink(DeclType, StringRef Name) const;
+
+  CommentInfo *CurrentCI;
+  Documentation Docs;
+};
+
+} // namespace doc
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPORTER_H
Index: tools/clang-doc/ClangDocReporter.cpp
===================================================================
--- /dev/null
+++ tools/clang-doc/ClangDocReporter.cpp
@@ -0,0 +1,297 @@
+//===-- ClangDocReporter.cpp - ClangDoc Reporter ----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangDocReporter.h"
+#include "ClangDocYAML.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using clang::comments::FullComment;
+
+namespace clang {
+namespace doc {
+
+ClangDocReporter::ClangDocReporter(const std::vector<std::string> &Sources) {
+  for (const std::string &Path : Sources)
+    addFile(Path);
+}
+
+void ClangDocReporter::addUnattachedComment(StringRef Filename,
+                                            const CommentInfo &CI) {
+  Docs.Files[Filename].UnattachedComments.push_back(CI);
+}
+
+void ClangDocReporter::addFile(StringRef Filename) {
+  File F;
+  F.Filename = Filename;
+  Docs.Files.insert(std::make_pair(Filename, F));
+}
+
+void ClangDocReporter::createNamespaceInfo(const NamespaceDecl *D,
+                                             const FullComment *C,
+                                             StringRef Loc) {
+  std::string Name = D->getQualifiedNameAsString();
+  Info I;
+  llvm::StringMapIterator<Info> Pair = Docs.Namespaces.find(Name);
+  if (Pair != Docs.Namespaces.end())
+    I = Pair->second;
+  else {
+    I.FullyQualifiedName = Name;
+    I.SimpleName = D->getNameAsString();
+    I.Namespace = makeLink(DeclType::NAMESPACE, getParentNamespace(D));
+  }
+  addLocation(I, Loc);
+  addComment(I, C); 
+  Docs.Namespaces[Name] = I;
+}
+
+void ClangDocReporter::createTypeInfo(const TagDecl *D, const FullComment *C,
+                                        StringRef Loc, StringRef DefFile) {
+  std::string Name = D->getQualifiedNameAsString();
+  TypeInfo I;
+  llvm::StringMapIterator<TypeInfo> Pair = Docs.Types.find(Name);
+  if (Pair != Docs.Types.end())
+    I = Pair->second;
+  else {
+    I.FullyQualifiedName = Name;
+    I.SimpleName = D->getNameAsString();
+    I.TagType = D->getTagKind();
+    I.Namespace = makeLink(DeclType::NAMESPACE, getParentNamespace(D));
+    if (!DefFile.empty())
+      I.DefinitionFile = makeLink(DeclType::FILE, DefFile);
+  }
+  addLocation(I, Loc);
+  addComment(I, C);
+  if (const auto *E = dyn_cast<EnumDecl>(D))
+    parseEnumerators(I, E);
+  else if (const auto *R = dyn_cast<RecordDecl>(D)) {
+    parseFields(I, R);
+    if (const auto *CXXR = dyn_cast<CXXRecordDecl>(D))
+      parseBases(I, CXXR);
+  }
+  Docs.Types[Name] = I;
+}
+
+void ClangDocReporter::createFunctionInfo(const FunctionDecl *D,
+                                            const FullComment *C, StringRef Loc,
+                                            StringRef DefFile,
+                                            StringRef MangledName) {
+  FunctionInfo I;
+  llvm::StringMapIterator<FunctionInfo> Pair = Docs.Functions.find(MangledName);
+  if (Pair != Docs.Functions.end())
+    I = Pair->second;
+  else {
+    I.FullyQualifiedName = D->getQualifiedNameAsString();
+    I.SimpleName = D->getNameAsString();
+    I.DefinitionFile = makeLink(DeclType::FILE, DefFile);
+    I.ReturnType = makeLink(DeclType::TYPE, D->getReturnType().getAsString());
+    I.Namespace = makeLink(DeclType::NAMESPACE, getParentNamespace(D));
+    std::string Parent;
+    if (const auto *M = dyn_cast<CXXMethodDecl>(D)) {
+      Parent = M->getParent()->getQualifiedNameAsString();
+      I.Access = D->getAccess();
+    } else {
+      Parent = "";
+      I.Access = clang::AccessSpecifier::AS_none;
+    }
+    I.Parent = makeLink(DeclType::TYPE, Parent);
+    parseParameters(I, D);
+  }
+  addLocation(I, Loc);
+  addComment(I, C);
+  Docs.Functions[MangledName] = I;
+}
+
+void ClangDocReporter::parseFullComment(const FullComment *C, CommentInfo &CI) {
+  parseComment(&CI, C);
+}
+
+void ClangDocReporter::visitTextComment(const TextComment *C) {
+  if (!isWhitespaceOnly(C->getText()))
+    CurrentCI->Text = C->getText();
+}
+
+void ClangDocReporter::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 ClangDocReporter::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->Attrs.insert(std::make_pair(Attr.Name, Attr.Value));
+  }
+}
+
+void ClangDocReporter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
+  CurrentCI->Name = C->getTagName();
+  CurrentCI->SelfClosing = true;
+}
+
+void ClangDocReporter::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 ClangDocReporter::visitParamCommandComment(const ParamCommandComment *C) {
+  CurrentCI->Direction =
+      ParamCommandComment::getDirectionAsString(C->getDirection());
+  CurrentCI->Explicit = C->isDirectionExplicit();
+  if (C->hasParamName() && C->isParamIndexValid())
+    CurrentCI->ParamName = C->getParamNameAsWritten();
+}
+
+void ClangDocReporter::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(C->getIndex(i));
+  }
+}
+
+void ClangDocReporter::visitVerbatimBlockComment(
+    const VerbatimBlockComment *C) {
+  CurrentCI->Name = getCommandName(C->getCommandID());
+  CurrentCI->CloseName = C->getCloseName();
+}
+
+void ClangDocReporter::visitVerbatimBlockLineComment(
+    const VerbatimBlockLineComment *C) {
+  if (!isWhitespaceOnly(C->getText()))
+    CurrentCI->Text = C->getText();
+}
+
+void ClangDocReporter::visitVerbatimLineComment(const VerbatimLineComment *C) {
+  if (!isWhitespaceOnly(C->getText()))
+    CurrentCI->Text = C->getText();
+}
+
+bool ClangDocReporter::hasFile(StringRef Filename) const {
+  return Docs.Files.find(Filename) != Docs.Files.end();
+}
+
+void ClangDocReporter::serialize(OutFormat Format, raw_ostream &OS) const {
+  Format == clang::doc::OutFormat::LLVM ? serializeLLVM(OS) : serializeYAML(OS);
+}
+
+void ClangDocReporter::addComment(Info &Info, const FullComment *C) {
+  if (!C)
+    return;
+  CommentInfo CI;
+  parseFullComment(C, CI);
+  Info.Descriptions.push_back(CI);
+}
+
+void ClangDocReporter::addLocation(Info &I, StringRef Location) const {
+  I.Locations.push_back(makeLink(DeclType::FILE, Location));
+}
+
+void ClangDocReporter::parseComment(CommentInfo *CI,
+                                    const comments::Comment *C) {
+  CurrentCI = CI;
+  CI->Kind = C->getCommentKindName();
+  ConstCommentVisitor<ClangDocReporter>::visit(C);
+  for (comments::Comment *Child :
+       make_range(C->child_begin(), C->child_end())) {
+    CommentInfo ChildCI;
+    parseComment(&ChildCI, Child);
+    CI->Children.push_back(ChildCI);
+  }
+}
+
+void ClangDocReporter::parseFields(TypeInfo &I, const RecordDecl *D) const {
+  for (const FieldDecl *F : D->fields()) {
+    NamedType N;
+    N.Type = makeLink(DeclType::TYPE, 
+                      F->getTypeSourceInfo()->getType().getAsString());
+    N.Name = F->getQualifiedNameAsString();
+    if (const auto *M = dyn_cast<CXXMethodDecl>(D))
+      N.Access = M->getAccess();
+    else 
+      N.Access = clang::AccessSpecifier::AS_none;
+    I.Members.push_back(N);
+  }
+}
+
+void ClangDocReporter::parseEnumerators(TypeInfo &I, const EnumDecl *D) const {
+  for (const EnumConstantDecl *E : D->enumerators()) {
+    NamedType N;
+    N.Type = makeLink(DeclType::TYPE, E->getQualifiedNameAsString());
+    N.Access = clang::AccessSpecifier::AS_none;
+    I.Members.push_back(N);
+  }
+}
+
+void ClangDocReporter::parseParameters(FunctionInfo &I,
+                                       const FunctionDecl *D) const {
+  for (const ParmVarDecl *P : D->parameters()) {
+    NamedType Parm;
+    Parm.Type = makeLink(DeclType::TYPE, P->getOriginalType().getAsString());
+    Parm.Access = clang::AccessSpecifier::AS_none;
+    Parm.Name = P->getQualifiedNameAsString();
+    I.Params.push_back(Parm);
+  }
+}
+
+void ClangDocReporter::parseBases(TypeInfo &I, const CXXRecordDecl *D) const {
+  for (const CXXBaseSpecifier &B : D->bases()) {
+    std::string Parent = B.getType().getAsString();
+    B.isVirtual() ? I.VirtualParents.push_back(makeLink(DeclType::TYPE, Parent))
+                  : I.Parents.push_back(makeLink(DeclType::TYPE, Parent));
+  }
+  for (const CXXBaseSpecifier &B : D->vbases())
+    I.VirtualParents.push_back(makeLink(DeclType::TYPE, 
+                                        B.getType().getAsString()));
+}
+
+void ClangDocReporter::serializeYAML(raw_ostream &OS) const {
+  yaml::Output Output(OS);
+  Documentation NonConstValue = Docs;
+  Output << NonConstValue;
+}
+
+void ClangDocReporter::serializeLLVM(raw_ostream &OS) const {
+  // TODO: Implement.
+  OS << "Not yet implemented.\n";
+}
+
+const char *ClangDocReporter::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 ClangDocReporter::isWhitespaceOnly(StringRef S) const {
+  return S.find_first_not_of(" \t\n\v\f\r") == std::string::npos || S.empty();
+}
+
+std::string ClangDocReporter::getParentNamespace(const DeclContext *D) const {
+  if (const auto *N = dyn_cast<NamedDecl>(D->getParent()))
+    return N->getQualifiedNameAsString();
+  return "";
+}
+
+Link ClangDocReporter::makeLink(DeclType Type, StringRef Name) const {
+  Link L{Type, Name};
+  return L;
+}
+
+} // namespace doc
+} // namespace clang
Index: tools/clang-doc/ClangDoc.h
===================================================================
--- /dev/null
+++ tools/clang-doc/ClangDoc.h
@@ -0,0 +1,96 @@
+//===-- 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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
+
+#include "ClangDocReporter.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/Tooling.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace doc {
+
+// A Context which contains extra options which are used in ClangMoveTool.
+struct ClangDocContext {
+  // Which format in which to emit representation.
+  OutFormat EmitFormat;
+};
+
+class ClangDocVisitor : public RecursiveASTVisitor<ClangDocVisitor> {
+public:
+  explicit ClangDocVisitor(ASTContext *Ctx, ClangDocReporter &Reporter)
+      : Context(Ctx), Manager(Ctx->getSourceManager()),
+        MC(Ctx->createMangleContext()), Reporter(Reporter) {}
+
+  bool VisitTagDecl(const TagDecl *D);
+  bool VisitNamespaceDecl(const NamespaceDecl *D);
+  bool VisitFunctionDecl(const FunctionDecl *D);
+  void parseUnattachedComments();
+
+private:
+  bool isUnparsed(SourceLocation Loc) const;
+  std::string getLocation(const Decl *D) const;
+  comments::FullComment* getComment(const Decl *D);
+  StringRef mangleName(const FunctionDecl *D);
+
+  ASTContext *Context;
+  SourceManager &Manager;
+  MangleContext *MC;
+  ClangDocReporter &Reporter;
+};
+
+class ClangDocConsumer : public clang::ASTConsumer {
+public:
+  explicit ClangDocConsumer(ASTContext *Ctx, ClangDocReporter &Reporter)
+      : Visitor(Ctx, Reporter), Reporter(Reporter) {}
+
+  virtual void HandleTranslationUnit(clang::ASTContext &Context);
+
+private:
+  ClangDocVisitor Visitor;
+  ClangDocReporter &Reporter;
+};
+
+class ClangDocAction : public clang::ASTFrontendAction {
+public:
+  ClangDocAction(ClangDocReporter &Reporter) : Reporter(Reporter) {}
+
+  virtual std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(clang::CompilerInstance &C, llvm::StringRef InFile);
+
+private:
+  ClangDocReporter &Reporter;
+};
+
+class ClangDocActionFactory : public tooling::FrontendActionFactory {
+public:
+  ClangDocActionFactory(ClangDocContext &Ctx, ClangDocReporter &Reporter)
+      : Context(Ctx), Reporter(Reporter) {}
+
+  clang::FrontendAction *create() override {
+    return new ClangDocAction(Reporter);
+  }
+
+private:
+  ClangDocContext &Context;
+  ClangDocReporter &Reporter;
+};
+
+} // namespace doc
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
Index: tools/clang-doc/ClangDoc.cpp
===================================================================
--- /dev/null
+++ tools/clang-doc/ClangDoc.cpp
@@ -0,0 +1,113 @@
+//===-- 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/Comment.h"
+#include "clang/AST/Mangle.h"
+#include "clang/Frontend/CompilerInstance.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+bool ClangDocVisitor::VisitTagDecl(const TagDecl *D) {
+  if (!isUnparsed(D->getLocation()))
+    return true;
+
+  if (D->isThisDeclarationADefinition())
+    Reporter.createTypeInfo(D, getComment(D), getLocation(D), 
+                            D->getLocStart().printToString(Manager));
+  return true;
+}
+
+bool ClangDocVisitor::VisitNamespaceDecl(const NamespaceDecl *D) {
+  if (!isUnparsed(D->getLocation()))
+    return true;
+  Reporter.createNamespaceInfo(D, getComment(D), getLocation(D));
+  return true;
+}
+
+bool ClangDocVisitor::VisitFunctionDecl(const FunctionDecl *D) {
+  if (!isUnparsed(D->getLocation()))
+    return true;
+
+  std::string DefFile;
+  if (D->isDefined())
+    DefFile = getLocation(D->getDefinition());
+
+  Reporter.createFunctionInfo(D, getComment(D), getLocation(D), DefFile, 
+                                mangleName(D));
+  return true;
+}
+
+comments::FullComment *ClangDocVisitor::getComment(const Decl *D) {
+  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;
+}
+
+std::string ClangDocVisitor::getLocation(const Decl *D) const {
+  return D->getLocStart().printToString(Manager);
+}
+
+void ClangDocVisitor::parseUnattachedComments() {
+  for (RawComment *Comment : Context->getRawCommentList().getComments()) {
+    if (!isUnparsed(Comment->getLocStart()) || Comment->isAttached())
+      continue;
+    CommentInfo CI;
+    Reporter.parseFullComment(Comment->parse(*Context, nullptr, nullptr), CI);
+    Reporter.addUnattachedComment(Manager.getFilename(Comment->getLocStart()),
+                                  CI);
+  }
+}
+
+bool ClangDocVisitor::isUnparsed(SourceLocation Loc) const {
+  if (!Loc.isValid())
+    return false;
+  const std::string &Filename = Manager.getFilename(Loc);
+
+  if (!Reporter.hasFile(Filename))
+    return false;
+  if (Manager.isInSystemHeader(Loc) || Manager.isInExternCSystemHeader(Loc))
+    return false;
+  return true;
+}
+
+StringRef ClangDocVisitor::mangleName(const FunctionDecl *D) {
+  std::string S;
+  llvm::raw_string_ostream MangledName(S);
+  if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))
+    MC->mangleCXXCtor(Ctor, CXXCtorType::Ctor_Complete, MangledName);
+  else if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(D))
+    MC->mangleCXXDtor(Dtor, CXXDtorType::Dtor_Complete, MangledName);
+  else
+    MC->mangleName(D, MangledName);
+  return MangledName.str();
+}
+
+void ClangDocConsumer::HandleTranslationUnit(ASTContext &Context) {
+  Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+  Visitor.parseUnattachedComments();
+}
+
+std::unique_ptr<ASTConsumer>
+ClangDocAction::CreateASTConsumer(CompilerInstance &C, StringRef InFile) {
+  return make_unique<ClangDocConsumer>(&C.getASTContext(), Reporter);
+}
+
+} // namespace doc
+} // namespace clang
Index: tools/clang-doc/CMakeLists.txt
===================================================================
--- /dev/null
+++ tools/clang-doc/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(clangDoc
+  ClangDoc.cpp
+  ClangDocReporter.cpp
+
+  LINK_LIBS
+  clangAnalysis
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
Index: tools/CMakeLists.txt
===================================================================
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_clang_subdirectory(diagtool)
 add_clang_subdirectory(driver)
 add_clang_subdirectory(clang-diff)
+add_clang_subdirectory(clang-doc)
 add_clang_subdirectory(clang-format)
 add_clang_subdirectory(clang-format-vs)
 add_clang_subdirectory(clang-fuzzer)
Index: test/lit.cfg.py
===================================================================
--- test/lit.cfg.py
+++ test/lit.cfg.py
@@ -57,9 +57,9 @@
 tool_dirs = [config.clang_tools_dir, config.llvm_tools_dir]
 
 tools = [
-    'c-index-test', 'clang-check', 'clang-diff', 'clang-format', 'opt',
-    ToolSubst('%clang_func_map', command=FindTool(
-        'clang-func-mapping'), unresolved='ignore'),
+    'c-index-test', 'clang-check', 'clang-diff', 'clang-doc', 'clang-format', 
+    'opt', ToolSubst('%clang_func_map', command=FindTool(
+            'clang-func-mapping'), unresolved='ignore'),
 ]
 
 if config.clang_examples:
Index: test/Tooling/clang-doc-basic.cpp
===================================================================
--- /dev/null
+++ test/Tooling/clang-doc-basic.cpp
@@ -0,0 +1,102 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo '[{"directory":"%t","command":"clang++ -c %t/test.cpp","file":"%t/test.cpp"}]' | sed -e 's/\\/\//g' > %t/compile_commands.json
+// RUN: cp "%s" "%t/test.cpp"
+// RUN: clang-doc -p %t %t/test.cpp | FileCheck %s -DROOT=%t --implicit-check-not Skipping
+
+/// Test namespace
+namespace A {
+  
+/// A function
+void f(int x);
+
+} // end namespace A
+
+/// A C++ class
+class C {
+private:
+  int i;
+};
+
+// CHECK: Parsing codebase...
+// CHECK: Writing docs...
+// CHECK: ---
+// CHECK: Files:           
+// CHECK:   - Filename:        [[ROOT]]/test.cpp
+// CHECK:     Description:     
+// CHECK:       Kind:            ''
+// CHECK: Namespaces:      
+// CHECK:   - Qualified Name:  A
+// CHECK:     Name:            A
+// CHECK:     Namespace:       
+// CHECK:       Type:            Namespace
+// CHECK:       Name:            ''
+// CHECK:     Descriptions:    
+// CHECK:       - Kind:            FullComment
+// CHECK:         Children:        
+// CHECK:           - Kind:            ParagraphComment
+// CHECK:             Children:        
+// CHECK:               - Kind:            TextComment
+// CHECK:                 Text:            ' Test namespace'
+// CHECK:     Locations:       
+// CHECK:       - Type:            File
+// CHECK:         Name:            '[[ROOT]]/test.cpp:8:1'
+// CHECK: Types:           
+// CHECK:   - Qualified Name:  C
+// CHECK:     Name:            C
+// CHECK:     Namespace:       
+// CHECK:       Type:            Namespace
+// CHECK:       Name:            ''
+// CHECK:     Descriptions:    
+// CHECK:       - Kind:            FullComment
+// CHECK:         Children:        
+// CHECK:           - Kind:            ParagraphComment
+// CHECK:             Children:        
+// CHECK:               - Kind:            TextComment
+// CHECK:                 Text:            ' A C++ class'
+// CHECK:     TagType:         Class
+// CHECK:     Locations:       
+// CHECK:       - Type:            File
+// CHECK:         Name:            '[[ROOT]]/test.cpp:16:1'
+// CHECK:     DefinitionFile:  
+// CHECK:       Type:            File
+// CHECK:       Name:            '[[ROOT]]/test.cpp:16:1'
+// CHECK:     Members:         
+// CHECK:       - Type:            
+// CHECK:           Type:            Type
+// CHECK:           Name:            int
+// CHECK:         Name:            'C::i'
+// CHECK:         Access:          None
+// CHECK: Functions:       
+// CHECK:   - Qualified Name:  'A::f'
+// CHECK:     Name:            f
+// CHECK:     Namespace:       
+// CHECK:       Type:            Namespace
+// CHECK:       Name:            A
+// CHECK:     Descriptions:    
+// CHECK:       - Kind:            FullComment
+// CHECK:         Children:        
+// CHECK:           - Kind:            ParagraphComment
+// CHECK:             Children:        
+// CHECK:               - Kind:            TextComment
+// CHECK:                 Text:            ' A function'
+// CHECK:     Locations:       
+// CHECK:       - Type:            File
+// CHECK:         Name:            '[[ROOT]]/test.cpp:11:1'
+// CHECK:     DefinitionFile:  
+// CHECK:       Type:            File
+// CHECK:       Name:            ''
+// CHECK:     Params:          
+// CHECK:       - Type:            
+// CHECK:           Type:            Type
+// CHECK:           Name:            int
+// CHECK:         Name:            x
+// CHECK:         Access:          None
+// CHECK:     ReturnType:      
+// CHECK:       Type:            Type
+// CHECK:       Name:            void
+// CHECK:     Parent:          
+// CHECK:       Type:            Type
+// CHECK:       Name:            ''
+// CHECK:     Access:          None
+// CHECK: ...
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -54,6 +54,7 @@
   clang-rename
   clang-refactor
   clang-diff
+  clang-doc
   )
   
 if(CLANG_ENABLE_STATIC_ANALYZER)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to