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