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