1*12c85518Srobert //===- ClangExtDefMapGen.cpp ---------------------------------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===--------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // Clang tool which creates a list of defined functions and the files in which
10e5dd7070Spatrick // they are defined.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===--------------------------------------------------------------------===//
13e5dd7070Spatrick
14e5dd7070Spatrick #include "clang/AST/ASTConsumer.h"
15e5dd7070Spatrick #include "clang/AST/ASTContext.h"
16*12c85518Srobert #include "clang/Basic/DiagnosticOptions.h"
17e5dd7070Spatrick #include "clang/Basic/SourceManager.h"
18e5dd7070Spatrick #include "clang/CrossTU/CrossTranslationUnit.h"
19e5dd7070Spatrick #include "clang/Frontend/CompilerInstance.h"
20e5dd7070Spatrick #include "clang/Frontend/FrontendActions.h"
21*12c85518Srobert #include "clang/Frontend/TextDiagnosticPrinter.h"
22e5dd7070Spatrick #include "clang/Tooling/CommonOptionsParser.h"
23e5dd7070Spatrick #include "clang/Tooling/Tooling.h"
24e5dd7070Spatrick #include "llvm/Support/CommandLine.h"
25e5dd7070Spatrick #include "llvm/Support/Signals.h"
26*12c85518Srobert #include <optional>
27e5dd7070Spatrick #include <sstream>
28e5dd7070Spatrick #include <string>
29e5dd7070Spatrick
30e5dd7070Spatrick using namespace llvm;
31e5dd7070Spatrick using namespace clang;
32e5dd7070Spatrick using namespace clang::cross_tu;
33e5dd7070Spatrick using namespace clang::tooling;
34e5dd7070Spatrick
35*12c85518Srobert static cl::OptionCategory
36*12c85518Srobert ClangExtDefMapGenCategory("clang-extdefmapgen options");
37e5dd7070Spatrick
38e5dd7070Spatrick class MapExtDefNamesConsumer : public ASTConsumer {
39e5dd7070Spatrick public:
MapExtDefNamesConsumer(ASTContext & Context,StringRef astFilePath=StringRef ())40*12c85518Srobert MapExtDefNamesConsumer(ASTContext &Context,
41*12c85518Srobert StringRef astFilePath = StringRef())
42*12c85518Srobert : Ctx(Context), SM(Context.getSourceManager()) {
43*12c85518Srobert CurrentFileName = astFilePath.str();
44*12c85518Srobert }
45e5dd7070Spatrick
~MapExtDefNamesConsumer()46e5dd7070Spatrick ~MapExtDefNamesConsumer() {
47e5dd7070Spatrick // Flush results to standard output.
48e5dd7070Spatrick llvm::outs() << createCrossTUIndexString(Index);
49e5dd7070Spatrick }
50e5dd7070Spatrick
HandleTranslationUnit(ASTContext & Context)51e5dd7070Spatrick void HandleTranslationUnit(ASTContext &Context) override {
52e5dd7070Spatrick handleDecl(Context.getTranslationUnitDecl());
53e5dd7070Spatrick }
54e5dd7070Spatrick
55e5dd7070Spatrick private:
56e5dd7070Spatrick void handleDecl(const Decl *D);
57e5dd7070Spatrick void addIfInMain(const DeclaratorDecl *DD, SourceLocation defStart);
58e5dd7070Spatrick
59e5dd7070Spatrick ASTContext &Ctx;
60e5dd7070Spatrick SourceManager &SM;
61e5dd7070Spatrick llvm::StringMap<std::string> Index;
62e5dd7070Spatrick std::string CurrentFileName;
63e5dd7070Spatrick };
64e5dd7070Spatrick
handleDecl(const Decl * D)65e5dd7070Spatrick void MapExtDefNamesConsumer::handleDecl(const Decl *D) {
66e5dd7070Spatrick if (!D)
67e5dd7070Spatrick return;
68e5dd7070Spatrick
69e5dd7070Spatrick if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
70e5dd7070Spatrick if (FD->isThisDeclarationADefinition())
71e5dd7070Spatrick if (const Stmt *Body = FD->getBody())
72e5dd7070Spatrick addIfInMain(FD, Body->getBeginLoc());
73e5dd7070Spatrick } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
74*12c85518Srobert if (cross_tu::shouldImport(VD, Ctx) && VD->hasInit())
75e5dd7070Spatrick if (const Expr *Init = VD->getInit())
76e5dd7070Spatrick addIfInMain(VD, Init->getBeginLoc());
77e5dd7070Spatrick }
78e5dd7070Spatrick
79e5dd7070Spatrick if (const auto *DC = dyn_cast<DeclContext>(D))
80e5dd7070Spatrick for (const Decl *D : DC->decls())
81e5dd7070Spatrick handleDecl(D);
82e5dd7070Spatrick }
83e5dd7070Spatrick
addIfInMain(const DeclaratorDecl * DD,SourceLocation defStart)84e5dd7070Spatrick void MapExtDefNamesConsumer::addIfInMain(const DeclaratorDecl *DD,
85e5dd7070Spatrick SourceLocation defStart) {
86*12c85518Srobert std::optional<std::string> LookupName =
87e5dd7070Spatrick CrossTranslationUnitContext::getLookupName(DD);
88e5dd7070Spatrick if (!LookupName)
89e5dd7070Spatrick return;
90e5dd7070Spatrick assert(!LookupName->empty() && "Lookup name should be non-empty.");
91e5dd7070Spatrick
92e5dd7070Spatrick if (CurrentFileName.empty()) {
93ec727ea7Spatrick CurrentFileName = std::string(
94ec727ea7Spatrick SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName());
95e5dd7070Spatrick if (CurrentFileName.empty())
96e5dd7070Spatrick CurrentFileName = "invalid_file";
97e5dd7070Spatrick }
98e5dd7070Spatrick
99e5dd7070Spatrick switch (DD->getLinkageInternal()) {
100e5dd7070Spatrick case ExternalLinkage:
101e5dd7070Spatrick case VisibleNoLinkage:
102e5dd7070Spatrick case UniqueExternalLinkage:
103e5dd7070Spatrick if (SM.isInMainFile(defStart))
104e5dd7070Spatrick Index[*LookupName] = CurrentFileName;
105e5dd7070Spatrick break;
106e5dd7070Spatrick default:
107e5dd7070Spatrick break;
108e5dd7070Spatrick }
109e5dd7070Spatrick }
110e5dd7070Spatrick
111e5dd7070Spatrick class MapExtDefNamesAction : public ASTFrontendAction {
112e5dd7070Spatrick protected:
CreateASTConsumer(CompilerInstance & CI,llvm::StringRef)113e5dd7070Spatrick std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
114a9ac8606Spatrick llvm::StringRef) override {
115e5dd7070Spatrick return std::make_unique<MapExtDefNamesConsumer>(CI.getASTContext());
116e5dd7070Spatrick }
117e5dd7070Spatrick };
118e5dd7070Spatrick
119e5dd7070Spatrick static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
120e5dd7070Spatrick
121*12c85518Srobert static IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
122*12c85518Srobert
GetDiagnosticsEngine()123*12c85518Srobert IntrusiveRefCntPtr<DiagnosticsEngine> GetDiagnosticsEngine() {
124*12c85518Srobert if (Diags) {
125*12c85518Srobert // Call reset to make sure we don't mix errors
126*12c85518Srobert Diags->Reset(false);
127*12c85518Srobert return Diags;
128*12c85518Srobert }
129*12c85518Srobert
130*12c85518Srobert IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
131*12c85518Srobert TextDiagnosticPrinter *DiagClient =
132*12c85518Srobert new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
133*12c85518Srobert DiagClient->setPrefix("clang-extdef-mappping");
134*12c85518Srobert IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
135*12c85518Srobert
136*12c85518Srobert IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
137*12c85518Srobert new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
138*12c85518Srobert Diags.swap(DiagEngine);
139*12c85518Srobert
140*12c85518Srobert // Retain this one time so it's not destroyed by ASTUnit::LoadFromASTFile
141*12c85518Srobert Diags->Retain();
142*12c85518Srobert return Diags;
143*12c85518Srobert }
144*12c85518Srobert
145*12c85518Srobert static CompilerInstance *CI = nullptr;
146*12c85518Srobert
HandleAST(StringRef AstPath)147*12c85518Srobert static bool HandleAST(StringRef AstPath) {
148*12c85518Srobert
149*12c85518Srobert if (!CI)
150*12c85518Srobert CI = new CompilerInstance();
151*12c85518Srobert
152*12c85518Srobert IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine = GetDiagnosticsEngine();
153*12c85518Srobert
154*12c85518Srobert std::unique_ptr<ASTUnit> Unit = ASTUnit::LoadFromASTFile(
155*12c85518Srobert AstPath.str(), CI->getPCHContainerOperations()->getRawReader(),
156*12c85518Srobert ASTUnit::LoadASTOnly, DiagEngine, CI->getFileSystemOpts());
157*12c85518Srobert
158*12c85518Srobert if (!Unit)
159*12c85518Srobert return false;
160*12c85518Srobert
161*12c85518Srobert FileManager FM(CI->getFileSystemOpts());
162*12c85518Srobert SmallString<128> AbsPath(AstPath);
163*12c85518Srobert FM.makeAbsolutePath(AbsPath);
164*12c85518Srobert
165*12c85518Srobert MapExtDefNamesConsumer Consumer =
166*12c85518Srobert MapExtDefNamesConsumer(Unit->getASTContext(), AbsPath);
167*12c85518Srobert Consumer.HandleTranslationUnit(Unit->getASTContext());
168*12c85518Srobert
169*12c85518Srobert return true;
170*12c85518Srobert }
171*12c85518Srobert
HandleFiles(ArrayRef<std::string> SourceFiles,CompilationDatabase & compilations)172*12c85518Srobert static int HandleFiles(ArrayRef<std::string> SourceFiles,
173*12c85518Srobert CompilationDatabase &compilations) {
174*12c85518Srobert std::vector<std::string> SourcesToBeParsed;
175*12c85518Srobert
176*12c85518Srobert // Loop over all input files, if they are pre-compiled AST
177*12c85518Srobert // process them directly in HandleAST, otherwise put them
178*12c85518Srobert // on a list for ClangTool to handle.
179*12c85518Srobert for (StringRef Src : SourceFiles) {
180*12c85518Srobert if (Src.endswith(".ast")) {
181*12c85518Srobert if (!HandleAST(Src)) {
182*12c85518Srobert return 1;
183*12c85518Srobert }
184*12c85518Srobert } else {
185*12c85518Srobert SourcesToBeParsed.push_back(Src.str());
186*12c85518Srobert }
187*12c85518Srobert }
188*12c85518Srobert
189*12c85518Srobert if (!SourcesToBeParsed.empty()) {
190*12c85518Srobert ClangTool Tool(compilations, SourcesToBeParsed);
191*12c85518Srobert return Tool.run(newFrontendActionFactory<MapExtDefNamesAction>().get());
192*12c85518Srobert }
193*12c85518Srobert
194*12c85518Srobert return 0;
195*12c85518Srobert }
196*12c85518Srobert
main(int argc,const char ** argv)197e5dd7070Spatrick int main(int argc, const char **argv) {
198e5dd7070Spatrick // Print a stack trace if we signal out.
199e5dd7070Spatrick sys::PrintStackTraceOnErrorSignal(argv[0], false);
200e5dd7070Spatrick PrettyStackTraceProgram X(argc, argv);
201e5dd7070Spatrick
202e5dd7070Spatrick const char *Overview = "\nThis tool collects the USR name and location "
203e5dd7070Spatrick "of external definitions in the source files "
204*12c85518Srobert "(excluding headers).\n"
205*12c85518Srobert "Input can be either source files that are compiled "
206*12c85518Srobert "with compile database or .ast files that are "
207*12c85518Srobert "created from clang's -emit-ast option.\n";
208a9ac8606Spatrick auto ExpectedParser = CommonOptionsParser::create(
209a9ac8606Spatrick argc, argv, ClangExtDefMapGenCategory, cl::ZeroOrMore, Overview);
210a9ac8606Spatrick if (!ExpectedParser) {
211a9ac8606Spatrick llvm::errs() << ExpectedParser.takeError();
212a9ac8606Spatrick return 1;
213a9ac8606Spatrick }
214a9ac8606Spatrick CommonOptionsParser &OptionsParser = ExpectedParser.get();
215e5dd7070Spatrick
216*12c85518Srobert return HandleFiles(OptionsParser.getSourcePathList(),
217*12c85518Srobert OptionsParser.getCompilations());
218e5dd7070Spatrick }
219