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