xref: /llvm-project/clang/unittests/Frontend/ASTUnitTest.cpp (revision df9a14d7bbf1180e4f1474254c9d7ed6bcb4ce55)
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