Implement a (simple) Markdown generator
authorJulie Hockett <juliehockett@google.com>
Thu, 16 Aug 2018 21:54:34 +0000 (21:54 +0000)
committerJulie Hockett <juliehockett@google.com>
Thu, 16 Aug 2018 21:54:34 +0000 (21:54 +0000)
Implementing a simple Markdown generator from the emitted bitcode
summary of declarations. Very primitive at this point, but will be
expanded. Currently emits an .md file for each class and namespace,
listing its contents.

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

Differential Revision: https://reviews.llvm.org/D43424

llvm-svn: 339948

13 files changed:
clang-tools-extra/clang-doc/CMakeLists.txt
clang-tools-extra/clang-doc/Generators.cpp
clang-tools-extra/clang-doc/Generators.h
clang-tools-extra/clang-doc/MDGenerator.cpp [new file with mode: 0644]
clang-tools-extra/clang-doc/Representation.h
clang-tools-extra/clang-doc/YAMLGenerator.cpp
clang-tools-extra/clang-doc/gen_tests.py
clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
clang-tools-extra/test/clang-doc/md-comment.cpp [new file with mode: 0644]
clang-tools-extra/test/clang-doc/md-linkage.cpp [new file with mode: 0644]
clang-tools-extra/test/clang-doc/md-module.cpp [new file with mode: 0644]
clang-tools-extra/test/clang-doc/md-namespace.cpp [new file with mode: 0644]
clang-tools-extra/test/clang-doc/md-record.cpp [new file with mode: 0644]

index eaebf61..1d70bb0 100644 (file)
@@ -10,6 +10,7 @@ add_clang_library(clangDoc
   ClangDoc.cpp
   Generators.cpp
   Mapper.cpp
+  MDGenerator.cpp
   Representation.cpp
   Serialize.cpp
   YAMLGenerator.cpp
index fe01d61..5a0d0c5 100644 (file)
@@ -29,8 +29,11 @@ findGeneratorByName(llvm::StringRef Format) {
 // This anchor is used to force the linker to link in the generated object file
 // and thus register the generators.
 extern volatile int YAMLGeneratorAnchorSource;
+extern volatile int MDGeneratorAnchorSource;
 static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest =
     YAMLGeneratorAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED MDGeneratorAnchorDest =
+    MDGeneratorAnchorSource;
 
 } // namespace doc
 } // namespace clang
index 9106d2c..90a81e8 100644 (file)
@@ -27,7 +27,7 @@ public:
   virtual ~Generator() = default;
 
   // Write out the decl info in the specified format.
-  virtual bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0;
+  virtual llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0;
 };
 
 typedef llvm::Registry<Generator> GeneratorRegistry;
diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
new file mode 100644 (file)
index 0000000..f989f3f
--- /dev/null
@@ -0,0 +1,314 @@
+//===-- MDGenerator.cpp - Markdown Generator --------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Generators.h"
+#include "Representation.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include <string>
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+// Enum conversion
+
+std::string getAccess(AccessSpecifier AS) {
+  switch (AS) {
+  case AccessSpecifier::AS_public:
+    return "public";
+  case AccessSpecifier::AS_protected:
+    return "protected";
+  case AccessSpecifier::AS_private:
+    return "private";
+  case AccessSpecifier::AS_none:
+    return {};
+  }
+}
+
+std::string getTagType(TagTypeKind AS) {
+  switch (AS) {
+  case TagTypeKind::TTK_Class:
+    return "class";
+  case TagTypeKind::TTK_Union:
+    return "union";
+  case TagTypeKind::TTK_Interface:
+    return "interface";
+  case TagTypeKind::TTK_Struct:
+    return "struct";
+  case TagTypeKind::TTK_Enum:
+    return "enum";
+  }
+}
+
+// Markdown generation
+
+std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; }
+
+std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; }
+
+std::string genLink(const Twine &Text, const Twine &Link) {
+  return "[" + Text.str() + "](" + Link.str() + ")";
+}
+
+std::string genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
+  std::string Buffer;
+  llvm::raw_string_ostream Stream(Buffer);
+  bool First = true;
+  for (const auto &R : Refs) {
+    if (!First)
+      Stream << ", ";
+    Stream << R.Name;
+    First = false;
+  }
+  return Stream.str();
+}
+
+void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n"; }
+
+void writeNewLine(raw_ostream &OS) { OS << "\n"; }
+
+void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) {
+  OS << std::string(Num, '#') + " " + Text << "\n";
+}
+
+void writeFileDefinition(const Location &L, raw_ostream &OS) {
+  OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " +
+                  L.Filename)
+     << "\n";
+}
+
+void writeDescription(const CommentInfo &I, raw_ostream &OS) {
+  if (I.Kind == "FullComment") {
+    for (const auto &Child : I.Children)
+      writeDescription(*Child, OS);
+  } else if (I.Kind == "ParagraphComment") {
+    for (const auto &Child : I.Children)
+      writeDescription(*Child, OS);
+    writeNewLine(OS);
+  } else if (I.Kind == "BlockCommandComment") {
+    OS << genEmphasis(I.Name);
+    for (const auto &Child : I.Children)
+      writeDescription(*Child, OS);
+  } else if (I.Kind == "InlineCommandComment") {
+    OS << genEmphasis(I.Name) << " " << I.Text;
+  } else if (I.Kind == "ParamCommandComment") {
+    std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
+    OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n";
+  } else if (I.Kind == "TParamCommandComment") {
+    std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
+    OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n";
+  } else if (I.Kind == "VerbatimBlockComment") {
+    for (const auto &Child : I.Children)
+      writeDescription(*Child, OS);
+  } else if (I.Kind == "VerbatimBlockLineComment") {
+    OS << I.Text;
+    writeNewLine(OS);
+  } else if (I.Kind == "VerbatimLineComment") {
+    OS << I.Text;
+    writeNewLine(OS);
+  } else if (I.Kind == "HTMLStartTagComment") {
+    if (I.AttrKeys.size() != I.AttrValues.size())
+      return;
+    std::string Buffer;
+    llvm::raw_string_ostream Attrs(Buffer);
+    for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx)
+      Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\"";
+
+    std::string CloseTag = I.SelfClosing ? "/>" : ">";
+    writeLine("<" + I.Name + Attrs.str() + CloseTag, OS);
+  } else if (I.Kind == "HTMLEndTagComment") {
+    writeLine("</" + I.Name + ">", OS);
+  } else if (I.Kind == "TextComment") {
+    OS << I.Text;
+  } else {
+    OS << "Unknown comment kind: " << I.Kind << ".\n";
+  }
+}
+
+void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) {
+  if (I.Scoped)
+    writeLine("| enum class " + I.Name + " |", OS);
+  else
+    writeLine("| enum " + I.Name + " |", OS);
+  writeLine("--", OS);
+
+  std::string Buffer;
+  llvm::raw_string_ostream Members(Buffer);
+  if (!I.Members.empty())
+    for (const auto &N : I.Members)
+      Members << "| " << N << " |\n";
+  writeLine(Members.str(), OS);
+  if (I.DefLoc)
+    writeFileDefinition(I.DefLoc.getValue(), OS);
+
+  for (const auto &C : I.Description)
+    writeDescription(C, OS);
+}
+
+void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) {
+  std::string Buffer;
+  llvm::raw_string_ostream Stream(Buffer);
+  bool First = true;
+  for (const auto &N : I.Params) {
+    if (!First)
+      Stream << ", ";
+    Stream << N.Type.Name + " " + N.Name;
+    First = false;
+  }
+  Twine Signature =
+      I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")";
+  std::string Access = getAccess(I.Access);
+  if (Access != "")
+    writeHeader(genItalic(Access) + " " + Signature, 3, OS);
+  else
+    writeHeader(Signature, 3, OS);
+  if (I.DefLoc)
+    writeFileDefinition(I.DefLoc.getValue(), OS);
+
+  for (const auto &C : I.Description)
+    writeDescription(C, OS);
+}
+
+void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) {
+  if (I.Name == "")
+    writeHeader("Global Namespace", 1, OS);
+  else
+    writeHeader("namespace " + I.Name, 1, OS);
+  writeNewLine(OS);
+
+  if (!I.Description.empty()) {
+    for (const auto &C : I.Description)
+      writeDescription(C, OS);
+    writeNewLine(OS);
+  }
+
+  if (!I.ChildNamespaces.empty()) {
+    writeHeader("Namespaces", 2, OS);
+    for (const auto &R : I.ChildNamespaces)
+      writeLine(R.Name, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildRecords.empty()) {
+    writeHeader("Records", 2, OS);
+    for (const auto &R : I.ChildRecords)
+      writeLine(R.Name, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildFunctions.empty()) {
+    writeHeader("Functions", 2, OS);
+    for (const auto &F : I.ChildFunctions)
+      genMarkdown(F, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildEnums.empty()) {
+    writeHeader("Enums", 2, OS);
+    for (const auto &E : I.ChildEnums)
+      genMarkdown(E, OS);
+    writeNewLine(OS);
+  }
+}
+
+void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) {
+  writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS);
+  if (I.DefLoc)
+    writeFileDefinition(I.DefLoc.getValue(), OS);
+
+  if (!I.Description.empty()) {
+    for (const auto &C : I.Description)
+      writeDescription(C, OS);
+    writeNewLine(OS);
+  }
+
+  std::string Parents = genReferenceList(I.Parents);
+  std::string VParents = genReferenceList(I.VirtualParents);
+  if (!Parents.empty() || !VParents.empty()) {
+    if (Parents.empty())
+      writeLine("Inherits from " + VParents, OS);
+    else if (VParents.empty())
+      writeLine("Inherits from " + Parents, OS);
+    else
+      writeLine("Inherits from " + Parents + ", " + VParents, OS);
+    writeNewLine(OS);
+  }
+
+  if (!I.Members.empty()) {
+    writeHeader("Members", 2, OS);
+    for (const auto Member : I.Members) {
+      std::string Access = getAccess(Member.Access);
+      if (Access != "")
+        writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS);
+      else
+        writeLine(Member.Type.Name + " " + Member.Name, OS);
+    }
+    writeNewLine(OS);
+  }
+
+  if (!I.ChildRecords.empty()) {
+    writeHeader("Records", 2, OS);
+    for (const auto &R : I.ChildRecords)
+      writeLine(R.Name, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildFunctions.empty()) {
+    writeHeader("Functions", 2, OS);
+    for (const auto &F : I.ChildFunctions)
+      genMarkdown(F, OS);
+    writeNewLine(OS);
+  }
+  if (!I.ChildEnums.empty()) {
+    writeHeader("Enums", 2, OS);
+    for (const auto &E : I.ChildEnums)
+      genMarkdown(E, OS);
+    writeNewLine(OS);
+  }
+}
+
+/// Generator for Markdown documentation.
+class MDGenerator : public Generator {
+public:
+  static const char *Format;
+
+  llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override;
+};
+
+const char *MDGenerator::Format = "md";
+
+llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
+  switch (I->IT) {
+  case InfoType::IT_namespace:
+    genMarkdown(*static_cast<clang::doc::NamespaceInfo *>(I), OS);
+    break;
+  case InfoType::IT_record:
+    genMarkdown(*static_cast<clang::doc::RecordInfo *>(I), OS);
+    break;
+  case InfoType::IT_enum:
+    genMarkdown(*static_cast<clang::doc::EnumInfo *>(I), OS);
+    break;
+  case InfoType::IT_function:
+    genMarkdown(*static_cast<clang::doc::FunctionInfo *>(I), OS);
+    break;
+  case InfoType::IT_default:
+    return llvm::make_error<llvm::StringError>("Unexpected info type.\n",
+                                               llvm::inconvertibleErrorCode());
+  }
+  return llvm::Error::success();
+}
+
+static GeneratorRegistry::Add<MDGenerator> MD(MDGenerator::Format,
+                                              "Generator for MD output.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the generator.
+volatile int MDGeneratorAnchorSource = 0;
+
+} // namespace doc
+} // namespace clang
index 9e88bd9..57f5101 100644 (file)
@@ -48,13 +48,14 @@ struct CommentInfo {
   CommentInfo(CommentInfo &Other) = delete;
   CommentInfo(CommentInfo &&Other) = default;
 
-  SmallString<16> Kind; // Kind of comment (TextComment, InlineCommandComment,
-                        // HTMLStartTagComment, HTMLEndTagComment,
-                        // BlockCommandComment, ParamCommandComment,
-                        // TParamCommandComment, VerbatimBlockComment,
-                        // VerbatimBlockLineComment, VerbatimLineComment).
-  SmallString<64> Text; // Text of the comment.
-  SmallString<16> Name; // Name of the comment (for Verbatim and HTML).
+  SmallString<16>
+      Kind; // Kind of comment (FullComment, ParagraphComment, TextComment,
+            // InlineCommandComment, HTMLStartTagComment, HTMLEndTagComment,
+            // BlockCommandComment, ParamCommandComment,
+            // TParamCommandComment, VerbatimBlockComment,
+            // VerbatimBlockLineComment, VerbatimLineComment).
+  SmallString<64> Text;      // Text of the comment.
+  SmallString<16> Name;      // Name of the comment (for Verbatim and HTML).
   SmallString<8> Direction;  // Parameter direction (for (T)ParamCommand).
   SmallString<16> ParamName; // Parameter name (for (T)ParamCommand).
   SmallString<16> CloseName; // Closing tag name (for VerbatimBlock).
index 58c1e1f..e093901 100644 (file)
@@ -242,12 +242,12 @@ class YAMLGenerator : public Generator {
 public:
   static const char *Format;
 
-  bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) override;
+  llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override;
 };
 
 const char *YAMLGenerator::Format = "yaml";
 
