brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.1 KiB · ed56c2a Raw
241 lines · cpp
1//===--- IndexAction.cpp -----------------------------------------*- 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 "IndexAction.h"10#include "AST.h"11#include "Headers.h"12#include "clang-include-cleaner/Record.h"13#include "index/Relation.h"14#include "index/SymbolCollector.h"15#include "index/SymbolOrigin.h"16#include "clang/AST/ASTConsumer.h"17#include "clang/AST/ASTContext.h"18#include "clang/Basic/SourceLocation.h"19#include "clang/Basic/SourceManager.h"20#include "clang/Frontend/CompilerInstance.h"21#include "clang/Frontend/FrontendAction.h"22#include "clang/Index/IndexingAction.h"23#include "clang/Index/IndexingOptions.h"24#include <cstddef>25#include <functional>26#include <memory>27#include <optional>28#include <utility>29 30namespace clang {31namespace clangd {32namespace {33 34std::optional<std::string> toURI(OptionalFileEntryRef File) {35  if (!File)36    return std::nullopt;37  auto AbsolutePath = File->getFileEntry().tryGetRealPathName();38  if (AbsolutePath.empty())39    return std::nullopt;40  return URI::create(AbsolutePath).toString();41}42 43// Collects the nodes and edges of include graph during indexing action.44// Important: The graph generated by those callbacks might contain cycles and45// self edges.46struct IncludeGraphCollector : public PPCallbacks {47public:48  IncludeGraphCollector(const SourceManager &SM, IncludeGraph &IG)49      : SM(SM), IG(IG) {}50 51  // Populates everything except direct includes for a node, which represents52  // edges in the include graph and populated in inclusion directive.53  // We cannot populate the fields in InclusionDirective because it does not54  // have access to the contents of the included file.55  void FileChanged(SourceLocation Loc, FileChangeReason Reason,56                   SrcMgr::CharacteristicKind FileType,57                   FileID PrevFID) override {58    // We only need to process each file once. So we don't care about anything59    // but entries.60    if (Reason != FileChangeReason::EnterFile)61      return;62 63    const auto FileID = SM.getFileID(Loc);64    auto File = SM.getFileEntryRefForID(FileID);65    auto URI = toURI(File);66    if (!URI)67      return;68    auto I = IG.try_emplace(*URI).first;69 70    auto &Node = I->getValue();71    // Node has already been populated.72    if (Node.URI.data() == I->getKeyData()) {73#ifndef NDEBUG74      auto Digest = digestFile(SM, FileID);75      assert(Digest && Node.Digest == *Digest &&76             "Same file, different digest?");77#endif78      return;79    }80    if (auto Digest = digestFile(SM, FileID))81      Node.Digest = std::move(*Digest);82    if (FileID == SM.getMainFileID())83      Node.Flags |= IncludeGraphNode::SourceFlag::IsTU;84    Node.URI = I->getKey();85  }86 87  // Add edges from including files to includes.88  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,89                          llvm::StringRef FileName, bool IsAngled,90                          CharSourceRange FilenameRange,91                          OptionalFileEntryRef File, llvm::StringRef SearchPath,92                          llvm::StringRef RelativePath,93                          const Module *SuggestedModule, bool ModuleImported,94                          SrcMgr::CharacteristicKind FileType) override {95    auto IncludeURI = toURI(File);96    if (!IncludeURI)97      return;98 99    auto IncludingURI = toURI(SM.getFileEntryRefForID(SM.getFileID(HashLoc)));100    if (!IncludingURI)101      return;102 103    auto NodeForInclude = IG.try_emplace(*IncludeURI).first->getKey();104    auto NodeForIncluding = IG.try_emplace(*IncludingURI);105 106    NodeForIncluding.first->getValue().DirectIncludes.push_back(NodeForInclude);107  }108 109  // Sanity check to ensure we have already populated a skipped file.110  void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,111                   SrcMgr::CharacteristicKind FileType) override {112#ifndef NDEBUG113    auto URI = toURI(SkippedFile);114    if (!URI)115      return;116    auto I = IG.try_emplace(*URI);117    assert(!I.second && "File inserted for the first time on skip.");118    assert(I.first->getKeyData() == I.first->getValue().URI.data() &&119           "Node have not been populated yet");120#endif121  }122 123private:124  const SourceManager &SM;125  IncludeGraph &IG;126};127 128// Wraps the index action and reports index data after each translation unit.129class IndexAction : public ASTFrontendAction {130public:131  IndexAction(std::shared_ptr<SymbolCollector> C,132              std::unique_ptr<include_cleaner::PragmaIncludes> PI,133              const index::IndexingOptions &Opts,134              std::function<void(SymbolSlab)> SymbolsCallback,135              std::function<void(RefSlab)> RefsCallback,136              std::function<void(RelationSlab)> RelationsCallback,137              std::function<void(IncludeGraph)> IncludeGraphCallback)138      : SymbolsCallback(SymbolsCallback), RefsCallback(RefsCallback),139        RelationsCallback(RelationsCallback),140        IncludeGraphCallback(IncludeGraphCallback), Collector(C),141        PI(std::move(PI)), Opts(Opts) {142    this->Opts.ShouldTraverseDecl = [this](const Decl *D) {143      // Many operations performed during indexing is linear in terms of depth144      // of the decl (USR generation, name lookups, figuring out role of a145      // reference are some examples). Since we index all the decls nested146      // inside, it becomes quadratic. So we give up on nested symbols.147      if (isDeeplyNested(D))148        return false;149      auto &SM = D->getASTContext().getSourceManager();150      auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));151      if (!FID.isValid())152        return true;153      return Collector->shouldIndexFile(FID);154    };155  }156 157  std::unique_ptr<ASTConsumer>158  CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {159    PI->record(CI.getPreprocessor());160    if (IncludeGraphCallback != nullptr)161      CI.getPreprocessor().addPPCallbacks(162          std::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));163 164    return index::createIndexingASTConsumer(Collector, Opts,165                                            CI.getPreprocessorPtr());166  }167 168  bool BeginInvocation(CompilerInstance &CI) override {169    // We want all comments, not just the doxygen ones.170    CI.getLangOpts().CommentOpts.ParseAllComments = true;171    CI.getLangOpts().RetainCommentsFromSystemHeaders = true;172    // Index the whole file even if there are warnings and -Werror is set.173    // Avoids some analyses too. Set in two places as we're late to the party.174    CI.getDiagnosticOpts().IgnoreWarnings = true;175    CI.getDiagnostics().setIgnoreAllWarnings(true);176    // Instruct the parser to ask our ASTConsumer if it should skip function177    // bodies. The ASTConsumer will take care of skipping only functions inside178    // the files that we have already processed.179    CI.getFrontendOpts().SkipFunctionBodies = true;180    return true;181  }182 183  void EndSourceFileAction() override {184    SymbolsCallback(Collector->takeSymbols());185    if (RefsCallback != nullptr)186      RefsCallback(Collector->takeRefs());187    if (RelationsCallback != nullptr)188      RelationsCallback(Collector->takeRelations());189    if (IncludeGraphCallback != nullptr) {190#ifndef NDEBUG191      // This checks if all nodes are initialized.192      for (const auto &Node : IG)193        assert(Node.getKeyData() == Node.getValue().URI.data());194#endif195      IncludeGraphCallback(std::move(IG));196    }197  }198 199private:200  std::function<void(SymbolSlab)> SymbolsCallback;201  std::function<void(RefSlab)> RefsCallback;202  std::function<void(RelationSlab)> RelationsCallback;203  std::function<void(IncludeGraph)> IncludeGraphCallback;204  std::shared_ptr<SymbolCollector> Collector;205  std::unique_ptr<include_cleaner::PragmaIncludes> PI;206  index::IndexingOptions Opts;207  IncludeGraph IG;208};209 210} // namespace211 212std::unique_ptr<FrontendAction> createStaticIndexingAction(213    SymbolCollector::Options Opts,214    std::function<void(SymbolSlab)> SymbolsCallback,215    std::function<void(RefSlab)> RefsCallback,216    std::function<void(RelationSlab)> RelationsCallback,217    std::function<void(IncludeGraph)> IncludeGraphCallback) {218  index::IndexingOptions IndexOpts;219  IndexOpts.SystemSymbolFilter =220      index::IndexingOptions::SystemSymbolFilterKind::All;221  // We index function-local classes and its member functions only.222  IndexOpts.IndexFunctionLocals = true;223  Opts.CollectIncludePath = true;224  if (Opts.Origin == SymbolOrigin::Unknown)225    Opts.Origin = SymbolOrigin::Static;226  Opts.StoreAllDocumentation = false;227  if (RefsCallback != nullptr) {228    Opts.RefFilter = RefKind::All;229    Opts.RefsInHeaders = true;230  }231  auto PragmaIncludes = std::make_unique<include_cleaner::PragmaIncludes>();232  Opts.PragmaIncludes = PragmaIncludes.get();233  return std::make_unique<IndexAction>(std::make_shared<SymbolCollector>(Opts),234                                       std::move(PragmaIncludes), IndexOpts,235                                       SymbolsCallback, RefsCallback,236                                       RelationsCallback, IncludeGraphCallback);237}238 239} // namespace clangd240} // namespace clang241