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

source code of clang/unittests/Frontend/CompilerInstanceTest.cpp