Index: clang-doc/CMakeLists.txt =================================================================== --- clang-doc/CMakeLists.txt +++ clang-doc/CMakeLists.txt @@ -10,6 +10,7 @@ ClangDoc.cpp Generators.cpp Mapper.cpp + MDGenerator.cpp Representation.cpp Serialize.cpp YAMLGenerator.cpp Index: clang-doc/Generators.h =================================================================== --- clang-doc/Generators.h +++ clang-doc/Generators.h @@ -27,7 +27,7 @@ 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 GeneratorRegistry; Index: clang-doc/Generators.cpp =================================================================== --- clang-doc/Generators.cpp +++ clang-doc/Generators.cpp @@ -29,8 +29,11 @@ // 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: clang-doc/MDGenerator.cpp =================================================================== --- clang-doc/MDGenerator.cpp +++ clang-doc/MDGenerator.cpp @@ -0,0 +1,312 @@ +//===-- 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 + +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 &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("", 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; + } + std::string Access = getAccess(I.Access); + if (Access != "") + writeHeader(genItalic(Access) + " " + I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 3, OS); + else + writeHeader(I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 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(I), OS); + break; + case InfoType::IT_record: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_enum: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_function: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_default: + return llvm::make_error("Unexpected info type.\n", + llvm::inconvertibleErrorCode()); + } + return llvm::Error::success(); +} + +static GeneratorRegistry::Add 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: clang-doc/Representation.h =================================================================== --- clang-doc/Representation.h +++ clang-doc/Representation.h @@ -48,13 +48,14 @@ 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: clang-doc/YAMLGenerator.cpp =================================================================== --- clang-doc/YAMLGenerator.cpp +++ clang-doc/YAMLGenerator.cpp @@ -242,12 +242,12 @@ 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 @@ InfoYAML << *static_cast(I); break; case InfoType::IT_default: - llvm::errs() << "Unexpected info type in index.\n"; - return true; + return llvm::make_error("Unexpected info type.\n", + llvm::inconvertibleErrorCode()); } - return false; + return llvm::Error::success(); } static GeneratorRegistry::Add YAML(YAMLGenerator::Format, Index: clang-doc/gen_tests.py =================================================================== --- clang-doc/gen_tests.py +++ clang-doc/gen_tests.py @@ -18,16 +18,19 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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: clang-doc/tool/ClangDocMain.cpp =================================================================== --- clang-doc/tool/ClangDocMain.cpp +++ clang-doc/tool/ClangDocMain.cpp @@ -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 @@ llvm::cl::init(false), llvm::cl::cat(ClangDocCategory)); enum OutputFormatTy { + md, yaml, }; -static llvm::cl::opt 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 + 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 DoxygenOnly( "doxygen", @@ -155,10 +161,12 @@ 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 @@ 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 @@ 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 @@ 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; Index: test/clang-doc/md-comment.cpp =================================================================== --- test/clang-doc/md-comment.cpp +++ test/clang-doc/md-comment.cpp @@ -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. +/// +///
    +///
  • Testing. +///
+/// +/// \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:
    +// CHECK-0:
  • +// CHECK-0: Testing.
+// CHECK-0: The description continues. +// CHECK-0: -- +// CHECK-0: **I** [out] +// CHECK-0: **J** +// CHECK-0: **return** void +// CHECK-0: Bonus comment on definition Index: test/clang-doc/md-linkage.cpp =================================================================== --- test/clang-doc/md-linkage.cpp +++ test/clang-doc/md-linkage.cpp @@ -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() Index: test/clang-doc/md-module.cpp =================================================================== --- test/clang-doc/md-module.cpp +++ test/clang-doc/md-module.cpp @@ -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) Index: test/clang-doc/md-namespace.cpp =================================================================== --- test/clang-doc/md-namespace.cpp +++ test/clang-doc/md-namespace.cpp @@ -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* Index: test/clang-doc/md-record.cpp =================================================================== --- test/clang-doc/md-record.cpp +++ test/clang-doc/md-record.cpp @@ -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