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 ExternalLinkage
:
102 case VisibleNoLinkage
:
103 case UniqueExternalLinkage
:
104 if (SM
.isInMainFile(defStart
))
105 Index
[*LookupName
] = CurrentFileName
;
112 class MapExtDefNamesAction
: public ASTFrontendAction
{
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() {
126 // Call reset to make sure we don't mix errors
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
146 static CompilerInstance
*CI
= nullptr;
148 static bool HandleAST(StringRef AstPath
) {
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());
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());
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
)) {
187 SourcesToBeParsed
.push_back(Src
.str());
191 if (!SourcesToBeParsed
.empty()) {
192 ClangTool
Tool(compilations
, SourcesToBeParsed
);
193 return Tool
.run(newFrontendActionFactory
<MapExtDefNamesAction
>().get());
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();
216 CommonOptionsParser
&OptionsParser
= ExpectedParser
.get();
218 llvm::InitializeAllTargetInfos();
219 llvm::InitializeAllTargetMCs();
220 llvm::InitializeAllAsmParsers();
222 return HandleFiles(OptionsParser
.getSourcePathList(),
223 OptionsParser
.getCompilations());