juliehockett created this revision.
juliehockett added reviewers: klimek, jakehehrlich, sammccall, lebedev.ri.
juliehockett added a project: clang-tools-extra.
Herald added a subscriber: mgorny.
juliehockett added a dependency: D43341: [clang-doc] Implement reducer portion 
of the frontend framework.

Implmenting a YAML generator from the emitted bitcode summary of declarations. 
Emits one YAML file with all declaration information.

For a more detailed overview of the tool, see the design document on the 
mailing list: RFC: clang-doc proposal 
<http://lists.llvm.org/pipermail/cfe-dev/2017-December/056203.html>


https://reviews.llvm.org/D43667

Files:
  clang-doc/CMakeLists.txt
  clang-doc/generators/CMakeLists.txt
  clang-doc/generators/GeneratorBase.cpp
  clang-doc/generators/Generators.h
  clang-doc/generators/YAMLGenerator.cpp
  clang-doc/tool/CMakeLists.txt
  clang-doc/tool/ClangDocMain.cpp
  test/clang-doc/namespace-yaml.cpp
  test/clang-doc/record-yaml.cpp

Index: test/clang-doc/record-yaml.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/record-yaml.cpp
@@ -0,0 +1,119 @@
+// 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 -output=%t/docs
+// RUN: cat %t/docs/yaml/docs.yaml | FileCheck %s
+
+union A { int X; int Y; };
+
+enum B { X, Y };
+
+struct C { int i; };
+
+class D {};
+
+class E {
+public:
+  E() {}
+  ~E() {}
+
+protected:
+  void ProtectedMethod();
+};
+
+void E::ProtectedMethod() {}
+
+class F : virtual private D, public E {};
+
+// CHECK: ---
+// CHECK: Namespaces:      
+// CHECK: Records:         
+// CHECK:   - Name:            A
+// CHECK:     TagType:         Union
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     Members:         
+// CHECK:       - Type:            int
+// CHECK:         Name:            'A::X'
+// CHECK:         Access:          None
+// CHECK:         Description:     
+// CHECK:       - Type:            int
+// CHECK:         Name:            'A::Y'
+// CHECK:         Access:          None
+// CHECK:         Description:     
+// CHECK:     ParentUSRs:      
+// CHECK:     VirtualParentUSRs: 
+// CHECK:   - Name:            C
+// CHECK:     TagType:         Struct
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     Members:         
+// CHECK:       - Type:            int
+// CHECK:         Name:            'C::i'
+// CHECK:         Access:          None
+// CHECK:         Description:     
+// CHECK:     ParentUSRs:      
+// CHECK:     VirtualParentUSRs: 
+// CHECK:   - Name:            D
+// CHECK:     TagType:         Class
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     Members:         
+// CHECK:     ParentUSRs:      
+// CHECK:     VirtualParentUSRs: 
+// CHECK:   - Name:            E
+// CHECK:     TagType:         Class
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     Members:         
+// CHECK:     ParentUSRs:      
+// CHECK:     VirtualParentUSRs: 
+// CHECK:   - Name:            F
+// CHECK:     TagType:         Class
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     Members:         
+// CHECK:     ParentUSRs:      
+// CHECK:       - 'c:@S@E'
+// CHECK:     VirtualParentUSRs: 
+// CHECK:       - 'c:@S@D'
+// CHECK: Functions:       
+// CHECK:   - Name:            E
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     ParentUSR:       E
+// CHECK:     Params:          
+// CHECK:     ReturnType:      
+// CHECK:       Type:            void
+// CHECK:       Description:     
+// CHECK:     Access:          Public
+// CHECK:   - Name:            '~E'
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     ParentUSR:       '~E'
+// CHECK:     Params:          
+// CHECK:     ReturnType:      
+// CHECK:       Type:            void
+// CHECK:       Description:     
+// CHECK:     Access:          Public
+// CHECK:   - Name:            ProtectedMethod
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     ParentUSR:       ProtectedMethod
+// CHECK:     Params:          
+// CHECK:     ReturnType:      
+// CHECK:       Type:            void
+// CHECK:       Description:     
+// CHECK:     Access:          Public
+// CHECK: Enums:           
+// CHECK:   - Name:            B
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     Scoped:          false
+// CHECK:     Members:         
+// CHECK:       - Type:            X
+// CHECK:         Description:     
+// CHECK:       - Type:            Y
+// CHECK:         Description:     
+// CHECK: ...
Index: test/clang-doc/namespace-yaml.cpp
===================================================================
--- /dev/null
+++ test/clang-doc/namespace-yaml.cpp
@@ -0,0 +1,72 @@
+// 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 -output=%t/docs
+// RUN: cat %t/docs/yaml/docs.yaml | FileCheck %s
+
+namespace A {
+
+void f();
+void f() {};
+
+} // A
+
+namespace A {
+namespace B {
+
+enum E { X };
+
+E func(int i) { 
+  return X;
+}
+
+}
+}
+
+// CHECK: ---
+// CHECK: Namespaces:      
+// CHECK:   - Name:            A
+// CHECK:   - Name:            B
+// CHECK:     Namespace:       
+// CHECK:       - 'c:@N@A'
+// CHECK: Records:         
+// CHECK: Functions:       
+// CHECK:   - Name:            f
+// CHECK:     Namespace:       
+// CHECK:       - 'c:@N@A'
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     ParentUSR:       f
+// CHECK:     Params:          
+// CHECK:     ReturnType:      
+// CHECK:       Type:            void
+// CHECK:       Description:     
+// CHECK:     Access:          Public
+// CHECK:   - Name:            func
+// CHECK:     Namespace:       
+// CHECK:       - 'c:@N@A@N@B'
+// CHECK:       - 'c:@N@A'
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     ParentUSR:       func
+// CHECK:     Params:          
+// CHECK:       - Type:            int
+// CHECK:         Name:            i
+// CHECK:         Description:     
+// CHECK:     ReturnType:      
+// CHECK:       Type:            'enum A::B::E'
+// CHECK:       Description:     
+// CHECK:     Access:          Public
+// CHECK: Enums:           
+// CHECK:   - Name:            E
+// CHECK:     Namespace:       
+// CHECK:       - 'c:@N@A@N@B'
+// CHECK:       - 'c:@N@A'
+// CHECK:     IsDefinition:    true
+// CHECK:     Location:        
+// CHECK:     Scoped:          false
+// CHECK:     Members:         
+// CHECK:       - Type:            'A::B::X'
+// CHECK:         Description:     
+// CHECK: ...
Index: clang-doc/tool/ClangDocMain.cpp
===================================================================
--- clang-doc/tool/ClangDocMain.cpp
+++ clang-doc/tool/ClangDocMain.cpp
@@ -26,6 +26,7 @@
 #include "clang/Tooling/Execution.h"
 #include "clang/Tooling/StandaloneExecution.h"
 #include "clang/Tooling/Tooling.h"
