Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / tools / clang-extdef-mapping / ClangExtDefMapGen.cpp
blob2d4f398f76f6d7e176cc00288781e99e63716287
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.
12 //===--------------------------------------------------------------------===//
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 "llvm/Support/TargetSelect.h"
27 #include <optional>
28 #include <sstream>
29 #include <string>
31 using namespace llvm;
32 using namespace clang;
33 using namespace clang::cross_tu;
34 using namespace clang::tooling;
36 static cl::OptionCategory
37 ClangExtDefMapGenCategory("clang-extdefmapgen options");
39 class MapExtDefNamesConsumer : public ASTConsumer {
40 public:
41 MapExtDefNamesConsumer(ASTContext &Context,
42 StringRef astFilePath = StringRef())
43 : Ctx(Context), SM(Context.getSourceManager()) {
44 CurrentFileName = astFilePath.str();
47 ~MapExtDefNamesConsumer() {
48 // Flush results to standard output.
49 llvm::outs() << createCrossTUIndexString(Index);
52 void HandleTranslationUnit(ASTContext &Context) override {
53 handleDecl(Context.getTranslationUnitDecl());
56 private:
57 void handleDecl(const Decl *D);
58 void addIfInMain(const DeclaratorDecl *DD, SourceLocation defStart);
60 ASTContext &Ctx;
61 SourceManager &SM;
62 llvm::StringMap<std::string> Index;
63 std::string CurrentFileName;
66 void MapExtDefNamesConsumer::handleDecl(const Decl *D) {
67 if (!D)
68 return;
70 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
71 if (FD->isThisDeclarationADefinition())
72 if (const Stmt *Body = FD->getBody())
73 addIfInMain(FD, Body->getBeginLoc());
74 } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
75 if (cross_tu::shouldImport(VD, Ctx) && VD->hasInit())
76 if (const Expr *Init = VD->getInit())
77 addIfInMain(VD, Init->getBeginLoc());
80 if (const auto *DC = dyn_cast<DeclContext>(D))
81 for (const Decl *D : DC->decls())
82 handleDecl(D);
85 void MapExtDefNamesConsumer::addIfInMain(const DeclaratorDecl *DD,
86 SourceLocation defStart) {
87 std::optional<std::string> LookupName =
88 CrossTranslationUnitContext::getLookupName(DD);
89 if (!LookupName)
90 return;
91 assert(!LookupName->empty() && "Lookup name should be non-empty.");
93 if (CurrentFileName.empty()) {
94 CurrentFileName = std::string(
95 SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName());
96 if (CurrentFileName.empty())
97 CurrentFileName = "invalid_file";
100 switch (DD->getLinkageInternal()) {
101 case ExternalLinkage:
102 case VisibleNoLinkage:
103 case UniqueExternalLinkage:
104 if (SM.isInMainFile(defStart))
105 Index[*LookupName] = CurrentFileName;
106 break;
107 default:
108 break;
112 class MapExtDefNamesAction : public ASTFrontendAction {
113 protected:
114 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
115 llvm::StringRef) override {
116 return std::make_unique<MapExtDefNamesConsumer>(CI.getASTContext());
120 static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
122 static IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
124 IntrusiveRefCntPtr<DiagnosticsEngine> GetDiagnosticsEngine() {
125 if (Diags) {
126 // Call reset to make sure we don't mix errors
127 Diags->Reset(false);
128 return Diags;
131 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
132 TextDiagnosticPrinter *DiagClient =
133 new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
134 DiagClient->setPrefix("clang-extdef-mappping");
135 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
137 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
138 new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
139 Diags.swap(DiagEngine);
141 // Retain this one time so it's not destroyed by ASTUnit::LoadFromASTFile
142 Diags->Retain();
143 return Diags;
146 static CompilerInstance *CI = nullptr;
148 static bool HandleAST(StringRef AstPath) {
150 if (!CI)
151 CI = new CompilerInstance();
153 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine = GetDiagnosticsEngine();
155 std::unique_ptr<ASTUnit> Unit = ASTUnit::LoadFromASTFile(
156 AstPath.str(), CI->getPCHContainerOperations()->getRawReader(),
157 ASTUnit::LoadASTOnly, DiagEngine, CI->getFileSystemOpts(),
158 CI->getHeaderSearchOptsPtr());
160 if (!Unit)
161 return false;
163 FileManager FM(CI->getFileSystemOpts());
164 SmallString<128> AbsPath(AstPath);
165 FM.makeAbsolutePath(AbsPath);
167 MapExtDefNamesConsumer Consumer =
168 MapExtDefNamesConsumer(Unit->getASTContext(), AbsPath);
169 Consumer.HandleTranslationUnit(Unit->getASTContext());
171 return true;
174 static int HandleFiles(ArrayRef<std::string> SourceFiles,
175 CompilationDatabase &compilations) {
176 std::vector<std::string> SourcesToBeParsed;
178 // Loop over all input files, if they are pre-compiled AST
179 // process them directly in HandleAST, otherwise put them
180 // on a list for ClangTool to handle.
181 for (StringRef Src : SourceFiles) {
182 if (Src.endswith(".ast")) {
183 if (!HandleAST(Src)) {
184 return 1;
186 } else {
187 SourcesToBeParsed.push_back(Src.str());
191 if (!SourcesToBeParsed.empty()) {
192 ClangTool Tool(compilations, SourcesToBeParsed);
193 return Tool.run(newFrontendActionFactory<MapExtDefNamesAction>().get());
196 return 0;
199 int main(int argc, const char **argv) {
200 // Print a stack trace if we signal out.
201 sys::PrintStackTraceOnErrorSignal(argv[0], false);
202 PrettyStackTraceProgram X(argc, argv);
204 const char *Overview = "\nThis tool collects the USR name and location "
205 "of external definitions in the source files "
206 "(excluding headers).\n"
207 "Input can be either source files that are compiled "
208 "with compile database or .ast files that are "
209 "created from clang's -emit-ast option.\n";
210 auto ExpectedParser = CommonOptionsParser::create(
211 argc, argv, ClangExtDefMapGenCategory, cl::ZeroOrMore, Overview);
212 if (!ExpectedParser) {
213 llvm::errs() << ExpectedParser.takeError();
214 return 1;
216 CommonOptionsParser &OptionsParser = ExpectedParser.get();
218 llvm::InitializeAllTargetInfos();
219 llvm::InitializeAllTargetMCs();
220 llvm::InitializeAllAsmParsers();
222 return HandleFiles(OptionsParser.getSourcePathList(),
223 OptionsParser.getCompilations());