Coverage Report

Created: 2020-02-15 09:57

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