+#include "generators/Generators.h"
 #include "llvm/ADT/APFloat.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
@@ -47,6 +48,10 @@
     "output", cl::desc("Directory for outputting generated files."),
     cl::init("docs"), cl::cat(ClangDocCategory));
 
+static cl::opt<std::string> Format(
+    "format", cl::desc("Format for outputted docs (Current options are yaml)."),
+    cl::init("yaml"), cl::cat(ClangDocCategory));
+
 static cl::opt<bool> DumpResult(
     "dump", cl::desc("Dump intermediate results to bitcode file."),
     cl::init(false), cl::cat(ClangDocCategory));
@@ -116,5 +121,23 @@
     OS.close();
   }
 
-  return 0;
+  errs() << "Generating docs...\n";
+  SmallString<128> DocsRootPath;
+  sys::path::native(OutDirectory, DocsRootPath);
+  sys::path::append(DocsRootPath, Format);
+  std::error_code RemoveStatus = sys::fs::remove_directories(DocsRootPath);
+  if (RemoveStatus != OK) {
+    errs() << "Unable to remove existing documentation directory for " << Format
+           << ".\n";
+    return 1;
+  }
+  std::error_code DirectoryStatus = sys::fs::create_directories(DocsRootPath);
+  if (DirectoryStatus != OK) {
+    errs() << "Unable to create documentation directories.\n";
+    return 1;
+  }
+  std::unique_ptr<doc::Generator> G =
+      doc::GeneratorFactory::create(Infos, DocsRootPath, Format);
+  if (!G) return 1;
+  return G->generate();
 }
Index: clang-doc/tool/CMakeLists.txt
===================================================================
--- clang-doc/tool/CMakeLists.txt
+++ clang-doc/tool/CMakeLists.txt
@@ -11,6 +11,7 @@
   clangBasic
   clangFrontend
   clangDoc
+  clangDocGenerators
   clangTooling
   clangToolingCore
   )