-bool YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
+llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
   llvm::yaml::Output InfoYAML(OS);
   switch (I->IT) {
   case InfoType::IT_namespace:
@@ -263,10 +263,10 @@ bool YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
     InfoYAML << *static_cast<clang::doc::FunctionInfo *>(I);
     break;
   case InfoType::IT_default:
-    llvm::errs() << "Unexpected info type in index.\n";
-    return true;
+    return llvm::make_error<llvm::StringError>("Unexpected info type.\n",
+                                               llvm::inconvertibleErrorCode());
   }
-  return false;
+  return llvm::Error::success();
 }
 
 static GeneratorRegistry::Add<YAMLGenerator> YAML(YAMLGenerator::Format,
index ccdb069..9259684 100644 (file)
@@ -18,16 +18,19 @@ accuracy before using.
 
 To generate all current tests:
 - Generate mapper tests:
-    python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper
+    python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper -use-check-next
 
 - Generate reducer tests:
-    python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc
+    python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc -use-check-next
 
 - Generate yaml tests:
-    python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml
+    python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml -use-check-next
 
 - Generate public decl tests:
-    python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public
+    python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public -use-check-next
+
+- Generate Markdown tests:
+    python gen_tests.py -flag='--format=md' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix md
 
 This script was written on/for Linux, and has not been tested on any other
 platform and so it may not work.
@@ -95,7 +98,8 @@ def get_test_case_code(test_case_path, flags):
     return code
 
 
-def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer):
+def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer,
+                check_next=True):
     output = ''
     run_cmd = ''
     if '--dump-mapper' in flags or '--dump-intermediate' in flags:
