///===-- Representation.cpp - ClangDoc Representation -----------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines the merging of different types of infos. The data in the // calling Info is preserved during a merge unless that field is empty or // default. In that case, the data from the parameter Info is used to replace // the empty or default data. // // For most fields, the first decl seen provides the data. Exceptions to this // include the location and description fields, which are collections of data on // all decls related to a given definition. All other fields are ignored in new // decls unless the first seen decl didn't, for whatever reason, incorporate // data on that field (e.g. a forward declared class wouldn't have information // on members on the forward declaration, but would have the class name). // //===----------------------------------------------------------------------===// #include "Representation.h" #include "llvm/Support/Error.h" namespace clang { namespace doc { namespace { const SymbolID EmptySID = SymbolID(); template llvm::Expected> reduce(std::vector> &Values) { if (Values.empty()) return llvm::make_error(" No values to reduce.\n", llvm::inconvertibleErrorCode()); std::unique_ptr Merged = llvm::make_unique(Values[0]->USR); T *Tmp = static_cast(Merged.get()); for (auto &I : Values) Tmp->merge(std::move(*static_cast(I.get()))); return std::move(Merged); } // Return the index of the matching child in the vector, or -1 if merge is not // necessary. template int getChildIndexIfExists(std::vector &Children, T &ChildToMerge) { for (unsigned long I = 0; I < Children.size(); I++) { if (ChildToMerge.USR == Children[I].USR) return I; } return -1; } // For References, we don't need to actually merge them, we just don't want // duplicates. void reduceChildren(std::vector &Children, std::vector &&ChildrenToMerge) { for (auto &ChildToMerge : ChildrenToMerge) { if (getChildIndexIfExists(Children, ChildToMerge) == -1) Children.push_back(std::move(ChildToMerge)); } } void reduceChildren(std::vector &Children, std::vector &&ChildrenToMerge) { for (auto &ChildToMerge : ChildrenToMerge) { int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); if (mergeIdx == -1) { Children.push_back(std::move(ChildToMerge)); continue; } Children[mergeIdx].merge(std::move(ChildToMerge)); } } void reduceChildren(std::vector &Children, std::vector &&ChildrenToMerge) { for (auto &ChildToMerge : ChildrenToMerge) { int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); if (mergeIdx == -1) { Children.push_back(std::move(ChildToMerge)); continue; } Children[mergeIdx].merge(std::move(ChildToMerge)); } } } // namespace // Dispatch function. llvm::Expected> mergeInfos(std::vector> &Values) { if (Values.empty()) return llvm::make_error("No info values to merge.\n", llvm::inconvertibleErrorCode()); switch (Values[0]->IT) { case InfoType::IT_namespace: return reduce(Values); case InfoType::IT_record: return reduce(Values); case InfoType::IT_enum: return reduce(Values); case InfoType::IT_function: return reduce(Values); default: return llvm::make_error("Unexpected info type.\n", llvm::inconvertibleErrorCode()); } } void Info::mergeBase(Info &&Other) { assert(mergeable(Other)); if (USR == EmptySID) USR = Other.USR; if (Name == "") Name = Other.Name; if (Namespace.empty()) Namespace = std::move(Other.Namespace); // Unconditionally extend the description, since each decl may have a comment. std::move(Other.Description.begin(), Other.Description.end(), std::back_inserter(Description)); } bool Info::mergeable(const Info &Other) { return IT == Other.IT && USR == Other.USR; } void SymbolInfo::merge(SymbolInfo &&Other) { assert(mergeable(Other)); if (!DefLoc) DefLoc = std::move(Other.DefLoc); // Unconditionally extend the list of locations, since we want all of them. std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc)); mergeBase(std::move(Other)); } void NamespaceInfo::merge(NamespaceInfo &&Other) { assert(mergeable(Other)); // Reduce children if necessary. reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces)); reduceChildren(ChildRecords, std::move(Other.ChildRecords)); reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); reduceChildren(ChildEnums, std::move(Other.ChildEnums)); mergeBase(std::move(Other)); } void RecordInfo::merge(RecordInfo &&Other) { assert(mergeable(Other)); if (!TagType) TagType = Other.TagType; if (Members.empty()) Members = std::move(Other.Members); if (Parents.empty()) Parents = std::move(Other.Parents); if (VirtualParents.empty()) VirtualParents = std::move(Other.VirtualParents); // Reduce children if necessary. reduceChildren(ChildRecords, std::move(Other.ChildRecords)); reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); reduceChildren(ChildEnums, std::move(Other.ChildEnums)); SymbolInfo::merge(std::move(Other)); } void EnumInfo::merge(EnumInfo &&Other) { assert(mergeable(Other)); if (!Scoped) Scoped = Other.Scoped; if (Members.empty()) Members = std::move(Other.Members); SymbolInfo::merge(std::move(Other)); } void FunctionInfo::merge(FunctionInfo &&Other) { assert(mergeable(Other)); if (!IsMethod) IsMethod = Other.IsMethod; if (!Access) Access = Other.Access; if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "") ReturnType = std::move(Other.ReturnType); if (Parent.USR == EmptySID && Parent.Name == "") Parent = std::move(Other.Parent); if (Params.empty()) Params = std::move(Other.Params); SymbolInfo::merge(std::move(Other)); } } // namespace doc } // namespace clang