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 "llvm/Support/TargetSelect.h" 27 #include <optional> 28 #include <sstream> 29 #include <string> 30 31 using namespace llvm; 32 using namespace clang; 33 using namespace clang::cross_tu; 34 using namespace clang::tooling; 35 36 static cl::OptionCategory 37 ClangExtDefMapGenCategory("clang-extdefmapgen options"); 38 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(); 45 } 46 47 ~MapExtDefNamesConsumer() { 48 // Flush results to standard output. 49 llvm::outs() << createCrossTUIndexString(Index); 50 } 51 52 void HandleTranslationUnit(ASTContext &Context) override { 53 handleDecl(Context.getTranslationUnitDecl()); 54 } 55 56 private: 57 void handleDecl(const Decl *D); 58 void addIfInMain(const DeclaratorDecl *DD, SourceLocation defStart); 59 60 ASTContext &Ctx; 61 SourceManager &SM; 62 llvm::StringMap<std::string> Index; 63 std::string CurrentFileName; 64 }; 65 66 void MapExtDefNamesConsumer::handleDecl(const Decl *D) { 67 if (!D) 68 return; 69 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()); 78 } 79 80 if (const auto *DC = dyn_cast<DeclContext>(D)) 81 for (const Decl *D : DC->decls()) 82 handleDecl(D); 83 } 84 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."); 92 93 if (CurrentFileName.empty()) { 94 CurrentFileName = std::string( 95 SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName()); 96 if (CurrentFileName.empty()) 97 CurrentFileName = "invalid_file"; 98 } 99 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; 106 break; 107 case Linkage::Invalid: 108 llvm_unreachable("Linkage has not been computed!"); 109 default: 110 break; 111 } 112 } 113 114 class MapExtDefNamesAction : public ASTFrontendAction { 115 protected: 116 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, 117 llvm::StringRef) override { 118 return std::make_unique<MapExtDefNamesConsumer>(CI.getASTContext()); 119 } 120 }; 121 122 static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); 123 124 static IntrusiveRefCntPtr<DiagnosticsEngine> Diags; 125 126 IntrusiveRefCntPtr<DiagnosticsEngine> GetDiagnosticsEngine() { 127 if (Diags) { 128 // Call reset to make sure we don't mix errors 129 Diags->Reset(false); 130 return Diags; 131 } 132 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()); 138 139 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine( 140 new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); 141 Diags.swap(DiagEngine); 142 143 // Retain this one time so it's not destroyed by ASTUnit::LoadFromASTFile 144 Diags->Retain(); 145 return Diags; 146 } 147 148 static CompilerInstance *CI = nullptr; 149 150 static bool HandleAST(StringRef AstPath) { 151 152 if (!CI) 153 CI = new CompilerInstance(); 154 155 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine = GetDiagnosticsEngine(); 156 157 std::unique_ptr<ASTUnit> Unit = ASTUnit::LoadFromASTFile( 158 AstPath, CI->getPCHContainerOperations()->getRawReader(), 159 ASTUnit::LoadASTOnly, DiagEngine, CI->getFileSystemOpts(), 160 CI->getHeaderSearchOptsPtr()); 161 162 if (!Unit) 163 return false; 164 165 FileManager FM(CI->getFileSystemOpts()); 166 SmallString<128> AbsPath(AstPath); 167 FM.makeAbsolutePath(AbsPath); 168 169 MapExtDefNamesConsumer Consumer = 170 MapExtDefNamesConsumer(Unit->getASTContext(), AbsPath); 171 Consumer.HandleTranslationUnit(Unit->getASTContext()); 172 173 return true; 174 } 175 176 static int HandleFiles(ArrayRef<std::string> SourceFiles, 177 CompilationDatabase &compilations) { 178 std::vector<std::string> SourcesToBeParsed; 179 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)) { 186 return 1; 187 } 188 } else { 189 SourcesToBeParsed.push_back(Src.str()); 190 } 191 } 192 193 if (!SourcesToBeParsed.empty()) { 194 ClangTool Tool(compilations, SourcesToBeParsed); 195 return Tool.run(newFrontendActionFactory<MapExtDefNamesAction>().get()); 196 } 197 198 return 0; 199 } 200 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); 205 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(); 216 return 1; 217 } 218 CommonOptionsParser &OptionsParser = ExpectedParser.get(); 219 220 llvm::InitializeAllTargetInfos(); 221 llvm::InitializeAllTargetMCs(); 222 llvm::InitializeAllAsmParsers(); 223 224 return HandleFiles(OptionsParser.getSourcePathList(), 225 OptionsParser.getCompilations()); 226 } 227