Coverage Report

Created: 2022-07-16 07:03

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