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