blob: 398d1025e973b0c8545c8026f909092117ec4710 [file] [log] [blame]
Julie Hockettd0f9a872018-06-04 17:22:20 +00001///===-- Representation.cpp - ClangDoc Representation -----------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Julie Hockettd0f9a872018-06-04 17:22:20 +00006//
7//===----------------------------------------------------------------------===//
8//
9// This file defines the merging of different types of infos. The data in the
10// calling Info is preserved during a merge unless that field is empty or
11// default. In that case, the data from the parameter Info is used to replace
12// the empty or default data.
13//
14// For most fields, the first decl seen provides the data. Exceptions to this
15// include the location and description fields, which are collections of data on
16// all decls related to a given definition. All other fields are ignored in new
17// decls unless the first seen decl didn't, for whatever reason, incorporate
18// data on that field (e.g. a forward declared class wouldn't have information
19// on members on the forward declaration, but would have the class name).
20//
21//===----------------------------------------------------------------------===//
22#include "Representation.h"
23#include "llvm/Support/Error.h"
24
25namespace clang {
26namespace doc {
27
Julie Hockett8899c292018-08-02 20:10:17 +000028namespace {
29
30const SymbolID EmptySID = SymbolID();
Julie Hockettd0f9a872018-06-04 17:22:20 +000031
32template <typename T>
Julie Hockett8899c292018-08-02 20:10:17 +000033llvm::Expected<std::unique_ptr<Info>>
34reduce(std::vector<std::unique_ptr<Info>> &Values) {
35 if (Values.empty())
36 return llvm::make_error<llvm::StringError>(" No values to reduce.\n",
37 llvm::inconvertibleErrorCode());
38 std::unique_ptr<Info> Merged = llvm::make_unique<T>(Values[0]->USR);
Julie Hockettd0f9a872018-06-04 17:22:20 +000039 T *Tmp = static_cast<T *>(Merged.get());
40 for (auto &I : Values)
41 Tmp->merge(std::move(*static_cast<T *>(I.get())));
Julie Hockett8899c292018-08-02 20:10:17 +000042 return std::move(Merged);
Julie Hockettd0f9a872018-06-04 17:22:20 +000043}
44
Julie Hockett8899c292018-08-02 20:10:17 +000045// Return the index of the matching child in the vector, or -1 if merge is not
46// necessary.
47template <typename T>
48int getChildIndexIfExists(std::vector<T> &Children, T &ChildToMerge) {
49 for (unsigned long I = 0; I < Children.size(); I++) {
50 if (ChildToMerge.USR == Children[I].USR)
51 return I;
52 }
53 return -1;
54}
55
56// For References, we don't need to actually merge them, we just don't want
57// duplicates.
58void reduceChildren(std::vector<Reference> &Children,
59 std::vector<Reference> &&ChildrenToMerge) {
60 for (auto &ChildToMerge : ChildrenToMerge) {
61 if (getChildIndexIfExists(Children, ChildToMerge) == -1)
62 Children.push_back(std::move(ChildToMerge));
63 }
64}
65
66void reduceChildren(std::vector<FunctionInfo> &Children,
67 std::vector<FunctionInfo> &&ChildrenToMerge) {
68 for (auto &ChildToMerge : ChildrenToMerge) {
69 int mergeIdx = getChildIndexIfExists(Children, ChildToMerge);
70 if (mergeIdx == -1) {
71 Children.push_back(std::move(ChildToMerge));
72 continue;
73 }
74 Children[mergeIdx].merge(std::move(ChildToMerge));
75 }
76}
77
78void reduceChildren(std::vector<EnumInfo> &Children,
79 std::vector<EnumInfo> &&ChildrenToMerge) {
80 for (auto &ChildToMerge : ChildrenToMerge) {
81 int mergeIdx = getChildIndexIfExists(Children, ChildToMerge);
82 if (mergeIdx == -1) {
83 Children.push_back(std::move(ChildToMerge));
84 continue;
85 }
86 Children[mergeIdx].merge(std::move(ChildToMerge));
87 }
88}
89
90} // namespace
91
Julie Hockettd0f9a872018-06-04 17:22:20 +000092// Dispatch function.
93llvm::Expected<std::unique_ptr<Info>>
94mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
95 if (Values.empty())
96 return llvm::make_error<llvm::StringError>("No info values to merge.\n",
Julie Hocketteb50a2e2018-07-20 18:49:55 +000097 llvm::inconvertibleErrorCode());
Julie Hockettd0f9a872018-06-04 17:22:20 +000098
99 switch (Values[0]->IT) {
100 case InfoType::IT_namespace:
101 return reduce<NamespaceInfo>(Values);
102 case InfoType::IT_record:
103 return reduce<RecordInfo>(Values);
104 case InfoType::IT_enum:
105 return reduce<EnumInfo>(Values);
106 case InfoType::IT_function:
107 return reduce<FunctionInfo>(Values);
108 default:
109 return llvm::make_error<llvm::StringError>("Unexpected info type.\n",
110 llvm::inconvertibleErrorCode());
111 }
112}
113
114void Info::mergeBase(Info &&Other) {
115 assert(mergeable(Other));
116 if (USR == EmptySID)
117 USR = Other.USR;
118 if (Name == "")
119 Name = Other.Name;
120 if (Namespace.empty())
121 Namespace = std::move(Other.Namespace);
122 // Unconditionally extend the description, since each decl may have a comment.
123 std::move(Other.Description.begin(), Other.Description.end(),
124 std::back_inserter(Description));
125}
126
127bool Info::mergeable(const Info &Other) {
Julie Hockett8899c292018-08-02 20:10:17 +0000128 return IT == Other.IT && USR == Other.USR;
Julie Hockettd0f9a872018-06-04 17:22:20 +0000129}
130
131void SymbolInfo::merge(SymbolInfo &&Other) {
132 assert(mergeable(Other));
133 if (!DefLoc)
134 DefLoc = std::move(Other.DefLoc);
135 // Unconditionally extend the list of locations, since we want all of them.
136 std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));
137 mergeBase(std::move(Other));
138}
139
140void NamespaceInfo::merge(NamespaceInfo &&Other) {
141 assert(mergeable(Other));
Julie Hockett8899c292018-08-02 20:10:17 +0000142 // Reduce children if necessary.
143 reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces));
144 reduceChildren(ChildRecords, std::move(Other.ChildRecords));
145 reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
146 reduceChildren(ChildEnums, std::move(Other.ChildEnums));
Julie Hockettd0f9a872018-06-04 17:22:20 +0000147 mergeBase(std::move(Other));
148}
149
150void RecordInfo::merge(RecordInfo &&Other) {
151 assert(mergeable(Other));
152 if (!TagType)
153 TagType = Other.TagType;
154 if (Members.empty())
155 Members = std::move(Other.Members);
156 if (Parents.empty())
157 Parents = std::move(Other.Parents);
158 if (VirtualParents.empty())
159 VirtualParents = std::move(Other.VirtualParents);
Julie Hockett8899c292018-08-02 20:10:17 +0000160 // Reduce children if necessary.
161 reduceChildren(ChildRecords, std::move(Other.ChildRecords));
162 reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
163 reduceChildren(ChildEnums, std::move(Other.ChildEnums));
Julie Hockettd0f9a872018-06-04 17:22:20 +0000164 SymbolInfo::merge(std::move(Other));
165}
166
167void EnumInfo::merge(EnumInfo &&Other) {
168 assert(mergeable(Other));
169 if (!Scoped)
170 Scoped = Other.Scoped;
171 if (Members.empty())
172 Members = std::move(Other.Members);
173 SymbolInfo::merge(std::move(Other));
174}
175
176void FunctionInfo::merge(FunctionInfo &&Other) {
177 assert(mergeable(Other));
178 if (!IsMethod)
179 IsMethod = Other.IsMethod;
180 if (!Access)
181 Access = Other.Access;
182 if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
183 ReturnType = std::move(Other.ReturnType);
184 if (Parent.USR == EmptySID && Parent.Name == "")
185 Parent = std::move(Other.Parent);
186 if (Params.empty())
187 Params = std::move(Other.Params);
188 SymbolInfo::merge(std::move(Other));
189}
190
191} // namespace doc
192} // namespace clang