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: @@ -284,6 +288,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: @@ -656,7 +662,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,12 +100,14 @@ ENUM_SCOPED, RECORD_USR, RECORD_NAME, + RECORD_PATH, RECORD_DEFLOCATION, RECORD_LOCATION, RECORD_TAG_TYPE, 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}}, @@ -168,6 +170,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) { @@ -198,18 +201,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_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION, + RECORD_LOCATION, RECORD_TAG_TYPE}}, // 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 @@ -380,6 +385,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); } @@ -427,6 +433,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) @@ -462,6 +469,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,13 +39,13 @@ 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). if (I) CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I->USR)), - serialize::serialize(I)); + serialize::serialize(I)); return true; } Index: clang-tools-extra/clang-doc/Representation.h =================================================================== --- clang-tools-extra/clang-doc/Representation.h +++ clang-tools-extra/clang-doc/Representation.h @@ -74,8 +74,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) == @@ -87,6 +90,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 @@ -94,7 +99,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; } @@ -104,11 +112,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); @@ -120,12 +130,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) == @@ -171,6 +184,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); @@ -282,6 +297,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 @@ -117,6 +117,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 @@ -28,15 +28,20 @@ namespace serialize { std::unique_ptr emitInfo(const NamespaceDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); + int LineNumber, StringRef File, bool PublicOnly, + StringRef OutDirectory); std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); + int LineNumber, StringRef File, bool PublicOnly, + StringRef OutDirectory); std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); + int LineNumber, StringRef File, bool PublicOnly, + StringRef OutDirectory); std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); + int LineNumber, StringRef File, bool PublicOnly, + StringRef OutDirectory); std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); + int LineNumber, 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,41 @@ return llvm::SHA1::hash(arrayRefFromStringRef(USR)); } +template +static void +populateParentNamespaces(llvm::SmallVector &Namespaces, + const T *D); + +// 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(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(StringRef Root, const Decl *D) { + llvm::SmallVector Namespaces; + populateParentNamespaces(Namespaces, D); + return getInfoOutputFile(Root, Namespaces); +} + class ClangDocCommentVisitor : public ConstCommentVisitor { public: @@ -195,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, + StringRef OutDirectory) { for (const FieldDecl *F : D->fields()) { if (PublicOnly && !isPublic(F->getAccessUnsafe(), F->getLinkageInternal())) continue; @@ -204,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; } } @@ -224,16 +262,20 @@ I.Members.emplace_back(E->getNameAsString()); } -static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { +static void parseParameters(FunctionInfo &I, const FunctionDecl *D, + 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; } } @@ -242,24 +284,27 @@ } } -static void parseBases(RecordInfo &I, const CXXRecordDecl *D) { +static void parseBases(RecordInfo &I, const CXXRecordDecl *D, + StringRef OutDirectory) { // Don't parse bases if this isn't a definition. if (!D->isThisDeclarationADefinition()) return; for (const CXXBaseSpecifier &B : D->bases()) { if (B.isVirtual()) continue; - if (const auto *P = getDeclForType(B.getType())) + if (const auto *P = getDeclForType(B.getType())) { I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(), - InfoType::IT_record); - else + 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())) + if (const auto *P = getDeclForType(B.getType())) { I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(), - InfoType::IT_record); - else + InfoType::IT_record, + getInfoOutputFile(OutDirectory, P)); + } else I.VirtualParents.emplace_back(B.getType().getAsString()); } } @@ -308,53 +353,57 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, const FullComment *FC, int LineNumber, - StringRef Filename) { + StringRef Filename, StringRef OutDirectory) { populateSymbolInfo(I, D, FC, LineNumber, Filename); 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::unique_ptr emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, llvm::StringRef File, - bool PublicOnly) { + bool PublicOnly, StringRef OutDirectory) { if (PublicOnly && ((D->isAnonymousNamespace()) || !isPublic(D->getAccess(), D->getLinkageInternal()))) return nullptr; auto I = llvm::make_unique(); populateInfo(*I, D, FC); + I->Path = getInfoOutputFile(OutDirectory, I->Namespace); return std::unique_ptr{std::move(I)}; } std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, llvm::StringRef File, - bool PublicOnly) { + bool PublicOnly, StringRef OutDirectory) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) return nullptr; auto I = llvm::make_unique(); populateSymbolInfo(*I, D, FC, LineNumber, File); I->TagType = D->getTagKind(); - parseFields(*I, D, PublicOnly); + parseFields(*I, D, PublicOnly, OutDirectory); if (const auto *C = dyn_cast(D)) - parseBases(*I, C); + parseBases(*I, C, OutDirectory); + I->Path = getInfoOutputFile(OutDirectory, I->Namespace); return std::unique_ptr{std::move(I)}; } std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, llvm::StringRef File, - bool PublicOnly) { + bool PublicOnly, StringRef OutDirectory) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) return nullptr; FunctionInfo Func; - populateFunctionInfo(Func, D, FC, LineNumber, File); + populateFunctionInfo(Func, D, FC, LineNumber, File, OutDirectory); Func.Access = clang::AccessSpecifier::AS_none; // Wrap in enclosing scope @@ -364,16 +413,17 @@ else I->USR = SymbolID(); I->ChildFunctions.emplace_back(std::move(Func)); + I->Path = getInfoOutputFile(OutDirectory, I->Namespace); return std::unique_ptr{std::move(I)}; } std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, llvm::StringRef File, - bool PublicOnly) { + bool PublicOnly, StringRef OutDirectory) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) return nullptr; FunctionInfo Func; - populateFunctionInfo(Func, D, FC, LineNumber, File); + populateFunctionInfo(Func, D, FC, LineNumber, File, OutDirectory); Func.IsMethod = true; SymbolID ParentUSR = getUSRForDecl(D->getParent()); @@ -385,12 +435,13 @@ auto I = llvm::make_unique(); I->USR = ParentUSR; I->ChildFunctions.emplace_back(std::move(Func)); + I->Path = getInfoOutputFile(OutDirectory, I->Namespace); return std::unique_ptr{std::move(I)}; } std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, llvm::StringRef File, - bool PublicOnly) { + bool PublicOnly, StringRef OutDirectory) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) return nullptr; EnumInfo Enum; @@ -422,6 +473,7 @@ auto I = llvm::make_unique(); I->USR = SymbolID(); I->ChildEnums.emplace_back(std::move(Enum)); + I->Path = getInfoOutputFile(OutDirectory, I->Namespace); return std::unique_ptr{std::move(I)}; } 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()); @@ -200,7 +193,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) { @@ -227,11 +220,10 @@ doc::Info *I = Reduced.get().get(); - auto InfoPath = - getInfoOutputFile(OutDirectory, I->Namespace, I->Name, "." + Format); + auto InfoPath = getInfoOutputFile(I->Path, I->Name, "." + 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 @@ bool VisitNamespaceDecl(const NamespaceDecl *D) { auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, - /*File=*/"test.cpp", Public); + /*File=*/"test.cpp", Public, ""); if (I) EmittedInfos.emplace_back(std::move(I)); return true; @@ -48,7 +48,7 @@ if (dyn_cast(D)) return true; auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, - /*File=*/"test.cpp", Public); + /*File=*/"test.cpp", Public, ""); if (I) EmittedInfos.emplace_back(std::move(I)); return true; @@ -56,7 +56,7 @@ bool VisitCXXMethodDecl(const CXXMethodDecl *D) { auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, - /*File=*/"test.cpp", Public); + /*File=*/"test.cpp", Public, ""); if (I) EmittedInfos.emplace_back(std::move(I)); return true; @@ -64,7 +64,7 @@ bool VisitRecordDecl(const RecordDecl *D) { auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, - /*File=*/"test.cpp", Public); + /*File=*/"test.cpp", Public, ""); if (I) EmittedInfos.emplace_back(std::move(I)); return true; @@ -72,7 +72,7 @@ bool VisitEnumDecl(const EnumDecl *D) { auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, - /*File=*/"test.cpp", Public); + /*File=*/"test.cpp", Public, ""); if (I) EmittedInfos.emplace_back(std::move(I)); return true; @@ -142,7 +142,8 @@ E() {} protected: void ProtectedMethod(); -};)raw", 3, /*Public=*/false, Infos); +};)raw", + 3, /*Public=*/false, Infos); RecordInfo *E = InfoAsRecord(Infos[0].get()); RecordInfo ExpectedE(EmptySID, "E");