Index: clang-doc/generators/YAMLGenerator.cpp
===================================================================
--- /dev/null
+++ clang-doc/generators/YAMLGenerator.cpp
@@ -0,0 +1,199 @@
+//===--  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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_YAML_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_YAML_H
+
+#include "Generators.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang::doc;
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<CommentInfo>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<TypeInfo>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<FieldTypeInfo>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<MemberTypeInfo>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<NamespaceInfo>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<RecordInfo>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<EnumInfo>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<FunctionInfo>)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Location)
+
+namespace llvm {
+namespace yaml {
+
+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<Location> {
+  static void mapping(IO &IO, Location &Location) {
+    IO.mapRequired("LineNumber", Location.LineNumber);
+    IO.mapRequired("Filename", Location.Filename);
+  }
+};
+
+template <>
+struct MappingTraits<std::unique_ptr<TypeInfo>> {
+  static void mapping(IO &IO, std::unique_ptr<TypeInfo> &T) {
+    IO.mapRequired("Type", T->TypeUSR);
+    IO.mapRequired("Description", T->Description);
+  }
+};
+
+template <>
+struct MappingTraits<std::unique_ptr<FieldTypeInfo>> {
+  static void mapping(IO &IO, std::unique_ptr<FieldTypeInfo> &T) {
+    IO.mapRequired("Type", T->TypeUSR);
+    IO.mapRequired("Name", T->Name);
+    IO.mapRequired("Description", T->Description);
+  }
+};
+
+template <>
+struct MappingTraits<std::unique_ptr<MemberTypeInfo>> {
+  static void mapping(IO &IO, std::unique_ptr<MemberTypeInfo> &T) {
+    IO.mapRequired("Type", T->TypeUSR);
+    IO.mapRequired("Name", T->Name);
+    IO.mapRequired("Access", T->Access);
+    IO.mapRequired("Description", T->Description);
+  }
+};
+
+template <>
+struct MappingTraits<std::unique_ptr<NamespaceInfo>> {
+  static void mapping(IO &IO, std::unique_ptr<NamespaceInfo> &N) {
+    IO.mapRequired("Name", N->Name);
+    if (!N->Namespace.empty()) IO.mapRequired("Namespace", N->Namespace);
+    if (!N->Description.empty()) IO.mapRequired("Description", N->Description);
+  }
+};
+
+template <>
+struct MappingTraits<std::unique_ptr<RecordInfo>> {
+  static void mapping(IO &IO, std::unique_ptr<RecordInfo> &R) {
+    IO.mapRequired("Name", R->Name);
+    if (!R->Namespace.empty()) IO.mapRequired("Namespace", R->Namespace);
+    if (!R->Description.empty()) IO.mapRequired("Description", R->Description);
+    IO.mapRequired("TagType", R->TagType);
+    IO.mapRequired("IsDefinition", R->IsDefinition);
+    IO.mapRequired("DefinitionLocation", R->DefLoc);
+    IO.mapRequired("Location", R->Loc);
+    IO.mapRequired("Members", R->Members);
+    IO.mapRequired("ParentUSRs", R->ParentUSRs);
+    IO.mapRequired("VirtualParentUSRs", R->VirtualParentUSRs);
+  }
+};
+
+template <>
+struct MappingTraits<std::unique_ptr<EnumInfo>> {
+  static void mapping(IO &IO, std::unique_ptr<EnumInfo> &E) {
+    IO.mapRequired("Name", E->Name);
+    if (!E->Namespace.empty()) IO.mapRequired("Namespace", E->Namespace);
+    if (!E->Description.empty()) IO.mapRequired("Description", E->Description);
+    IO.mapRequired("IsDefinition", E->IsDefinition);
+    IO.mapRequired("DefinitionLocation", E->DefLoc);
+    IO.mapRequired("Location", E->Loc);
+    IO.mapRequired("Scoped", E->Scoped);
+    IO.mapRequired("Members", E->Members);
+  }
+};
+
+template <>
+struct MappingTraits<std::unique_ptr<FunctionInfo>> {
+  static void mapping(IO &IO, std::unique_ptr<FunctionInfo> &F) {
+    IO.mapRequired("Name", F->Name);
+    if (!F->Namespace.empty()) IO.mapRequired("Namespace", F->Namespace);
+    if (!F->Description.empty()) IO.mapRequired("Description", F->Description);
+    IO.mapRequired("IsDefinition", F->IsDefinition);
+    IO.mapRequired("DefinitionLocation", F->DefLoc);
+    IO.mapRequired("Location", F->Loc);
+    IO.mapRequired("ParentUSR", F->Name);
+    IO.mapRequired("Params", F->Params);
+    IO.mapRequired("ReturnType", F->ReturnType);
+    IO.mapRequired("Access", F->Access);
+  }
+};
+
+template <>
+struct MappingTraits<std::unique_ptr<CommentInfo>> {
+  static void mapping(IO &IO, std::unique_ptr<CommentInfo> &C) {
+    IO.mapRequired("Kind", C->Kind);
+    if (!C->Text.empty()) IO.mapRequired("Text", C->Text);
+    if (!C->Name.empty()) IO.mapRequired("Name", C->Name);
+    if (!C->Direction.empty()) IO.mapRequired("Direction", C->Direction);
+    if (!C->ParamName.empty()) IO.mapRequired("ParamName", C->ParamName);
+    if (!C->CloseName.empty()) IO.mapRequired("CloseName", C->CloseName);
+    if (C->SelfClosing) IO.mapRequired("SelfClosing", C->SelfClosing);
+    if (C->Explicit) IO.mapRequired("Explicit", C->Explicit);
+    if (!C->Args.empty()) IO.mapRequired("Args", C->Args);
+    if (!C->AttrValues.empty()) IO.mapRequired("AttrKeys", C->AttrKeys);
+    if (!C->AttrValues.empty()) IO.mapRequired("AttrValues", C->AttrValues);
+    if (!C->Position.empty()) IO.mapRequired("Position", C->Position);
+    if (!C->Children.empty()) IO.mapRequired("Children", C->Children);
+  }
+};
+
+template <>
+struct MappingTraits<InfoSet> {
+  static void mapping(IO &IO, InfoSet &IS) {
+    IO.mapRequired("Namespaces", IS.getNamespaceInfos());
+    IO.mapRequired("Records", IS.getRecordInfos());
+    IO.mapRequired("Functions", IS.getFunctionInfos());
+    IO.mapRequired("Enums", IS.getEnumInfos());
+  }
+};
+
+}  // end namespace yaml
+}  // end namespace llvm
+
+namespace clang {
+namespace doc {
+
+int YAMLGenerator::generate() {
+  SmallString<128> Path;
+  sys::path::native(Root, Path);
+  sys::path::append(Path, "docs.yaml");
+  std::error_code OutErrorInfo;
+  raw_fd_ostream OS(Path, OutErrorInfo, sys::fs::F_None);
+  if (OutErrorInfo != OK) {
+    errs() << "Error opening yaml file.\n";
+    return 1;
+  }
+
+  yaml::Output Output(OS);
+  Output << *IS;
+  OS.close();
+  return 0;
+}
+
+}  // namespace doc
+}  // namespace clang
+
+#endif  // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_YAML_H
Index: clang-doc/generators/Generators.h
===================================================================
--- /dev/null
+++ clang-doc/generators/Generators.h
@@ -0,0 +1,68 @@
+//===-- Generators.h - ClangDoc Generator ----------------------*- 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_GENERATOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H
+
+#include <string>
+#include "../Representation.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+class Generator {
+ public:
+  Generator(std::unique_ptr<InfoSet> &IS, StringRef Root, StringRef Format)
+      : IS(IS), Root(Root), Format(Format){};
+  virtual ~Generator(){};
+
+  virtual int generate() = 0;
+
+ protected:
+  virtual bool buildDirectory(StringRef Dir);
+  virtual bool removeExistingDirectory(StringRef Dir);
+
+  std::unique_ptr<InfoSet> &IS;
+  std::string Root;
+  std::string Format;
+  std::error_code OK;
+};
+
+class YAMLGenerator : public Generator {
+ public:
+  YAMLGenerator(std::unique_ptr<InfoSet> &IS, StringRef Root, StringRef Format)
+      : Generator(IS, Root, Format){};
+  virtual ~YAMLGenerator(){};
+
+  int generate() override;
+};
+
+class GeneratorFactory {
+ public:
+  static std::unique_ptr<Generator> create(std::unique_ptr<InfoSet> &IS,
+                                           StringRef Root, StringRef Format) {
+    if (Format == "yaml")
+      return llvm::make_unique<YAMLGenerator>(IS, Root, Format);
+
+    errs() << "Unsupported documentation format.\n";
+    return nullptr;
+  }
+};
+
+}  // namespace doc
+}  // namespace clang
+
+#endif  // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H
Index: clang-doc/generators/GeneratorBase.cpp
===================================================================
--- /dev/null
+++ clang-doc/generators/GeneratorBase.cpp
@@ -0,0 +1,41 @@
+//===-- GeneratorBase.cpp - ClangDoc Generator ------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../Representation.h"
+#include "Generators.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+bool Generator::buildDirectory(StringRef Dir) {
+  std::error_code DirectoryStatus = sys::fs::create_directories(Dir);
+  if (DirectoryStatus != OK) {
+    errs() << "Unable to create documentation directories.\n";
+    return false;
+  }
+  return true;
+}
+
+bool Generator::removeExistingDirectory(StringRef Dir) {
+  std::error_code DirectoryStatus = sys::fs::remove_directories(Dir);
+  if (DirectoryStatus != OK) {
+    errs() << "Unable to remove existing documentation directories.\n";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace doc
+}  // namespace clang
Index: clang-doc/generators/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-doc/generators/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangDocGenerators
+  GeneratorBase.cpp
+  YAMLGenerator.cpp
+
+  LINK_LIBS
+  clangDoc
+  )
Index: clang-doc/CMakeLists.txt
===================================================================
--- clang-doc/CMakeLists.txt
+++ clang-doc/CMakeLists.txt
@@ -22,3 +22,4 @@
   )
 
 add_subdirectory(tool)
+add_subdirectory(generators)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to