1fd9fad9eSVedant Kumar //===- unittests/Frontend/ASTUnitTest.cpp - ASTUnit tests -----------------===// 2fd9fad9eSVedant Kumar // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6fd9fad9eSVedant Kumar // 7fd9fad9eSVedant Kumar //===----------------------------------------------------------------------===// 8fd9fad9eSVedant Kumar 9fd9fad9eSVedant Kumar #include <fstream> 10fd9fad9eSVedant Kumar 11e08464fbSReid Kleckner #include "clang/Basic/FileManager.h" 12fd9fad9eSVedant Kumar #include "clang/Frontend/ASTUnit.h" 13fd9fad9eSVedant Kumar #include "clang/Frontend/CompilerInstance.h" 14fd9fad9eSVedant Kumar #include "clang/Frontend/CompilerInvocation.h" 15fd9fad9eSVedant Kumar #include "clang/Frontend/PCHContainerOperations.h" 16e2d61ae5SAdam Czachorowski #include "clang/Lex/HeaderSearch.h" 17fd9fad9eSVedant Kumar #include "llvm/Support/FileSystem.h" 18fd9fad9eSVedant Kumar #include "llvm/Support/Path.h" 19fd9fad9eSVedant Kumar #include "llvm/Support/ToolOutputFile.h" 20*df9a14d7SKadir Cetinkaya #include "llvm/Support/VirtualFileSystem.h" 21fd9fad9eSVedant Kumar #include "gtest/gtest.h" 22fd9fad9eSVedant Kumar 23fd9fad9eSVedant Kumar using namespace llvm; 24fd9fad9eSVedant Kumar using namespace clang; 25fd9fad9eSVedant Kumar 26fd9fad9eSVedant Kumar namespace { 27fd9fad9eSVedant Kumar 282ebe3a02SIvan Donchevskii class ASTUnitTest : public ::testing::Test { 292ebe3a02SIvan Donchevskii protected: 302ebe3a02SIvan Donchevskii int FD; 312ebe3a02SIvan Donchevskii llvm::SmallString<256> InputFileName; 322ebe3a02SIvan Donchevskii std::unique_ptr<ToolOutputFile> input_file; 332ebe3a02SIvan Donchevskii IntrusiveRefCntPtr<DiagnosticsEngine> Diags; 342ebe3a02SIvan Donchevskii std::shared_ptr<CompilerInvocation> CInvok; 352ebe3a02SIvan Donchevskii std::shared_ptr<PCHContainerOperations> PCHContainerOps; 362ebe3a02SIvan Donchevskii 372ebe3a02SIvan Donchevskii std::unique_ptr<ASTUnit> createASTUnit(bool isVolatile) { 382ebe3a02SIvan Donchevskii EXPECT_FALSE(llvm::sys::fs::createTemporaryFile("ast-unit", "cpp", FD, 392ebe3a02SIvan Donchevskii InputFileName)); 402b3d49b6SJonas Devlieghere input_file = std::make_unique<ToolOutputFile>(InputFileName, FD); 412ebe3a02SIvan Donchevskii input_file->os() << ""; 422ebe3a02SIvan Donchevskii 432ebe3a02SIvan Donchevskii const char *Args[] = {"clang", "-xc++", InputFileName.c_str()}; 442ebe3a02SIvan Donchevskii 45*df9a14d7SKadir Cetinkaya auto VFS = llvm::vfs::getRealFileSystem(); 46*df9a14d7SKadir Cetinkaya Diags = CompilerInstance::createDiagnostics(*VFS, new DiagnosticOptions()); 472ebe3a02SIvan Donchevskii 48499d0b96SSam McCall CreateInvocationOptions CIOpts; 49499d0b96SSam McCall CIOpts.Diags = Diags; 50*df9a14d7SKadir Cetinkaya CIOpts.VFS = VFS; 51499d0b96SSam McCall CInvok = createInvocation(Args, std::move(CIOpts)); 522ebe3a02SIvan Donchevskii 532ebe3a02SIvan Donchevskii if (!CInvok) 542ebe3a02SIvan Donchevskii return nullptr; 552ebe3a02SIvan Donchevskii 56*df9a14d7SKadir Cetinkaya FileManager *FileMgr = new FileManager(FileSystemOptions(), VFS); 572ebe3a02SIvan Donchevskii PCHContainerOps = std::make_shared<PCHContainerOperations>(); 582ebe3a02SIvan Donchevskii 592ebe3a02SIvan Donchevskii return ASTUnit::LoadFromCompilerInvocation( 608edd8da4SNikolai Kosjar CInvok, PCHContainerOps, Diags, FileMgr, false, CaptureDiagsKind::None, 618edd8da4SNikolai Kosjar 0, TU_Complete, false, false, isVolatile); 622ebe3a02SIvan Donchevskii } 632ebe3a02SIvan Donchevskii }; 642ebe3a02SIvan Donchevskii 652ebe3a02SIvan Donchevskii TEST_F(ASTUnitTest, SaveLoadPreservesLangOptionsInPrintingPolicy) { 66fd9fad9eSVedant Kumar // Check that the printing policy is restored with the correct language 67fd9fad9eSVedant Kumar // options when loading an ASTUnit from a file. To this end, an ASTUnit 68fd9fad9eSVedant Kumar // for a C++ translation unit is set up and written to a temporary file. 69fd9fad9eSVedant Kumar 70fd9fad9eSVedant Kumar // By default `UseVoidForZeroParams` is true for non-C++ language options, 71fd9fad9eSVedant Kumar // thus we can check this field after loading the ASTUnit to deduce whether 72fd9fad9eSVedant Kumar // the correct (C++) language options were used when setting up the printing 73fd9fad9eSVedant Kumar // policy. 74fd9fad9eSVedant Kumar 75fd9fad9eSVedant Kumar { 76fd9fad9eSVedant Kumar PrintingPolicy PolicyWithDefaultLangOpt(LangOptions{}); 77fd9fad9eSVedant Kumar EXPECT_TRUE(PolicyWithDefaultLangOpt.UseVoidForZeroParams); 78fd9fad9eSVedant Kumar } 79fd9fad9eSVedant Kumar 802ebe3a02SIvan Donchevskii std::unique_ptr<ASTUnit> AST = createASTUnit(false); 81fd9fad9eSVedant Kumar 82fd9fad9eSVedant Kumar if (!AST) 83fd9fad9eSVedant Kumar FAIL() << "failed to create ASTUnit"; 84fd9fad9eSVedant Kumar 85fd9fad9eSVedant Kumar EXPECT_FALSE(AST->getASTContext().getPrintingPolicy().UseVoidForZeroParams); 86fd9fad9eSVedant Kumar 87fd9fad9eSVedant Kumar llvm::SmallString<256> ASTFileName; 882ebe3a02SIvan Donchevskii ASSERT_FALSE( 892ebe3a02SIvan Donchevskii llvm::sys::fs::createTemporaryFile("ast-unit", "ast", FD, ASTFileName)); 902590edf6SReid Kleckner ToolOutputFile ast_file(ASTFileName, FD); 91fd9fad9eSVedant Kumar AST->Save(ASTFileName.str()); 92fd9fad9eSVedant Kumar 93fd9fad9eSVedant Kumar EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName)); 94807aa261SChuanqi Xu auto HSOpts = std::make_shared<HeaderSearchOptions>(); 95fd9fad9eSVedant Kumar 96fd9fad9eSVedant Kumar std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile( 973cd3202bSKazu Hirata ASTFileName, PCHContainerOps->getRawReader(), ASTUnit::LoadEverything, 983cd3202bSKazu Hirata Diags, FileSystemOptions(), HSOpts); 99fd9fad9eSVedant Kumar 100fd9fad9eSVedant Kumar if (!AU) 101fd9fad9eSVedant Kumar FAIL() << "failed to load ASTUnit"; 102fd9fad9eSVedant Kumar 103fd9fad9eSVedant Kumar EXPECT_FALSE(AU->getASTContext().getPrintingPolicy().UseVoidForZeroParams); 104fd9fad9eSVedant Kumar } 105fd9fad9eSVedant Kumar 1062ebe3a02SIvan Donchevskii TEST_F(ASTUnitTest, GetBufferForFileMemoryMapping) { 1072ebe3a02SIvan Donchevskii std::unique_ptr<ASTUnit> AST = createASTUnit(true); 1082ebe3a02SIvan Donchevskii 1092ebe3a02SIvan Donchevskii if (!AST) 1102ebe3a02SIvan Donchevskii FAIL() << "failed to create ASTUnit"; 1112ebe3a02SIvan Donchevskii 1122ebe3a02SIvan Donchevskii std::unique_ptr<llvm::MemoryBuffer> memoryBuffer = 1132ebe3a02SIvan Donchevskii AST->getBufferForFile(InputFileName); 1142ebe3a02SIvan Donchevskii 1152ebe3a02SIvan Donchevskii EXPECT_NE(memoryBuffer->getBufferKind(), 1162ebe3a02SIvan Donchevskii llvm::MemoryBuffer::MemoryBuffer_MMap); 1172ebe3a02SIvan Donchevskii } 1182ebe3a02SIvan Donchevskii 119e2d61ae5SAdam Czachorowski TEST_F(ASTUnitTest, ModuleTextualHeader) { 120e2d61ae5SAdam Czachorowski llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFs = 121e2d61ae5SAdam Czachorowski new llvm::vfs::InMemoryFileSystem(); 122e2d61ae5SAdam Czachorowski InMemoryFs->addFile("test.cpp", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( 123e2d61ae5SAdam Czachorowski #include "Textual.h" 124e2d61ae5SAdam Czachorowski void foo() {} 125e2d61ae5SAdam Czachorowski )cpp")); 126e2d61ae5SAdam Czachorowski InMemoryFs->addFile("m.modulemap", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( 127e2d61ae5SAdam Czachorowski module M { 128e2d61ae5SAdam Czachorowski module Textual { 129e2d61ae5SAdam Czachorowski textual header "Textual.h" 130e2d61ae5SAdam Czachorowski } 131e2d61ae5SAdam Czachorowski } 132e2d61ae5SAdam Czachorowski )cpp")); 133e2d61ae5SAdam Czachorowski InMemoryFs->addFile("Textual.h", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( 134e2d61ae5SAdam Czachorowski void foo(); 135e2d61ae5SAdam Czachorowski )cpp")); 136e2d61ae5SAdam Czachorowski 137e2d61ae5SAdam Czachorowski const char *Args[] = {"clang", "test.cpp", "-fmodule-map-file=m.modulemap", 138e2d61ae5SAdam Czachorowski "-fmodule-name=M"}; 139*df9a14d7SKadir Cetinkaya Diags = 140*df9a14d7SKadir Cetinkaya CompilerInstance::createDiagnostics(*InMemoryFs, new DiagnosticOptions()); 141499d0b96SSam McCall CreateInvocationOptions CIOpts; 142499d0b96SSam McCall CIOpts.Diags = Diags; 143499d0b96SSam McCall CInvok = createInvocation(Args, std::move(CIOpts)); 144e2d61ae5SAdam Czachorowski ASSERT_TRUE(CInvok); 145e2d61ae5SAdam Czachorowski 146e2d61ae5SAdam Czachorowski FileManager *FileMgr = new FileManager(FileSystemOptions(), InMemoryFs); 147e2d61ae5SAdam Czachorowski PCHContainerOps = std::make_shared<PCHContainerOperations>(); 148e2d61ae5SAdam Czachorowski 149e2d61ae5SAdam Czachorowski auto AU = ASTUnit::LoadFromCompilerInvocation( 150e2d61ae5SAdam Czachorowski CInvok, PCHContainerOps, Diags, FileMgr, false, CaptureDiagsKind::None, 1, 151e2d61ae5SAdam Czachorowski TU_Complete, false, false, false); 152e2d61ae5SAdam Czachorowski ASSERT_TRUE(AU); 153e2d61ae5SAdam Czachorowski auto File = AU->getFileManager().getFileRef("Textual.h", false, false); 154e2d61ae5SAdam Czachorowski ASSERT_TRUE(bool(File)); 155e2d61ae5SAdam Czachorowski // Verify that we do not crash here. 156b0abc9ddSJan Svoboda EXPECT_TRUE( 157b0abc9ddSJan Svoboda AU->getPreprocessor().getHeaderSearchInfo().getExistingFileInfo(*File)); 158e2d61ae5SAdam Czachorowski } 159e2d61ae5SAdam Czachorowski 160fd14a1f6SLemonBoy TEST_F(ASTUnitTest, LoadFromCommandLineEarlyError) { 161fd14a1f6SLemonBoy EXPECT_FALSE( 162fd14a1f6SLemonBoy llvm::sys::fs::createTemporaryFile("ast-unit", "c", FD, InputFileName)); 163fd14a1f6SLemonBoy input_file = std::make_unique<ToolOutputFile>(InputFileName, FD); 164fd14a1f6SLemonBoy input_file->os() << ""; 165fd14a1f6SLemonBoy 166fd14a1f6SLemonBoy const char *Args[] = {"clang", "-target", "foobar", InputFileName.c_str()}; 167fd14a1f6SLemonBoy 168*df9a14d7SKadir Cetinkaya auto Diags = CompilerInstance::createDiagnostics( 169*df9a14d7SKadir Cetinkaya *llvm::vfs::getRealFileSystem(), new DiagnosticOptions()); 170fd14a1f6SLemonBoy auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); 171fd14a1f6SLemonBoy std::unique_ptr<clang::ASTUnit> ErrUnit; 172fd14a1f6SLemonBoy 173a57bdc8fSHamish Knight std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCommandLine( 17455f7e00aSIgor Kushnir &Args[0], &Args[4], PCHContainerOps, Diags, "", false, "", false, 1754dd55c56SJay Foad CaptureDiagsKind::All, {}, true, 0, TU_Complete, false, false, false, 1764dd55c56SJay Foad SkipFunctionBodiesScope::None, false, true, false, false, std::nullopt, 1774dd55c56SJay Foad &ErrUnit, nullptr); 178fd14a1f6SLemonBoy 179fd14a1f6SLemonBoy ASSERT_EQ(AST, nullptr); 180fd14a1f6SLemonBoy ASSERT_NE(ErrUnit, nullptr); 181fd14a1f6SLemonBoy ASSERT_TRUE(Diags->hasErrorOccurred()); 182fd14a1f6SLemonBoy ASSERT_NE(ErrUnit->stored_diag_size(), 0U); 183fd14a1f6SLemonBoy } 184fd14a1f6SLemonBoy 18562e4c22cSHamish Knight TEST_F(ASTUnitTest, LoadFromCommandLineWorkingDirectory) { 18662e4c22cSHamish Knight EXPECT_FALSE( 18762e4c22cSHamish Knight llvm::sys::fs::createTemporaryFile("bar", "c", FD, InputFileName)); 18862e4c22cSHamish Knight auto Input = std::make_unique<ToolOutputFile>(InputFileName, FD); 18962e4c22cSHamish Knight Input->os() << ""; 19062e4c22cSHamish Knight 19162e4c22cSHamish Knight SmallString<128> WorkingDir; 19262e4c22cSHamish Knight ASSERT_FALSE(sys::fs::createUniqueDirectory("foo", WorkingDir)); 19362e4c22cSHamish Knight const char *Args[] = {"clang", "-working-directory", WorkingDir.c_str(), 19462e4c22cSHamish Knight InputFileName.c_str()}; 19562e4c22cSHamish Knight 196*df9a14d7SKadir Cetinkaya auto Diags = CompilerInstance::createDiagnostics( 197*df9a14d7SKadir Cetinkaya *llvm::vfs::getRealFileSystem(), new DiagnosticOptions()); 19862e4c22cSHamish Knight auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); 19962e4c22cSHamish Knight std::unique_ptr<clang::ASTUnit> ErrUnit; 20062e4c22cSHamish Knight 201a57bdc8fSHamish Knight std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCommandLine( 20262e4c22cSHamish Knight &Args[0], &Args[4], PCHContainerOps, Diags, "", false, "", false, 2034dd55c56SJay Foad CaptureDiagsKind::All, {}, true, 0, TU_Complete, false, false, false, 2044dd55c56SJay Foad SkipFunctionBodiesScope::None, false, true, false, false, std::nullopt, 2054dd55c56SJay Foad &ErrUnit, nullptr); 20662e4c22cSHamish Knight 20762e4c22cSHamish Knight ASSERT_NE(AST, nullptr); 20862e4c22cSHamish Knight ASSERT_FALSE(Diags->hasErrorOccurred()); 20962e4c22cSHamish Knight 21062e4c22cSHamish Knight // Make sure '-working-directory' sets both the FileSystemOpts and underlying 21162e4c22cSHamish Knight // VFS working directory. 21262e4c22cSHamish Knight const auto &FM = AST->getFileManager(); 21362e4c22cSHamish Knight const auto &VFS = FM.getVirtualFileSystem(); 21462e4c22cSHamish Knight ASSERT_EQ(*VFS.getCurrentWorkingDirectory(), WorkingDir.str()); 21562e4c22cSHamish Knight ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir.str()); 21662e4c22cSHamish Knight } 21762e4c22cSHamish Knight 218fd9fad9eSVedant Kumar } // anonymous namespace 219