@@ -119,8 +123,14 @@ def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer):
     output = re.sub(YAML_USR_REGEX, YAML_USR, output)
     output = re.sub(BITCODE_USR_REGEX, BITCODE_USR, output)
     output = CHECK.format(checkname) + output.rstrip()
-    output = run_cmd + output.replace('\n',
-                                      '\n' + CHECK_NEXT.format(checkname))
+    
+    if check_next:
+      check_comment = CHECK_NEXT.format(checkname)
+    else:
+      check_comment = CHECK.format(checkname)
+    
+    output = output.replace('\n', '\n' + check_comment)
+    output = run_cmd + output.replace('%s\n' % check_comment, "")
 
     return output + '\n'
 
@@ -151,6 +161,12 @@ def main():
         metavar="PATH",
         default='llvm-bcanalyzer',
         help='path to llvm-bcanalyzer binary')
+    parser.add_argument(
+        '-use-check-next',
+        dest='check_next',
+        default=False,
+        action='store_true',
+        help='Whether or not to use CHECK-NEXT in the resulting tests.')
     args = parser.parse_args()
 
     flags = ' '.join(args.flags)
@@ -188,7 +204,8 @@ def main():
                 if len(usr) < 2:
                     continue
                 all_output += get_output(root, out_file, out_dir, args.flags,
-                                         num_outputs, args.bcanalyzer)
+                                         num_outputs, args.bcanalyzer, 
+                                         args.check_next)
                 num_outputs += 1
 
         # Add test case code to test
