Representation.cpp 15 KB

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