brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.2 KiB · 929112f Raw
515 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines the merging of different types of infos. The data in the10// calling Info is preserved during a merge unless that field is empty or11// default. In that case, the data from the parameter Info is used to replace12// the empty or default data.13//14// For most fields, the first decl seen provides the data. Exceptions to this15// include the location and description fields, which are collections of data on16// all decls related to a given definition. All other fields are ignored in new17// decls unless the first seen decl didn't, for whatever reason, incorporate18// data on that field (e.g. a forward declared class wouldn't have information19// on members on the forward declaration, but would have the class name).20//21//===----------------------------------------------------------------------===//22#include "Representation.h"23#include "llvm/ADT/StringMap.h"24#include "llvm/Support/Error.h"25#include "llvm/Support/Path.h"26 27namespace clang {28namespace doc {29 30CommentKind stringToCommentKind(llvm::StringRef KindStr) {31  static const llvm::StringMap<CommentKind> KindMap = {32      {"FullComment", CommentKind::CK_FullComment},33      {"ParagraphComment", CommentKind::CK_ParagraphComment},34      {"TextComment", CommentKind::CK_TextComment},35      {"InlineCommandComment", CommentKind::CK_InlineCommandComment},36      {"HTMLStartTagComment", CommentKind::CK_HTMLStartTagComment},37      {"HTMLEndTagComment", CommentKind::CK_HTMLEndTagComment},38      {"BlockCommandComment", CommentKind::CK_BlockCommandComment},39      {"ParamCommandComment", CommentKind::CK_ParamCommandComment},40      {"TParamCommandComment", CommentKind::CK_TParamCommandComment},41      {"VerbatimBlockComment", CommentKind::CK_VerbatimBlockComment},42      {"VerbatimBlockLineComment", CommentKind::CK_VerbatimBlockLineComment},43      {"VerbatimLineComment", CommentKind::CK_VerbatimLineComment},44  };45 46  auto It = KindMap.find(KindStr);47  if (It != KindMap.end()) {48    return It->second;49  }50  return CommentKind::CK_Unknown;51}52 53llvm::StringRef commentKindToString(CommentKind Kind) {54  switch (Kind) {55  case CommentKind::CK_FullComment:56    return "FullComment";57  case CommentKind::CK_ParagraphComment:58    return "ParagraphComment";59  case CommentKind::CK_TextComment:60    return "TextComment";61  case CommentKind::CK_InlineCommandComment:62    return "InlineCommandComment";63  case CommentKind::CK_HTMLStartTagComment:64    return "HTMLStartTagComment";65  case CommentKind::CK_HTMLEndTagComment:66    return "HTMLEndTagComment";67  case CommentKind::CK_BlockCommandComment:68    return "BlockCommandComment";69  case CommentKind::CK_ParamCommandComment:70    return "ParamCommandComment";71  case CommentKind::CK_TParamCommandComment:72    return "TParamCommandComment";73  case CommentKind::CK_VerbatimBlockComment:74    return "VerbatimBlockComment";75  case CommentKind::CK_VerbatimBlockLineComment:76    return "VerbatimBlockLineComment";77  case CommentKind::CK_VerbatimLineComment:78    return "VerbatimLineComment";79  case CommentKind::CK_Unknown:80    return "Unknown";81  }82  llvm_unreachable("Unhandled CommentKind");83}84 85namespace {86 87const SymbolID EmptySID = SymbolID();88 89template <typename T>90llvm::Expected<std::unique_ptr<Info>>91reduce(std::vector<std::unique_ptr<Info>> &Values) {92  if (Values.empty() || !Values[0])93    return llvm::createStringError(llvm::inconvertibleErrorCode(),94                                   "no value to reduce");95  std::unique_ptr<Info> Merged = std::make_unique<T>(Values[0]->USR);96  T *Tmp = static_cast<T *>(Merged.get());97  for (auto &I : Values)98    Tmp->merge(std::move(*static_cast<T *>(I.get())));99  return std::move(Merged);100}101 102// Return the index of the matching child in the vector, or -1 if merge is not103// necessary.104template <typename T>105int getChildIndexIfExists(std::vector<T> &Children, T &ChildToMerge) {106  for (unsigned long I = 0; I < Children.size(); I++) {107    if (ChildToMerge.USR == Children[I].USR)108      return I;109  }110  return -1;111}112 113template <typename T>114void reduceChildren(std::vector<T> &Children,115                    std::vector<T> &&ChildrenToMerge) {116  for (auto &ChildToMerge : ChildrenToMerge) {117    int MergeIdx = getChildIndexIfExists(Children, ChildToMerge);118    if (MergeIdx == -1) {119      Children.push_back(std::move(ChildToMerge));120      continue;121    }122    Children[MergeIdx].merge(std::move(ChildToMerge));123  }124}125 126} // namespace127 128// Dispatch function.129llvm::Expected<std::unique_ptr<Info>>130mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {131  if (Values.empty() || !Values[0])132    return llvm::createStringError(llvm::inconvertibleErrorCode(),133                                   "no info values to merge");134 135  switch (Values[0]->IT) {136  case InfoType::IT_namespace:137    return reduce<NamespaceInfo>(Values);138  case InfoType::IT_record:139    return reduce<RecordInfo>(Values);140  case InfoType::IT_enum:141    return reduce<EnumInfo>(Values);142  case InfoType::IT_function:143    return reduce<FunctionInfo>(Values);144  case InfoType::IT_typedef:145    return reduce<TypedefInfo>(Values);146  case InfoType::IT_concept:147    return reduce<ConceptInfo>(Values);148  case InfoType::IT_variable:149    return reduce<VarInfo>(Values);150  case InfoType::IT_friend:151    return reduce<FriendInfo>(Values);152  case InfoType::IT_default:153    return llvm::createStringError(llvm::inconvertibleErrorCode(),154                                   "unexpected info type");155  }156  llvm_unreachable("unhandled enumerator");157}158 159bool CommentInfo::operator==(const CommentInfo &Other) const {160  auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,161                          SelfClosing, Explicit, AttrKeys, AttrValues, Args);162  auto SecondCI =163      std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,164               Other.ParamName, Other.CloseName, Other.SelfClosing,165               Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);166 167  if (FirstCI != SecondCI || Children.size() != Other.Children.size())168    return false;169 170  return std::equal(Children.begin(), Children.end(), Other.Children.begin(),171                    llvm::deref<std::equal_to<>>{});172}173 174bool CommentInfo::operator<(const CommentInfo &Other) const {175  auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,176                          SelfClosing, Explicit, AttrKeys, AttrValues, Args);177  auto SecondCI =178      std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction,179               Other.ParamName, Other.CloseName, Other.SelfClosing,180               Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args);181 182  if (FirstCI < SecondCI)183    return true;184 185  if (FirstCI == SecondCI) {186    return std::lexicographical_compare(187        Children.begin(), Children.end(), Other.Children.begin(),188        Other.Children.end(), llvm::deref<std::less<>>());189  }190 191  return false;192}193 194static llvm::SmallString<64>195calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,196                          const StringRef &Name, const StringRef &CurrentPath) {197  llvm::SmallString<64> FilePath;198 199  if (CurrentPath != Path) {200    // iterate back to the top201    for (llvm::sys::path::const_iterator I =202             llvm::sys::path::begin(CurrentPath);203         I != llvm::sys::path::end(CurrentPath); ++I)204      llvm::sys::path::append(FilePath, "..");205    llvm::sys::path::append(FilePath, Path);206  }207 208  // Namespace references have a Path to the parent namespace, but209  // the file is actually in the subdirectory for the namespace.210  if (Type == doc::InfoType::IT_namespace)211    llvm::sys::path::append(FilePath, Name);212 213  return llvm::sys::path::relative_path(FilePath);214}215 216llvm::SmallString<64>217Reference::getRelativeFilePath(const StringRef &CurrentPath) const {218  return calculateRelativeFilePath(RefType, Path, Name, CurrentPath);219}220 221llvm::SmallString<16> Reference::getFileBaseName() const {222  if (RefType == InfoType::IT_namespace)223    return llvm::SmallString<16>("index");224 225  return Name;226}227 228llvm::SmallString<64>229Info::getRelativeFilePath(const StringRef &CurrentPath) const {230  return calculateRelativeFilePath(IT, Path, extractName(), CurrentPath);231}232 233llvm::SmallString<16> Info::getFileBaseName() const {234  if (IT == InfoType::IT_namespace)235    return llvm::SmallString<16>("index");236 237  return extractName();238}239 240bool Reference::mergeable(const Reference &Other) {241  return RefType == Other.RefType && USR == Other.USR;242}243 244void Reference::merge(Reference &&Other) {245  assert(mergeable(Other));246  if (Name.empty())247    Name = Other.Name;248  if (Path.empty())249    Path = Other.Path;250  if (DocumentationFileName.empty())251    DocumentationFileName = Other.DocumentationFileName;252}253 254bool FriendInfo::mergeable(const FriendInfo &Other) {255  return Ref.USR == Other.Ref.USR && Ref.Name == Other.Ref.Name;256}257 258void FriendInfo::merge(FriendInfo &&Other) {259  assert(mergeable(Other));260  Ref.merge(std::move(Other.Ref));261}262 263void Info::mergeBase(Info &&Other) {264  assert(mergeable(Other));265  if (USR == EmptySID)266    USR = Other.USR;267  if (Name == "")268    Name = Other.Name;269  if (Path == "")270    Path = Other.Path;271  if (Namespace.empty())272    Namespace = std::move(Other.Namespace);273  // Unconditionally extend the description, since each decl may have a comment.274  std::move(Other.Description.begin(), Other.Description.end(),275            std::back_inserter(Description));276  llvm::sort(Description);277  auto Last = llvm::unique(Description);278  Description.erase(Last, Description.end());279}280 281bool Info::mergeable(const Info &Other) {282  return IT == Other.IT && USR == Other.USR;283}284 285void SymbolInfo::merge(SymbolInfo &&Other) {286  assert(mergeable(Other));287  if (!DefLoc)288    DefLoc = std::move(Other.DefLoc);289  // Unconditionally extend the list of locations, since we want all of them.290  std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));291  llvm::sort(Loc);292  auto *Last = llvm::unique(Loc);293  Loc.erase(Last, Loc.end());294  mergeBase(std::move(Other));295  if (MangledName.empty())296    MangledName = std::move(Other.MangledName);297}298 299NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)300    : Info(InfoType::IT_namespace, USR, Name, Path) {}301 302void NamespaceInfo::merge(NamespaceInfo &&Other) {303  assert(mergeable(Other));304  // Reduce children if necessary.305  reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));306  reduceChildren(Children.Records, std::move(Other.Children.Records));307  reduceChildren(Children.Functions, std::move(Other.Children.Functions));308  reduceChildren(Children.Enums, std::move(Other.Children.Enums));309  reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));310  reduceChildren(Children.Concepts, std::move(Other.Children.Concepts));311  reduceChildren(Children.Variables, std::move(Other.Children.Variables));312  mergeBase(std::move(Other));313}314 315RecordInfo::RecordInfo(SymbolID USR, StringRef Name, StringRef Path)316    : SymbolInfo(InfoType::IT_record, USR, Name, Path) {}317 318void RecordInfo::merge(RecordInfo &&Other) {319  assert(mergeable(Other));320  if (!llvm::to_underlying(TagType))321    TagType = Other.TagType;322  IsTypeDef = IsTypeDef || Other.IsTypeDef;323  if (Members.empty())324    Members = std::move(Other.Members);325  if (Bases.empty())326    Bases = std::move(Other.Bases);327  if (Parents.empty())328    Parents = std::move(Other.Parents);329  if (VirtualParents.empty())330    VirtualParents = std::move(Other.VirtualParents);331  if (Friends.empty())332    Friends = std::move(Other.Friends);333  // Reduce children if necessary.334  reduceChildren(Children.Records, std::move(Other.Children.Records));335  reduceChildren(Children.Functions, std::move(Other.Children.Functions));336  reduceChildren(Children.Enums, std::move(Other.Children.Enums));337  reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));338  SymbolInfo::merge(std::move(Other));339  if (!Template)340    Template = Other.Template;341}342 343void EnumInfo::merge(EnumInfo &&Other) {344  assert(mergeable(Other));345  if (!Scoped)346    Scoped = Other.Scoped;347  if (Members.empty())348    Members = std::move(Other.Members);349  SymbolInfo::merge(std::move(Other));350}351 352void FunctionInfo::merge(FunctionInfo &&Other) {353  assert(mergeable(Other));354  if (!IsMethod)355    IsMethod = Other.IsMethod;356  if (!Access)357    Access = Other.Access;358  if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")359    ReturnType = std::move(Other.ReturnType);360  if (Parent.USR == EmptySID && Parent.Name == "")361    Parent = std::move(Other.Parent);362  if (Params.empty())363    Params = std::move(Other.Params);364  SymbolInfo::merge(std::move(Other));365  if (!Template)366    Template = Other.Template;367}368 369void TypedefInfo::merge(TypedefInfo &&Other) {370  assert(mergeable(Other));371  if (!IsUsing)372    IsUsing = Other.IsUsing;373  if (Underlying.Type.Name == "")374    Underlying = Other.Underlying;375  SymbolInfo::merge(std::move(Other));376}377 378void ConceptInfo::merge(ConceptInfo &&Other) {379  assert(mergeable(Other));380  if (!IsType)381    IsType = Other.IsType;382  if (ConstraintExpression.empty())383    ConstraintExpression = std::move(Other.ConstraintExpression);384  if (Template.Constraints.empty())385    Template.Constraints = std::move(Other.Template.Constraints);386  if (Template.Params.empty())387    Template.Params = std::move(Other.Template.Params);388  SymbolInfo::merge(std::move(Other));389}390 391void VarInfo::merge(VarInfo &&Other) {392  assert(mergeable(Other));393  if (!IsStatic)394    IsStatic = Other.IsStatic;395  if (Type.Type.USR == EmptySID && Type.Type.Name == "")396    Type = std::move(Other.Type);397  SymbolInfo::merge(std::move(Other));398}399 400BaseRecordInfo::BaseRecordInfo() : RecordInfo() {}401 402BaseRecordInfo::BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path,403                               bool IsVirtual, AccessSpecifier Access,404                               bool IsParent)405    : RecordInfo(USR, Name, Path), IsVirtual(IsVirtual), Access(Access),406      IsParent(IsParent) {}407 408llvm::SmallString<16> Info::extractName() const {409  if (!Name.empty())410    return Name;411 412  switch (IT) {413  case InfoType::IT_namespace:414    // Cover the case where the project contains a base namespace called415    // 'GlobalNamespace' (i.e. a namespace at the same level as the global416    // namespace, which would conflict with the hard-coded global namespace name417    // below.)418    if (Name == "GlobalNamespace" && Namespace.empty())419      return llvm::SmallString<16>("@GlobalNamespace");420    // The case of anonymous namespaces is taken care of in serialization,421    // so here we can safely assume an unnamed namespace is the global422    // one.423    return llvm::SmallString<16>("GlobalNamespace");424  case InfoType::IT_record:425    return llvm::SmallString<16>("@nonymous_record_" +426                                 toHex(llvm::toStringRef(USR)));427  case InfoType::IT_enum:428    return llvm::SmallString<16>("@nonymous_enum_" +429                                 toHex(llvm::toStringRef(USR)));430  case InfoType::IT_typedef:431    return llvm::SmallString<16>("@nonymous_typedef_" +432                                 toHex(llvm::toStringRef(USR)));433  case InfoType::IT_function:434    return llvm::SmallString<16>("@nonymous_function_" +435                                 toHex(llvm::toStringRef(USR)));436  case InfoType::IT_concept:437    return llvm::SmallString<16>("@nonymous_concept_" +438                                 toHex(llvm::toStringRef(USR)));439  case InfoType::IT_variable:440    return llvm::SmallString<16>("@nonymous_variable_" +441                                 toHex(llvm::toStringRef(USR)));442  case InfoType::IT_friend:443    return llvm::SmallString<16>("@nonymous_friend_" +444                                 toHex(llvm::toStringRef(USR)));445  case InfoType::IT_default:446    return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));447  }448  llvm_unreachable("Invalid InfoType.");449  return llvm::SmallString<16>("");450}451 452// Order is based on the Name attribute: case insensitive order453bool Index::operator<(const Index &Other) const {454  // Loop through each character of both strings455  for (unsigned I = 0; I < Name.size() && I < Other.Name.size(); ++I) {456    // Compare them after converting both to lower case457    int D = tolower(Name[I]) - tolower(Other.Name[I]);458    if (D == 0)459      continue;460    return D < 0;461  }462  // If both strings have the size it means they would be equal if changed to463  // lower case. In here, lower case will be smaller than upper case464  // Example: string < stRing = true465  // This is the opposite of how operator < handles strings466  if (Name.size() == Other.Name.size())467    return Name > Other.Name;468  // If they are not the same size; the shorter string is smaller469  return Name.size() < Other.Name.size();470}471 472void Index::sort() {473  llvm::sort(Children);474  for (auto &C : Children)475    C.sort();476}477 478ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,479                                 StringRef ProjectName, bool PublicOnly,480                                 StringRef OutDirectory, StringRef SourceRoot,481                                 StringRef RepositoryUrl,482                                 StringRef RepositoryLinePrefix, StringRef Base,483                                 std::vector<std::string> UserStylesheets,484                                 bool FTimeTrace)485    : ECtx(ECtx), ProjectName(ProjectName), PublicOnly(PublicOnly),486      FTimeTrace(FTimeTrace), OutDirectory(OutDirectory),487      UserStylesheets(UserStylesheets), Base(Base) {488  llvm::SmallString<128> SourceRootDir(SourceRoot);489  if (SourceRoot.empty())490    // If no SourceRoot was provided the current path is used as the default491    llvm::sys::fs::current_path(SourceRootDir);492  this->SourceRoot = std::string(SourceRootDir);493  if (!RepositoryUrl.empty()) {494    this->RepositoryUrl = std::string(RepositoryUrl);495    if (!RepositoryUrl.empty() && !RepositoryUrl.starts_with("http://") &&496        !RepositoryUrl.starts_with("https://"))497      this->RepositoryUrl->insert(0, "https://");498 499    if (!RepositoryLinePrefix.empty())500      this->RepositoryLinePrefix = std::string(RepositoryLinePrefix);501  }502}503 504void ScopeChildren::sort() {505  llvm::sort(Namespaces);506  llvm::sort(Records);507  llvm::sort(Functions);508  llvm::sort(Enums);509  llvm::sort(Typedefs);510  llvm::sort(Concepts);511  llvm::sort(Variables);512}513} // namespace doc514} // namespace clang515