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