//===- unittests/Frontend/ASTUnitTest.cpp - ASTUnit tests -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include #include "clang/Basic/FileManager.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/PCHContainerOperations.h" #include "clang/Lex/HeaderSearch.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/ToolOutputFile.h" #include "gtest/gtest.h" using namespace llvm; using namespace clang; namespace { class ASTUnitTest : public ::testing::Test { protected: int FD; llvm::SmallString<256> InputFileName; std::unique_ptr input_file; IntrusiveRefCntPtr Diags; std::shared_ptr CInvok; std::shared_ptr PCHContainerOps; std::unique_ptr createASTUnit(bool isVolatile) { EXPECT_FALSE(llvm::sys::fs::createTemporaryFile("ast-unit", "cpp", FD, InputFileName)); input_file = std::make_unique(InputFileName, FD); input_file->os() << ""; const char *Args[] = {"clang", "-xc++", InputFileName.c_str()}; Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions()); CInvok = createInvocationFromCommandLine(Args, Diags); if (!CInvok) return nullptr; FileManager *FileMgr = new FileManager(FileSystemOptions(), vfs::getRealFileSystem()); PCHContainerOps = std::make_shared(); return ASTUnit::LoadFromCompilerInvocation( CInvok, PCHContainerOps, Diags, FileMgr, false, CaptureDiagsKind::None, 0, TU_Complete, false, false, isVolatile); } }; TEST_F(ASTUnitTest, SaveLoadPreservesLangOptionsInPrintingPolicy) { // Check that the printing policy is restored with the correct language // options when loading an ASTUnit from a file. To this end, an ASTUnit // for a C++ translation unit is set up and written to a temporary file. // By default `UseVoidForZeroParams` is true for non-C++ language options, // thus we can check this field after loading the ASTUnit to deduce whether // the correct (C++) language options were used when setting up the printing // policy. { PrintingPolicy PolicyWithDefaultLangOpt(LangOptions{}); EXPECT_TRUE(PolicyWithDefaultLangOpt.UseVoidForZeroParams); } std::unique_ptr AST = createASTUnit(false); if (!AST) FAIL() << "failed to create ASTUnit"; EXPECT_FALSE(AST->getASTContext().getPrintingPolicy().UseVoidForZeroParams); llvm::SmallString<256> ASTFileName; ASSERT_FALSE( llvm::sys::fs::createTemporaryFile("ast-unit", "ast", FD, ASTFileName)); ToolOutputFile ast_file(ASTFileName, FD); AST->Save(ASTFileName.str()); EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName)); std::unique_ptr AU = ASTUnit::LoadFromASTFile( std::string(ASTFileName.str()), PCHContainerOps->getRawReader(), ASTUnit::LoadEverything, Diags, FileSystemOptions(), /*UseDebugInfo=*/false); if (!AU) FAIL() << "failed to load ASTUnit"; EXPECT_FALSE(AU->getASTContext().getPrintingPolicy().UseVoidForZeroParams); } TEST_F(ASTUnitTest, GetBufferForFileMemoryMapping) { std::unique_ptr AST = createASTUnit(true); if (!AST) FAIL() << "failed to create ASTUnit"; std::unique_ptr memoryBuffer = AST->getBufferForFile(InputFileName); EXPECT_NE(memoryBuffer->getBufferKind(), llvm::MemoryBuffer::MemoryBuffer_MMap); } TEST_F(ASTUnitTest, ModuleTextualHeader) { llvm::IntrusiveRefCntPtr InMemoryFs = new llvm::vfs::InMemoryFileSystem(); InMemoryFs->addFile("test.cpp", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( #include "Textual.h" void foo() {} )cpp")); InMemoryFs->addFile("m.modulemap", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( module M { module Textual { textual header "Textual.h" } } )cpp")); InMemoryFs->addFile("Textual.h", 0, llvm::MemoryBuffer::getMemBuffer(R"cpp( void foo(); )cpp")); const char *Args[] = {"clang", "test.cpp", "-fmodule-map-file=m.modulemap", "-fmodule-name=M"}; Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions()); CInvok = createInvocationFromCommandLine(Args, Diags); ASSERT_TRUE(CInvok); FileManager *FileMgr = new FileManager(FileSystemOptions(), InMemoryFs); PCHContainerOps = std::make_shared(); auto AU = ASTUnit::LoadFromCompilerInvocation( CInvok, PCHContainerOps, Diags, FileMgr, false, CaptureDiagsKind::None, 1, TU_Complete, false, false, false); ASSERT_TRUE(AU); auto File = AU->getFileManager().getFileRef("Textual.h", false, false); ASSERT_TRUE(bool(File)); // Verify that we do not crash here. EXPECT_TRUE(AU->getPreprocessor().getHeaderSearchInfo().getExistingFileInfo( &File->getFileEntry())); } TEST_F(ASTUnitTest, LoadFromCommandLineEarlyError) { EXPECT_FALSE( llvm::sys::fs::createTemporaryFile("ast-unit", "c", FD, InputFileName)); input_file = std::make_unique(InputFileName, FD); input_file->os() << ""; const char *Args[] = {"clang", "-target", "foobar", InputFileName.c_str()}; auto Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions()); auto PCHContainerOps = std::make_shared(); std::unique_ptr ErrUnit; ASTUnit *AST = ASTUnit::LoadFromCommandLine( &Args[0], &Args[4], PCHContainerOps, Diags, "", false, CaptureDiagsKind::All, None, true, 0, TU_Complete, false, false, false, SkipFunctionBodiesScope::None, false, true, false, false, None, &ErrUnit, nullptr); ASSERT_EQ(AST, nullptr); ASSERT_NE(ErrUnit, nullptr); ASSERT_TRUE(Diags->hasErrorOccurred()); ASSERT_NE(ErrUnit->stored_diag_size(), 0U); } } // anonymous namespace