xref: /openbsd-src/gnu/llvm/clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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