Index: clang-tools-extra/clang-doc/BitcodeReader.cpp =================================================================== --- clang-tools-extra/clang-doc/BitcodeReader.cpp +++ clang-tools-extra/clang-doc/BitcodeReader.cpp @@ -148,6 +148,8 @@ return decodeRecord(R, I->USR, Blob); case NAMESPACE_NAME: return decodeRecord(R, I->Name, Blob); + case NAMESPACE_PATH: + return decodeRecord(R, I->Path, Blob); default: return llvm::make_error( "Invalid field for NamespaceInfo.\n", llvm::inconvertibleErrorCode()); @@ -161,6 +163,8 @@ return decodeRecord(R, I->USR, Blob); case RECORD_NAME: return decodeRecord(R, I->Name, Blob); + case RECORD_PATH: + return decodeRecord(R, I->Path, Blob); case RECORD_DEFLOCATION: return decodeRecord(R, I->DefLoc, Blob); case RECORD_LOCATION: @@ -286,6 +290,8 @@ return decodeRecord(R, I->Name, Blob); case REFERENCE_TYPE: return decodeRecord(R, I->RefType, Blob); + case REFERENCE_PATH: + return decodeRecord(R, I->Path, Blob); case REFERENCE_FIELD: return decodeRecord(R, F, Blob); default: @@ -684,7 +690,7 @@ std::unique_ptr I = llvm::make_unique(); if (auto Err = readBlock(ID, static_cast(I.get()))) return std::move(Err); - return std::unique_ptr{std::move(I)};; + return std::unique_ptr{std::move(I)}; } llvm::Expected> Index: clang-tools-extra/clang-doc/BitcodeWriter.h =================================================================== --- clang-tools-extra/clang-doc/BitcodeWriter.h +++ clang-tools-extra/clang-doc/BitcodeWriter.h @@ -91,6 +91,7 @@ MEMBER_TYPE_ACCESS, NAMESPACE_USR, NAMESPACE_NAME, + NAMESPACE_PATH, ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, @@ -99,6 +100,7 @@ ENUM_SCOPED, RECORD_USR, RECORD_NAME, + RECORD_PATH, RECORD_DEFLOCATION, RECORD_LOCATION, RECORD_TAG_TYPE, @@ -106,6 +108,7 @@ REFERENCE_USR, REFERENCE_NAME, REFERENCE_TYPE, + REFERENCE_PATH, REFERENCE_FIELD, RI_LAST, RI_FIRST = VERSION Index: clang-tools-extra/clang-doc/BitcodeWriter.cpp =================================================================== --- clang-tools-extra/clang-doc/BitcodeWriter.cpp +++ clang-tools-extra/clang-doc/BitcodeWriter.cpp @@ -148,6 +148,7 @@ {MEMBER_TYPE_ACCESS, {"Access", &IntAbbrev}}, {NAMESPACE_USR, {"USR", &SymbolIDAbbrev}}, {NAMESPACE_NAME, {"Name", &StringAbbrev}}, + {NAMESPACE_PATH, {"Path", &StringAbbrev}}, {ENUM_USR, {"USR", &SymbolIDAbbrev}}, {ENUM_NAME, {"Name", &StringAbbrev}}, {ENUM_DEFLOCATION, {"DefLocation", &LocationAbbrev}}, @@ -156,6 +157,7 @@ {ENUM_SCOPED, {"Scoped", &BoolAbbrev}}, {RECORD_USR, {"USR", &SymbolIDAbbrev}}, {RECORD_NAME, {"Name", &StringAbbrev}}, + {RECORD_PATH, {"Path", &StringAbbrev}}, {RECORD_DEFLOCATION, {"DefLocation", &LocationAbbrev}}, {RECORD_LOCATION, {"Location", &LocationAbbrev}}, {RECORD_TAG_TYPE, {"TagType", &IntAbbrev}}, @@ -169,6 +171,7 @@ {REFERENCE_USR, {"USR", &SymbolIDAbbrev}}, {REFERENCE_NAME, {"Name", &StringAbbrev}}, {REFERENCE_TYPE, {"RefType", &IntAbbrev}}, + {REFERENCE_PATH, {"Path", &StringAbbrev}}, {REFERENCE_FIELD, {"Field", &IntAbbrev}}}; assert(Inits.size() == RecordIdCount); for (const auto &Init : Inits) { @@ -199,18 +202,20 @@ {ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_MEMBER, ENUM_SCOPED}}, // Namespace Block - {BI_NAMESPACE_BLOCK_ID, {NAMESPACE_USR, NAMESPACE_NAME}}, + {BI_NAMESPACE_BLOCK_ID, + {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}}, // Record Block {BI_RECORD_BLOCK_ID, - {RECORD_USR, RECORD_NAME, RECORD_DEFLOCATION, RECORD_LOCATION, - RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF}}, + {RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION, + RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF}}, // Function Block {BI_FUNCTION_BLOCK_ID, {FUNCTION_USR, FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION, FUNCTION_ACCESS, FUNCTION_IS_METHOD}}, // Reference Block {BI_REFERENCE_BLOCK_ID, - {REFERENCE_USR, REFERENCE_NAME, REFERENCE_TYPE, REFERENCE_FIELD}}}; + {REFERENCE_USR, REFERENCE_NAME, REFERENCE_TYPE, REFERENCE_PATH, + REFERENCE_FIELD}}}; // AbbreviationMap @@ -381,6 +386,7 @@ emitRecord(R.USR, REFERENCE_USR); emitRecord(R.Name, REFERENCE_NAME); emitRecord((unsigned)R.RefType, REFERENCE_TYPE); + emitRecord(R.Path, REFERENCE_PATH); emitRecord((unsigned)Field, REFERENCE_FIELD); } @@ -428,6 +434,7 @@ StreamSubBlockGuard Block(Stream, BI_NAMESPACE_BLOCK_ID); emitRecord(I.USR, NAMESPACE_USR); emitRecord(I.Name, NAMESPACE_NAME); + emitRecord(I.Path, NAMESPACE_PATH); for (const auto &N : I.Namespace) emitBlock(N, FieldId::F_namespace); for (const auto &CI : I.Description) @@ -463,6 +470,7 @@ StreamSubBlockGuard Block(Stream, BI_RECORD_BLOCK_ID); emitRecord(I.USR, RECORD_USR); emitRecord(I.Name, RECORD_NAME); + emitRecord(I.Path, RECORD_PATH); for (const auto &N : I.Namespace) emitBlock(N, FieldId::F_namespace); for (const auto &CI : I.Description) Index: clang-tools-extra/clang-doc/Generators.h =================================================================== --- clang-tools-extra/clang-doc/Generators.h +++ clang-tools-extra/clang-doc/Generators.h @@ -38,8 +38,6 @@ std::string getTagType(TagTypeKind AS); -std::string genReferenceList(const llvm::SmallVectorImpl &Refs); - } // namespace doc } // namespace clang Index: clang-tools-extra/clang-doc/Generators.cpp =================================================================== --- clang-tools-extra/clang-doc/Generators.cpp +++ clang-tools-extra/clang-doc/Generators.cpp @@ -57,19 +57,6 @@ llvm_unreachable("Unknown TagTypeKind"); } -// Generates a comma-separated list of Refs -// Used to display the parents of a record -std::string genReferenceList(const llvm::SmallVectorImpl &Refs) { - std::string Buffer; - llvm::raw_string_ostream Stream(Buffer); - for (const auto &R : Refs) { - if (&R != Refs.begin()) - Stream << ", "; - Stream << R.Name; - } - return Stream.str(); -} - // This anchor is used to force the linker to link in the generated object file // and thus register the generators. extern volatile int YAMLGeneratorAnchorSource; Index: clang-tools-extra/clang-doc/HTMLGenerator.cpp =================================================================== --- clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -33,6 +33,7 @@ TAG_P, TAG_UL, TAG_LI, + TAG_A, }; HTMLTag() = default; @@ -58,8 +59,8 @@ }; struct TextNode : public HTMLNode { - TextNode(llvm::StringRef Text, bool Indented) - : Text(Text), Indented(Indented) {} + TextNode(const Twine &Text, bool Indented = true) + : Text(Text.str()), Indented(Indented) {} std::string Text; // Content of node bool Indented; // Indicates if an indentation must be rendered before the text @@ -114,6 +115,7 @@ case HTMLTag::TAG_P: case HTMLTag::TAG_UL: case HTMLTag::TAG_LI: + case HTMLTag::TAG_A: return false; } } @@ -126,6 +128,7 @@ case HTMLTag::TAG_H2: case HTMLTag::TAG_H3: case HTMLTag::TAG_LI: + case HTMLTag::TAG_A: return true; case HTMLTag::TAG_DIV: case HTMLTag::TAG_P: @@ -154,6 +157,8 @@ return llvm::SmallString<16>("ul"); case HTMLTag::TAG_LI: return llvm::SmallString<16>("li"); + case HTMLTag::TAG_A: + return llvm::SmallString<16>("a"); } } @@ -188,6 +193,31 @@ // HTML generation +static std::unique_ptr genLink(const Twine &Text, const Twine &Link) { + auto LinkNode = llvm::make_unique(HTMLTag::TAG_A, Text); + LinkNode->Attributes.try_emplace("href", Link.str()); + return LinkNode; +} + +static std::unique_ptr genTypeReference(const Reference &Type) { + if (Type.Path.empty()) + return llvm::make_unique(Type.Name); + llvm::SmallString<128> Path = Type.Path; + ::llvm::sys::path::append(Path, Type.Name + ".html"); + return genLink(Type.Name, Path); +} + +static std::vector> +genReferenceList(const llvm::SmallVectorImpl &Refs) { + std::vector> Out; + for (const auto &R : Refs) { + if (&R != Refs.begin()) + Out.emplace_back(llvm::make_unique(", ")); + Out.emplace_back(genTypeReference(R)); + } + return Out; +} + static std::vector> genHTML(const EnumInfo &I); static std::vector> genHTML(const FunctionInfo &I); @@ -249,8 +279,11 @@ std::string Access = getAccess(M.Access); if (Access != "") Access = Access + " "; - ULBody->Children.emplace_back(llvm::make_unique( - HTMLTag::TAG_LI, Access + M.Type.Name + " " + M.Name)); + auto LIBody = llvm::make_unique(HTMLTag::TAG_LI); + LIBody->Children.emplace_back(llvm::make_unique(Access)); + LIBody->Children.emplace_back(genTypeReference(M.Type)); + LIBody->Children.emplace_back(llvm::make_unique(" " + M.Name)); + ULBody->Children.emplace_back(std::move(LIBody)); } return Out; } @@ -343,21 +376,28 @@ std::vector> Out; Out.emplace_back(llvm::make_unique(HTMLTag::TAG_H3, I.Name)); - std::string Buffer; - llvm::raw_string_ostream Stream(Buffer); - for (const auto &P : I.Params) { - if (&P != I.Params.begin()) - Stream << ", "; - Stream << P.Type.Name + " " + P.Name; - } + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P)); + auto &FunctionHeader = Out.back(); std::string Access = getAccess(I.Access); if (Access != "") - Access = Access + " "; + FunctionHeader->Children.emplace_back( + llvm::make_unique(Access + " ")); + if (I.ReturnType.Type.Name != "") { + FunctionHeader->Children.emplace_back(genTypeReference(I.ReturnType.Type)); + FunctionHeader->Children.emplace_back(llvm::make_unique(" ")); + } + FunctionHeader->Children.emplace_back( + llvm::make_unique(I.Name + "(")); - Out.emplace_back(llvm::make_unique( - HTMLTag::TAG_P, Access + I.ReturnType.Type.Name + " " + I.Name + "(" + - Stream.str() + ")")); + for (const auto &P : I.Params) { + if (&P != I.Params.begin()) + FunctionHeader->Children.emplace_back(llvm::make_unique(", ")); + FunctionHeader->Children.emplace_back(genTypeReference(P.Type)); + FunctionHeader->Children.emplace_back( + llvm::make_unique(" " + P.Name)); + } + FunctionHeader->Children.emplace_back(llvm::make_unique(")")); if (I.DefLoc) Out.emplace_back(writeFileDefinition(I.DefLoc.getValue())); @@ -415,18 +455,26 @@ if (!I.Description.empty()) Out.emplace_back(genHTML(I.Description)); - std::string Parents = genReferenceList(I.Parents); - std::string VParents = genReferenceList(I.VirtualParents); + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P)); + auto &PBody = Out.back(); + PBody->Children.emplace_back(llvm::make_unique("Inherits from ")); + std::vector> Parents = genReferenceList(I.Parents); + std::vector> VParents = + genReferenceList(I.VirtualParents); if (!Parents.empty() || !VParents.empty()) { if (Parents.empty()) - Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P, - "Inherits from " + VParents)); + std::move(VParents.begin(), VParents.end(), + std::back_inserter(PBody->Children)); else if (VParents.empty()) - Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P, - "Inherits from " + Parents)); - else - Out.emplace_back(llvm::make_unique( - HTMLTag::TAG_P, "Inherits from " + Parents + ", " + VParents)); + std::move(Parents.begin(), Parents.end(), + std::back_inserter(PBody->Children)); + else { + std::move(Parents.begin(), Parents.end(), + std::back_inserter(PBody->Children)); + PBody->Children.emplace_back(llvm::make_unique(", ")); + std::move(VParents.begin(), VParents.end(), + std::back_inserter(PBody->Children)); + } } std::vector> Members = Index: clang-tools-extra/clang-doc/MDGenerator.cpp =================================================================== --- clang-tools-extra/clang-doc/MDGenerator.cpp +++ clang-tools-extra/clang-doc/MDGenerator.cpp @@ -20,14 +20,30 @@ // Markdown generation -static std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; } +static std::string genItalic(const Twine &Text) { + return "*" + Text.str() + "*"; +} -static std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; } +static std::string genEmphasis(const Twine &Text) { + return "**" + Text.str() + "**"; +} static std::string genLink(const Twine &Text, const Twine &Link) { return "[" + Text.str() + "](" + Link.str() + ")"; } +static std::string +genReferenceList(const llvm::SmallVectorImpl &Refs) { + std::string Buffer; + llvm::raw_string_ostream Stream(Buffer); + for (const auto &R : Refs) { + if (&R != Refs.begin()) + Stream << ", "; + Stream << R.Name; + } + return Stream.str(); +} + static void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n\n"; } Index: clang-tools-extra/clang-doc/Mapper.cpp =================================================================== --- clang-tools-extra/clang-doc/Mapper.cpp +++ clang-tools-extra/clang-doc/Mapper.cpp @@ -39,7 +39,7 @@ auto I = serialize::emitInfo( D, getComment(D, D->getASTContext()), getLine(D, D->getASTContext()), - getFile(D, D->getASTContext()), CDCtx.PublicOnly); + getFile(D, D->getASTContext()), CDCtx.PublicOnly, CDCtx.OutDirectory); // A null in place of I indicates that the serializer is skipping this decl // for some reason (e.g. we're only reporting public decls). Index: clang-tools-extra/clang-doc/Representation.h =================================================================== --- clang-tools-extra/clang-doc/Representation.h +++ clang-tools-extra/clang-doc/Representation.h @@ -114,8 +114,11 @@ struct Reference { Reference() = default; Reference(llvm::StringRef Name) : Name(Name) {} + Reference(llvm::StringRef Name, StringRef Path) : Name(Name), Path(Path) {} Reference(SymbolID USR, StringRef Name, InfoType IT) : USR(USR), Name(Name), RefType(IT) {} + Reference(SymbolID USR, StringRef Name, InfoType IT, StringRef Path) + : USR(USR), Name(Name), RefType(IT), Path(Path) {} bool operator==(const Reference &Other) const { return std::tie(USR, Name, RefType) == @@ -127,6 +130,8 @@ InfoType RefType = InfoType::IT_default; // Indicates the type of this // Reference (namespace, record, // function, enum, default). + llvm::SmallString<128> Path; // Path of directory where the clang-doc + // generated file will be saved }; // A base struct for TypeInfos @@ -134,7 +139,10 @@ TypeInfo() = default; TypeInfo(SymbolID Type, StringRef Field, InfoType IT) : Type(Type, Field, IT) {} + TypeInfo(SymbolID Type, StringRef Field, InfoType IT, StringRef Path) + : Type(Type, Field, IT, Path) {} TypeInfo(llvm::StringRef RefName) : Type(RefName) {} + TypeInfo(llvm::StringRef RefName, StringRef Path) : Type(RefName, Path) {} bool operator==(const TypeInfo &Other) const { return Type == Other.Type; } @@ -144,11 +152,13 @@ // Info for field types. struct FieldTypeInfo : public TypeInfo { FieldTypeInfo() = default; - FieldTypeInfo(SymbolID Type, StringRef Field, InfoType IT, + FieldTypeInfo(SymbolID Type, StringRef Field, InfoType IT, StringRef Path, llvm::StringRef Name) - : TypeInfo(Type, Field, IT), Name(Name) {} + : TypeInfo(Type, Field, IT, Path), Name(Name) {} FieldTypeInfo(llvm::StringRef RefName, llvm::StringRef Name) : TypeInfo(RefName), Name(Name) {} + FieldTypeInfo(llvm::StringRef RefName, StringRef Path, llvm::StringRef Name) + : TypeInfo(RefName, Path), Name(Name) {} bool operator==(const FieldTypeInfo &Other) const { return std::tie(Type, Name) == std::tie(Other.Type, Other.Name); @@ -160,12 +170,15 @@ // Info for member types. struct MemberTypeInfo : public FieldTypeInfo { MemberTypeInfo() = default; - MemberTypeInfo(SymbolID Type, StringRef Field, InfoType IT, + MemberTypeInfo(SymbolID Type, StringRef Field, InfoType IT, StringRef Path, llvm::StringRef Name, AccessSpecifier Access) - : FieldTypeInfo(Type, Field, IT, Name), Access(Access) {} + : FieldTypeInfo(Type, Field, IT, Path, Name), Access(Access) {} MemberTypeInfo(llvm::StringRef RefName, llvm::StringRef Name, AccessSpecifier Access) : FieldTypeInfo(RefName, Name), Access(Access) {} + MemberTypeInfo(llvm::StringRef RefName, StringRef Path, llvm::StringRef Name, + AccessSpecifier Access) + : FieldTypeInfo(RefName, Path, Name), Access(Access) {} bool operator==(const MemberTypeInfo &Other) const { return std::tie(Type, Name, Access) == @@ -220,6 +233,8 @@ llvm::SmallVector Namespace; // List of parent namespaces for this decl. std::vector Description; // Comment description of this decl. + llvm::SmallString<128> Path; // Path of directory where the clang-doc + // generated file will be saved void mergeBase(Info &&I); bool mergeable(const Info &Other); @@ -334,6 +349,7 @@ struct ClangDocContext { tooling::ExecutionContext *ECtx; bool PublicOnly; + std::string OutDirectory; }; } // namespace doc Index: clang-tools-extra/clang-doc/Representation.cpp =================================================================== --- clang-tools-extra/clang-doc/Representation.cpp +++ clang-tools-extra/clang-doc/Representation.cpp @@ -118,6 +118,8 @@ USR = Other.USR; if (Name == "") Name = Other.Name; + if (Path == "") + Path = Other.Path; if (Namespace.empty()) Namespace = std::move(Other.Namespace); // Unconditionally extend the description, since each decl may have a comment. Index: clang-tools-extra/clang-doc/Serialize.h =================================================================== --- clang-tools-extra/clang-doc/Serialize.h +++ clang-tools-extra/clang-doc/Serialize.h @@ -38,19 +38,19 @@ // nullptr. std::pair, std::unique_ptr> emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool PublicOnly); + StringRef File, bool PublicOnly, StringRef OutDirectory); std::pair, std::unique_ptr> emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool PublicOnly); + StringRef File, bool PublicOnly, StringRef OutDirectory); std::pair, std::unique_ptr> emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool PublicOnly); + StringRef File, bool PublicOnly, StringRef OutDirectory); std::pair, std::unique_ptr> emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool PublicOnly); + StringRef File, bool PublicOnly, StringRef OutDirectory); std::pair, std::unique_ptr> emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool PublicOnly); + StringRef File, bool PublicOnly, StringRef OutDirectory); // Function to hash a given USR value for storage. // As USRs (Unified Symbol Resolution) could be large, especially for functions Index: clang-tools-extra/clang-doc/Serialize.cpp =================================================================== --- clang-tools-extra/clang-doc/Serialize.cpp +++ clang-tools-extra/clang-doc/Serialize.cpp @@ -24,6 +24,42 @@ return llvm::SHA1::hash(arrayRefFromStringRef(USR)); } +template +static void +populateParentNamespaces(llvm::SmallVector &Namespaces, + const T *D, bool &IsAnonymousNamespace); + +// A function to extract the appropriate directory name for a given info's +// documentation. The path returned is a composite of the parent namespaces. +// +// Example: Given the below, the diretory path for class C info will be +// /A/B +// +// namespace A { +// namesapce B { +// +// class C {}; +// +// } +// } +llvm::SmallString<128> +getInfoOutputFile(llvm::StringRef Root, + const 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); + return Path; +} + +llvm::SmallString<128> getInfoOutputFile(llvm::StringRef Root, const Decl *D) { + llvm::SmallVector Namespaces; + bool B = true; + populateParentNamespaces(Namespaces, D, B); + return getInfoOutputFile(Root, Namespaces); +} + class ClangDocCommentVisitor : public ConstCommentVisitor { public: @@ -194,7 +230,8 @@ return false; // otherwise, linkage is some form of internal linkage } -static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly) { +static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, + llvm::StringRef OutDirectory) { for (const FieldDecl *F : D->fields()) { if (PublicOnly && !isPublic(F->getAccessUnsafe(), F->getLinkageInternal())) continue; @@ -203,13 +240,15 @@ // valid, as opposed to an assert. if (const auto *N = dyn_cast(T)) { I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(), - InfoType::IT_enum, F->getNameAsString(), - N->getAccessUnsafe()); + InfoType::IT_enum, + getInfoOutputFile(OutDirectory, N), + F->getNameAsString(), N->getAccessUnsafe()); continue; } else if (const auto *N = dyn_cast(T)) { I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(), - InfoType::IT_record, F->getNameAsString(), - N->getAccessUnsafe()); + InfoType::IT_record, + getInfoOutputFile(OutDirectory, N), + F->getNameAsString(), N->getAccessUnsafe()); continue; } } @@ -223,16 +262,19 @@ I.Members.emplace_back(E->getNameAsString()); } -static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { +static void parseParameters(FunctionInfo &I, const FunctionDecl *D, + llvm::StringRef OutDirectory) { for (const ParmVarDecl *P : D->parameters()) { if (const auto *T = getDeclForType(P->getOriginalType())) { if (const auto *N = dyn_cast(T)) { - I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(), - InfoType::IT_enum, P->getNameAsString()); + I.Params.emplace_back( + getUSRForDecl(N), N->getNameAsString(), InfoType::IT_enum, + getInfoOutputFile(OutDirectory, N), P->getNameAsString()); continue; } else if (const auto *N = dyn_cast(T)) { - I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(), - InfoType::IT_record, P->getNameAsString()); + I.Params.emplace_back( + getUSRForDecl(N), N->getNameAsString(), InfoType::IT_record, + getInfoOutputFile(OutDirectory, N), P->getNameAsString()); continue; } } @@ -241,7 +283,8 @@ } } -static void parseBases(RecordInfo &I, const CXXRecordDecl *D) { +static void parseBases(RecordInfo &I, const CXXRecordDecl *D, + llvm::StringRef OutDirectory) { // Don't parse bases if this isn't a definition. if (!D->isThisDeclarationADefinition()) return; @@ -254,14 +297,16 @@ InfoType::IT_record); } else if (const RecordDecl *P = getDeclForType(B.getType())) I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(), - InfoType::IT_record); + InfoType::IT_record, + getInfoOutputFile(OutDirectory, P)); else I.Parents.emplace_back(B.getType().getAsString()); } for (const CXXBaseSpecifier &B : D->vbases()) { if (const auto *P = getDeclForType(B.getType())) I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(), - InfoType::IT_record); + InfoType::IT_record, + getInfoOutputFile(OutDirectory, P)); else I.VirtualParents.emplace_back(B.getType().getAsString()); } @@ -270,14 +315,14 @@ template static void populateParentNamespaces(llvm::SmallVector &Namespaces, - const T *D, bool &IsAnonymousNamespace) { + const T *D, bool &IsInAnonymousNamespace) { const auto *DC = dyn_cast(D); while ((DC = DC->getParent())) { if (const auto *N = dyn_cast(DC)) { std::string Namespace; if (N->isAnonymousNamespace()) { Namespace = "@nonymous_namespace"; - IsAnonymousNamespace = true; + IsInAnonymousNamespace = true; } else Namespace = N->getNameAsString(); Namespaces.emplace_back(getUSRForDecl(N), Namespace, @@ -320,24 +365,27 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, const FullComment *FC, int LineNumber, StringRef Filename, - bool &IsInAnonymousNamespace) { + bool &IsInAnonymousNamespace, + llvm::StringRef OutDirectory) { populateSymbolInfo(I, D, FC, LineNumber, Filename, IsInAnonymousNamespace); if (const auto *T = getDeclForType(D->getReturnType())) { if (dyn_cast(T)) I.ReturnType = - TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_enum); + TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_enum, + getInfoOutputFile(OutDirectory, T)); else if (dyn_cast(T)) I.ReturnType = - TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_record); + TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_record, + getInfoOutputFile(OutDirectory, T)); } else { I.ReturnType = TypeInfo(D->getReturnType().getAsString()); } - parseParameters(I, D); + parseParameters(I, D, OutDirectory); } std::pair, std::unique_ptr> emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool PublicOnly) { + llvm::StringRef File, bool PublicOnly, llvm::StringRef OutDirectory) { auto I = llvm::make_unique(); bool IsInAnonymousNamespace = false; populateInfo(*I, D, FC, IsInAnonymousNamespace); @@ -347,21 +395,25 @@ I->Name = D->isAnonymousNamespace() ? llvm::SmallString<16>("@nonymous_namespace") : I->Name; + I->Path = getInfoOutputFile(OutDirectory, I->Namespace); if (I->Namespace.empty() && I->USR == SymbolID()) return {std::unique_ptr{std::move(I)}, nullptr}; - SymbolID ParentUSR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR; - - auto Parent = llvm::make_unique(); - Parent->USR = ParentUSR; - Parent->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace); + auto ParentI = llvm::make_unique(); + ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR; + ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, + InfoType::IT_namespace); + if (!I->Namespace.empty()) + ParentI->Namespace = llvm::SmallVector( + I->Namespace.begin() + 1, I->Namespace.end()); + ParentI->Path = getInfoOutputFile(OutDirectory, ParentI->Namespace); return {std::unique_ptr{std::move(I)}, - std::unique_ptr{std::move(Parent)}}; + std::unique_ptr{std::move(ParentI)}}; } std::pair, std::unique_ptr> emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool PublicOnly) { + llvm::StringRef File, bool PublicOnly, llvm::StringRef OutDirectory) { auto I = llvm::make_unique(); bool IsInAnonymousNamespace = false; populateSymbolInfo(*I, D, FC, LineNumber, File, IsInAnonymousNamespace); @@ -370,37 +422,45 @@ return {}; I->TagType = D->getTagKind(); - parseFields(*I, D, PublicOnly); + parseFields(*I, D, PublicOnly, OutDirectory); if (const auto *C = dyn_cast(D)) { if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) { I->Name = TD->getNameAsString(); I->IsTypeDef = true; } - parseBases(*I, C); + parseBases(*I, C, OutDirectory); } + I->Path = getInfoOutputFile(OutDirectory, I->Namespace); if (I->Namespace.empty()) { - auto Parent = llvm::make_unique(); - Parent->USR = SymbolID(); - Parent->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record); + auto ParentI = llvm::make_unique(); + ParentI->USR = SymbolID(); + ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record); + ParentI->Path = getInfoOutputFile(OutDirectory, ParentI->Namespace); return {std::unique_ptr{std::move(I)}, - std::unique_ptr{std::move(Parent)}}; + std::unique_ptr{std::move(ParentI)}}; } switch (I->Namespace[0].RefType) { case InfoType::IT_namespace: { - auto Parent = llvm::make_unique(); - Parent->USR = I->Namespace[0].USR; - Parent->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record); + auto ParentI = llvm::make_unique(); + ParentI->USR = I->Namespace[0].USR; + ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record); + ParentI->Namespace = llvm::SmallVector( + I->Namespace.begin() + 1, I->Namespace.end()); + ParentI->Path = getInfoOutputFile(OutDirectory, ParentI->Namespace); return {std::unique_ptr{std::move(I)}, - std::unique_ptr{std::move(Parent)}}; + std::unique_ptr{std::move(ParentI)}}; } case InfoType::IT_record: { - auto Parent = llvm::make_unique(); - Parent->USR = I->Namespace[0].USR; - Parent->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record); + auto ParentI = llvm::make_unique(); + ParentI->USR = I->Namespace[0].USR; + ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record); + ParentI->Namespace = llvm::SmallVector( + I->Namespace.begin() + 1, I->Namespace.end()); + ParentI->Path = getInfoOutputFile(OutDirectory, ParentI->Namespace); return {std::unique_ptr{std::move(I)}, - std::unique_ptr{std::move(Parent)}}; + std::unique_ptr{std::move(ParentI)}}; } default: llvm_unreachable("Invalid reference type"); @@ -409,10 +469,11 @@ std::pair, std::unique_ptr> emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool PublicOnly) { + llvm::StringRef File, bool PublicOnly, llvm::StringRef OutDirectory) { FunctionInfo Func; bool IsInAnonymousNamespace = false; - populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace); + populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace, + OutDirectory); if (PublicOnly && ((IsInAnonymousNamespace || !isPublic(D->getAccess(), D->getLinkageInternal())))) return {}; @@ -420,22 +481,27 @@ Func.Access = clang::AccessSpecifier::AS_none; // Wrap in enclosing scope - auto I = llvm::make_unique(); + auto ParentI = llvm::make_unique(); if (!Func.Namespace.empty()) - I->USR = Func.Namespace[0].USR; + ParentI->USR = Func.Namespace[0].USR; else - I->USR = SymbolID(); - I->ChildFunctions.emplace_back(std::move(Func)); - // Info es wrapped in its parent scope so it's returned in the second position - return {nullptr, std::unique_ptr{std::move(I)}}; + ParentI->USR = SymbolID(); + ParentI->ChildFunctions.emplace_back(std::move(Func)); + if (!Func.Namespace.empty()) + ParentI->Namespace = llvm::SmallVector( + Func.Namespace.begin(), Func.Namespace.end() - 1); + ParentI->Path = getInfoOutputFile(OutDirectory, ParentI->Namespace); + // Info is wrapped in its parent scope so it's returned in the second position + return {nullptr, std::unique_ptr{std::move(ParentI)}}; } std::pair, std::unique_ptr> emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool PublicOnly) { + llvm::StringRef File, bool PublicOnly, llvm::StringRef OutDirectory) { FunctionInfo Func; bool IsInAnonymousNamespace = false; - populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace); + populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace, + OutDirectory); if (PublicOnly && ((IsInAnonymousNamespace || !isPublic(D->getAccess(), D->getLinkageInternal())))) return {}; @@ -455,16 +521,20 @@ Func.Access = D->getAccess(); // Wrap in enclosing scope - auto I = llvm::make_unique(); - I->USR = ParentUSR; - I->ChildFunctions.emplace_back(std::move(Func)); + auto ParentI = llvm::make_unique(); + ParentI->USR = ParentUSR; + ParentI->ChildFunctions.emplace_back(std::move(Func)); + if (!Func.Namespace.empty()) + ParentI->Namespace = llvm::SmallVector( + Func.Namespace.begin() + 1, Func.Namespace.end()); + ParentI->Path = getInfoOutputFile(OutDirectory, ParentI->Namespace); // Info is wrapped in its parent scope so it's returned in the second position - return {nullptr, std::unique_ptr{std::move(I)}}; + return {nullptr, std::unique_ptr{std::move(ParentI)}}; } std::pair, std::unique_ptr> emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool PublicOnly) { + llvm::StringRef File, bool PublicOnly, llvm::StringRef OutDirectory) { EnumInfo Enum; bool IsInAnonymousNamespace = false; populateSymbolInfo(Enum, D, FC, LineNumber, File, IsInAnonymousNamespace); @@ -479,20 +549,26 @@ if (!Enum.Namespace.empty()) { switch (Enum.Namespace[0].RefType) { case InfoType::IT_namespace: { - auto I = llvm::make_unique(); - I->USR = Enum.Namespace[0].USR; - I->ChildEnums.emplace_back(std::move(Enum)); + auto ParentI = llvm::make_unique(); + ParentI->USR = Enum.Namespace[0].USR; + ParentI->ChildEnums.emplace_back(std::move(Enum)); + ParentI->Namespace = llvm::SmallVector( + Enum.Namespace.begin() + 1, Enum.Namespace.end()); + ParentI->Path = getInfoOutputFile(OutDirectory, ParentI->Namespace); // Info is wrapped in its parent scope so it's returned in the second // position - return {nullptr, std::unique_ptr{std::move(I)}}; + return {nullptr, std::unique_ptr{std::move(ParentI)}}; } case InfoType::IT_record: { - auto I = llvm::make_unique(); - I->USR = Enum.Namespace[0].USR; - I->ChildEnums.emplace_back(std::move(Enum)); + auto ParentI = llvm::make_unique(); + ParentI->USR = Enum.Namespace[0].USR; + ParentI->ChildEnums.emplace_back(std::move(Enum)); + ParentI->Namespace = llvm::SmallVector( + Enum.Namespace.begin() + 1, Enum.Namespace.end()); + ParentI->Path = getInfoOutputFile(OutDirectory, ParentI->Namespace); // Info is wrapped in its parent scope so it's returned in the second // position - return {nullptr, std::unique_ptr{std::move(I)}}; + return {nullptr, std::unique_ptr{std::move(ParentI)}}; } default: break; @@ -500,11 +576,12 @@ } // Put in global namespace - auto I = llvm::make_unique(); - I->USR = SymbolID(); - I->ChildEnums.emplace_back(std::move(Enum)); + auto ParentI = llvm::make_unique(); + ParentI->USR = SymbolID(); + ParentI->ChildEnums.emplace_back(std::move(Enum)); + ParentI->Path = getInfoOutputFile(OutDirectory, ParentI->Namespace); // Info is wrapped in its parent scope so it's returned in the second position - return {nullptr, std::unique_ptr{std::move(I)}}; + return {nullptr, std::unique_ptr{std::move(ParentI)}}; } } // namespace serialize Index: clang-tools-extra/clang-doc/tool/ClangDocMain.cpp =================================================================== --- clang-tools-extra/clang-doc/tool/ClangDocMain.cpp +++ clang-tools-extra/clang-doc/tool/ClangDocMain.cpp @@ -110,12 +110,13 @@ return false; } -// A function to extract the appropriate path name for a given info's -// documentation. The path returned is a composite of the parent namespaces as -// directories plus the decl name as the filename. +// A function to extract the appropriate file name for a given info's +// documentation. The path returned is a composite of the direcory and the +// info's name. The directory should have been constructed in the serialization +// phase. // -// Example: Given the below, the path for class C will be < -// root>/A/B/C. +// Example: Given the below, the path for class C will be +// /A/B/C. // // namespace A { // namesapce B { @@ -125,15 +126,7 @@ // } // } llvm::Expected> -getInfoOutputFile(StringRef Root, - llvm::SmallVectorImpl &Namespaces, - StringRef Name, StringRef Ext) { - 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); - +getInfoOutputFile(llvm::SmallString<128> Path, StringRef Name, StringRef Ext) { if (CreateDirectory(Path)) return llvm::make_error("Unable to create directory.\n", llvm::inconvertibleErrorCode()); @@ -197,7 +190,7 @@ // Mapping phase llvm::outs() << "Mapping decls...\n"; clang::doc::ClangDocContext CDCtx = {Exec->get()->getExecutionContext(), - PublicOnly}; + PublicOnly, OutDirectory}; auto Err = Exec->get()->execute(doc::newMapperActionFactory(CDCtx), ArgAdjuster); if (Err) { @@ -223,12 +216,10 @@ } doc::Info *I = Reduced.get().get(); - - auto InfoPath = getInfoOutputFile(OutDirectory, I->Namespace, - I->extractName(), "." + Format); + auto InfoPath = getInfoOutputFile(I->Path, I->extractName(), "." + Format); if (!InfoPath) { llvm::errs() << toString(InfoPath.takeError()) << "\n"; - continue; + return 1; } std::error_code FileErr; llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr, llvm::sys::fs::F_None); Index: clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp +++ clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp @@ -57,7 +57,8 @@

