1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
//===- unittests/Frontend/CompilerInstanceTest.cpp - CI tests -------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace clang;
namespace {
TEST(CompilerInstance, DefaultVFSOverlayFromInvocation) {
// Create a temporary VFS overlay yaml file.
int FD;
SmallString<256> FileName;
ASSERT_FALSE(sys::fs::createTemporaryFile("vfs", "yaml", FD, FileName));
ToolOutputFile File(FileName, FD);
SmallString<256> CurrentPath;
sys::fs::current_path(CurrentPath);
sys::fs::make_absolute(CurrentPath, FileName);
// Mount the VFS file itself on the path 'virtual.file'. Makes this test
// a bit shorter than creating a new dummy file just for this purpose.
const std::string CurrentPathStr = std::string(CurrentPath.str());
const std::string FileNameStr = std::string(FileName.str());
const char *VFSYaml = "{ 'version': 0, 'roots': [\n"
" { 'name': '%s',\n"
" 'type': 'directory',\n"
" 'contents': [\n"
" { 'name': 'vfs-virtual.file', 'type': 'file',\n"
" 'external-contents': '%s'\n"
" }\n"
" ]\n"
" }\n"
"]}\n";
File.os() << format(VFSYaml, CurrentPathStr.c_str(), FileName.c_str());
File.os().flush();
// Create a CompilerInvocation that uses this overlay file.
const std::string VFSArg = "-ivfsoverlay" + FileNameStr;
const char *Args[] = {"clang", VFSArg.c_str(), "-xc++", "-"};
DiagnosticOptions DiagOpts;
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
CompilerInstance::createDiagnostics(*llvm::vfs::getRealFileSystem(),
DiagOpts);
CreateInvocationOptions CIOpts;
CIOpts.Diags = Diags;
std::shared_ptr<CompilerInvocation> CInvok =
createInvocation(Args, std::move(CIOpts));
if (!CInvok)
FAIL() << "could not create compiler invocation";
// Create a minimal CompilerInstance which should use the VFS we specified
// in the CompilerInvocation (as we don't explicitly set our own).
CompilerInstance Instance(std::move(CInvok));
Instance.setDiagnostics(Diags.get());
Instance.createFileManager();
// Check if the virtual file exists which means that our VFS is used by the
// CompilerInstance.
ASSERT_TRUE(Instance.getFileManager().getOptionalFileRef("vfs-virtual.file"));
}
TEST(CompilerInstance, AllowDiagnosticLogWithUnownedDiagnosticConsumer) {
DiagnosticOptions DiagOpts;
// Tell the diagnostics engine to emit the diagnostic log to STDERR. This
// ensures that a chained diagnostic consumer is created so that the test can
// exercise the unowned diagnostic consumer in a chained consumer.
DiagOpts.DiagnosticLogFile = "-";
// Create the diagnostic engine with unowned consumer.
std::string DiagnosticOutput;
llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
auto DiagPrinter =
std::make_unique<TextDiagnosticPrinter>(DiagnosticsOS, DiagOpts);
CompilerInstance Instance;
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
Instance.createDiagnostics(*llvm::vfs::getRealFileSystem(), DiagOpts,
DiagPrinter.get(), /*ShouldOwnClient=*/false);
Diags->Report(diag::err_expected) << "no crash";
ASSERT_EQ(DiagnosticOutput, "error: expected no crash\n");
}
TEST(CompilerInstance, MultipleInputsCleansFileIDs) {
auto VFS = makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
VFS->addFile("a.cc", /*ModificationTime=*/{},
MemoryBuffer::getMemBuffer(R"cpp(
#include "a.h"
)cpp"));
// Paddings of `void foo();` in the sources below are "important". We're
// testing against source locations from previous compilations colliding.
// Hence the `unused` variable in `b.h` needs to be within `#pragma clang
// diagnostic` block from `a.h`.
VFS->addFile("a.h", /*ModificationTime=*/{}, MemoryBuffer::getMemBuffer(R"cpp(
#include "b.h"
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wunused"
void foo();
#pragma clang diagnostic pop
)cpp"));
VFS->addFile("b.h", /*ModificationTime=*/{}, MemoryBuffer::getMemBuffer(R"cpp(
void foo(); void foo(); void foo(); void foo();
inline void foo() { int unused = 2; }
)cpp"));
DiagnosticOptions DiagOpts;
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
CompilerInstance::createDiagnostics(*VFS, DiagOpts);
CreateInvocationOptions CIOpts;
CIOpts.Diags = Diags;
const char *Args[] = {"clang", "-xc++", "a.cc"};
std::shared_ptr<CompilerInvocation> CInvok =
createInvocation(Args, std::move(CIOpts));
ASSERT_TRUE(CInvok) << "could not create compiler invocation";
CompilerInstance Instance(std::move(CInvok));
Instance.setDiagnostics(Diags.get());
Instance.createFileManager(VFS);
// Run once for `a.cc` and then for `a.h`. This makes sure we get the same
// file ID for `b.h` in the second run as `a.h` from first run.
const auto &OrigInputKind = Instance.getFrontendOpts().Inputs[0].getKind();
Instance.getFrontendOpts().Inputs.emplace_back("a.h", OrigInputKind);
SyntaxOnlyAction Act;
EXPECT_TRUE(Instance.ExecuteAction(Act)) << "Failed to execute action";
EXPECT_FALSE(Diags->hasErrorOccurred());
EXPECT_EQ(Diags->getNumWarnings(), 0u);
}
} // anonymous namespace
|