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
createASTUnit(bool isVolatile)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
TEST_F(ASTUnitTest,SaveLoadPreservesLangOptionsInPrintingPolicy)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
TEST_F(ASTUnitTest,GetBufferForFileMemoryMapping)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
TEST_F(ASTUnitTest,ModuleTextualHeader)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
TEST_F(ASTUnitTest,LoadFromCommandLineEarlyError)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