diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp index a0b334e0be598..738c8b6559eb6 100644 --- a/clang-tools-extra/clang-doc/Representation.cpp +++ b/clang-tools-extra/clang-doc/Representation.cpp @@ -116,7 +116,30 @@ static void reduceChildren(OwningVec &Children, Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; }); if (It == Children.end()) { - Children.push_back(*allocateListNodeTransient(Ptr)); + InfoNode *NewNode = allocateListNodePersistent(Ptr->USR); + NewNode->Ptr->merge(std::move(*Ptr)); + Children.push_back(*NewNode); + } else { + It->Ptr->merge(std::move(*Ptr)); + } + } +} + +template <> +void reduceChildren(OwningVec &Children, + OwningVec &&ChildrenToMerge) { + while (!ChildrenToMerge.empty()) { + Reference *Ptr = ChildrenToMerge.front().Ptr; + ChildrenToMerge.pop_front(); + + auto It = llvm::find_if( + Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; }); + if (It == Children.end()) { + InfoNode *NewNode = allocateListNodePersistent(); + NewNode->Ptr->USR = Ptr->USR; + NewNode->Ptr->RefType = Ptr->RefType; + NewNode->Ptr->merge(std::move(*Ptr)); + Children.push_back(*NewNode); } else { It->Ptr->merge(std::move(*Ptr)); } @@ -129,11 +152,108 @@ static void mergeUnkeyed(OwningVec &Target, OwningVec &&Source) { T *Ptr = Source.front().Ptr; Source.pop_front(); - if (!llvm::any_of(Target, [Ptr](const auto &E) { return *E.Ptr == *Ptr; })) - Target.push_back(*allocateListNodeTransient(Ptr)); + if (!llvm::any_of(Target, + [Ptr](const auto &E) { return *E.Ptr == *Ptr; })) { + Target.push_back(*allocateListNodePersistent(*Ptr)); + } } } +template <> +void mergeUnkeyed(OwningVec &Target, + OwningVec &&Source) { + while (!Source.empty()) { + CommentInfo *Ptr = Source.front().Ptr; + Source.pop_front(); + + if (!llvm::any_of(Target, + [Ptr](const auto &E) { return *E.Ptr == *Ptr; })) { + Target.push_back( + *allocateListNodePersistent(*Ptr, PersistentArena)); + } + } +} + +llvm::Error mergeSingleInfo(doc::OwnedPtr &Reduced, + doc::OwnedPtr &&NewInfo, + llvm::BumpPtrAllocator &Arena) { + if (!Reduced) { + switch (NewInfo->IT) { + case InfoType::IT_namespace: + Reduced = allocatePtr(Arena, NewInfo->USR); + break; + case InfoType::IT_record: + Reduced = allocatePtr(Arena, NewInfo->USR); + break; + case InfoType::IT_enum: + Reduced = allocatePtr(Arena, NewInfo->USR); + break; + case InfoType::IT_function: + Reduced = allocatePtr(Arena, NewInfo->USR); + break; + case InfoType::IT_typedef: + Reduced = allocatePtr(Arena, NewInfo->USR); + break; + case InfoType::IT_concept: + Reduced = allocatePtr(Arena, NewInfo->USR); + break; + case InfoType::IT_variable: + Reduced = allocatePtr(Arena, NewInfo->USR); + break; + case InfoType::IT_friend: + Reduced = allocatePtr(Arena, NewInfo->USR); + break; + default: + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "unknown info type"); + } + } + + if (Reduced->IT != NewInfo->IT) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "info types mismatch"); + + switch (Reduced->IT) { + case InfoType::IT_namespace: + static_cast(getPtr(Reduced)) + ->merge(std::move(*static_cast(getPtr(NewInfo)))); + break; + case InfoType::IT_record: + static_cast(getPtr(Reduced)) + ->merge(std::move(*static_cast(getPtr(NewInfo)))); + break; + case InfoType::IT_enum: + static_cast(getPtr(Reduced)) + ->merge(std::move(*static_cast(getPtr(NewInfo)))); + break; + case InfoType::IT_function: + static_cast(getPtr(Reduced)) + ->merge(std::move(*static_cast(getPtr(NewInfo)))); + break; + case InfoType::IT_typedef: + static_cast(getPtr(Reduced)) + ->merge(std::move(*static_cast(getPtr(NewInfo)))); + break; + case InfoType::IT_concept: + static_cast(getPtr(Reduced)) + ->merge(std::move(*static_cast(getPtr(NewInfo)))); + break; + case InfoType::IT_variable: + static_cast(getPtr(Reduced)) + ->merge(std::move(*static_cast(getPtr(NewInfo)))); + break; + case InfoType::IT_friend: + static_cast(getPtr(Reduced)) + ->merge(std::move(*static_cast(getPtr(NewInfo)))); + break; + default: + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "unknown info type"); + } + + return llvm::Error::success(); +} + // Dispatch function. llvm::Expected> mergeInfos(OwningPtrArray &Values) { if (Values.empty() || !Values[0]) @@ -164,6 +284,20 @@ llvm::Expected> mergeInfos(OwningPtrArray &Values) { llvm_unreachable("unhandled enumerator"); } +TemplateSpecializationInfo::TemplateSpecializationInfo( + const TemplateSpecializationInfo &Other, llvm::BumpPtrAllocator &Arena) + : SpecializationOf(Other.SpecializationOf) { + Params = allocateArray(Other.Params, Arena); +} + +TemplateInfo::TemplateInfo(const TemplateInfo &Other, + llvm::BumpPtrAllocator &Arena) { + Params = allocateArray(Other.Params, Arena); + if (Other.Specialization) + Specialization = TemplateSpecializationInfo(*Other.Specialization, Arena); + Constraints = allocateArray(Other.Constraints, Arena); +} + bool CommentInfo::operator==(const CommentInfo &Other) const { auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName, SelfClosing, Explicit, AttrKeys, AttrValues, Args); @@ -273,10 +407,14 @@ bool Reference::mergeable(const Reference &Other) { void Reference::merge(Reference &&Other) { assert(mergeable(Other)); + assert(RefType != InfoType::IT_default && + "Merging reference with default InfoType"); if (Name.empty()) Name = Other.Name; if (Path.empty()) Path = Other.Path; + if (QualName.empty()) + QualName = Other.QualName; if (DocumentationFileName.empty()) DocumentationFileName = Other.DocumentationFileName; } @@ -291,16 +429,42 @@ void FriendInfo::merge(FriendInfo &&Other) { SymbolInfo::merge(std::move(Other)); } +FriendInfo::FriendInfo(const FriendInfo &Other, llvm::BumpPtrAllocator &Arena) + : SymbolInfo(Other, Arena) { + Ref = Other.Ref; + if (Other.Template) + Template.emplace(*Other.Template, Arena); + if (Other.ReturnType) + ReturnType = Other.ReturnType; + if (!Other.Params.empty()) + Params = allocateArray(Other.Params, Arena); + IsClass = Other.IsClass; +} + +Info::Info(const Info &Other, llvm::BumpPtrAllocator &Arena) + : Path(Other.Path), Name(Other.Name), + DocumentationFileName(Other.DocumentationFileName), USR(Other.USR), + ParentUSR(Other.ParentUSR), IT(Other.IT) { + Namespace = allocateArray(Other.Namespace, Arena); + if (!Other.Description.empty()) { + for (const auto &Desc : Other.Description) { + CommentInfo *NewDesc = allocatePtr(Arena, Desc, Arena); + Description.push_back(*allocateListNode(Arena, NewDesc)); + } + } +} + void Info::mergeBase(Info &&Other) { assert(mergeable(Other)); + assert(IT != InfoType::IT_default && "Merging info with default InfoType"); if (USR == EmptySID) USR = Other.USR; if (Name == "") Name = Other.Name; if (Path == "") Path = Other.Path; - if (Namespace.empty()) - Namespace = std::move(Other.Namespace); + if (Namespace.empty() && !Other.Namespace.empty()) + Namespace = allocateArray(Other.Namespace, PersistentArena); // Unconditionally extend the description, since each decl may have a comment. mergeUnkeyed(Description, std::move(Other.Description)); if (ParentUSR == EmptySID) @@ -313,6 +477,17 @@ bool Info::mergeable(const Info &Other) { return IT == Other.IT && USR == Other.USR; } +SymbolInfo::SymbolInfo(const SymbolInfo &Other, llvm::BumpPtrAllocator &Arena) + : Info(Other, Arena), DefLoc(Other.DefLoc), MangledName(Other.MangledName), + IsStatic(Other.IsStatic) { + if (!Other.Loc.empty()) { + for (const auto &L : Other.Loc) { + Location *NewL = allocatePtr(Arena, L); + Loc.push_back(*allocateListNode(Arena, NewL)); + } + } +} + void SymbolInfo::merge(SymbolInfo &&Other) { assert(mergeable(Other)); if (!DefLoc) @@ -322,6 +497,8 @@ void SymbolInfo::merge(SymbolInfo &&Other) { // Unconditionally extend the list of locations, since we want all of them. mergeUnkeyed(Loc, std::move(Other.Loc)); mergeBase(std::move(Other)); + if (!IsStatic) + IsStatic = Other.IsStatic; } NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path) @@ -343,37 +520,76 @@ void NamespaceInfo::merge(NamespaceInfo &&Other) { RecordInfo::RecordInfo(SymbolID USR, StringRef Name, StringRef Path) : SymbolInfo(InfoType::IT_record, USR, Name, Path) {} +// FIXME: This constructor is currently unsafe for cross-arena copies of +// populated records. Because a default copy of ScopeChildren will shallow-copy +// the intrusive pointers, leading to a use-after-free when the TransientArena +// is reset. Subsequent patches will address this by deep-copying children +// individually via reduceChildren. +RecordInfo::RecordInfo(const RecordInfo &Other, llvm::BumpPtrAllocator &Arena) + : SymbolInfo(Other, Arena), TagType(Other.TagType), + IsTypeDef(Other.IsTypeDef) { + Members = deepCopyArray(Other.Members, Arena); + Parents = allocateArray(Other.Parents, Arena); + VirtualParents = allocateArray(Other.VirtualParents, Arena); + Bases = deepCopyArray(Other.Bases, Arena); + Friends = deepCopyArray(Other.Friends, Arena); +} + +MemberTypeInfo::MemberTypeInfo(const MemberTypeInfo &Other, + llvm::BumpPtrAllocator &Arena) + : FieldTypeInfo(Other), Access(Other.Access), IsStatic(Other.IsStatic) { + if (!Other.Description.empty()) { + for (const auto &Desc : Other.Description) { + CommentInfo *NewDesc = allocatePtr(Arena, Desc, Arena); + Description.push_back(*allocateListNode(Arena, NewDesc)); + } + } +} + void RecordInfo::merge(RecordInfo &&Other) { assert(mergeable(Other)); if (!llvm::to_underlying(TagType)) TagType = Other.TagType; IsTypeDef = IsTypeDef || Other.IsTypeDef; - if (Members.empty()) - Members = std::move(Other.Members); - if (Bases.empty()) - Bases = std::move(Other.Bases); - if (Parents.empty()) - Parents = std::move(Other.Parents); - if (VirtualParents.empty()) - VirtualParents = std::move(Other.VirtualParents); - if (Friends.empty()) - Friends = std::move(Other.Friends); + if (Members.empty() && !Other.Members.empty()) + Members = deepCopyArray(Other.Members, PersistentArena); + if (Bases.empty() && !Other.Bases.empty()) + Bases = deepCopyArray(Other.Bases, PersistentArena); + if (Parents.empty() && !Other.Parents.empty()) + Parents = allocateArray(Other.Parents, PersistentArena); + if (VirtualParents.empty() && !Other.VirtualParents.empty()) + VirtualParents = allocateArray(Other.VirtualParents, PersistentArena); + if (Friends.empty() && !Other.Friends.empty()) + Friends = deepCopyArray(Other.Friends, PersistentArena); // Reduce children if necessary. reduceChildren(Children.Records, std::move(Other.Children.Records)); reduceChildren(Children.Functions, std::move(Other.Children.Functions)); reduceChildren(Children.Enums, std::move(Other.Children.Enums)); reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs)); - if (!Template) - Template = Other.Template; + if (!Template && Other.Template) + Template = TemplateInfo(*Other.Template, PersistentArena); SymbolInfo::merge(std::move(Other)); } +EnumValueInfo::EnumValueInfo(const EnumValueInfo &Other, + llvm::BumpPtrAllocator &Arena) + : Name(Other.Name), Value(Other.Value), ValueExpr(Other.ValueExpr) { + if (!Other.Description.empty()) { + for (const auto &Desc : Other.Description) { + CommentInfo *NewDesc = allocatePtr(Arena, Desc, Arena); + Description.push_back(*allocateListNode(Arena, NewDesc)); + } + } +} + void EnumInfo::merge(EnumInfo &&Other) { assert(mergeable(Other)); if (!Scoped) Scoped = Other.Scoped; - if (Members.empty()) - Members = std::move(Other.Members); + if (!BaseType && Other.BaseType) + BaseType = std::move(Other.BaseType); + if (Members.empty() && !Other.Members.empty()) + Members = deepCopyArray(Other.Members, PersistentArena); SymbolInfo::merge(std::move(Other)); } @@ -387,10 +603,10 @@ void FunctionInfo::merge(FunctionInfo &&Other) { ReturnType = std::move(Other.ReturnType); if (Parent.USR == EmptySID && Parent.Name == "") Parent = std::move(Other.Parent); - if (Params.empty()) - Params = std::move(Other.Params); - if (!Template) - Template = Other.Template; + if (Params.empty() && !Other.Params.empty()) + Params = allocateArray(Other.Params, PersistentArena); + if (!Template && Other.Template) + Template = TemplateInfo(*Other.Template, PersistentArena); SymbolInfo::merge(std::move(Other)); } @@ -400,8 +616,8 @@ void TypedefInfo::merge(TypedefInfo &&Other) { IsUsing = Other.IsUsing; if (Underlying.Type.Name == "") Underlying = Other.Underlying; - if (!Template) - Template = Other.Template; + if (!Template && Other.Template) + Template = TemplateInfo(*Other.Template, PersistentArena); SymbolInfo::merge(std::move(Other)); } @@ -411,10 +627,11 @@ void ConceptInfo::merge(ConceptInfo &&Other) { IsType = Other.IsType; if (ConstraintExpression.empty()) ConstraintExpression = std::move(Other.ConstraintExpression); - if (Template.Constraints.empty()) - Template.Constraints = std::move(Other.Template.Constraints); - if (Template.Params.empty()) - Template.Params = std::move(Other.Template.Params); + if (Template.Constraints.empty() && !Other.Template.Constraints.empty()) + Template.Constraints = + allocateArray(Other.Template.Constraints, PersistentArena); + if (Template.Params.empty() && !Other.Template.Params.empty()) + Template.Params = allocateArray(Other.Template.Params, PersistentArena); SymbolInfo::merge(std::move(Other)); } @@ -429,6 +646,11 @@ void VarInfo::merge(VarInfo &&Other) { BaseRecordInfo::BaseRecordInfo() : RecordInfo() {} +BaseRecordInfo::BaseRecordInfo(const BaseRecordInfo &Other, + llvm::BumpPtrAllocator &Arena) + : RecordInfo(Other, Arena), Access(Other.Access), + IsVirtual(Other.IsVirtual), IsParent(Other.IsParent) {} + BaseRecordInfo::BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path, bool IsVirtual, AccessSpecifier Access, bool IsParent) diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index 638110acd479a..7d84998afd77b 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -52,6 +52,7 @@ class ConcurrentStringPool { ConcurrentStringPool &getGlobalStringPool(); extern thread_local llvm::BumpPtrAllocator TransientArena; +extern thread_local llvm::BumpPtrAllocator PersistentArena; inline StringRef internString(const Twine &T) { if (T.isTriviallyEmpty()) @@ -175,6 +176,15 @@ template InfoNode *allocateListNodeTransient(T *Item) { return allocateListNode(TransientArena, Item); } +template +InfoNode *allocateListNodePersistent(Args &&...args) { + return allocateListNode(PersistentArena, std::forward(args)...); +} + +template InfoNode *allocateListNodePersistent(T *Item) { + return allocateListNode(PersistentArena, Item); +} + // An abstraction for lists that are dynamically managed (inserted/removed). // To be eventually transitioned to llvm::simple_ilist. template using OwningVec = llvm::simple_ilist>; @@ -411,6 +421,10 @@ struct TemplateParamInfo { }; struct TemplateSpecializationInfo { + TemplateSpecializationInfo() = default; + TemplateSpecializationInfo(const TemplateSpecializationInfo &Other, + llvm::BumpPtrAllocator &Arena); + // Indicates the declaration that this specializes. SymbolID SpecializationOf; @@ -430,6 +444,9 @@ struct ConstraintInfo { // Records the template information for a struct or function that is a template // or an explicit template specialization. struct TemplateInfo { + TemplateInfo() = default; + TemplateInfo(const TemplateInfo &Other, llvm::BumpPtrAllocator &Arena); + // May be empty for non-partial specializations. llvm::ArrayRef Params = {}; @@ -461,6 +478,7 @@ struct FieldTypeInfo : public TypeInfo { // Info for member types. struct MemberTypeInfo : public FieldTypeInfo { MemberTypeInfo() = default; + MemberTypeInfo(const MemberTypeInfo &Other, llvm::BumpPtrAllocator &Arena); MemberTypeInfo(const TypeInfo &TI, StringRef Name, AccessSpecifier Access, bool IsStatic = false) : FieldTypeInfo(TI, Name), Access(Access), IsStatic(IsStatic) {} @@ -517,6 +535,7 @@ struct Info { StringRef Name = StringRef(), StringRef Path = StringRef()) : Path(internString(Path)), Name(internString(Name)), USR(USR), IT(IT) {} + Info(const Info &Other, llvm::BumpPtrAllocator &Arena); Info(const Info &Other) = delete; Info(Info &&Other) = default; @@ -579,6 +598,8 @@ struct SymbolInfo : public Info { StringRef Name = StringRef(), StringRef Path = StringRef()) : Info(IT, USR, Name, Path) {} + SymbolInfo(const SymbolInfo &Other, llvm::BumpPtrAllocator &Arena); + void merge(SymbolInfo &&I); bool operator<(const SymbolInfo &Other) const { @@ -607,6 +628,7 @@ struct FriendInfo : public SymbolInfo { FriendInfo(const InfoType IT, const SymbolID &USR, const StringRef Name = StringRef()) : SymbolInfo(IT, USR, Name) {} + FriendInfo(const FriendInfo &Other, llvm::BumpPtrAllocator &Arena); bool mergeable(const FriendInfo &Other); void merge(FriendInfo &&Other); @@ -658,6 +680,8 @@ struct RecordInfo : public SymbolInfo { RecordInfo(SymbolID USR = SymbolID(), StringRef Name = StringRef(), StringRef Path = StringRef()); + RecordInfo(const RecordInfo &Other, llvm::BumpPtrAllocator &Arena); + void merge(RecordInfo &&I); // Type of this record (struct, class, union, interface). @@ -712,6 +736,7 @@ struct TypedefInfo : public SymbolInfo { struct BaseRecordInfo : public RecordInfo { BaseRecordInfo(); + BaseRecordInfo(const BaseRecordInfo &Other, llvm::BumpPtrAllocator &Arena); BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path, bool IsVirtual, AccessSpecifier Access, bool IsParent); @@ -731,6 +756,8 @@ struct EnumValueInfo { : Name(internString(Name)), Value(internString(Value)), ValueExpr(internString(ValueExpr)) {} + EnumValueInfo(const EnumValueInfo &Other, llvm::BumpPtrAllocator &Arena); + bool operator==(const EnumValueInfo &Other) const { return std::tie(Name, Value, ValueExpr) == std::tie(Other.Name, Other.Value, Other.ValueExpr); @@ -806,6 +833,12 @@ struct Index : public Reference { // if they are different. llvm::Expected> mergeInfos(OwningPtrArray &Values); +// Merges a single new Info into an existing Reduced Info (allocating it if +// needed). +llvm::Error mergeSingleInfo(doc::OwnedPtr &Reduced, + doc::OwnedPtr &&NewInfo, + llvm::BumpPtrAllocator &Arena); + struct ClangDocContext { ClangDocContext(tooling::ExecutionContext *ECtx, StringRef ProjectName, bool PublicOnly, StringRef OutDirectory, StringRef SourceRoot, diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 17180368550b6..1c8969b56441a 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -233,14 +233,16 @@ void ClangDocCommentVisitor::parseComment(const comments::Comment *C) { if (NumChildren > 0) { CommentInfo *ChildrenArray = TransientArena.Allocate(NumChildren); - unsigned I = 0; + unsigned Idx = 0; for (comments::Comment *Child : llvm::make_range(C->child_begin(), C->child_end())) { - new (&ChildrenArray[I]) CommentInfo(); - ClangDocCommentVisitor Visitor(ChildrenArray[I]); + new (&ChildrenArray[Idx]) CommentInfo(); + ClangDocCommentVisitor Visitor(ChildrenArray[Idx]); Visitor.parseComment(Child); - I++; + Idx++; } + assert(Idx == NumChildren && + "Mismatch between child_count and actual children"); CurrentCI.Children = llvm::ArrayRef(ChildrenArray, NumChildren); } diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp index f627ee5887528..3c38901f4a0f9 100644 --- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp +++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp @@ -29,6 +29,7 @@ #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Execution.h" #include "llvm/ADT/APFloat.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" @@ -358,15 +359,22 @@ Example usage for a project using a compile commands database: llvm::hardware_concurrency(ExecutorConcurrency)); { llvm::TimeTraceScope TS("Reduce"); - for (auto &Group : USRToBitcode) { - Pool.async([&, &Diags = Diags]() { // time trace decoding bitcode - if (FTimeTrace) + for (const auto &Group : USRToBitcode) { + StringRef Key = Group.getKey(); + std::vector Bitcodes = Group.getValue(); + Pool.async([Key, Bitcodes, &CDCtx, &Diags, &USRToInfo, &USRToInfoMutex, + &IndexMutex, &DiagMutex, &Error, DiagIDBitcodeReading, + DiagIDBitcodeMerging]() { + if (CDCtx.FTimeTrace) llvm::timeTraceProfilerInitialize(200, "clang-doc"); - doc::OwningPtrVec Infos; + doc::OwnedPtr Reduced = nullptr; { - llvm::TimeTraceScope Red("decoding bitcode"); - for (auto &Bitcode : Group.getValue()) { + llvm::TimeTraceScope Red("decoding and merging bitcode"); + for (const auto &Bitcode : Bitcodes) { + + llvm::scope_exit ArenaGuard( + [] { clang::doc::TransientArena.Reset(); }); llvm::BitstreamCursor Stream(Bitcode); doc::ClangDocBitcodeReader Reader(Stream, Diags); auto ReadInfos = Reader.readBitcode(); @@ -378,25 +386,17 @@ Example usage for a project using a compile commands database: Error = true; return; } - std::move(ReadInfos->begin(), ReadInfos->end(), - std::back_inserter(Infos)); - } - } // time trace decoding bitcode - - doc::OwnedPtr Reduced; - - { - llvm::TimeTraceScope Merge("merging bitcode"); - auto ExpReduced = doc::mergeInfos(Infos); - - if (!ExpReduced) { - std::lock_guard Guard(DiagMutex); - Diags.Report(DiagIDBitcodeMerging) - << toString(ExpReduced.takeError()); - return; + for (auto &I : *ReadInfos) { + if (auto Err = doc::mergeSingleInfo( + Reduced, std::move(I), clang::doc::PersistentArena)) { + std::lock_guard Guard(DiagMutex); + Diags.Report(DiagIDBitcodeMerging) + << toString(std::move(Err)); + return; + } + } } - Reduced = std::move(*ExpReduced); - } // time trace merging bitcode + } // time trace decoding and merging bitcode // Add a reference to this Info in the Index { @@ -408,7 +408,7 @@ Example usage for a project using a compile commands database: { llvm::TimeTraceScope Merge("USRToInfo"); std::lock_guard Guard(USRToInfoMutex); - USRToInfo[Group.getKey()] = std::move(Reduced); + USRToInfo[Key] = std::move(Reduced); } if (CDCtx.FTimeTrace) diff --git a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp index 8b7c20bc0adfb..a8e7b9be1c39a 100644 --- a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp @@ -111,6 +111,113 @@ TEST_F(MergeTest, mergeNamespaceInfos) { CheckNamespaceInfo(InfoAsNamespace(&Expected), InfoAsNamespace(Actual.get())); } +TEST_F(MergeTest, mergeSingleNamespaceInfo) { + NamespaceInfo One; + One.Name = "Namespace"; + Reference Ns1[] = {Reference(EmptySID, "A", InfoType::IT_namespace)}; + One.Namespace = llvm::ArrayRef(Ns1); + + Reference RA(NonEmptySID, "ChildNamespace", InfoType::IT_namespace); + InfoNode RANode(&RA); + One.Children.Namespaces.push_back(RANode); + Reference RC1(NonEmptySID, "ChildStruct", InfoType::IT_record); + InfoNode RC1Node(&RC1); + One.Children.Records.push_back(RC1Node); + + FunctionInfo F1; + F1.Name = "OneFunction"; + F1.USR = NonEmptySID; + InfoNode F1Node(&F1); + One.Children.Functions.push_back(F1Node); + + EnumInfo E1; + E1.Name = "OneEnum"; + E1.USR = NonEmptySID; + InfoNode E1Node(&E1); + One.Children.Enums.push_back(E1Node); + + NamespaceInfo Two; + Two.Name = "Namespace"; + Reference Ns2[] = {Reference(EmptySID, "A", InfoType::IT_namespace)}; + Two.Namespace = llvm::ArrayRef(Ns2); + + Reference RB(EmptySID, "OtherChildNamespace", InfoType::IT_namespace); + InfoNode RBNode(&RB); + Two.Children.Namespaces.push_back(RBNode); + Reference RC2(EmptySID, "OtherChildStruct", InfoType::IT_record); + InfoNode RC2Node(&RC2); + Two.Children.Records.push_back(RC2Node); + + FunctionInfo F2; + F2.Name = "TwoFunction"; + InfoNode F2Node(&F2); + Two.Children.Functions.push_back(F2Node); + + EnumInfo E2; + E2.Name = "TwoEnum"; + InfoNode E2Node(&E2); + Two.Children.Enums.push_back(E2Node); + + NamespaceInfo Expected; + Expected.Name = "Namespace"; + Reference NsExpected[] = {Reference(EmptySID, "A", InfoType::IT_namespace)}; + Expected.Namespace = llvm::ArrayRef(NsExpected); + + Reference RC(NonEmptySID, "ChildNamespace", InfoType::IT_namespace); + InfoNode RCNode(&RC); + Expected.Children.Namespaces.push_back(RCNode); + Reference RCE1(NonEmptySID, "ChildStruct", InfoType::IT_record); + InfoNode RCE1Node(&RCE1); + Expected.Children.Records.push_back(RCE1Node); + Reference RD(EmptySID, "OtherChildNamespace", InfoType::IT_namespace); + InfoNode RDNode(&RD); + Expected.Children.Namespaces.push_back(RDNode); + Reference RCE2(EmptySID, "OtherChildStruct", InfoType::IT_record); + InfoNode RCE2Node(&RCE2); + Expected.Children.Records.push_back(RCE2Node); + + FunctionInfo FE1; + FE1.Name = "OneFunction"; + FE1.USR = NonEmptySID; + InfoNode FE1Node(&FE1); + Expected.Children.Functions.push_back(FE1Node); + + FunctionInfo FE2; + FE2.Name = "TwoFunction"; + InfoNode FE2Node(&FE2); + Expected.Children.Functions.push_back(FE2Node); + + EnumInfo EE1; + EE1.Name = "OneEnum"; + EE1.USR = NonEmptySID; + InfoNode EE1Node(&EE1); + Expected.Children.Enums.push_back(EE1Node); + + EnumInfo EE2; + EE2.Name = "TwoEnum"; + InfoNode EE2Node(&EE2); + Expected.Children.Enums.push_back(EE2Node); + NamespaceInfo ReducedObj; + ReducedObj.IT = InfoType::IT_namespace; + doc::OwnedPtr Reduced = &ReducedObj; + + Info *PtrOne = &One; + auto Err1 = mergeSingleInfo(Reduced, std::move(PtrOne), doc::PersistentArena); + assert(!Err1); + + Info *PtrTwo = &Two; + auto Err2 = mergeSingleInfo(Reduced, std::move(PtrTwo), doc::PersistentArena); + assert(!Err2); + + CheckNamespaceInfo(InfoAsNamespace(&Expected), + static_cast(getPtr(Reduced))); + + auto *RedNS = static_cast(getPtr(Reduced)); + // Check that children functions are NOT the same instances as in One or Two + ASSERT_NE(RedNS->Children.Functions.front().Ptr, &F1); + ASSERT_NE(RedNS->Children.Functions.back().Ptr, &F2); +} + TEST_F(MergeTest, mergeRecordInfos) { RecordInfo One; One.Name = "r";