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