1 //===--- IndexerMain.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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // clangd-indexer is a tool to gather index data (symbols, xrefs) from source.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "index/IndexAction.h"
14 #include "index/Merge.h"
15 #include "index/Ref.h"
16 #include "index/Serialization.h"
17 #include "index/Symbol.h"
18 #include "index/SymbolCollector.h"
19 #include "support/Logger.h"
20 #include "clang/Tooling/ArgumentsAdjusters.h"
21 #include "clang/Tooling/CommonOptionsParser.h"
22 #include "clang/Tooling/Execution.h"
23 #include "clang/Tooling/Tooling.h"
24 #include "llvm/Support/CommandLine.h"
25 #include "llvm/Support/Signals.h"
26 
27 namespace clang {
28 namespace clangd {
29 namespace {
30 
31 static llvm::cl::opt<IndexFileFormat>
32     Format("format", llvm::cl::desc("Format of the index to be written"),
33            llvm::cl::values(clEnumValN(IndexFileFormat::YAML, "yaml",
34                                        "human-readable YAML format"),
35                             clEnumValN(IndexFileFormat::RIFF, "binary",
36                                        "binary RIFF format")),
37            llvm::cl::init(IndexFileFormat::RIFF));
38 
39 class IndexActionFactory : public tooling::FrontendActionFactory {
40 public:
IndexActionFactory(IndexFileIn & Result)41   IndexActionFactory(IndexFileIn &Result) : Result(Result) {}
42 
create()43   std::unique_ptr<FrontendAction> create() override {
44     SymbolCollector::Options Opts;
45     Opts.CountReferences = true;
46     Opts.FileFilter = [&](const SourceManager &SM, FileID FID) {
47       const auto *F = SM.getFileEntryForID(FID);
48       if (!F)
49         return false; // Skip invalid files.
50       auto AbsPath = getCanonicalPath(F, SM);
51       if (!AbsPath)
52         return false; // Skip files without absolute path.
53       std::lock_guard<std::mutex> Lock(FilesMu);
54       return Files.insert(*AbsPath).second; // Skip already processed files.
55     };
56     return createStaticIndexingAction(
57         Opts,
58         [&](SymbolSlab S) {
59           // Merge as we go.
60           std::lock_guard<std::mutex> Lock(SymbolsMu);
61           for (const auto &Sym : S) {
62             if (const auto *Existing = Symbols.find(Sym.ID))
63               Symbols.insert(mergeSymbol(*Existing, Sym));
64             else
65               Symbols.insert(Sym);
66           }
67         },
68         [&](RefSlab S) {
69           std::lock_guard<std::mutex> Lock(RefsMu);
70           for (const auto &Sym : S) {
71             // Deduplication happens during insertion.
72             for (const auto &Ref : Sym.second)
73               Refs.insert(Sym.first, Ref);
74           }
75         },
76         [&](RelationSlab S) {
77           std::lock_guard<std::mutex> Lock(RelsMu);
78           for (const auto &R : S) {
79             Relations.insert(R);
80           }
81         },
82         /*IncludeGraphCallback=*/nullptr);
83   }
84 
85   // Awkward: we write the result in the destructor, because the executor
86   // takes ownership so it's the easiest way to get our data back out.
~IndexActionFactory()87   ~IndexActionFactory() {
88     Result.Symbols = std::move(Symbols).build();
89     Result.Refs = std::move(Refs).build();
90     Result.Relations = std::move(Relations).build();
91   }
92 
93 private:
94   IndexFileIn &Result;
95   std::mutex FilesMu;
96   llvm::StringSet<> Files;
97   std::mutex SymbolsMu;
98   SymbolSlab::Builder Symbols;
99   std::mutex RefsMu;
100   RefSlab::Builder Refs;
101   std::mutex RelsMu;
102   RelationSlab::Builder Relations;
103 };
104 
105 } // namespace
106 } // namespace clangd
107 } // namespace clang
108 
main(int argc,const char ** argv)109 int main(int argc, const char **argv) {
110   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
111 
112   const char *Overview = R"(
113   Creates an index of symbol information etc in a whole project.
114 
115   Example usage for a project using CMake compile commands:
116 
117   $ clangd-indexer --executor=all-TUs compile_commands.json > clangd.dex
118 
119   Example usage for file sequence index without flags:
120 
121   $ clangd-indexer File1.cpp File2.cpp ... FileN.cpp > clangd.dex
122 
123   Note: only symbols from header files will be indexed.
124   )";
125 
126   auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
127       argc, argv, llvm::cl::GeneralCategory, Overview);
128 
129   if (!Executor) {
130     llvm::errs() << llvm::toString(Executor.takeError()) << "\n";
131     return 1;
132   }
133 
134   // Collect symbols found in each translation unit, merging as we go.
135   clang::clangd::IndexFileIn Data;
136   auto Err = Executor->get()->execute(
137       std::make_unique<clang::clangd::IndexActionFactory>(Data),
138       clang::tooling::getStripPluginsAdjuster());
139   if (Err) {
140     clang::clangd::elog("{0}", std::move(Err));
141   }
142 
143   // Emit collected data.
144   clang::clangd::IndexFileOut Out(Data);
145   Out.Format = clang::clangd::Format;
146   llvm::outs() << Out;
147   return 0;
148 }
149