228 lines · cpp
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-exception6//7//===--------------------------------------------------------------------===//8//9// Clang tool which creates a list of defined functions and the files in which10// 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 31using namespace llvm;32using namespace clang;33using namespace clang::cross_tu;34using namespace clang::tooling;35 36static cl::OptionCategory37 ClangExtDefMapGenCategory("clang-extdefmapgen options");38 39class MapExtDefNamesConsumer : public ASTConsumer {40public: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 56private: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 66void 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 85void 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 114class MapExtDefNamesAction : public ASTFrontendAction {115protected:116 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,117 llvm::StringRef) override {118 return std::make_unique<MapExtDefNamesConsumer>(CI.getASTContext());119 }120};121 122static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);123 124static IntrusiveRefCntPtr<DiagnosticsEngine> Diags;125 126IntrusiveRefCntPtr<DiagnosticsEngine>127GetDiagnosticsEngine(DiagnosticOptions &DiagOpts) {128 if (Diags) {129 // Call reset to make sure we don't mix errors130 Diags->Reset(false);131 return Diags;132 }133 134 TextDiagnosticPrinter *DiagClient =135 new TextDiagnosticPrinter(llvm::errs(), DiagOpts);136 DiagClient->setPrefix("clang-extdef-mappping");137 138 auto DiagEngine = llvm::makeIntrusiveRefCnt<DiagnosticsEngine>(139 DiagnosticIDs::create(), DiagOpts, DiagClient);140 Diags.swap(DiagEngine);141 142 // Retain this one time so it's not destroyed by ASTUnit::LoadFromASTFile143 Diags->Retain();144 return Diags;145}146 147static CompilerInstance *CI = nullptr;148 149static bool HandleAST(StringRef AstPath) {150 151 if (!CI)152 CI = new CompilerInstance();153 154 auto DiagOpts = std::make_shared<DiagnosticOptions>();155 IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine =156 GetDiagnosticsEngine(*DiagOpts);157 158 std::unique_ptr<ASTUnit> Unit = ASTUnit::LoadFromASTFile(159 AstPath, CI->getPCHContainerOperations()->getRawReader(),160 ASTUnit::LoadASTOnly, CI->getVirtualFileSystemPtr(), DiagOpts, DiagEngine,161 CI->getFileSystemOpts(), CI->getHeaderSearchOpts());162 163 if (!Unit)164 return false;165 166 FileManager FM(CI->getFileSystemOpts());167 SmallString<128> AbsPath(AstPath);168 FM.makeAbsolutePath(AbsPath);169 170 MapExtDefNamesConsumer Consumer =171 MapExtDefNamesConsumer(Unit->getASTContext(), AbsPath);172 Consumer.HandleTranslationUnit(Unit->getASTContext());173 174 return true;175}176 177static int HandleFiles(ArrayRef<std::string> SourceFiles,178 CompilationDatabase &compilations) {179 std::vector<std::string> SourcesToBeParsed;180 181 // Loop over all input files, if they are pre-compiled AST182 // process them directly in HandleAST, otherwise put them183 // on a list for ClangTool to handle.184 for (StringRef Src : SourceFiles) {185 if (Src.ends_with(".ast")) {186 if (!HandleAST(Src)) {187 return 1;188 }189 } else {190 SourcesToBeParsed.push_back(Src.str());191 }192 }193 194 if (!SourcesToBeParsed.empty()) {195 ClangTool Tool(compilations, SourcesToBeParsed);196 return Tool.run(newFrontendActionFactory<MapExtDefNamesAction>().get());197 }198 199 return 0;200}201 202int main(int argc, const char **argv) {203 // Print a stack trace if we signal out.204 sys::PrintStackTraceOnErrorSignal(argv[0], false);205 PrettyStackTraceProgram X(argc, argv);206 207 const char *Overview = "\nThis tool collects the USR name and location "208 "of external definitions in the source files "209 "(excluding headers).\n"210 "Input can be either source files that are compiled "211 "with compile database or .ast files that are "212 "created from clang's -emit-ast option.\n";213 auto ExpectedParser = CommonOptionsParser::create(214 argc, argv, ClangExtDefMapGenCategory, cl::ZeroOrMore, Overview);215 if (!ExpectedParser) {216 llvm::errs() << ExpectedParser.takeError();217 return 1;218 }219 CommonOptionsParser &OptionsParser = ExpectedParser.get();220 221 llvm::InitializeAllTargetInfos();222 llvm::InitializeAllTargetMCs();223 llvm::InitializeAllAsmParsers();224 225 return HandleFiles(OptionsParser.getSourcePathList(),226 OptionsParser.getCompilations());227}228