Index: clang-doc/CMakeLists.txt =================================================================== --- clang-doc/CMakeLists.txt +++ clang-doc/CMakeLists.txt @@ -8,9 +8,11 @@ BitcodeReader.cpp BitcodeWriter.cpp ClangDoc.cpp + Generators.cpp Mapper.cpp Representation.cpp Serialize.cpp + YAMLGenerator.cpp LINK_LIBS clangAnalysis Index: clang-doc/Generators.h =================================================================== --- /dev/null +++ clang-doc/Generators.h @@ -0,0 +1,41 @@ +//===-- 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. +// +//===----------------------------------------------------------------------===// +// Generator classes for converting declaration information into documentation +// in a specified format. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H + +#include "Representation.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Registry.h" + +namespace clang { +namespace doc { + +// Abstract base class for generators. +// This is expected to be implemented and exposed via the GeneratorRegistry. +class Generator { +public: + virtual ~Generator() = default; + + // Write out the decl info in the specified format. + virtual bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0; +}; + +typedef llvm::Registry GeneratorRegistry; + +llvm::Expected> +findGeneratorByName(llvm::StringRef Format); + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H Index: clang-doc/Generators.cpp =================================================================== --- /dev/null +++ clang-doc/Generators.cpp @@ -0,0 +1,36 @@ +//===---- Generator.cpp - Generator Registry ---------------------*- 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" + +LLVM_INSTANTIATE_REGISTRY(clang::doc::GeneratorRegistry) + +namespace clang { +namespace doc { + +llvm::Expected> +findGeneratorByName(llvm::StringRef Format) { + for (auto I = GeneratorRegistry::begin(), E = GeneratorRegistry::end(); + I != E; ++I) { + if (I->getName() != Format) + continue; + return I->instantiate(); + } + return llvm::make_error("Can't find generator: " + Format, + llvm::inconvertibleErrorCode()); +} + +// This anchor is used to force the linker to link in the generated object file +// and thus register the generators. +extern volatile int YAMLGeneratorAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest = + YAMLGeneratorAnchorSource; + +} // namespace doc +} // namespace clang Index: clang-doc/Representation.h =================================================================== --- clang-doc/Representation.h +++ clang-doc/Representation.h @@ -73,6 +73,11 @@ Reference(SymbolID USR, StringRef Name, InfoType IT) : USR(USR), Name(Name), RefType(IT) {} + bool operator==(const Reference &Other) const { + return std::tie(USR, Name, RefType) == + std::tie(Other.USR, Other.Name, Other.RefType); + } + SymbolID USR = SymbolID(); // Unique identifer for referenced decl SmallString<16> Name; // Name of type (possibly unresolved). InfoType RefType = InfoType::IT_default; // Indicates the type of this @@ -87,6 +92,8 @@ : Type(Type, Field, IT) {} TypeInfo(llvm::StringRef RefName) : Type(RefName) {} + bool operator==(const TypeInfo &Other) const { return Type == Other.Type; } + Reference Type; // Referenced type in this info. }; @@ -99,6 +106,10 @@ FieldTypeInfo(llvm::StringRef RefName, llvm::StringRef Name) : TypeInfo(RefName), Name(Name) {} + bool operator==(const FieldTypeInfo &Other) const { + return std::tie(Type, Name) == std::tie(Other.Type, Other.Name); + } + SmallString<16> Name; // Name associated with this info. }; @@ -112,6 +123,11 @@ AccessSpecifier Access) : FieldTypeInfo(RefName, Name), Access(Access) {} + bool operator==(const MemberTypeInfo &Other) const { + return std::tie(Type, Name, Access) == + std::tie(Other.Type, Other.Name, Other.Access); + } + AccessSpecifier Access = AccessSpecifier::AS_none; // Access level associated // with this info (public, // protected, private, @@ -123,6 +139,11 @@ Location(int LineNumber, SmallString<16> Filename) : LineNumber(LineNumber), Filename(std::move(Filename)) {} + bool operator==(const Location &Other) const { + return std::tie(LineNumber, Filename) == + std::tie(Other.LineNumber, Other.Filename); + } + int LineNumber; // Line number of this Location. SmallString<32> Filename; // File for this Location. }; @@ -173,10 +194,9 @@ bool IsMethod = false; // Indicates whether this function is a class method. Reference Parent; // Reference to the parent class decl for this method. TypeInfo ReturnType; // Info about the return type of this function. - llvm::SmallVector Params; // List of parameters. - AccessSpecifier Access = AccessSpecifier::AS_none; // Access level for this - // method (public, private, - // protected, none). + llvm::SmallVector Params; // List of parameters. + // Access level for this method (public, private, protected, none). + AccessSpecifier Access = AccessSpecifier::AS_none; }; // TODO: Expand to allow for documenting templating, inheritance access, Index: clang-doc/YAMLGenerator.cpp =================================================================== --- /dev/null +++ clang-doc/YAMLGenerator.cpp @@ -0,0 +1,268 @@ +//===-- ClangDocYAML.cpp - ClangDoc YAML -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Implementation of the YAML generator, converting decl info into YAML output. +//===----------------------------------------------------------------------===// + +#include "Generators.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang::doc; + +LLVM_YAML_IS_SEQUENCE_VECTOR(FieldTypeInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(MemberTypeInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(Reference) +LLVM_YAML_IS_SEQUENCE_VECTOR(Location) +LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>) + +namespace llvm { +namespace yaml { + +// Enumerations to YAML output. + +template <> struct ScalarEnumerationTraits { + 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 { + 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 ScalarEnumerationTraits { + static void enumeration(IO &IO, InfoType &Value) { + IO.enumCase(Value, "Namespace", InfoType::IT_namespace); + IO.enumCase(Value, "Record", InfoType::IT_record); + IO.enumCase(Value, "Function", InfoType::IT_function); + IO.enumCase(Value, "Enum", InfoType::IT_enum); + IO.enumCase(Value, "Default", InfoType::IT_default); + } +}; + +// Scalars to YAML output. +template struct ScalarTraits> { + + static void output(const SmallString &S, void *, llvm::raw_ostream &OS) { + for (const auto &C : S) + OS << C; + } + + static StringRef input(StringRef Scalar, void *, SmallString &Value) { + Value.assign(Scalar.begin(), Scalar.end()); + return StringRef(); + } + + static QuotingType mustQuote(StringRef) { return QuotingType::Single; } +}; + +template <> struct ScalarTraits> { + + static void output(const std::array &S, void *, + llvm::raw_ostream &OS) { + OS << toHex(toStringRef(S)); + } + + static StringRef input(StringRef Scalar, void *, + std::array &Value) { + if (Scalar.size() != 40) + return "Error: Incorrect scalar size for USR."; + Value = StringToSymbol(Scalar); + return StringRef(); + } + + static SymbolID StringToSymbol(llvm::StringRef Value) { + SymbolID USR; + std::string HexString = fromHex(Value); + std::copy(HexString.begin(), HexString.end(), USR.begin()); + return SymbolID(USR); + } + + static QuotingType mustQuote(StringRef) { return QuotingType::Single; } +}; + +// Helper functions to map infos to YAML. + +static void TypeInfoMapping(IO &IO, TypeInfo &I) { + IO.mapOptional("Type", I.Type, Reference()); +} + +static void FieldTypeInfoMapping(IO &IO, FieldTypeInfo &I) { + TypeInfoMapping(IO, I); + IO.mapOptional("Name", I.Name, SmallString<16>()); +} + +static void InfoMapping(IO &IO, Info &I) { + IO.mapRequired("USR", I.USR); + IO.mapOptional("Name", I.Name, SmallString<16>()); + IO.mapOptional("Namespace", I.Namespace, llvm::SmallVector()); + IO.mapOptional("Description", I.Description); +} + +static void SymbolInfoMapping(IO &IO, SymbolInfo &I) { + InfoMapping(IO, I); + IO.mapOptional("DefLocation", I.DefLoc, Optional()); + IO.mapOptional("Location", I.Loc, llvm::SmallVector()); +} + +static void CommentInfoMapping(IO &IO, CommentInfo &I) { + IO.mapOptional("Kind", I.Kind, SmallString<16>()); + IO.mapOptional("Text", I.Text, SmallString<64>()); + IO.mapOptional("Name", I.Name, SmallString<16>()); + IO.mapOptional("Direction", I.Direction, SmallString<8>()); + IO.mapOptional("ParamName", I.ParamName, SmallString<16>()); + IO.mapOptional("CloseName", I.CloseName, SmallString<16>()); + IO.mapOptional("SelfClosing", I.SelfClosing, false); + IO.mapOptional("Explicit", I.Explicit, false); + IO.mapOptional("Args", I.Args, llvm::SmallVector, 4>()); + IO.mapOptional("AttrKeys", I.AttrKeys, + llvm::SmallVector, 4>()); + IO.mapOptional("AttrValues", I.AttrValues, + llvm::SmallVector, 4>()); + IO.mapOptional("Children", I.Children); +} + +// Template specialization to YAML traits for Infos. + +template <> struct MappingTraits { + static void mapping(IO &IO, Location &Loc) { + IO.mapOptional("LineNumber", Loc.LineNumber, 0); + IO.mapOptional("Filename", Loc.Filename, SmallString<32>()); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, Reference &Ref) { + IO.mapOptional("Type", Ref.RefType, InfoType::IT_default); + IO.mapOptional("Name", Ref.Name, SmallString<16>()); + IO.mapOptional("USR", Ref.USR, SymbolID()); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, TypeInfo &I) { TypeInfoMapping(IO, I); } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, FieldTypeInfo &I) { + TypeInfoMapping(IO, I); + IO.mapOptional("Name", I.Name, SmallString<16>()); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, MemberTypeInfo &I) { + FieldTypeInfoMapping(IO, I); + IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, NamespaceInfo &I) { InfoMapping(IO, I); } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, RecordInfo &I) { + SymbolInfoMapping(IO, I); + IO.mapOptional("TagType", I.TagType, clang::TagTypeKind::TTK_Struct); + IO.mapOptional("Members", I.Members); + IO.mapOptional("Parents", I.Parents, llvm::SmallVector()); + IO.mapOptional("VirtualParents", I.VirtualParents, + llvm::SmallVector()); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, EnumInfo &I) { + SymbolInfoMapping(IO, I); + IO.mapOptional("Scoped", I.Scoped, false); + IO.mapOptional("Members", I.Members); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, FunctionInfo &I) { + SymbolInfoMapping(IO, I); + IO.mapOptional("IsMethod", I.IsMethod, false); + IO.mapOptional("Parent", I.Parent, Reference()); + IO.mapOptional("Params", I.Params); + IO.mapOptional("ReturnType", I.ReturnType); + IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, CommentInfo &I) { CommentInfoMapping(IO, I); } +}; + +template <> struct MappingTraits> { + static void mapping(IO &IO, std::unique_ptr &I) { + if (I) + CommentInfoMapping(IO, *I); + } +}; + +} // end namespace yaml +} // end namespace llvm + +namespace clang { +namespace doc { + +/// Generator for YAML documentation. +class YAMLGenerator : public Generator { +public: + static const char *Format; + + bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; +}; + +const char *YAMLGenerator::Format = "yaml"; + +bool YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { + llvm::yaml::Output InfoYAML(OS); + switch (I->IT) { + case InfoType::IT_namespace: + InfoYAML << *static_cast(I); + break; + case InfoType::IT_record: + InfoYAML << *static_cast(I); + break; + case InfoType::IT_enum: + InfoYAML << *static_cast(I); + break; + case InfoType::IT_function: + InfoYAML << *static_cast(I); + break; + case InfoType::IT_default: + llvm::errs() << "Unexpected info type in index.\n"; + return true; + } + return false; +} + +static GeneratorRegistry::Add YAML(YAMLGenerator::Format, + "Generator for YAML output."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the generator. +volatile int YAMLGeneratorAnchorSource = 0; + +} // namespace doc +} // namespace clang Index: clang-doc/tool/ClangDocMain.cpp =================================================================== --- clang-doc/tool/ClangDocMain.cpp +++ clang-doc/tool/ClangDocMain.cpp @@ -21,6 +21,7 @@ #include "BitcodeReader.h" #include "BitcodeWriter.h" #include "ClangDoc.h" +#include "Generators.h" #include "Representation.h" #include "clang/AST/AST.h" #include "clang/AST/Decl.h" @@ -67,10 +68,10 @@ yaml, }; -static llvm::cl::opt - Format("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(clEnumVal(yaml, "Documentation in YAML format.")), + llvm::cl::init(yaml), llvm::cl::cat(ClangDocCategory)); static llvm::cl::opt DoxygenOnly( "doxygen", @@ -116,8 +117,41 @@ return false; } +llvm::Expected> +getPath(StringRef Root, StringRef Ext, StringRef Name, + llvm::SmallVectorImpl &Namespaces) { + std::error_code OK; + llvm::SmallString<128> Path; + llvm::sys::path::native(Root, Path); + for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R) + llvm::sys::path::append(Path, R->Name); + + if (CreateDirectory(Path)) + return llvm::make_error("Unable to create directory.\n", + llvm::inconvertibleErrorCode()); + + llvm::sys::path::append(Path, Name + Ext); + return Path; +} + +std::string getFormatString(OutputFormatTy Ty) { + switch (Ty) { + case yaml: + return "yaml"; + } +} + 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); @@ -173,7 +207,7 @@ } }); - // Reducing phase + // Reducing and generation phases llvm::outs() << "Reducing " << MapOutput.size() << " infos...\n"; llvm::StringMap> ReduceOutput; for (auto &Group : MapOutput) { @@ -186,16 +220,27 @@ llvm::BitstreamWriter Stream(Buffer); doc::ClangDocBitcodeWriter Writer(Stream); Writer.dispatchInfoForWrite(Reduced.get().get()); - if (DumpResultToFile("bc", Group.getKey() + ".bc", Buffer)) { - llvm::errs() << "Error writing " << Group.getKey() << " to file.\n"; - continue; - } + if (DumpResultToFile("bc", Group.getKey() + ".bc", Buffer)) + llvm::errs() << "Error dumping to bitcode.\n"; + continue; } - ReduceOutput.insert( - std::make_pair(Group.getKey(), std::move(Reduced.get()))); + // Create the relevant ostream and emit the documentation for this decl. + doc::Info *I = Reduced.get().get(); + auto InfoPath = getPath(OutDirectory, "." + Format, I->Name, I->Namespace); + if (!InfoPath) { + llvm::errs() << toString(InfoPath.takeError()) << "\n"; + continue; + } + std::error_code FileErr; + llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr, llvm::sys::fs::F_None); + if (FileErr != OK) { + llvm::errs() << "Error opening index file: " << FileErr.message() << "\n"; + continue; + } - // FIXME: Add support for emitting different output formats. + if (G->get()->generateDocForInfo(I, InfoOS)) + llvm::errs() << "Unable to generate docs for info.\n"; } return 0; Index: test/clang-doc/yaml-comments.cpp =================================================================== --- /dev/null +++ test/clang-doc/yaml-comments.cpp @@ -0,0 +1,129 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc -doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: cat %t/docs/F.yaml | FileCheck %s + +/// \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) {} + +// CHECK: --- +// CHECK-NEXT: USR: '7574630614A535710E5A6ABCFFF98BCA2D06A4CA' +// CHECK-NEXT: Name: 'F' +// CHECK-NEXT: Description: +// CHECK-NEXT: - Kind: 'FullComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: - Kind: 'BlockCommandComment' +// CHECK-NEXT: Name: 'brief' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: Text: ' Brief description.' +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: Text: ' Extended description that' +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: Text: ' continues onto the next line.' +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-NEXT: Name: 'ul' +// CHECK-NEXT: AttrKeys: +// CHECK-NEXT: - 'class' +// CHECK-NEXT: AttrValues: +// CHECK-NEXT: - 'test' +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-NEXT: Name: 'li' +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: Text: ' Testing.' +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: - Kind: 'HTMLEndTagComment' +// CHECK-NEXT: Name: 'ul' +// CHECK-NEXT: SelfClosing: true +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: - Kind: 'VerbatimBlockComment' +// CHECK-NEXT: Name: 'verbatim' +// CHECK-NEXT: CloseName: 'endverbatim' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'VerbatimBlockLineComment' +// CHECK-NEXT: Text: ' The description continues.' +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: - Kind: 'ParamCommandComment' +// CHECK-NEXT: Direction: '[out]' +// CHECK-NEXT: ParamName: 'I' +// CHECK-NEXT: Explicit: true +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: Text: ' is a parameter.' +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: - Kind: 'ParamCommandComment' +// CHECK-NEXT: Direction: '[in]' +// CHECK-NEXT: ParamName: 'J' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: Text: ' is a parameter.' +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: - Kind: 'BlockCommandComment' +// CHECK-NEXT: Name: 'return' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: Text: ' void' +// CHECK-NEXT: - Kind: 'FullComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'ParagraphComment' +// CHECK-NEXT: Children: +// CHECK-NEXT: - Kind: 'TextComment' +// CHECK-NEXT: Text: ' Bonus comment on definition' +// CHECK-NEXT: DefLocation: +// CHECK-NEXT: LineNumber: 27 +// CHECK-NEXT: Filename: '{{.*}}' +// CHECK-NEXT: Location: +// CHECK-NEXT: - LineNumber: 24 +// CHECK-NEXT: Filename: '{{.*}}' +// CHECK-NEXT: Params: +// CHECK-NEXT: - Type: +// CHECK-NEXT: Name: 'int' +// CHECK-NEXT: Name: 'I' +// CHECK-NEXT: - Type: +// CHECK-NEXT: Name: 'int' +// CHECK-NEXT: Name: 'J' +// CHECK-NEXT: ReturnType: +// CHECK-NEXT: Type: +// CHECK-NEXT: Name: 'void' +// CHECK-NEXT: ... Index: test/clang-doc/yaml-namespace.cpp =================================================================== --- /dev/null +++ test/clang-doc/yaml-namespace.cpp @@ -0,0 +1,102 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc -doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: cat %t/docs/A.yaml | FileCheck %s --check-prefix=CHECK-A +// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix=CHECK-B +// RUN: cat %t/docs/A/f.yaml | FileCheck %s --check-prefix=CHECK-F +// RUN: cat %t/docs/A/B/E.yaml | FileCheck %s --check-prefix=CHECK-E +// RUN: cat %t/docs/A/B/func.yaml | FileCheck %s --check-prefix=CHECK-FUNC + +namespace A { + +// CHECK-A: --- +// CHECK-A-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-A-NEXT: Name: 'A' +// CHECK-A-NEXT: ... + + +void f(); + +} // namespace A + +namespace A { + +void f(){}; + +// CHECK-F: --- +// CHECK-F-NEXT: USR: '39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC' +// CHECK-F-NEXT: Name: 'f' +// CHECK-F-NEXT: Namespace: +// CHECK-F-NEXT: - Type: Namespace +// CHECK-F-NEXT: Name: 'A' +// CHECK-F-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-F-NEXT: DefLocation: +// CHECK-F-NEXT: LineNumber: 26 +// CHECK-F-NEXT: Filename: '{{.*}}' +// CHECK-F-NEXT: Location: +// CHECK-F-NEXT: - LineNumber: 20 +// CHECK-F-NEXT: Filename: 'test' +// CHECK-F-NEXT: ReturnType: +// CHECK-F-NEXT: Type: +// CHECK-F-NEXT: Name: 'void' +// CHECK-F-NEXT: ... + +namespace B { + +// CHECK-B: --- +// CHECK-B-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' +// CHECK-B-NEXT: Name: 'B' +// CHECK-B-NEXT: Namespace: +// CHECK-B-NEXT: - Type: Namespace +// CHECK-B-NEXT: Name: 'A' +// CHECK-B-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-B-NEXT: ... + + +enum E { X }; + +// CHECK-E: --- +// CHECK-E-NEXT: USR: 'E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775' +// CHECK-E-NEXT: Name: 'E' +// CHECK-E-NEXT: Namespace: +// CHECK-E-NEXT: - Type: Namespace +// CHECK-E-NEXT: Name: 'B' +// CHECK-E-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' +// CHECK-E-NEXT: - Type: Namespace +// CHECK-E-NEXT: Name: 'A' +// CHECK-E-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-E-NEXT: DefLocation: +// CHECK-E-NEXT: LineNumber: 58 +// CHECK-E-NEXT: Filename: '{{.*}}' +// CHECK-E-NEXT: Members: +// CHECK-E-NEXT: - 'X' +// CHECK-E-NEXT: ... + +E func(int i) { return X; } + +// CHECK-FUNC: --- +// CHECK-FUNC-NEXT: USR: '9A82CB33ED0FDF81EE383D31CD0957D153C5E840' +// CHECK-FUNC-NEXT: Name: 'func' +// CHECK-FUNC-NEXT: Namespace: +// CHECK-FUNC-NEXT: - Type: Namespace +// CHECK-FUNC-NEXT: Name: 'B' +// CHECK-FUNC-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' +// CHECK-FUNC-NEXT: - Type: Namespace +// CHECK-FUNC-NEXT: Name: 'A' +// CHECK-FUNC-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-FUNC-NEXT: DefLocation: +// CHECK-FUNC-NEXT: LineNumber: 77 +// CHECK-FUNC-NEXT: Filename: '{{.*}}' +// CHECK-FUNC-NEXT: Params: +// CHECK-FUNC-NEXT: - Type: +// CHECK-FUNC-NEXT: Name: 'int' +// CHECK-FUNC-NEXT: Name: 'i' +// CHECK-FUNC-NEXT: ReturnType: +// CHECK-FUNC-NEXT: Type: +// CHECK-FUNC-NEXT: Name: 'enum A::B::E' +// CHECK-FUNC-NEXT: ... + +} // namespace B +} // namespace A Index: test/clang-doc/yaml-record.cpp =================================================================== --- /dev/null +++ test/clang-doc/yaml-record.cpp @@ -0,0 +1,250 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc -doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: cat %t/docs/A.yaml | FileCheck %s --check-prefix=CHECK-A +// RUN: cat %t/docs/Bc.yaml | FileCheck %s --check-prefix=CHECK-BC +// RUN: cat %t/docs/B.yaml | FileCheck %s --check-prefix=CHECK-B +// RUN: cat %t/docs/C.yaml | FileCheck %s --check-prefix=CHECK-C +// RUN: cat %t/docs/D.yaml | FileCheck %s --check-prefix=CHECK-D +// RUN: cat %t/docs/E.yaml | FileCheck %s --check-prefix=CHECK-E +// RUN: cat %t/docs/E/ProtectedMethod.yaml | FileCheck %s --check-prefix=CHECK-EPM +// RUN: cat %t/docs/E/E.yaml | FileCheck %s --check-prefix=CHECK-ECON +// RUN: cat %t/docs/E/'~E.yaml' | FileCheck %s --check-prefix=CHECK-EDES +// RUN: cat %t/docs/F.yaml | FileCheck %s --check-prefix=CHECK-F +// RUN: cat %t/docs/X.yaml | FileCheck %s --check-prefix=CHECK-X +// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix=CHECK-Y +// RUN: cat %t/docs/H.yaml | FileCheck %s --check-prefix=CHECK-H +// RUN: cat %t/docs/H/I.yaml | FileCheck %s --check-prefix=CHECK-I + +union A { int X; int Y; }; + +// CHECK-A: --- +// CHECK-A-NEXT: USR: 'ACE81AFA6627B4CEF2B456FB6E1252925674AF7E' +// CHECK-A-NEXT: Name: 'A' +// CHECK-A-NEXT: DefLocation: +// CHECK-A-NEXT: LineNumber: 21 +// CHECK-A-NEXT: Filename: '{{.*}}' +// CHECK-A-NEXT: TagType: Union +// CHECK-A-NEXT: Members: +// CHECK-A-NEXT: - Type: +// CHECK-A-NEXT: Name: 'int' +// CHECK-A-NEXT: Name: 'X' +// CHECK-A-NEXT: - Type: +// CHECK-A-NEXT: Name: 'int' +// CHECK-A-NEXT: Name: 'Y' +// CHECK-A-NEXT: ... + + +enum B { X, Y }; + +// CHECK-B: --- +// CHECK-B-NEXT: USR: 'FC07BD34D5E77782C263FA944447929EA8753740' +// CHECK-B-NEXT: Name: 'B' +// CHECK-B-NEXT: DefLocation: +// CHECK-B-NEXT: LineNumber: 40 +// CHECK-B-NEXT: Filename: '{{.*}}' +// CHECK-B-NEXT: Members: +// CHECK-B-NEXT: - 'X' +// CHECK-B-NEXT: - 'Y' +// CHECK-B-NEXT: ... + +enum class Bc { A, B }; + +// CHECK-BC: --- +// CHECK-BC-NEXT: USR: '1E3438A08BA22025C0B46289FF0686F92C8924C5' +// CHECK-BC-NEXT: Name: 'Bc' +// CHECK-BC-NEXT: DefLocation: +// CHECK-BC-NEXT: LineNumber: 53 +// CHECK-BC-NEXT: Filename: '{{.*}}' +// CHECK-BC-NEXT: Scoped: true +// CHECK-BC-NEXT: Members: +// CHECK-BC-NEXT: - 'A' +// CHECK-BC-NEXT: - 'B' +// CHECK-BC-NEXT: ... + +struct C { int i; }; + +// CHECK-C: --- +// CHECK-C-NEXT: USR: '06B5F6A19BA9F6A832E127C9968282B94619B210' +// CHECK-C-NEXT: Name: 'C' +// CHECK-C-NEXT: DefLocation: +// CHECK-C-NEXT: LineNumber: 67 +// CHECK-C-NEXT: Filename: '{{.*}}' +// CHECK-C-NEXT: Members: +// CHECK-C-NEXT: - Type: +// CHECK-C-NEXT: Name: 'int' +// CHECK-C-NEXT: Name: 'i' +// CHECK-C-NEXT: ... + +class D {}; + +// CHECK-D: --- +// CHECK-D-NEXT: USR: '0921737541208B8FA9BB42B60F78AC1D779AA054' +// CHECK-D-NEXT: Name: 'D' +// CHECK-D-NEXT: DefLocation: +// CHECK-D-NEXT: LineNumber: 81 +// CHECK-D-NEXT: Filename: '{{.*}}' +// CHECK-D-NEXT: TagType: Class +// CHECK-D-NEXT: ... + +class E { +public: + E() {} + +// CHECK-ECON: --- +// CHECK-ECON-NEXT: USR: 'DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4' +// CHECK-ECON-NEXT: Name: 'E' +// CHECK-ECON-NEXT: Namespace: +// CHECK-ECON-NEXT: - Type: Record +// CHECK-ECON-NEXT: Name: 'E' +// CHECK-ECON-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-ECON-NEXT: DefLocation: +// CHECK-ECON-NEXT: LineNumber: 94 +// CHECK-ECON-NEXT: Filename: '{{.*}}' +// CHECK-ECON-NEXT: IsMethod: true +// CHECK-ECON-NEXT: Parent: +// CHECK-ECON-NEXT: Type: Record +// CHECK-ECON-NEXT: Name: 'E' +// CHECK-ECON-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-ECON-NEXT: ReturnType: +// CHECK-ECON-NEXT: Type: +// CHECK-ECON-NEXT: Name: 'void' +// CHECK-ECON-NEXT: ... + + ~E() {} + +// CHECK-EDES: --- +// CHECK-EDES-NEXT: USR: 'BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17' +// CHECK-EDES-NEXT: Name: '~E' +// CHECK-EDES-NEXT: Namespace: +// CHECK-EDES-NEXT: - Type: Record +// CHECK-EDES-NEXT: Name: 'E' +// CHECK-EDES-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-EDES-NEXT: DefLocation: +// CHECK-EDES-NEXT: LineNumber: 116 +// CHECK-EDES-NEXT: Filename: '{{.*}}' +// CHECK-EDES-NEXT: IsMethod: true +// CHECK-EDES-NEXT: Parent: +// CHECK-EDES-NEXT: Type: Record +// CHECK-EDES-NEXT: Name: 'E' +// CHECK-EDES-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-EDES-NEXT: ReturnType: +// CHECK-EDES-NEXT: Type: +// CHECK-EDES-NEXT: Name: 'void' +// CHECK-EDES-NEXT: ... + + +protected: + void ProtectedMethod(); +}; + +// CHECK-E: --- +// CHECK-E-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-E-NEXT: Name: 'E' +// CHECK-E-NEXT: DefLocation: +// CHECK-E-NEXT: LineNumber: 92 +// CHECK-E-NEXT: Filename: '{{.*}}' +// CHECK-E-NEXT: TagType: Class +// CHECK-E-NEXT: ... + +void E::ProtectedMethod() {} + +// CHECK-EPM: --- +// CHECK-EPM-NEXT: USR: '5093D428CDC62096A67547BA52566E4FB9404EEE' +// CHECK-EPM-NEXT: Name: 'ProtectedMethod' +// CHECK-EPM-NEXT: Namespace: +// CHECK-EPM-NEXT: - Type: Record +// CHECK-EPM-NEXT: Name: 'E' +// CHECK-EPM-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-EPM-NEXT: DefLocation: +// CHECK-EPM-NEXT: LineNumber: 152 +// CHECK-EPM-NEXT: Filename: '{{.*}}' +// CHECK-EPM-NEXT: Location: +// CHECK-EPM-NEXT: - LineNumber: 140 +// CHECK-EPM-NEXT: Filename: '{{.*}}' +// CHECK-EPM-NEXT: IsMethod: true +// CHECK-EPM-NEXT: Parent: +// CHECK-EPM-NEXT: Type: Record +// CHECK-EPM-NEXT: Name: 'E' +// CHECK-EPM-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-EPM-NEXT: ReturnType: +// CHECK-EPM-NEXT: Type: +// CHECK-EPM-NEXT: Name: 'void' +// CHECK-EPM-NEXT: ... + +class F : virtual private D, public E {}; + +// CHECK-F: --- +// CHECK-F-NEXT: USR: 'E3B54702FABFF4037025BA194FC27C47006330B5' +// CHECK-F-NEXT: Name: 'F' +// CHECK-F-NEXT: DefLocation: +// CHECK-F-NEXT: LineNumber: 177 +// CHECK-F-NEXT: Filename: '{{.*}}' +// CHECK-F-NEXT: TagType: Class +// CHECK-F-NEXT: Parents: +// CHECK-F-NEXT: - Type: Record +// CHECK-F-NEXT: Name: 'E' +// CHECK-F-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-F-NEXT: VirtualParents: +// CHECK-F-NEXT: - Type: Record +// CHECK-F-NEXT: Name: 'D' +// CHECK-F-NEXT: USR: '0921737541208B8FA9BB42B60F78AC1D779AA054' +// CHECK-F-NEXT: ... + +class X { + class Y {}; + +// CHECK-Y: --- +// CHECK-Y-NEXT: USR: '641AB4A3D36399954ACDE29C7A8833032BF40472' +// CHECK-Y-NEXT: Name: 'Y' +// CHECK-Y-NEXT: Namespace: +// CHECK-Y-NEXT: - Type: Record +// CHECK-Y-NEXT: Name: 'X' +// CHECK-Y-NEXT: USR: 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' +// CHECK-Y-NEXT: DefLocation: +// CHECK-Y-NEXT: LineNumber: 197 +// CHECK-Y-NEXT: Filename: '{{.*}}' +// CHECK-Y-NEXT: TagType: Class +// CHECK-Y-NEXT: ... + +}; + +// CHECK-X: --- +// CHECK-X-NEXT: USR: 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' +// CHECK-X-NEXT: Name: 'X' +// CHECK-X-NEXT: DefLocation: +// CHECK-X-NEXT: LineNumber: 196 +// CHECK-X-NEXT: Filename: '{{.*}}' +// CHECK-X-NEXT: TagType: Class +// CHECK-X-NEXT: ... + +void H() { + class I {}; + +// CHECK-I: --- +// CHECK-I-NEXT: USR: '{{.*}}' +// CHECK-I-NEXT: Name: 'I' +// CHECK-I-NEXT: Namespace: +// CHECK-I-NEXT: - Type: Function +// CHECK-I-NEXT: Name: 'H' +// CHECK-I-NEXT: USR: 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' +// CHECK-I-NEXT: DefLocation: +// CHECK-I-NEXT: LineNumber: 224 +// CHECK-I-NEXT: Filename: 'test' +// CHECK-I-NEXT: TagType: Class +// CHECK-I-NEXT: ... + +} + +// CHECK-H: --- +// CHECK-H-NEXT: USR: 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' +// CHECK-H-NEXT: Name: 'H' +// CHECK-H-NEXT: DefLocation: +// CHECK-H-NEXT: LineNumber: 223 +// CHECK-H-NEXT: Filename: 'test' +// CHECK-H-NEXT: ReturnType: +// CHECK-H-NEXT: Type: +// CHECK-H-NEXT: Name: 'void' +// CHECK-H-NEXT: ...