index 6e4a92d..6b50f6c 100644 (file)
@@ -34,6 +34,7 @@
 #include "clang/Tooling/StandaloneExecution.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/APFloat.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
@@ -69,13 +70,18 @@ static llvm::cl::opt<bool>
                llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
 
 enum OutputFormatTy {
+  md,
   yaml,
 };
 
-static llvm::cl::opt<OutputFormatTy> FormatEnum(
-    "format", llvm::cl::desc("Format for outputted docs."),
-    llvm::cl::values(clEnumVal(yaml, "Documentation in YAML format.")),
-    llvm::cl::init(yaml), llvm::cl::cat(ClangDocCategory));
+static llvm::cl::opt<OutputFormatTy>
+    FormatEnum("format", llvm::cl::desc("Format for outputted docs."),
+               llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml",
+                                           "Documentation in YAML format."),
+                                clEnumValN(OutputFormatTy::md, "md",
+                                           "Documentation in MD format.")),
+               llvm::cl::init(OutputFormatTy::yaml),
+               llvm::cl::cat(ClangDocCategory));
 
 static llvm::cl::opt<bool> DoxygenOnly(
     "doxygen",
@@ -155,10 +161,12 @@ getInfoOutputFile(StringRef Root,
   return Path;
 }
 
-std::string getFormatString(OutputFormatTy Ty) {
-  switch (Ty) {
-  case yaml:
+std::string getFormatString() {
+  switch (FormatEnum) {
+  case OutputFormatTy::yaml:
     return "yaml";
+  case OutputFormatTy::md:
+    return "md";
   }
   llvm_unreachable("Unknown OutputFormatTy");
 }
@@ -191,14 +199,6 @@ int main(int argc, const char **argv) {
   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
   std::error_code OK;
 
-  // Fail early if an invalid format was provided.
-  std::string Format = getFormatString(FormatEnum);
-  auto G = doc::findGeneratorByName(Format);
-  if (!G) {
-    llvm::errs() << toString(G.takeError()) << "\n";
-    return 1;
-  }
-
   auto Exec = clang::tooling::createExecutorFromCommandLineArgs(
       argc, argv, ClangDocCategory);
 
@@ -207,6 +207,15 @@ int main(int argc, const char **argv) {
     return 1;
   }
 
+  // Fail early if an invalid format was provided.
+  std::string Format = getFormatString();
+  llvm::outs() << "Emiting docs in " << Format << " format.\n";
+  auto G = doc::findGeneratorByName(Format);
+  if (!G) {
+    llvm::errs() << toString(G.takeError()) << "\n";
+    return 1;
+  }
+
   ArgumentsAdjuster ArgAdjuster;
   if (!DoxygenOnly)
     ArgAdjuster = combineAdjusters(
@@ -277,8 +286,8 @@ int main(int argc, const char **argv) {
       continue;
     }
 
-    if (G->get()->generateDocForInfo(I, InfoOS))
-      llvm::errs() << "Unable to generate docs for info.\n";
+    if (auto Err = G->get()->generateDocForInfo(I, InfoOS))
+      llvm::errs() << toString(std::move(Err)) << "\n";
   }
 
   return 0;
diff --git a/clang-tools-extra/test/clang-doc/md-comment.cpp b/clang-tools-extra/test/clang-doc/md-comment.cpp
new file mode 100644 (file)
index 0000000..ae14eaa
--- /dev/null
@@ -0,0 +1,48 @@
+// THIS IS A GENERATED TEST. DO NOT EDIT.
+// To regenerate, see clang-doc/gen_test.py docstring.
+//
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+
+/// \brief Brief description.
+///
+/// Extended description that
+/// continues onto the next line.
+/// 
+/// <ul class="test">
+///   <li> Testing.
+/// </ul>
+///
+/// \verbatim
+/// The description continues.
+/// \endverbatim
+/// --
+/// \param [out] I is a parameter.
+/// \param J is a parameter.
+/// \return void
+void F(int I, int J);
+
+/// Bonus comment on definition
+void F(int I, int J) {}
+
+// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs
+
+
+// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0
+// CHECK-0: # Global Namespace
+// CHECK-0: ## Functions
+// CHECK-0: ### void F(int I, int J)
+// CHECK-0: *Defined at line 28 of test*
+// CHECK-0: **brief** Brief description.
+// CHECK-0:  Extended description that continues onto the next line.
+// CHECK-0: <ul "class=test">
+// CHECK-0: <li>
+// CHECK-0:  Testing.</ul>
+// CHECK-0:  The description continues.
+// CHECK-0:  --
+// CHECK-0: **I** [out]
+// CHECK-0: **J**
+// CHECK-0: **return** void
+// CHECK-0:  Bonus comment on definition
diff --git a/clang-tools-extra/test/clang-doc/md-linkage.cpp b/clang-tools-extra/test/clang-doc/md-linkage.cpp
new file mode 100644 (file)
index 0000000..942ff35
--- /dev/null
@@ -0,0 +1,134 @@
+// THIS IS A GENERATED TEST. DO NOT EDIT.
+// To regenerate, see clang-doc/gen_test.py docstring.
+//
+// REQUIRES: system-linux
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+
+void function(int x);
+
+inline int inlinedFunction(int x);
+
+int functionWithInnerClass(int x) {
+  class InnerClass { //NoLinkage
+  public:
+    int innerPublicMethod() { return 2; };
+  }; //end class
+  InnerClass temp;
+  return temp.innerPublicMethod();
+};
+
+inline int inlinedFunctionWithInnerClass(int x) {
+  class InnerClass { //VisibleNoLinkage
+  public:
+    int innerPublicMethod() { return 2; };
+  }; //end class
+  InnerClass temp;
+  return temp.innerPublicMethod();
+};
+
+class Class {
+public:
+  void publicMethod();
+  int publicField;
+
+protected:
+  void protectedMethod();
+  int protectedField;
+
+private:
+  void privateMethod();
+  int privateField;
+};
+
+namespace named {
+class NamedClass {
+public:
+  void namedPublicMethod();
+  int namedPublicField;
+
+protected:
+  void namedProtectedMethod();
+  int namedProtectedField;
+
+private:
+  void namedPrivateMethod();
+  int namedPrivateField;
+};
+
+void namedFunction();
+static void namedStaticFunction();
+inline void namedInlineFunction();
+} // namespace named
+
+static void staticFunction(int x); //Internal Linkage
+
+static int staticFunctionWithInnerClass(int x) {
+  class InnerClass { //NoLinkage
+  public:
+    int innerPublicMethod() { return 2; };
+  }; //end class
+  InnerClass temp;
+  return temp.innerPublicMethod();
+};
+
+namespace {
+class AnonClass {
+public:
+  void anonPublicMethod();
+  int anonPublicField;
+
+protected:
+  void anonProtectedMethod();
+  int anonProtectedField;
+
+private:
+  void anonPrivateMethod();
+  int anonPrivateField;
+};
+
+void anonFunction();
+static void anonStaticFunction();
+inline void anonInlineFunction();
+} // namespace
+
+// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs
+
+
+// RUN: cat %t/docs/./Class.md | FileCheck %s --check-prefix CHECK-0
+// CHECK-0: # class Class
+// CHECK-0: *Defined at line 32 of test*
+// CHECK-0: ## Members
+// CHECK-0: int publicField
+// CHECK-0: protected int protectedField
+// CHECK-0: ## Functions
+// CHECK-0: ### void publicMethod()
+// CHECK-0: ### void protectedMethod()
+
+// RUN: cat %t/docs/./named.md | FileCheck %s --check-prefix CHECK-1
+// CHECK-1: # namespace named
+// CHECK-1: ## Functions
+// CHECK-1: ### void namedFunction()
+// CHECK-1: ### void namedInlineFunction()
+
+// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2
+// CHECK-2: # Global Namespace
+// CHECK-2: ## Functions
+// CHECK-2: ### void function(int x)
+// CHECK-2: ### int inlinedFunction(int x)
+// CHECK-2: ### int functionWithInnerClass(int x)
+// CHECK-2: *Defined at line 14 of test*
+// CHECK-2: ### int inlinedFunctionWithInnerClass(int x)
+// CHECK-2: *Defined at line 23 of test*
+
+// RUN: cat %t/docs/named/NamedClass.md | FileCheck %s --check-prefix CHECK-3
+// CHECK-3: # class NamedClass
+// CHECK-3: *Defined at line 47 of test*
+// CHECK-3: ## Members
+// CHECK-3: int namedPublicField
+// CHECK-3: protected int namedProtectedField
+// CHECK-3: ## Functions
+// CHECK-3: ### void namedPublicMethod()
+// CHECK-3: ### void namedProtectedMethod()
diff --git a/clang-tools-extra/test/clang-doc/md-module.cpp b/clang-tools-extra/test/clang-doc/md-module.cpp
new file mode 100644 (file)
index 0000000..936f621
--- /dev/null
@@ -0,0 +1,24 @@
+// THIS IS A GENERATED TEST. DO NOT EDIT.
+// To regenerate, see clang-doc/gen_test.py docstring.
+//
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+
+export module M;
+
+int moduleFunction(int x); // ModuleLinkage
+
+static int staticModuleFunction(int x); // ModuleInternalLinkage
+
+export double exportedModuleFunction(double y, int z); // ExternalLinkage
+
+// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs
+
+
+// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0
+// CHECK-0: # Global Namespace
+// CHECK-0: ## Functions
+// CHECK-0: ### int moduleFunction(int x)
+// CHECK-0: ### double exportedModuleFunction(double y, int z)
diff --git a/clang-tools-extra/test/clang-doc/md-namespace.cpp b/clang-tools-extra/test/clang-doc/md-namespace.cpp
new file mode 100644 (file)
index 0000000..6db3fff
--- /dev/null
@@ -0,0 +1,46 @@
+// THIS IS A GENERATED TEST. DO NOT EDIT.
+// To regenerate, see clang-doc/gen_test.py docstring.
+//
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+
+namespace A {
+  
+void f();
+
+}  // namespace A
+
+namespace A {
+
+void f(){};
+
+namespace B {
+
+enum E { X };
+
+E func(int i) { return X; }
+
+}  // namespace B
+}  // namespace A
+
+// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs
+
+
+// RUN: cat %t/docs/./A.md | FileCheck %s --check-prefix CHECK-0
+// CHECK-0: # namespace A
+// CHECK-0: ## Functions
+// CHECK-0: ### void f()
+// CHECK-0: *Defined at line 17 of test*
+
+// RUN: cat %t/docs/A/B.md | FileCheck %s --check-prefix CHECK-1
+// CHECK-1: # namespace B
+// CHECK-1: ## Functions
+// CHECK-1: ### enum A::B::E func(int i)
+// CHECK-1: *Defined at line 23 of test*
+// CHECK-1: ## Enums
+// CHECK-1: | enum E |
+// CHECK-1: --
+// CHECK-1: | X |
+// CHECK-1: *Defined at line 21 of test*
diff --git a/clang-tools-extra/test/clang-doc/md-record.cpp b/clang-tools-extra/test/clang-doc/md-record.cpp
new file mode 100644 (file)
index 0000000..1c5a1ad
--- /dev/null
@@ -0,0 +1,97 @@
+// THIS IS A GENERATED TEST. DO NOT EDIT.
+// To regenerate, see clang-doc/gen_test.py docstring.
+//
+// This test requires Linux due to system-dependent USR for the inner class.
+// REQUIRES: system-linux
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo "" > %t/compile_flags.txt
+// RUN: cp "%s" "%t/test.cpp"
+
+void H() {
+  class I {};
+}
+
+union A { int X; int Y; };
+
+enum B { X, Y };
+
+enum class Bc { A, B };
+
+struct C { int i; };
+
+class D {};
+
+class E {
+public:
+  E() {}
+  ~E() {}
+
+protected:
+  void ProtectedMethod();
+};
+
+void E::ProtectedMethod() {}
+
+class F : virtual private D, public E {};
+
+class X {
+  class Y {};
+};
+
+// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs
+
+
+// RUN: cat %t/docs/./F.md | FileCheck %s --check-prefix CHECK-0
+// CHECK-0: # class F
+// CHECK-0: *Defined at line 36 of test*
+// CHECK-0: Inherits from E, D
+
+// RUN: cat %t/docs/./D.md | FileCheck %s --check-prefix CHECK-1
+// CHECK-1: # class D
+// CHECK-1: *Defined at line 23 of test*
+
+// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2
+// CHECK-2: # Global Namespace
+// CHECK-2: ## Functions
+// CHECK-2: ### void H()
+// CHECK-2: *Defined at line 11 of test*
+// CHECK-2: ## Enums
+// CHECK-2: | enum B |
+// CHECK-2: --
+// CHECK-2: | X |
+// CHECK-2: | Y |
+// CHECK-2: *Defined at line 17 of test*
+// CHECK-2: | enum class Bc |
+// CHECK-2: --
+// CHECK-2: | A |
+// CHECK-2: | B |
+// CHECK-2: *Defined at line 19 of test*
+
+// RUN: cat %t/docs/./E.md | FileCheck %s --check-prefix CHECK-3
+// CHECK-3: # class E
+// CHECK-3: *Defined at line 25 of test*
+// CHECK-3: ## Functions
+// CHECK-3: ### void E()
+// CHECK-3: *Defined at line 27 of test*
+// CHECK-3: ### void ~E()
+// CHECK-3: *Defined at line 28 of test*
+// CHECK-3: ### void ProtectedMethod()
+// CHECK-3: *Defined at line 34 of test*
+
+// RUN: cat %t/docs/./C.md | FileCheck %s --check-prefix CHECK-4
+// CHECK-4: # struct C
+// CHECK-4: *Defined at line 21 of test*
+// CHECK-4: ## Members
+// CHECK-4: int i
+
+// RUN: cat %t/docs/./X.md | FileCheck %s --check-prefix CHECK-5
+// CHECK-5: # class X
+// CHECK-5: *Defined at line 38 of test*
+
+// RUN: cat %t/docs/./A.md | FileCheck %s --check-prefix CHECK-6
+// CHECK-6: # union A
+// CHECK-6: *Defined at line 15 of test*
+// CHECK-6: ## Members
+// CHECK-6: int X
+// CHECK-6: int Y