brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.4 KiB · 1f30d27 Raw
271 lines · cpp
1//===-- FindAllSymbols.cpp - find all symbols--------------------*- 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#include "FindAllSymbols.h"10#include "HeaderMapCollector.h"11#include "PathConfig.h"12#include "SymbolInfo.h"13#include "clang/AST/Decl.h"14#include "clang/AST/DeclCXX.h"15#include "clang/AST/Type.h"16#include "clang/ASTMatchers/ASTMatchFinder.h"17#include "clang/ASTMatchers/ASTMatchers.h"18#include "clang/Tooling/Tooling.h"19#include "llvm/Support/FileSystem.h"20#include <optional>21 22using namespace clang::ast_matchers;23 24namespace clang {25namespace find_all_symbols {26namespace {27 28AST_MATCHER(EnumConstantDecl, isInScopedEnum) {29  if (const auto *ED = dyn_cast<EnumDecl>(Node.getDeclContext()))30    return ED->isScoped();31  return false;32}33 34AST_POLYMORPHIC_MATCHER(isFullySpecialized,35                        AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, VarDecl,36                                                        CXXRecordDecl)) {37  if (Node.getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {38    bool IsPartialSpecialization =39        llvm::isa<VarTemplatePartialSpecializationDecl>(Node) ||40        llvm::isa<ClassTemplatePartialSpecializationDecl>(Node);41    return !IsPartialSpecialization;42  }43  return false;44}45 46std::vector<SymbolInfo::Context> GetContexts(const NamedDecl *ND) {47  std::vector<SymbolInfo::Context> Contexts;48  for (const auto *Context = ND->getDeclContext(); Context;49       Context = Context->getParent()) {50    if (llvm::isa<TranslationUnitDecl>(Context) ||51        llvm::isa<LinkageSpecDecl>(Context))52      break;53 54    assert(llvm::isa<NamedDecl>(Context) &&55           "Expect Context to be a NamedDecl");56    if (const auto *NSD = dyn_cast<NamespaceDecl>(Context)) {57      if (!NSD->isInlineNamespace())58        Contexts.emplace_back(SymbolInfo::ContextType::Namespace,59                              NSD->getName().str());60    } else if (const auto *ED = dyn_cast<EnumDecl>(Context)) {61      Contexts.emplace_back(SymbolInfo::ContextType::EnumDecl,62                            ED->getName().str());63    } else {64      const auto *RD = cast<RecordDecl>(Context);65      Contexts.emplace_back(SymbolInfo::ContextType::Record,66                            RD->getName().str());67    }68  }69  return Contexts;70}71 72std::optional<SymbolInfo>73CreateSymbolInfo(const NamedDecl *ND, const SourceManager &SM,74                 const HeaderMapCollector *Collector) {75  SymbolInfo::SymbolKind Type;76  if (llvm::isa<VarDecl>(ND)) {77    Type = SymbolInfo::SymbolKind::Variable;78  } else if (llvm::isa<FunctionDecl>(ND)) {79    Type = SymbolInfo::SymbolKind::Function;80  } else if (llvm::isa<TypedefNameDecl>(ND)) {81    Type = SymbolInfo::SymbolKind::TypedefName;82  } else if (llvm::isa<EnumConstantDecl>(ND)) {83    Type = SymbolInfo::SymbolKind::EnumConstantDecl;84  } else if (llvm::isa<EnumDecl>(ND)) {85    Type = SymbolInfo::SymbolKind::EnumDecl;86    // Ignore anonymous enum declarations.87    if (ND->getName().empty())88      return std::nullopt;89  } else {90    assert(llvm::isa<RecordDecl>(ND) &&91           "Matched decl must be one of VarDecl, "92           "FunctionDecl, TypedefNameDecl, EnumConstantDecl, "93           "EnumDecl and RecordDecl!");94    // C-style record decl can have empty name, e.g "struct { ... } var;".95    if (ND->getName().empty())96      return std::nullopt;97    Type = SymbolInfo::SymbolKind::Class;98  }99 100  SourceLocation Loc = SM.getExpansionLoc(ND->getLocation());101  if (!Loc.isValid()) {102    llvm::errs() << "Declaration " << ND->getDeclName() << "("103                 << ND->getDeclKindName()104                 << ") has invalid declaration location.";105    return std::nullopt;106  }107 108  std::string FilePath = getIncludePath(SM, Loc, Collector);109  if (FilePath.empty())110    return std::nullopt;111 112  return SymbolInfo(ND->getNameAsString(), Type, FilePath, GetContexts(ND));113}114 115} // namespace116 117void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) {118  // FIXME: Handle specialization.119  auto IsInSpecialization = hasAncestor(120      decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),121                 functionDecl(isExplicitTemplateSpecialization()))));122 123  // Matchers for both C and C++.124  // We only match symbols from header files, i.e. not from main files (see125  // function's comment for detailed explanation).126  auto CommonFilter =127      allOf(unless(isImplicit()), unless(isExpansionInMainFile()));128 129  auto HasNSOrTUCtxMatcher =130      hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));131 132  // We need separate rules for C record types and C++ record types since some133  // template related matchers are inapplicable on C record declarations.134  //135  // Matchers specific to C++ code.136  // All declarations should be in namespace or translation unit.137  auto CCMatcher =138      allOf(HasNSOrTUCtxMatcher, unless(IsInSpecialization),139            unless(ast_matchers::isTemplateInstantiation()),140            unless(isInstantiated()), unless(isFullySpecialized()));141 142  // Matchers specific to code in extern "C" {...}.143  auto ExternCMatcher = hasDeclContext(linkageSpecDecl());144 145  // Matchers for variable declarations.146  //147  // In most cases, `ParmVarDecl` is filtered out by hasDeclContext(...)148  // matcher since the declaration context is usually `MethodDecl`. However,149  // this assumption does not hold for parameters of a function pointer150  // parameter.151  // For example, consider a function declaration:152  //        void Func(void (*)(float), int);153  // The float parameter of the function pointer has an empty name, and its154  // declaration context is an anonymous namespace; therefore, it won't be155  // filtered out by our matchers above.156  auto Vars = varDecl(CommonFilter, anyOf(ExternCMatcher, CCMatcher),157                      unless(parmVarDecl()));158 159  // Matchers for C-style record declarations in extern "C" {...}.160  auto CRecords = recordDecl(CommonFilter, ExternCMatcher, isDefinition());161  // Matchers for C++ record declarations.162  auto CXXRecords = cxxRecordDecl(CommonFilter, CCMatcher, isDefinition());163 164  // Matchers for function declarations.165  // We want to exclude friend declaration, but the `DeclContext` of a friend166  // function declaration is not the class in which it is declared, so we need167  // to explicitly check if the parent is a `friendDecl`.168  auto Functions = functionDecl(CommonFilter, unless(hasParent(friendDecl())),169                                anyOf(ExternCMatcher, CCMatcher));170 171  // Matcher for typedef and type alias declarations.172  //173  // typedef and type alias can come from C-style headers and C++ headers.174  // For C-style headers, `DeclContxet` can be either `TranslationUnitDecl`175  // or `LinkageSpecDecl`.176  // For C++ headers, `DeclContext ` can be either `TranslationUnitDecl`177  // or `NamespaceDecl`.178  // With the following context matcher, we can match `typedefNameDecl` from179  // both C-style headers and C++ headers (except for those in classes).180  // "cc_matchers" are not included since template-related matchers are not181  // applicable on `TypedefNameDecl`.182  auto Typedefs =183      typedefNameDecl(CommonFilter, anyOf(HasNSOrTUCtxMatcher,184                                          hasDeclContext(linkageSpecDecl())));185 186  // Matchers for enum declarations.187  auto Enums = enumDecl(CommonFilter, isDefinition(),188                        anyOf(HasNSOrTUCtxMatcher, ExternCMatcher));189 190  // Matchers for enum constant declarations.191  // We only match the enum constants in non-scoped enum declarations which are192  // inside toplevel translation unit or a namespace.193  auto EnumConstants = enumConstantDecl(194      CommonFilter, unless(isInScopedEnum()),195      anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher));196 197  // Most of the time we care about all matchable decls, or all types.198  auto Types = namedDecl(anyOf(CRecords, CXXRecords, Enums));199  auto Decls = namedDecl(anyOf(CRecords, CXXRecords, Enums, Typedefs, Vars,200                               EnumConstants, Functions));201 202  // We want eligible decls bound to "decl"...203  MatchFinder->addMatcher(Decls.bind("decl"), this);204 205  // ... and all uses of them bound to "use". These have many cases:206  // Uses of values/functions: these generate a declRefExpr.207  MatchFinder->addMatcher(208      declRefExpr(isExpansionInMainFile(), to(Decls.bind("use"))), this);209  // Uses of function templates:210  MatchFinder->addMatcher(211      declRefExpr(isExpansionInMainFile(),212                  to(functionDecl(hasParent(213                      functionTemplateDecl(has(Functions.bind("use"))))))),214      this);215 216  // Uses of most types: just look at what the typeLoc refers to.217  MatchFinder->addMatcher(218      typeLoc(isExpansionInMainFile(),219              loc(qualType(hasDeclaration(Types.bind("use"))))),220      this);221  // Uses of typedefs: these are often transparent to hasDeclaration, so we need222  // to handle them explicitly.223  MatchFinder->addMatcher(224      typeLoc(isExpansionInMainFile(),225              loc(typedefType(hasDeclaration(Typedefs.bind("use"))))),226      this);227  // Uses of class templates:228  // The typeLoc names the templateSpecializationType. Its declaration is the229  // ClassTemplateDecl, which contains the CXXRecordDecl we want.230  MatchFinder->addMatcher(231      typeLoc(isExpansionInMainFile(),232              loc(templateSpecializationType(hasDeclaration(233                  classTemplateSpecializationDecl(hasSpecializedTemplate(234                      classTemplateDecl(has(CXXRecords.bind("use"))))))))),235      this);236}237 238void FindAllSymbols::run(const MatchFinder::MatchResult &Result) {239  // Ignore Results in failing TUs.240  if (Result.Context->getDiagnostics().hasErrorOccurred()) {241    return;242  }243 244  SymbolInfo::Signals Signals;245  const NamedDecl *ND;246  if ((ND = Result.Nodes.getNodeAs<NamedDecl>("use")))247    Signals.Used = 1;248  else if ((ND = Result.Nodes.getNodeAs<NamedDecl>("decl")))249    Signals.Seen = 1;250  else251    assert(false && "Must match a NamedDecl!");252 253  const SourceManager *SM = Result.SourceManager;254  if (auto Symbol = CreateSymbolInfo(ND, *SM, Collector)) {255    Filename =256        std::string(SM->getFileEntryRefForID(SM->getMainFileID())->getName());257    FileSymbols[*Symbol] += Signals;258  }259}260 261void FindAllSymbols::onEndOfTranslationUnit() {262  if (Filename != "") {263    Reporter->reportSymbols(Filename, FileSymbols);264    FileSymbols.clear();265    Filename = "";266  }267}268 269} // namespace find_all_symbols270} // namespace clang271