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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits