Coverage Report

Created: 2021-08-24 07:12

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/tools/clang-import-test/clang-import-test.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- clang-import-test.cpp - ASTImporter/ExternalASTSource testbed -----===//
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
#include "clang/AST/ASTContext.h"
10
#include "clang/AST/ASTImporter.h"
11
#include "clang/AST/DeclObjC.h"
12
#include "clang/AST/ExternalASTMerger.h"
13
#include "clang/Basic/Builtins.h"
14
#include "clang/Basic/FileManager.h"
15
#include "clang/Basic/IdentifierTable.h"
16
#include "clang/Basic/SourceLocation.h"
17
#include "clang/Basic/TargetInfo.h"
18
#include "clang/Basic/TargetOptions.h"
19
#include "clang/CodeGen/ModuleBuilder.h"
20
#include "clang/Driver/Types.h"
21
#include "clang/Frontend/ASTConsumers.h"
22
#include "clang/Frontend/CompilerInstance.h"
23
#include "clang/Frontend/MultiplexConsumer.h"
24
#include "clang/Frontend/TextDiagnosticBuffer.h"
25
#include "clang/Lex/Lexer.h"
26
#include "clang/Lex/Preprocessor.h"
27
#include "clang/Parse/ParseAST.h"
28
29
#include "llvm/IR/LLVMContext.h"
30
#include "llvm/IR/Module.h"
31
#include "llvm/Support/CommandLine.h"
32
#include "llvm/Support/Error.h"
33
#include "llvm/Support/Host.h"
34
#include "llvm/Support/Signals.h"
35
36
#include <memory>
37
#include <string>
38
39
using namespace clang;
40
41
static llvm::cl::opt<std::string> Expression(
42
    "expression", llvm::cl::Required,
43
    llvm::cl::desc("Path to a file containing the expression to parse"));
44
45
static llvm::cl::list<std::string>
46
    Imports("import", llvm::cl::ZeroOrMore,
47
            llvm::cl::desc("Path to a file containing declarations to import"));
48
49
static llvm::cl::opt<bool>
50
    Direct("direct", llvm::cl::Optional,
51
           llvm::cl::desc("Use the parsed declarations without indirection"));
52
53
static llvm::cl::opt<bool> UseOrigins(
54
    "use-origins", llvm::cl::Optional,
55
    llvm::cl::desc(
56
        "Use DeclContext origin information for more accurate lookups"));
57
58
static llvm::cl::list<std::string>
59
    ClangArgs("Xcc", llvm::cl::ZeroOrMore,
60
              llvm::cl::desc("Argument to pass to the CompilerInvocation"),
61
              llvm::cl::CommaSeparated);
62
63
static llvm::cl::opt<std::string>
64
    Input("x", llvm::cl::Optional,
65
          llvm::cl::desc("The language to parse (default: c++)"),
66
          llvm::cl::init("c++"));
67
68
static llvm::cl::opt<bool> ObjCARC("objc-arc", llvm::cl::init(false),
69
                                   llvm::cl::desc("Emable ObjC ARC"));
70
71
static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
72
                                   llvm::cl::desc("Dump combined AST"));
73
74
static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(false),
75
                                  llvm::cl::desc("Dump IR from final parse"));
76
77
namespace init_convenience {
78
class TestDiagnosticConsumer : public DiagnosticConsumer {
79
private:
80
  std::unique_ptr<TextDiagnosticBuffer> Passthrough;
81
  const LangOptions *LangOpts = nullptr;
82
83
public:
84
  TestDiagnosticConsumer()
85
189
      : Passthrough(std::make_unique<TextDiagnosticBuffer>()) {}
86
87
  virtual void BeginSourceFile(const LangOptions &LangOpts,
88
124
                               const Preprocessor *PP = nullptr) override {
89
124
    this->LangOpts = &LangOpts;
90
124
    return Passthrough->BeginSourceFile(LangOpts, PP);
91
124
  }
92
93
123
  virtual void EndSourceFile() override {
94
123
    this->LangOpts = nullptr;
95
123
    Passthrough->EndSourceFile();
96
123
  }
97
98
38
  virtual bool IncludeInDiagnosticCounts() const override {
99
38
    return Passthrough->IncludeInDiagnosticCounts();
100
38
  }
101
102
private:
103
  static void PrintSourceForLocation(const SourceLocation &Loc,
104
13
                                     SourceManager &SM) {
105
13
    const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr);
106
13
    unsigned LocColumn =
107
13
        SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
108
13
    FileID FID = SM.getFileID(Loc);
109
13
    llvm::MemoryBufferRef Buffer = SM.getBufferOrFake(FID, Loc);
110
111
13
    assert(LocData >= Buffer.getBufferStart() &&
112
13
           LocData < Buffer.getBufferEnd());
113
114
0
    const char *LineBegin = LocData - LocColumn;
115
116
13
    assert(LineBegin >= Buffer.getBufferStart());
117
118
0
    const char *LineEnd = nullptr;
119
120
354
    for (LineEnd = LineBegin; *LineEnd != '\n' && 
*LineEnd != '\r'341
&&
121
354
                              
LineEnd < Buffer.getBufferEnd()341
;
122
341
         ++LineEnd)
123
341
      ;
124
125
13
    llvm::StringRef LineString(LineBegin, LineEnd - LineBegin);
126
127
13
    llvm::errs() << LineString << '\n';
128
13
    llvm::errs().indent(LocColumn);
129
13
    llvm::errs() << '^';
130
13
    llvm::errs() << '\n';
131
13
  }
132
133
  virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
134
18
                                const Diagnostic &Info) override {
135
18
    if (Info.hasSourceManager() && LangOpts) {
136
13
      SourceManager &SM = Info.getSourceManager();
137
138
13
      if (Info.getLocation().isValid()) {
139
13
        Info.getLocation().print(llvm::errs(), SM);
140
13
        llvm::errs() << ": ";
141
13
      }
142
143
13
      SmallString<16> DiagText;
144
13
      Info.FormatDiagnostic(DiagText);
145
13
      llvm::errs() << DiagText << '\n';
146
147
13
      if (Info.getLocation().isValid()) {
148
13
        PrintSourceForLocation(Info.getLocation(), SM);
149
13
      }
150
151
13
      for (const CharSourceRange &Range : Info.getRanges()) {
152
5
        bool Invalid = true;
153
5
        StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid);
154
5
        if (!Invalid) {
155
5
          llvm::errs() << Ref << '\n';
156
5
        }
157
5
      }
158
13
    }
159
18
    DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
160
18
  }
161
};
162
163
189
std::unique_ptr<CompilerInstance> BuildCompilerInstance() {
164
189
  auto Ins = std::make_unique<CompilerInstance>();
165
189
  auto DC = std::make_unique<TestDiagnosticConsumer>();
166
189
  const bool ShouldOwnClient = true;
167
189
  Ins->createDiagnostics(DC.release(), ShouldOwnClient);
168
169
189
  auto Inv = std::make_unique<CompilerInvocation>();
170
171
189
  std::vector<const char *> ClangArgv(ClangArgs.size());
172
189
  std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
173
189
                 [](const std::string &s) -> const char * 
{ return s.data(); }18
);
174
189
  CompilerInvocation::CreateFromArgs(*Inv, ClangArgv, Ins->getDiagnostics());
175
176
189
  {
177
189
    using namespace driver::types;
178
189
    ID Id = lookupTypeForTypeSpecifier(Input.c_str());
179
189
    assert(Id != TY_INVALID);
180
189
    if (isCXX(Id)) {
181
186
      Inv->getLangOpts()->CPlusPlus = true;
182
186
      Inv->getLangOpts()->CPlusPlus11 = true;
183
186
      Inv->getHeaderSearchOpts().UseLibcxx = true;
184
186
    }
185
189
    if (isObjC(Id)) {
186
25
      Inv->getLangOpts()->ObjC = 1;
187
25
    }
188
189
  }
189
0
  Inv->getLangOpts()->ObjCAutoRefCount = ObjCARC;
190
191
189
  Inv->getLangOpts()->Bool = true;
192
189
  Inv->getLangOpts()->WChar = true;
193
189
  Inv->getLangOpts()->Blocks = true;
194
189
  Inv->getLangOpts()->DebuggerSupport = true;
195
189
  Inv->getLangOpts()->SpellChecking = false;
196
189
  Inv->getLangOpts()->ThreadsafeStatics = false;
197
189
  Inv->getLangOpts()->AccessControl = false;
198
189
  Inv->getLangOpts()->DollarIdents = true;
199
189
  Inv->getLangOpts()->Exceptions = true;
200
189
  Inv->getLangOpts()->CXXExceptions = true;
201
  // Needed for testing dynamic_cast.
202
189
  Inv->getLangOpts()->RTTI = true;
203
189
  Inv->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
204
189
  Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
205
206
189
  Ins->setInvocation(std::move(Inv));
207
208
189
  TargetInfo *TI = TargetInfo::CreateTargetInfo(
209
189
      Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
210
189
  Ins->setTarget(TI);
211
189
  Ins->getTarget().adjust(Ins->getDiagnostics(), Ins->getLangOpts());
212
189
  Ins->createFileManager();
213
189
  Ins->createSourceManager(Ins->getFileManager());
214
189
  Ins->createPreprocessor(TU_Complete);
215
216
189
  return Ins;
217
189
}
218
219
std::unique_ptr<ASTContext>
220
189
BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
221
189
  auto &PP = CI.getPreprocessor();
222
189
  auto AST = std::make_unique<ASTContext>(
223
189
      CI.getLangOpts(), CI.getSourceManager(),
224
189
      PP.getIdentifierTable(), ST, BC, PP.TUKind);
225
189
  AST->InitBuiltinTypes(CI.getTarget());
226
189
  return AST;
227
189
}
228
229
std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
230
124
                                            llvm::LLVMContext &LLVMCtx) {
231
124
  StringRef ModuleName("$__module");
232
124
  return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
233
124
      CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(),
234
124
      CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx));
235
124
}
236
} // namespace init_convenience
237
238
namespace {
239
240
/// A container for a CompilerInstance (possibly with an ExternalASTMerger
241
/// attached to its ASTContext).
242
///
243
/// Provides an accessor for the DeclContext origins associated with the
244
/// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
245
/// attached).
246
///
247
/// This is the main unit of parsed source code maintained by clang-import-test.
248
struct CIAndOrigins {
249
  using OriginMap = clang::ExternalASTMerger::OriginMap;
250
  std::unique_ptr<CompilerInstance> CI;
251
252
685
  ASTContext &getASTContext() { return CI->getASTContext(); }
253
319
  FileManager &getFileManager() { return CI->getFileManager(); }
254
198
  const OriginMap &getOriginMap() {
255
198
    static const OriginMap EmptyOriginMap{};
256
198
    if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
257
129
      return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
258
69
    return EmptyOriginMap;
259
198
  }
260
370
  DiagnosticConsumer &getDiagnosticClient() {
261
370
    return CI->getDiagnosticClient();
262
370
  }
263
874
  CompilerInstance &getCompilerInstance() { return *CI; }
264
};
265
266
void AddExternalSource(CIAndOrigins &CI,
267
121
                       llvm::MutableArrayRef<CIAndOrigins> Imports) {
268
121
  ExternalASTMerger::ImporterTarget Target(
269
121
      {CI.getASTContext(), CI.getFileManager()});
270
121
  llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
271
121
  for (CIAndOrigins &Import : Imports)
272
132
    Sources.emplace_back(Import.getASTContext(), Import.getFileManager(),
273
132
                         Import.getOriginMap());
274
121
  auto ES = std::make_unique<ExternalASTMerger>(Target, Sources);
275
121
  CI.getASTContext().setExternalSource(ES.release());
276
121
  CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
277
121
}
278
279
65
CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
280
65
  CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()};
281
65
  auto ST = std::make_unique<SelectorTable>();
282
65
  auto BC = std::make_unique<Builtin::Context>();
283
65
  std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
284
65
      IndirectCI.getCompilerInstance(), *ST, *BC);
285
65
  IndirectCI.getCompilerInstance().setASTContext(AST.release());
286
65
  AddExternalSource(IndirectCI, CI);
287
65
  return IndirectCI;
288
65
}
289
290
llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
291
124
                        ASTConsumer &Consumer) {
292
124
  SourceManager &SM = CI.getSourceManager();
293
124
  auto FE = CI.getFileManager().getFileRef(Path);
294
124
  if (!FE) {
295
1
    llvm::consumeError(FE.takeError());
296
1
    return llvm::make_error<llvm::StringError>(
297
1
        llvm::Twine("No such file or directory: ", Path), std::error_code());
298
1
  }
299
123
  SM.setMainFileID(SM.createFileID(*FE, SourceLocation(), SrcMgr::C_User));
300
123
  ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext());
301
123
  return llvm::Error::success();
302
124
}
303
304
llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
305
                                   llvm::MutableArrayRef<CIAndOrigins> Imports,
306
124
                                   bool ShouldDumpAST, bool ShouldDumpIR) {
307
124
  CIAndOrigins CI{init_convenience::BuildCompilerInstance()};
308
124
  auto ST = std::make_unique<SelectorTable>();
309
124
  auto BC = std::make_unique<Builtin::Context>();
310
124
  std::unique_ptr<ASTContext> AST =
311
124
      init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC);
312
124
  CI.getCompilerInstance().setASTContext(AST.release());
313
124
  if (Imports.size())
314
56
    AddExternalSource(CI, Imports);
315
316
124
  std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
317
318
124
  auto LLVMCtx = std::make_unique<llvm::LLVMContext>();
319
124
  ASTConsumers.push_back(
320
124
      init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx));
321
124
  auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
322
323
124
  if (ShouldDumpAST)
324
35
    ASTConsumers.push_back(CreateASTDumper(nullptr /*Dump to stdout.*/, "",
325
35
                                           true, false, false, false,
326
35
                                           clang::ADOF_Default));
327
328
124
  CI.getDiagnosticClient().BeginSourceFile(
329
124
      CI.getCompilerInstance().getLangOpts(),
330
124
      &CI.getCompilerInstance().getPreprocessor());
331
124
  MultiplexConsumer Consumers(std::move(ASTConsumers));
332
124
  Consumers.Initialize(CI.getASTContext());
333
334
124
  if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers))
335
1
    return std::move(PE);
336
123
  CI.getDiagnosticClient().EndSourceFile();
337
123
  if (ShouldDumpIR)
338
3
    CG.GetModule()->print(llvm::outs(), nullptr);
339
123
  if (CI.getDiagnosticClient().getNumErrors())
340
2
    return llvm::make_error<llvm::StringError>(
341
2
        "Errors occurred while parsing the expression.", std::error_code());
342
121
  return std::move(CI);
343
123
}
344
345
55
void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
346
55
  llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
347
55
  for (CIAndOrigins &Import : Imports)
348
66
    Sources.push_back({Import.getASTContext(), Import.getFileManager(),
349
66
                       Import.getOriginMap()});
350
55
  ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
351
55
  auto *Merger = static_cast<ExternalASTMerger *>(Source);
352
55
  Merger->RemoveSources(Sources);
353
55
}
354
355
} // end namespace
356
357
58
int main(int argc, const char **argv) {
358
58
  const bool DisableCrashReporting = true;
359
58
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
360
58
  llvm::cl::ParseCommandLineOptions(argc, argv);
361
58
  std::vector<CIAndOrigins> ImportCIs;
362
68
  for (auto I : Imports) {
363
68
    llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false);
364
68
    if (auto E = ImportCI.takeError()) {
365
2
      llvm::errs() << "error: " << llvm::toString(std::move(E)) << "\n";
366
2
      exit(-1);
367
2
    }
368
66
    ImportCIs.push_back(std::move(*ImportCI));
369
66
  }
370
56
  std::vector<CIAndOrigins> IndirectCIs;
371
56
  if (!Direct || 
UseOrigins1
) {
372
65
    for (auto &ImportCI : ImportCIs) {
373
65
      CIAndOrigins IndirectCI = BuildIndirect(ImportCI);
374
65
      IndirectCIs.push_back(std::move(IndirectCI));
375
65
    }
376
55
  }
377
56
  if (UseOrigins)
378
1
    for (auto &ImportCI : ImportCIs)
379
1
      IndirectCIs.push_back(std::move(ImportCI));
380
56
  llvm::Expected<CIAndOrigins> ExpressionCI =
381
56
      Parse(Expression, (Direct && 
!UseOrigins1
) ?
ImportCIs1
:
IndirectCIs55
,
382
56
            DumpAST, DumpIR);
383
56
  if (auto E = ExpressionCI.takeError()) {
384
1
    llvm::errs() << "error: " << llvm::toString(std::move(E)) << "\n";
385
1
    exit(-1);
386
1
  }
387
55
  Forget(*ExpressionCI, (Direct && 
!UseOrigins1
) ?
ImportCIs1
:
IndirectCIs54
);
388
55
  return 0;
389
56
}