ClangDoc.cpp
Generators.cpp
Mapper.cpp
+ MDGenerator.cpp
Representation.cpp
Serialize.cpp
YAMLGenerator.cpp
// 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
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;
--- /dev/null
+//===-- 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
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).
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:
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,
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.
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:
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'
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)
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
#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"
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",
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");
}
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);
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(
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;
--- /dev/null
+// 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
--- /dev/null
+// 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()
--- /dev/null
+// 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)
--- /dev/null
+// 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*
--- /dev/null
+// 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