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/SourceManager.h"
17 #include "clang/CrossTU/CrossTranslationUnit.h"
18 #include "clang/Frontend/CompilerInstance.h"
19 #include "clang/Frontend/FrontendActions.h"
20 #include "clang/Tooling/CommonOptionsParser.h"
21 #include "clang/Tooling/Tooling.h"
22 #include "llvm/Support/CommandLine.h"
23 #include "llvm/Support/Signals.h"
24 #include <sstream>
25 #include <string>
26 
27 using namespace llvm;
28 using namespace clang;
29 using namespace clang::cross_tu;
30 using namespace clang::tooling;
31 
32 static cl::OptionCategory ClangExtDefMapGenCategory("clang-extdefmapgen options");
33 
34 class MapExtDefNamesConsumer : public ASTConsumer {
35 public:
36   MapExtDefNamesConsumer(ASTContext &Context)
37       : Ctx(Context), SM(Context.getSourceManager()) {}
38 
39   ~MapExtDefNamesConsumer() {
40     // Flush results to standard output.
41     llvm::outs() << createCrossTUIndexString(Index);
42   }
43 
44   void HandleTranslationUnit(ASTContext &Context) override {
45     handleDecl(Context.getTranslationUnitDecl());
46   }
47 
48 private:
49   void handleDecl(const Decl *D);
50   void addIfInMain(const DeclaratorDecl *DD, SourceLocation defStart);
51 
52   ASTContext &Ctx;
53   SourceManager &SM;
54   llvm::StringMap<std::string> Index;
55   std::string CurrentFileName;
56 };
57 
58 void MapExtDefNamesConsumer::handleDecl(const Decl *D) {
59   if (!D)
60     return;
61 
62   if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
63     if (FD->isThisDeclarationADefinition())
64       if (const Stmt *Body = FD->getBody())
65         addIfInMain(FD, Body->getBeginLoc());
66   } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
67     if (cross_tu::containsConst(VD, Ctx) && VD->hasInit())
68       if (const Expr *Init = VD->getInit())
69         addIfInMain(VD, Init->getBeginLoc());
70   }
71 
72   if (const auto *DC = dyn_cast<DeclContext>(D))
73     for (const Decl *D : DC->decls())
74       handleDecl(D);
75 }
76 
77 void MapExtDefNamesConsumer::addIfInMain(const DeclaratorDecl *DD,
78                                          SourceLocation defStart) {
79   llvm::Optional<std::string> LookupName =
80       CrossTranslationUnitContext::getLookupName(DD);
81   if (!LookupName)
82     return;
83   assert(!LookupName->empty() && "Lookup name should be non-empty.");
84 
85   if (CurrentFileName.empty()) {
86     CurrentFileName = std::string(
87         SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName());
88     if (CurrentFileName.empty())
89       CurrentFileName = "invalid_file";
90   }
91 
92   switch (DD->getLinkageInternal()) {
93   case ExternalLinkage:
94   case VisibleNoLinkage:
95   case UniqueExternalLinkage:
96     if (SM.isInMainFile(defStart))
97       Index[*LookupName] = CurrentFileName;
98     break;
99   default:
100     break;
101   }
102 }
103 
104 class MapExtDefNamesAction : public ASTFrontendAction {
105 protected:
106   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
107                                                  llvm::StringRef) override {
108     return std::make_unique<MapExtDefNamesConsumer>(CI.getASTContext());
109   }
110 };
111 
112 static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
113 
114 int main(int argc, const char **argv) {
115   // Print a stack trace if we signal out.
116   sys::PrintStackTraceOnErrorSignal(argv[0], false);
117   PrettyStackTraceProgram X(argc, argv);
118 
119   const char *Overview = "\nThis tool collects the USR name and location "
120                          "of external definitions in the source files "
121                          "(excluding headers).\n";
122   auto ExpectedParser = CommonOptionsParser::create(
123       argc, argv, ClangExtDefMapGenCategory, cl::ZeroOrMore, Overview);
124   if (!ExpectedParser) {
125     llvm::errs() << ExpectedParser.takeError();
126     return 1;
127   }
128   CommonOptionsParser &OptionsParser = ExpectedParser.get();
129 
130   ClangTool Tool(OptionsParser.getCompilations(),
131                  OptionsParser.getSourcePathList());
132 
133   return Tool.run(newFrontendActionFactory<MapExtDefNamesAction>().get());
134 }
135