224 lines · cpp
1//===- unittests/Frontend/ASTUnitTest.cpp - ASTUnit 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 <fstream>10 11#include "clang/Basic/FileManager.h"12#include "clang/Driver/CreateASTUnitFromArgs.h"13#include "clang/Driver/CreateInvocationFromArgs.h"14#include "clang/Frontend/ASTUnit.h"15#include "clang/Frontend/CompilerInstance.h"16#include "clang/Frontend/CompilerInvocation.h"17#include "clang/Frontend/PCHContainerOperations.h"18#include "clang/Lex/HeaderSearch.h"19#include "llvm/Support/FileSystem.h"20#include "llvm/Support/Path.h"21#include "llvm/Support/ToolOutputFile.h"22#include "llvm/Support/VirtualFileSystem.h"23#include "gtest/gtest.h"24 25using namespace llvm;26using namespace clang;27 28namespace {29 30class ASTUnitTest : public ::testing::Test {31protected:32 int FD;33 llvm::SmallString<256> InputFileName;34 std::unique_ptr<ToolOutputFile> input_file;35 std::shared_ptr<DiagnosticOptions> DiagOpts =36 std::make_shared<DiagnosticOptions>();37 IntrusiveRefCntPtr<DiagnosticsEngine> Diags;38 std::shared_ptr<CompilerInvocation> CInvok;39 std::shared_ptr<PCHContainerOperations> PCHContainerOps;40 41 std::unique_ptr<ASTUnit> createASTUnit(bool isVolatile) {42 EXPECT_FALSE(llvm::sys::fs::createTemporaryFile("ast-unit", "cpp", FD,43 InputFileName));44 input_file = std::make_unique<ToolOutputFile>(InputFileName, FD);45 input_file->os() << "";46 47 const char *Args[] = {"clang", "-xc++", InputFileName.c_str()};48 49 auto VFS = llvm::vfs::getRealFileSystem();50 Diags = CompilerInstance::createDiagnostics(*VFS, *DiagOpts);51 52 CreateInvocationOptions CIOpts;53 CIOpts.Diags = Diags;54 CIOpts.VFS = VFS;55 CInvok = createInvocation(Args, std::move(CIOpts));56 57 if (!CInvok)58 return nullptr;59 60 auto FileMgr =61 llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions(), VFS);62 PCHContainerOps = std::make_shared<PCHContainerOperations>();63 64 return ASTUnit::LoadFromCompilerInvocation(65 CInvok, PCHContainerOps, DiagOpts, Diags, FileMgr, false,66 CaptureDiagsKind::None, 0, TU_Complete, false, false, isVolatile);67 }68};69 70TEST_F(ASTUnitTest, SaveLoadPreservesLangOptionsInPrintingPolicy) {71 // Check that the printing policy is restored with the correct language72 // options when loading an ASTUnit from a file. To this end, an ASTUnit73 // for a C++ translation unit is set up and written to a temporary file.74 75 // By default `UseVoidForZeroParams` is true for non-C++ language options,76 // thus we can check this field after loading the ASTUnit to deduce whether77 // the correct (C++) language options were used when setting up the printing78 // policy.79 80 {81 PrintingPolicy PolicyWithDefaultLangOpt(LangOptions{});82 EXPECT_TRUE(PolicyWithDefaultLangOpt.UseVoidForZeroParams);83 }84 85 std::unique_ptr<ASTUnit> AST = createASTUnit(false);86 87 if (!AST)88 FAIL() << "failed to create ASTUnit";89 90 EXPECT_FALSE(AST->getASTContext().getPrintingPolicy().UseVoidForZeroParams);91 92 llvm::SmallString<256> ASTFileName;93 ASSERT_FALSE(94 llvm::sys::fs::createTemporaryFile("ast-unit", "ast", FD, ASTFileName));95 ToolOutputFile ast_file(ASTFileName, FD);96 AST->Save(ASTFileName.str());97 98 EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));99 HeaderSearchOptions HSOpts;100 101 std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(102 ASTFileName, PCHContainerOps->getRawReader(), ASTUnit::LoadEverything,103 llvm::vfs::getRealFileSystem(), DiagOpts, Diags, FileSystemOptions(),104 HSOpts);105 106 if (!AU)107 FAIL() << "failed to load ASTUnit";108 109 EXPECT_FALSE(AU->getASTContext().getPrintingPolicy().UseVoidForZeroParams);110}111 112TEST_F(ASTUnitTest, GetBufferForFileMemoryMapping) {113 std::unique_ptr<ASTUnit> AST = createASTUnit(true);114 115 if (!AST)116 FAIL() << "failed to create ASTUnit";117 118 std::unique_ptr<llvm::MemoryBuffer> memoryBuffer =119 AST->getBufferForFile(InputFileName);120 121 EXPECT_NE(memoryBuffer->getBufferKind(),122 llvm::MemoryBuffer::MemoryBuffer_MMap);123}124 125TEST_F(ASTUnitTest, ModuleTextualHeader) {126 auto InMemoryFs = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();127 InMemoryFs->addFile("test.cpp", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp(128 #include "Textual.h"129 void foo() {}130 )cpp"));131 InMemoryFs->addFile("m.modulemap", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp(132 module M {133 module Textual {134 textual header "Textual.h"135 }136 }137 )cpp"));138 InMemoryFs->addFile("Textual.h", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp(139 void foo();140 )cpp"));141 142 const char *Args[] = {"clang", "test.cpp", "-fmodule-map-file=m.modulemap",143 "-fmodule-name=M"};144 Diags = CompilerInstance::createDiagnostics(*InMemoryFs, *DiagOpts);145 CreateInvocationOptions CIOpts;146 CIOpts.Diags = Diags;147 CInvok = createInvocation(Args, std::move(CIOpts));148 ASSERT_TRUE(CInvok);149 150 auto FileMgr =151 llvm::makeIntrusiveRefCnt<FileManager>(FileSystemOptions(), InMemoryFs);152 PCHContainerOps = std::make_shared<PCHContainerOperations>();153 154 auto AU = ASTUnit::LoadFromCompilerInvocation(155 CInvok, PCHContainerOps, DiagOpts, Diags, FileMgr, false,156 CaptureDiagsKind::None, 1, TU_Complete, false, false, false);157 ASSERT_TRUE(AU);158 auto File = AU->getFileManager().getFileRef("Textual.h", false, false);159 ASSERT_TRUE(bool(File));160 // Verify that we do not crash here.161 EXPECT_TRUE(162 AU->getPreprocessor().getHeaderSearchInfo().getExistingFileInfo(*File));163}164 165TEST_F(ASTUnitTest, LoadFromCommandLineEarlyError) {166 EXPECT_FALSE(167 llvm::sys::fs::createTemporaryFile("ast-unit", "c", FD, InputFileName));168 input_file = std::make_unique<ToolOutputFile>(InputFileName, FD);169 input_file->os() << "";170 171 const char *Args[] = {"clang", "-target", "foobar", InputFileName.c_str()};172 173 auto Diags = CompilerInstance::createDiagnostics(174 *llvm::vfs::getRealFileSystem(), *DiagOpts);175 auto PCHContainerOps = std::make_shared<PCHContainerOperations>();176 std::unique_ptr<clang::ASTUnit> ErrUnit;177 178 std::unique_ptr<ASTUnit> AST = CreateASTUnitFromCommandLine(179 &Args[0], &Args[4], PCHContainerOps, DiagOpts, Diags, "", false, "",180 false, CaptureDiagsKind::All, {}, true, 0, TU_Complete, false, false,181 false, SkipFunctionBodiesScope::None, false, true, false, false,182 std::nullopt, &ErrUnit, nullptr);183 184 ASSERT_EQ(AST, nullptr);185 ASSERT_NE(ErrUnit, nullptr);186 ASSERT_TRUE(Diags->hasErrorOccurred());187 ASSERT_NE(ErrUnit->stored_diag_size(), 0U);188}189 190TEST_F(ASTUnitTest, LoadFromCommandLineWorkingDirectory) {191 EXPECT_FALSE(192 llvm::sys::fs::createTemporaryFile("bar", "c", FD, InputFileName));193 auto Input = std::make_unique<ToolOutputFile>(InputFileName, FD);194 Input->os() << "";195 196 SmallString<128> WorkingDir;197 ASSERT_FALSE(sys::fs::createUniqueDirectory("foo", WorkingDir));198 const char *Args[] = {"clang", "-working-directory", WorkingDir.c_str(),199 InputFileName.c_str()};200 201 auto Diags = CompilerInstance::createDiagnostics(202 *llvm::vfs::getRealFileSystem(), *DiagOpts);203 auto PCHContainerOps = std::make_shared<PCHContainerOperations>();204 std::unique_ptr<clang::ASTUnit> ErrUnit;205 206 std::unique_ptr<ASTUnit> AST = CreateASTUnitFromCommandLine(207 &Args[0], &Args[4], PCHContainerOps, DiagOpts, Diags, "", false, "",208 false, CaptureDiagsKind::All, {}, true, 0, TU_Complete, false, false,209 false, SkipFunctionBodiesScope::None, false, true, false, false,210 std::nullopt, &ErrUnit, nullptr);211 212 ASSERT_NE(AST, nullptr);213 ASSERT_FALSE(Diags->hasErrorOccurred());214 215 // Make sure '-working-directory' sets both the FileSystemOpts and underlying216 // VFS working directory.217 const auto &FM = AST->getFileManager();218 const auto &VFS = FM.getVirtualFileSystem();219 ASSERT_EQ(*VFS.getCurrentWorkingDirectory(), WorkingDir.str());220 ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir.str());221}222 223} // anonymous namespace224