OneFunction

- OneFunction() + OneFunction( + )

Enums

@@ -78,9 +79,10 @@ I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}); I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); - I.Members.emplace_back("int", "X", AccessSpecifier::AS_private); + I.Members.emplace_back("int", "/path/to", "X", AccessSpecifier::AS_private); I.TagType = TagTypeKind::TTK_Class; - I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record); + I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, + llvm::SmallString<128>("/path/to")); I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); @@ -104,11 +106,14 @@ Defined at line 10 of test.cpp

- Inherits from F, G + Inherits from + F + , + G

Members

    -
  • private int X
  • +
  • private int X

Records

    @@ -118,7 +123,8 @@

    OneFunction

    - OneFunction() + OneFunction( + )

    Enums

    @@ -139,8 +145,8 @@ I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}); I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); - I.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); - I.Params.emplace_back("int", "P"); + I.ReturnType = TypeInfo(EmptySID, "float", InfoType::IT_default, "/path/to"); + I.Params.emplace_back("int", "/path/to", "P"); I.IsMethod = true; I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record); @@ -156,7 +162,12 @@

    f

    - void f(int P) + float + + f( + int + P + )

    Defined at line 10 of test.cpp @@ -250,7 +261,15 @@

    f

    - void f(int I, int J) + void + + f( + int + I + , + int + J + )

    Defined at line 10 of test.cpp Index: clang-tools-extra/unittests/clang-doc/SerializeTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/SerializeTest.cpp +++ clang-tools-extra/unittests/clang-doc/SerializeTest.cpp @@ -37,7 +37,7 @@ template bool mapDecl(const T *D) { auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, - /*File=*/"test.cpp", Public); + /*File=*/"test.cpp", Public, ""); if (I.first) EmittedInfos.emplace_back(std::move(I.first)); if (I.second)