Index: clang-tools-extra/trunk/clang-doc/BitcodeReader.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/BitcodeReader.cpp +++ clang-tools-extra/trunk/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: @@ -685,7 +691,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/trunk/clang-doc/BitcodeWriter.h =================================================================== --- clang-tools-extra/trunk/clang-doc/BitcodeWriter.h +++ clang-tools-extra/trunk/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/trunk/clang-doc/BitcodeWriter.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/BitcodeWriter.cpp +++ clang-tools-extra/trunk/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/trunk/clang-doc/Generators.h =================================================================== --- clang-tools-extra/trunk/clang-doc/Generators.h +++ clang-tools-extra/trunk/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/trunk/clang-doc/Generators.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/Generators.cpp +++ clang-tools-extra/trunk/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/trunk/clang-doc/HTMLGenerator.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp +++ clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp @@ -18,13 +18,6 @@ namespace clang { namespace doc { -template ::value>> -static void AppendVector(std::vector &&New, - std::vector &Original) { - std::move(New.begin(), New.end(), std::back_inserter(Original)); -} - namespace { class HTMLTag { @@ -40,6 +33,7 @@ TAG_P, TAG_UL, TAG_LI, + TAG_A, }; HTMLTag() = default; @@ -58,15 +52,22 @@ TagType Value; }; +enum NodeType { + NODE_TEXT, + NODE_TAG, +}; + struct HTMLNode { + HTMLNode(NodeType Type) : Type(Type) {} virtual ~HTMLNode() = default; virtual void Render(llvm::raw_ostream &OS, int IndentationLevel) = 0; + NodeType Type; // Type of node }; struct TextNode : public HTMLNode { - TextNode(llvm::StringRef Text, bool Indented) - : Text(Text), Indented(Indented) {} + TextNode(const Twine &Text, bool Indented = true) + : HTMLNode(NodeType::NODE_TEXT), Text(Text.str()), Indented(Indented) {} std::string Text; // Content of node bool Indented; // Indicates if an indentation must be rendered before the text @@ -75,7 +76,8 @@ struct TagNode : public HTMLNode { TagNode(HTMLTag Tag) - : Tag(Tag), InlineChildren(Tag.HasInlineChildren()), + : HTMLNode(NodeType::NODE_TAG), Tag(Tag), + InlineChildren(Tag.HasInlineChildren()), SelfClosing(Tag.IsSelfClosing()) {} TagNode(HTMLTag Tag, const Twine &Text) : TagNode(Tag) { Children.emplace_back( @@ -121,6 +123,7 @@ case HTMLTag::TAG_P: case HTMLTag::TAG_UL: case HTMLTag::TAG_LI: + case HTMLTag::TAG_A: return false; } llvm_unreachable("Unhandled HTMLTag::TagType"); @@ -134,6 +137,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: @@ -163,6 +167,8 @@ return llvm::SmallString<16>("ul"); case HTMLTag::TAG_LI: return llvm::SmallString<16>("li"); + case HTMLTag::TAG_A: + return llvm::SmallString<16>("a"); } llvm_unreachable("Unhandled HTMLTag::TagType"); } @@ -185,21 +191,87 @@ OS << ">"; if (!InlineChildren) OS << "\n"; - int ChildrenIndentation = InlineChildren ? 0 : IndentationLevel + 1; + bool NewLineRendered = true; for (const auto &C : Children) { + int ChildrenIndentation = + InlineChildren || !NewLineRendered ? 0 : IndentationLevel + 1; C->Render(OS, ChildrenIndentation); - if (!InlineChildren) + if (!InlineChildren && (C == Children.back() || + (C->Type != NodeType::NODE_TEXT || + (&C + 1)->get()->Type != NodeType::NODE_TEXT))) { OS << "\n"; + NewLineRendered = true; + } else + NewLineRendered = false; } if (!InlineChildren) OS.indent(IndentationLevel * 2); OS << ""; } +template ::value>> +static void AppendVector(std::vector &&New, + std::vector &Original) { + std::move(New.begin(), New.end(), std::back_inserter(Original)); +} + +// Compute the relative path that names the file path relative to the given +// directory. +static SmallString<128> computeRelativePath(StringRef FilePath, + StringRef Directory) { + StringRef Path = FilePath; + while (!Path.empty()) { + if (Directory == Path) + return FilePath.substr(Path.size()); + Path = llvm::sys::path::parent_path(Path); + } + + StringRef Dir = Directory; + SmallString<128> Result; + while (!Dir.empty()) { + if (Dir == FilePath) + break; + Dir = llvm::sys::path::parent_path(Dir); + llvm::sys::path::append(Result, ".."); + } + llvm::sys::path::append(Result, FilePath.substr(Dir.size())); + return Result; +} + // 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, + StringRef CurrentDirectory) { + if (Type.Path.empty()) + return llvm::make_unique(Type.Name); + llvm::SmallString<128> Path = + computeRelativePath(Type.Path, CurrentDirectory); + llvm::sys::path::append(Path, Type.Name + ".html"); + return genLink(Type.Name, Path); +} + +static std::vector> +genReferenceList(const llvm::SmallVectorImpl &Refs, + const StringRef &CurrentDirectory) { + std::vector> Out; + for (const auto &R : Refs) { + if (&R != Refs.begin()) + Out.emplace_back(llvm::make_unique(", ")); + Out.emplace_back(genTypeReference(R, CurrentDirectory)); + } + return Out; +} + static std::vector> genHTML(const EnumInfo &I); -static std::vector> genHTML(const FunctionInfo &I); +static std::vector> genHTML(const FunctionInfo &I, + StringRef ParentInfoDir); static std::vector> genEnumsBlock(const std::vector &Enums) { @@ -229,7 +301,8 @@ } static std::vector> -genFunctionsBlock(const std::vector &Functions) { +genFunctionsBlock(const std::vector &Functions, + StringRef ParentInfoDir) { if (Functions.empty()) return {}; @@ -238,14 +311,15 @@ Out.emplace_back(llvm::make_unique(HTMLTag::TAG_DIV)); auto &DivBody = Out.back(); for (const auto &F : Functions) { - std::vector> Nodes = genHTML(F); + std::vector> Nodes = genHTML(F, ParentInfoDir); AppendVector(std::move(Nodes), DivBody->Children); } return Out; } static std::vector> -genRecordMembersBlock(const llvm::SmallVector &Members) { +genRecordMembersBlock(const llvm::SmallVector &Members, + StringRef ParentInfoDir) { if (Members.empty()) return {}; @@ -257,8 +331,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, ParentInfoDir)); + LIBody->Children.emplace_back(llvm::make_unique(" " + M.Name)); + ULBody->Children.emplace_back(std::move(LIBody)); } return Out; } @@ -346,25 +423,35 @@ return Out; } -static std::vector> genHTML(const FunctionInfo &I) { +static std::vector> genHTML(const FunctionInfo &I, + StringRef ParentInfoDir) { 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, ParentInfoDir)); + 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, ParentInfoDir)); + 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())); @@ -398,7 +485,7 @@ AppendVector(std::move(ChildRecords), Out); std::vector> ChildFunctions = - genFunctionsBlock(I.ChildFunctions); + genFunctionsBlock(I.ChildFunctions, I.Path); AppendVector(std::move(ChildFunctions), Out); std::vector> ChildEnums = genEnumsBlock(I.ChildEnums); @@ -420,29 +507,34 @@ if (!I.Description.empty()) Out.emplace_back(genHTML(I.Description)); - std::string Parents = genReferenceList(I.Parents); - std::string VParents = genReferenceList(I.VirtualParents); + std::vector> Parents = + genReferenceList(I.Parents, I.Path); + std::vector> VParents = + genReferenceList(I.VirtualParents, I.Path); if (!Parents.empty() || !VParents.empty()) { + Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P)); + auto &PBody = Out.back(); + PBody->Children.emplace_back(llvm::make_unique("Inherits from ")); if (Parents.empty()) - Out.emplace_back(llvm::make_unique(HTMLTag::TAG_P, - "Inherits from " + VParents)); + AppendVector(std::move(VParents), 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)); + AppendVector(std::move(Parents), PBody->Children); + else { + AppendVector(std::move(Parents), PBody->Children); + PBody->Children.emplace_back(llvm::make_unique(", ")); + AppendVector(std::move(VParents), PBody->Children); + } } std::vector> Members = - genRecordMembersBlock(I.Members); + genRecordMembersBlock(I.Members, I.Path); AppendVector(std::move(Members), Out); std::vector> ChildRecords = genReferencesBlock(I.ChildRecords, "Records"); AppendVector(std::move(ChildRecords), Out); std::vector> ChildFunctions = - genFunctionsBlock(I.ChildFunctions); + genFunctionsBlock(I.ChildFunctions, I.Path); AppendVector(std::move(ChildFunctions), Out); std::vector> ChildEnums = genEnumsBlock(I.ChildEnums); @@ -492,7 +584,7 @@ } case InfoType::IT_function: { std::vector> Nodes = - genHTML(*static_cast(I)); + genHTML(*static_cast(I), ""); AppendVector(std::move(Nodes), MainContentNode->Children); break; } Index: clang-tools-extra/trunk/clang-doc/MDGenerator.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/MDGenerator.cpp +++ clang-tools-extra/trunk/clang-doc/MDGenerator.cpp @@ -28,6 +28,18 @@ return "**" + Text.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/trunk/clang-doc/Representation.h =================================================================== --- clang-tools-extra/trunk/clang-doc/Representation.h +++ clang-tools-extra/trunk/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); Index: clang-tools-extra/trunk/clang-doc/Representation.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/Representation.cpp +++ clang-tools-extra/trunk/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/trunk/clang-doc/Serialize.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/Serialize.cpp +++ clang-tools-extra/trunk/clang-doc/Serialize.cpp @@ -24,6 +24,43 @@ return llvm::SHA1::hash(arrayRefFromStringRef(USR)); } +template +static void +populateParentNamespaces(llvm::SmallVector &Namespaces, + const T *D, bool &IsAnonymousNamespace); + +// A function to extract the appropriate relative path 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> +getInfoRelativePath(const llvm::SmallVectorImpl &Namespaces) { + std::error_code OK; + llvm::SmallString<128> Path; + for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R) + llvm::sys::path::append(Path, R->Name); + return Path; +} + +llvm::SmallString<128> getInfoRelativePath(const Decl *D) { + llvm::SmallVector Namespaces; + // The third arg in populateParentNamespaces is a boolean passed by reference, + // its value is not relevant in here so it's not used anywhere besides the + // function call + bool B = true; + populateParentNamespaces(Namespaces, D, B); + return getInfoRelativePath(Namespaces); +} + class ClangDocCommentVisitor : public ConstCommentVisitor { public: @@ -203,13 +240,13 @@ // 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, getInfoRelativePath(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, getInfoRelativePath(N), + F->getNameAsString(), N->getAccessUnsafe()); continue; } } @@ -228,11 +265,13 @@ 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()); + InfoType::IT_enum, getInfoRelativePath(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()); + InfoType::IT_record, getInfoRelativePath(N), + P->getNameAsString()); continue; } } @@ -254,14 +293,15 @@ 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, getInfoRelativePath(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, + getInfoRelativePath(P)); else I.VirtualParents.emplace_back(B.getType().getAsString()); } @@ -270,14 +310,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, @@ -324,11 +364,11 @@ 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); + I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(), + InfoType::IT_enum, getInfoRelativePath(T)); else if (dyn_cast(T)) - I.ReturnType = - TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_record); + I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(), + InfoType::IT_record, getInfoRelativePath(T)); } else { I.ReturnType = TypeInfo(D->getReturnType().getAsString()); } @@ -347,16 +387,18 @@ I->Name = D->isAnonymousNamespace() ? llvm::SmallString<16>("@nonymous_namespace") : I->Name; + I->Path = getInfoRelativePath(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->Path = getInfoRelativePath(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> @@ -378,32 +420,34 @@ } parseBases(*I, C); } + I->Path = getInfoRelativePath(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 = getInfoRelativePath(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); 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); 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"); + llvm_unreachable("Invalid reference type for parent namespace"); } } @@ -420,14 +464,16 @@ 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(); + if (Func.Namespace.empty()) + ParentI->Path = getInfoRelativePath(ParentI->Namespace); + ParentI->ChildFunctions.emplace_back(std::move(Func)); + // 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> @@ -455,11 +501,13 @@ 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; + if (Func.Namespace.empty()) + ParentI->Path = getInfoRelativePath(ParentI->Namespace); + ParentI->ChildFunctions.emplace_back(std::move(Func)); // 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> @@ -475,36 +523,38 @@ Enum.Scoped = D->isScoped(); parseEnumerators(Enum, D); - // Wrap in enclosing scope - 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)); - // Info is wrapped in its parent scope so it's returned in the second - // position - return {nullptr, std::unique_ptr{std::move(I)}}; - } - case InfoType::IT_record: { - auto I = llvm::make_unique(); - I->USR = Enum.Namespace[0].USR; - I->ChildEnums.emplace_back(std::move(Enum)); - // Info is wrapped in its parent scope so it's returned in the second - // position - return {nullptr, std::unique_ptr{std::move(I)}}; - } - default: - break; - } + // Put in global namespace + if (Enum.Namespace.empty()) { + auto ParentI = llvm::make_unique(); + ParentI->USR = SymbolID(); + ParentI->ChildEnums.emplace_back(std::move(Enum)); + ParentI->Path = getInfoRelativePath(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)}}; } - // Put in global namespace - auto I = llvm::make_unique(); - I->USR = SymbolID(); - I->ChildEnums.emplace_back(std::move(Enum)); - // Info is wrapped in its parent scope so it's returned in the second position - return {nullptr, std::unique_ptr{std::move(I)}}; + // Wrap in enclosing scope + switch (Enum.Namespace[0].RefType) { + case InfoType::IT_namespace: { + auto ParentI = llvm::make_unique(); + ParentI->USR = Enum.Namespace[0].USR; + ParentI->ChildEnums.emplace_back(std::move(Enum)); + // Info is wrapped in its parent scope so it's returned in the second + // position + return {nullptr, std::unique_ptr{std::move(ParentI)}}; + } + case InfoType::IT_record: { + auto ParentI = llvm::make_unique(); + ParentI->USR = Enum.Namespace[0].USR; + ParentI->ChildEnums.emplace_back(std::move(Enum)); + // Info is wrapped in its parent scope so it's returned in the second + // position + return {nullptr, std::unique_ptr{std::move(ParentI)}}; + } + default: + llvm_unreachable("Invalid reference type for parent namespace"); + } } } // namespace serialize Index: clang-tools-extra/trunk/clang-doc/YAMLGenerator.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/YAMLGenerator.cpp +++ clang-tools-extra/trunk/clang-doc/YAMLGenerator.cpp @@ -113,6 +113,7 @@ static void InfoMapping(IO &IO, Info &I) { IO.mapRequired("USR", I.USR); IO.mapOptional("Name", I.Name, SmallString<16>()); + IO.mapOptional("Path", I.Path, SmallString<128>()); IO.mapOptional("Namespace", I.Namespace, llvm::SmallVector()); IO.mapOptional("Description", I.Description); } @@ -154,6 +155,7 @@ IO.mapOptional("Type", Ref.RefType, InfoType::IT_default); IO.mapOptional("Name", Ref.Name, SmallString<16>()); IO.mapOptional("USR", Ref.USR, SymbolID()); + IO.mapOptional("Path", Ref.Path, SmallString<128>()); } }; Index: clang-tools-extra/trunk/clang-doc/tool/ClangDocMain.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/tool/ClangDocMain.cpp +++ clang-tools-extra/trunk/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 output directory, the +// info's relative path and name and the extension. The relative path 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 { @@ -124,16 +125,14 @@ // // } // } -llvm::Expected> -getInfoOutputFile(StringRef Root, - llvm::SmallVectorImpl &Namespaces, - StringRef Name, StringRef Ext) { +llvm::Expected> getInfoOutputFile(StringRef Root, + StringRef RelativePath, + 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); - + llvm::sys::path::append(Path, RelativePath); if (CreateDirectory(Path)) return llvm::make_error("Unable to create directory.\n", llvm::inconvertibleErrorCode()); @@ -223,12 +222,11 @@ } doc::Info *I = Reduced.get().get(); - - auto InfoPath = getInfoOutputFile(OutDirectory, I->Namespace, - I->extractName(), "." + Format); + auto InfoPath = getInfoOutputFile(OutDirectory, 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/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp +++ clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp @@ -57,7 +57,7 @@

OneFunction

- OneFunction() + OneFunction()

Enums

@@ -73,14 +73,16 @@ TEST(HTMLGeneratorTest, emitRecordHTML) { RecordInfo I; I.Name = "r"; + I.Path = "X/Y/Z"; I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); 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", "X/Y", "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,13 @@ 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 +122,7 @@

    OneFunction

    - OneFunction() + OneFunction()

    Enums

    @@ -139,8 +143,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 +160,10 @@

    f

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

    Defined at line 10 of test.cpp @@ -261,8 +268,7 @@ Brief description.

    - Extended description that - continues onto the next line. + Extended description that continues onto the next line.

    Index: clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp +++ clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp @@ -25,6 +25,7 @@ TEST(YAMLGeneratorTest, emitNamespaceYAML) { NamespaceInfo I; I.Name = "Namespace"; + I.Path = "path/to/A"; I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace", @@ -45,6 +46,7 @@ R"raw(--- USR: '0000000000000000000000000000000000000000' Name: 'Namespace' +Path: 'path/to/A' Namespace: - Type: Namespace Name: 'A' @@ -69,15 +71,18 @@ TEST(YAMLGeneratorTest, emitRecordYAML) { RecordInfo I; I.Name = "r"; + I.Path = "path/to/r"; I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); 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/int", "X", + AccessSpecifier::AS_private); I.TagType = TagTypeKind::TTK_Class; - I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record); - I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); + I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "path/to/F"); + I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record, + "path/to/G"); I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); I.ChildFunctions.emplace_back(); @@ -95,6 +100,7 @@ R"raw(--- USR: '0000000000000000000000000000000000000000' Name: 'r' +Path: 'path/to/r' Namespace: - Type: Namespace Name: 'A' @@ -108,14 +114,17 @@ Members: - Type: Name: 'int' + Path: 'path/to/int' Name: 'X' Access: Private Parents: - Type: Record Name: 'F' + Path: 'path/to/F' VirtualParents: - Type: Record Name: 'G' + Path: 'path/to/G' ChildRecords: - Type: Record Name: 'ChildStruct' @@ -139,8 +148,9 @@ 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, "void", InfoType::IT_default, "path/to/void"); + I.Params.emplace_back("int", "path/to/int", "P"); I.IsMethod = true; I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record); @@ -170,10 +180,12 @@ Params: - Type: Name: 'int' + Path: 'path/to/int' Name: 'P' ReturnType: Type: Name: 'void' + Path: 'path/to/void' ... )raw"; EXPECT_EQ(Expected, Actual.str());