1 //===- ClangExtDefMapGen.cpp ---------------------------------------------===//
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
7 //===--------------------------------------------------------------------===//
9 // Clang tool which creates a list of defined functions and the files in which
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"
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
{
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());
57 void handleDecl(const Decl
*D
);
58 void addIfInMain(const DeclaratorDecl
*DD
, SourceLocation defStart
);
62 llvm::StringMap
<std::string
> Index
;
63 std::string CurrentFileName
;
66 void MapExtDefNamesConsumer::handleDecl(const Decl
*D
) {
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())
85 void MapExtDefNamesConsumer::addIfInMain(const DeclaratorDecl
*DD
,
86 SourceLocation defStart
) {
87 std::optional
<std::string
> LookupName
=
88 CrossTranslationUnitContext::getLookupName(DD
);
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 Linkage::External
:
102 case Linkage::VisibleNone
:
103 case Linkage::UniqueExternal
:
104 if (SM
.isInMainFile(defStart
))
105 Index
[*LookupName
] = CurrentFileName
;
107 case Linkage::Invalid
:
108 llvm_unreachable("Linkage has not been computed!");
114 class MapExtDefNamesAction
: public ASTFrontendAction
{
116 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&CI
,
117 llvm::StringRef
) override
{
118 return std::make_unique
<MapExtDefNamesConsumer
>(CI
.getASTContext());
122 static cl::extrahelp
CommonHelp(CommonOptionsParser::HelpMessage
);
124 static IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags
;
126 IntrusiveRefCntPtr
<DiagnosticsEngine
> GetDiagnosticsEngine() {
128 // Call reset to make sure we don't mix errors
133 IntrusiveRefCntPtr
<DiagnosticOptions
> DiagOpts
= new DiagnosticOptions();
134 TextDiagnosticPrinter
*DiagClient
=
135 new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts
);
136 DiagClient
->setPrefix("clang-extdef-mappping");
137 IntrusiveRefCntPtr
<DiagnosticIDs
> DiagID(new DiagnosticIDs());
139 IntrusiveRefCntPtr
<DiagnosticsEngine
> DiagEngine(
140 new DiagnosticsEngine(DiagID
, &*DiagOpts
, DiagClient
));
141 Diags
.swap(DiagEngine
);
143 // Retain this one time so it's not destroyed by ASTUnit::LoadFromASTFile
148 static CompilerInstance
*CI
= nullptr;
150 static bool HandleAST(StringRef AstPath
) {
153 CI
= new CompilerInstance();
155 IntrusiveRefCntPtr
<DiagnosticsEngine
> DiagEngine
= GetDiagnosticsEngine();
157 std::unique_ptr
<ASTUnit
> Unit
= ASTUnit::LoadFromASTFile(
158 AstPath
, CI
->getPCHContainerOperations()->getRawReader(),
159 ASTUnit::LoadASTOnly
, DiagEngine
, CI
->getFileSystemOpts(),
160 CI
->getHeaderSearchOptsPtr());
165 FileManager
FM(CI
->getFileSystemOpts());
166 SmallString
<128> AbsPath(AstPath
);
167 FM
.makeAbsolutePath(AbsPath
);
169 MapExtDefNamesConsumer Consumer
=
170 MapExtDefNamesConsumer(Unit
->getASTContext(), AbsPath
);
171 Consumer
.HandleTranslationUnit(Unit
->getASTContext());
176 static int HandleFiles(ArrayRef
<std::string
> SourceFiles
,
177 CompilationDatabase
&compilations
) {
178 std::vector
<std::string
> SourcesToBeParsed
;
180 // Loop over all input files, if they are pre-compiled AST
181 // process them directly in HandleAST, otherwise put them
182 // on a list for ClangTool to handle.
183 for (StringRef Src
: SourceFiles
) {
184 if (Src
.ends_with(".ast")) {
185 if (!HandleAST(Src
)) {
189 SourcesToBeParsed
.push_back(Src
.str());
193 if (!SourcesToBeParsed
.empty()) {
194 ClangTool
Tool(compilations
, SourcesToBeParsed
);
195 return Tool
.run(newFrontendActionFactory
<MapExtDefNamesAction
>().get());
201 int main(int argc
, const char **argv
) {
202 // Print a stack trace if we signal out.
203 sys::PrintStackTraceOnErrorSignal(argv
[0], false);
204 PrettyStackTraceProgram
X(argc
, argv
);
206 const char *Overview
= "\nThis tool collects the USR name and location "
207 "of external definitions in the source files "
208 "(excluding headers).\n"
209 "Input can be either source files that are compiled "
210 "with compile database or .ast files that are "
211 "created from clang's -emit-ast option.\n";
212 auto ExpectedParser
= CommonOptionsParser::create(
213 argc
, argv
, ClangExtDefMapGenCategory
, cl::ZeroOrMore
, Overview
);
214 if (!ExpectedParser
) {
215 llvm::errs() << ExpectedParser
.takeError();
218 CommonOptionsParser
&OptionsParser
= ExpectedParser
.get();
220 llvm::InitializeAllTargetInfos();
221 llvm::InitializeAllTargetMCs();
222 llvm::InitializeAllAsmParsers();
224 return HandleFiles(OptionsParser
.getSourcePathList(),
225 OptionsParser
.getCompilations());