• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 ///===-- Representation.cpp - ClangDoc Representation -----------*- C++ -*-===//
2 //
3 // 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
6 //
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 #include "llvm/Support/Path.h"
25 
26 namespace clang {
27 namespace doc {
28 
29 namespace {
30 
31 const SymbolID EmptySID = SymbolID();
32 
33 template <typename T>
34 llvm::Expected<std::unique_ptr<Info>>
reduce(std::vector<std::unique_ptr<Info>> & Values)35 reduce(std::vector<std::unique_ptr<Info>> &Values) {
36   if (Values.empty())
37     return llvm::createStringError(llvm::inconvertibleErrorCode(),
38                                    "no value to reduce");
39   std::unique_ptr<Info> Merged = std::make_unique<T>(Values[0]->USR);
40   T *Tmp = static_cast<T *>(Merged.get());
41   for (auto &I : Values)
42     Tmp->merge(std::move(*static_cast<T *>(I.get())));
43   return std::move(Merged);
44 }
45 
46 // Return the index of the matching child in the vector, or -1 if merge is not
47 // necessary.
48 template <typename T>
getChildIndexIfExists(std::vector<T> & Children,T & ChildToMerge)49 int getChildIndexIfExists(std::vector<T> &Children, T &ChildToMerge) {
50   for (unsigned long I = 0; I < Children.size(); I++) {
51     if (ChildToMerge.USR == Children[I].USR)
52       return I;
53   }
54   return -1;
55 }
56 
reduceChildren(std::vector<Reference> & Children,std::vector<Reference> && ChildrenToMerge)57 void reduceChildren(std::vector<Reference> &Children,
58                     std::vector<Reference> &&ChildrenToMerge) {
59   for (auto &ChildToMerge : ChildrenToMerge) {
60     int mergeIdx = getChildIndexIfExists(Children, ChildToMerge);
61     if (mergeIdx == -1) {
62       Children.push_back(std::move(ChildToMerge));
63       continue;
64     }
65     Children[mergeIdx].merge(std::move(ChildToMerge));
66   }
67 }
68 
reduceChildren(std::vector<FunctionInfo> & Children,std::vector<FunctionInfo> && ChildrenToMerge)69 void reduceChildren(std::vector<FunctionInfo> &Children,
70                     std::vector<FunctionInfo> &&ChildrenToMerge) {
71   for (auto &ChildToMerge : ChildrenToMerge) {
72     int mergeIdx = getChildIndexIfExists(Children, ChildToMerge);
73     if (mergeIdx == -1) {
74       Children.push_back(std::move(ChildToMerge));
75       continue;
76     }
77     Children[mergeIdx].merge(std::move(ChildToMerge));
78   }
79 }
80 
reduceChildren(std::vector<EnumInfo> & Children,std::vector<EnumInfo> && ChildrenToMerge)81 void reduceChildren(std::vector<EnumInfo> &Children,
82                     std::vector<EnumInfo> &&ChildrenToMerge) {
83   for (auto &ChildToMerge : ChildrenToMerge) {
84     int mergeIdx = getChildIndexIfExists(Children, ChildToMerge);
85     if (mergeIdx == -1) {
86       Children.push_back(std::move(ChildToMerge));
87       continue;
88     }
89     Children[mergeIdx].merge(std::move(ChildToMerge));
90   }
91 }
92 
93 } // namespace
94 
95 // Dispatch function.
96 llvm::Expected<std::unique_ptr<Info>>
mergeInfos(std::vector<std::unique_ptr<Info>> & Values)97 mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
98   if (Values.empty())
99     return llvm::createStringError(llvm::inconvertibleErrorCode(),
100                                    "no info values to merge");
101 
102   switch (Values[0]->IT) {
103   case InfoType::IT_namespace:
104     return reduce<NamespaceInfo>(Values);
105   case InfoType::IT_record:
106     return reduce<RecordInfo>(Values);
107   case InfoType::IT_enum:
108     return reduce<EnumInfo>(Values);
109   case InfoType::IT_function:
110     return reduce<FunctionInfo>(Values);
111   default:
112     return llvm::createStringError(llvm::inconvertibleErrorCode(),
113                                    "unexpected info type");
114   }
115 }
116 
117 static llvm::SmallString<64>
calculateRelativeFilePath(const InfoType & Type,const StringRef & Path,const StringRef & Name,const StringRef & CurrentPath)118 calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
119                           const StringRef &Name, const StringRef &CurrentPath) {
120   llvm::SmallString<64> FilePath;
121 
122   if (CurrentPath != Path) {
123     // iterate back to the top
124     for (llvm::sys::path::const_iterator I =
125              llvm::sys::path::begin(CurrentPath);
126          I != llvm::sys::path::end(CurrentPath); ++I)
127       llvm::sys::path::append(FilePath, "..");
128     llvm::sys::path::append(FilePath, Path);
129   }
130 
131   // Namespace references have a Path to the parent namespace, but
132   // the file is actually in the subdirectory for the namespace.
133   if (Type == doc::InfoType::IT_namespace)
134     llvm::sys::path::append(FilePath, Name);
135 
136   return llvm::sys::path::relative_path(FilePath);
137 }
138 
139 llvm::SmallString<64>
getRelativeFilePath(const StringRef & CurrentPath) const140 Reference::getRelativeFilePath(const StringRef &CurrentPath) const {
141   return calculateRelativeFilePath(RefType, Path, Name, CurrentPath);
142 }
143 
getFileBaseName() const144 llvm::SmallString<16> Reference::getFileBaseName() const {
145   if (RefType == InfoType::IT_namespace)
146     return llvm::SmallString<16>("index");
147 
148   return Name;
149 }
150 
151 llvm::SmallString<64>
getRelativeFilePath(const StringRef & CurrentPath) const152 Info::getRelativeFilePath(const StringRef &CurrentPath) const {
153   return calculateRelativeFilePath(IT, Path, extractName(), CurrentPath);
154 }
155 
getFileBaseName() const156 llvm::SmallString<16> Info::getFileBaseName() const {
157   if (IT == InfoType::IT_namespace)
158     return llvm::SmallString<16>("index");
159 
160   return extractName();
161 }
162 
mergeable(const Reference & Other)163 bool Reference::mergeable(const Reference &Other) {
164   return RefType == Other.RefType && USR == Other.USR;
165 }
166 
merge(Reference && Other)167 void Reference::merge(Reference &&Other) {
168   assert(mergeable(Other));
169   if (Name.empty())
170     Name = Other.Name;
171   if (Path.empty())
172     Path = Other.Path;
173   if (!IsInGlobalNamespace)
174     IsInGlobalNamespace = Other.IsInGlobalNamespace;
175 }
176 
mergeBase(Info && Other)177 void Info::mergeBase(Info &&Other) {
178   assert(mergeable(Other));
179   if (USR == EmptySID)
180     USR = Other.USR;
181   if (Name == "")
182     Name = Other.Name;
183   if (Path == "")
184     Path = Other.Path;
185   if (Namespace.empty())
186     Namespace = std::move(Other.Namespace);
187   // Unconditionally extend the description, since each decl may have a comment.
188   std::move(Other.Description.begin(), Other.Description.end(),
189             std::back_inserter(Description));
190   llvm::sort(Description);
191   auto Last = std::unique(Description.begin(), Description.end());
192   Description.erase(Last, Description.end());
193 }
194 
mergeable(const Info & Other)195 bool Info::mergeable(const Info &Other) {
196   return IT == Other.IT && USR == Other.USR;
197 }
198 
merge(SymbolInfo && Other)199 void SymbolInfo::merge(SymbolInfo &&Other) {
200   assert(mergeable(Other));
201   if (!DefLoc)
202     DefLoc = std::move(Other.DefLoc);
203   // Unconditionally extend the list of locations, since we want all of them.
204   std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));
205   llvm::sort(Loc);
206   auto Last = std::unique(Loc.begin(), Loc.end());
207   Loc.erase(Last, Loc.end());
208   mergeBase(std::move(Other));
209 }
210 
merge(NamespaceInfo && Other)211 void NamespaceInfo::merge(NamespaceInfo &&Other) {
212   assert(mergeable(Other));
213   // Reduce children if necessary.
214   reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces));
215   reduceChildren(ChildRecords, std::move(Other.ChildRecords));
216   reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
217   reduceChildren(ChildEnums, std::move(Other.ChildEnums));
218   mergeBase(std::move(Other));
219 }
220 
merge(RecordInfo && Other)221 void RecordInfo::merge(RecordInfo &&Other) {
222   assert(mergeable(Other));
223   if (!TagType)
224     TagType = Other.TagType;
225   if (Members.empty())
226     Members = std::move(Other.Members);
227   if (Bases.empty())
228     Bases = std::move(Other.Bases);
229   if (Parents.empty())
230     Parents = std::move(Other.Parents);
231   if (VirtualParents.empty())
232     VirtualParents = std::move(Other.VirtualParents);
233   // Reduce children if necessary.
234   reduceChildren(ChildRecords, std::move(Other.ChildRecords));
235   reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
236   reduceChildren(ChildEnums, std::move(Other.ChildEnums));
237   SymbolInfo::merge(std::move(Other));
238 }
239 
merge(EnumInfo && Other)240 void EnumInfo::merge(EnumInfo &&Other) {
241   assert(mergeable(Other));
242   if (!Scoped)
243     Scoped = Other.Scoped;
244   if (Members.empty())
245     Members = std::move(Other.Members);
246   SymbolInfo::merge(std::move(Other));
247 }
248 
merge(FunctionInfo && Other)249 void FunctionInfo::merge(FunctionInfo &&Other) {
250   assert(mergeable(Other));
251   if (!IsMethod)
252     IsMethod = Other.IsMethod;
253   if (!Access)
254     Access = Other.Access;
255   if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
256     ReturnType = std::move(Other.ReturnType);
257   if (Parent.USR == EmptySID && Parent.Name == "")
258     Parent = std::move(Other.Parent);
259   if (Params.empty())
260     Params = std::move(Other.Params);
261   SymbolInfo::merge(std::move(Other));
262 }
263 
extractName() const264 llvm::SmallString<16> Info::extractName() const {
265   if (!Name.empty())
266     return Name;
267 
268   switch (IT) {
269   case InfoType::IT_namespace:
270     // Cover the case where the project contains a base namespace called
271     // 'GlobalNamespace' (i.e. a namespace at the same level as the global
272     // namespace, which would conflict with the hard-coded global namespace name
273     // below.)
274     if (Name == "GlobalNamespace" && Namespace.empty())
275       return llvm::SmallString<16>("@GlobalNamespace");
276     // The case of anonymous namespaces is taken care of in serialization,
277     // so here we can safely assume an unnamed namespace is the global
278     // one.
279     return llvm::SmallString<16>("GlobalNamespace");
280   case InfoType::IT_record:
281     return llvm::SmallString<16>("@nonymous_record_" +
282                                  toHex(llvm::toStringRef(USR)));
283   case InfoType::IT_enum:
284     return llvm::SmallString<16>("@nonymous_enum_" +
285                                  toHex(llvm::toStringRef(USR)));
286   case InfoType::IT_function:
287     return llvm::SmallString<16>("@nonymous_function_" +
288                                  toHex(llvm::toStringRef(USR)));
289   case InfoType::IT_default:
290     return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
291   }
292   llvm_unreachable("Invalid InfoType.");
293   return llvm::SmallString<16>("");
294 }
295 
296 // Order is based on the Name attribute: case insensitive order
operator <(const Index & Other) const297 bool Index::operator<(const Index &Other) const {
298   // Loop through each character of both strings
299   for (unsigned I = 0; I < Name.size() && I < Other.Name.size(); ++I) {
300     // Compare them after converting both to lower case
301     int D = tolower(Name[I]) - tolower(Other.Name[I]);
302     if (D == 0)
303       continue;
304     return D < 0;
305   }
306   // If both strings have the size it means they would be equal if changed to
307   // lower case. In here, lower case will be smaller than upper case
308   // Example: string < stRing = true
309   // This is the opposite of how operator < handles strings
310   if (Name.size() == Other.Name.size())
311     return Name > Other.Name;
312   // If they are not the same size; the shorter string is smaller
313   return Name.size() < Other.Name.size();
314 }
315 
sort()316 void Index::sort() {
317   llvm::sort(Children);
318   for (auto &C : Children)
319     C.sort();
320 }
321 
ClangDocContext(tooling::ExecutionContext * ECtx,StringRef ProjectName,bool PublicOnly,StringRef OutDirectory,StringRef SourceRoot,StringRef RepositoryUrl,std::vector<std::string> UserStylesheets,std::vector<std::string> JsScripts)322 ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
323                                  StringRef ProjectName, bool PublicOnly,
324                                  StringRef OutDirectory, StringRef SourceRoot,
325                                  StringRef RepositoryUrl,
326                                  std::vector<std::string> UserStylesheets,
327                                  std::vector<std::string> JsScripts)
328     : ECtx(ECtx), ProjectName(ProjectName), PublicOnly(PublicOnly),
329       OutDirectory(OutDirectory), UserStylesheets(UserStylesheets),
330       JsScripts(JsScripts) {
331   llvm::SmallString<128> SourceRootDir(SourceRoot);
332   if (SourceRoot.empty())
333     // If no SourceRoot was provided the current path is used as the default
334     llvm::sys::fs::current_path(SourceRootDir);
335   this->SourceRoot = std::string(SourceRootDir.str());
336   if (!RepositoryUrl.empty()) {
337     this->RepositoryUrl = std::string(RepositoryUrl);
338     if (!RepositoryUrl.empty() && RepositoryUrl.find("http://") != 0 &&
339         RepositoryUrl.find("https://") != 0)
340       this->RepositoryUrl->insert(0, "https://");
341   }
342 }
343 
344 } // namespace doc
345 } // namespace clang
346