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
22using namespace llvm;
23using namespace clang;
24
25namespace {
26
27class ASTUnitTest : public ::testing::Test {
28protected:
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>(args&: InputFileName, args&: FD);
40 input_file->os() << "";
41
42 const char *Args[] = {"clang", "-xc++", InputFileName.c_str()};
43
44 Diags = CompilerInstance::createDiagnostics(Opts: new DiagnosticOptions());
45
46 CreateInvocationOptions CIOpts;
47 CIOpts.Diags = Diags;
48 CInvok = createInvocation(Args, Opts: 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 CI: CInvok, PCHContainerOps, Diags, FileMgr, OnlyLocalDecls: false, CaptureDiagnostics: CaptureDiagsKind::None,
59 PrecompilePreambleAfterNParses: 0, TUKind: TU_Complete, CacheCodeCompletionResults: false, IncludeBriefCommentsInCodeCompletion: false, UserFilesAreVolatile: isVolatile);
60 }
61};
62
63TEST_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(isVolatile: 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(File: ASTFileName.str());
90
91 EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));
92 auto HSOpts = std::make_shared<HeaderSearchOptions>();
93
94 std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
95 Filename: std::string(ASTFileName.str()), PCHContainerRdr: PCHContainerOps->getRawReader(),
96 ToLoad: ASTUnit::LoadEverything, Diags, FileSystemOpts: FileSystemOptions(), HSOpts);
97
98 if (!AU)
99 FAIL() << "failed to load ASTUnit";
100
101 EXPECT_FALSE(AU->getASTContext().getPrintingPolicy().UseVoidForZeroParams);
102}
103
104TEST_F(ASTUnitTest, GetBufferForFileMemoryMapping) {
105 std::unique_ptr<ASTUnit> AST = createASTUnit(isVolatile: true);
106
107 if (!AST)
108 FAIL() << "failed to create ASTUnit";
109
110 std::unique_ptr<llvm::MemoryBuffer> memoryBuffer =
111 AST->getBufferForFile(Filename: InputFileName);
112
113 EXPECT_NE(memoryBuffer->getBufferKind(),
114 llvm::MemoryBuffer::MemoryBuffer_MMap);
115}
116
117TEST_F(ASTUnitTest, ModuleTextualHeader) {
118 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFs =
119 new llvm::vfs::InMemoryFileSystem();
120 InMemoryFs->addFile(Path: "test.cpp", ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: R"cpp(
121 #include "Textual.h"
122 void foo() {}
123 )cpp"));
124 InMemoryFs->addFile(Path: "m.modulemap", ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: R"cpp(
125 module M {
126 module Textual {
127 textual header "Textual.h"
128 }
129 }
130 )cpp"));
131 InMemoryFs->addFile(Path: "Textual.h", ModificationTime: 0, Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: 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(Opts: new DiagnosticOptions());
138 CreateInvocationOptions CIOpts;
139 CIOpts.Diags = Diags;
140 CInvok = createInvocation(Args, Opts: 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 CI: CInvok, PCHContainerOps, Diags, FileMgr, OnlyLocalDecls: false, CaptureDiagnostics: CaptureDiagsKind::None, PrecompilePreambleAfterNParses: 1,
148 TUKind: TU_Complete, CacheCodeCompletionResults: false, IncludeBriefCommentsInCodeCompletion: false, UserFilesAreVolatile: false);
149 ASSERT_TRUE(AU);
150 auto File = AU->getFileManager().getFileRef(Filename: "Textual.h", OpenFile: false, CacheFailure: false);
151 ASSERT_TRUE(bool(File));
152 // Verify that we do not crash here.
153 EXPECT_TRUE(
154 AU->getPreprocessor().getHeaderSearchInfo().getExistingFileInfo(*File));
155}
156
157TEST_F(ASTUnitTest, LoadFromCommandLineEarlyError) {
158 EXPECT_FALSE(
159 llvm::sys::fs::createTemporaryFile("ast-unit", "c", FD, InputFileName));
160 input_file = std::make_unique<ToolOutputFile>(args&: InputFileName, args&: FD);
161 input_file->os() << "";
162
163 const char *Args[] = {"clang", "-target", "foobar", InputFileName.c_str()};
164
165 auto Diags = CompilerInstance::createDiagnostics(Opts: new DiagnosticOptions());
166 auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
167 std::unique_ptr<clang::ASTUnit> ErrUnit;
168
169 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCommandLine(
170 ArgBegin: &Args[0], ArgEnd: &Args[4], PCHContainerOps, Diags, ResourceFilesPath: "", StorePreamblesInMemory: false, PreambleStoragePath: "", OnlyLocalDecls: false,
171 CaptureDiagnostics: CaptureDiagsKind::All, RemappedFiles: std::nullopt, RemappedFilesKeepOriginalName: true, PrecompilePreambleAfterNParses: 0, TUKind: TU_Complete, CacheCodeCompletionResults: false, IncludeBriefCommentsInCodeCompletion: false,
172 AllowPCHWithCompilerErrors: false, SkipFunctionBodies: SkipFunctionBodiesScope::None, SingleFileParse: false, UserFilesAreVolatile: true, ForSerialization: false, RetainExcludedConditionalBlocks: false,
173 ModuleFormat: std::nullopt, ErrAST: &ErrUnit, VFS: 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
181TEST_F(ASTUnitTest, LoadFromCommandLineWorkingDirectory) {
182 EXPECT_FALSE(
183 llvm::sys::fs::createTemporaryFile("bar", "c", FD, InputFileName));
184 auto Input = std::make_unique<ToolOutputFile>(args&: InputFileName, args&: FD);
185 Input->os() << "";
186
187 SmallString<128> WorkingDir;
188 ASSERT_FALSE(sys::fs::createUniqueDirectory("foo", WorkingDir));
189 const char *Args[] = {"clang", "-working-directory", WorkingDir.c_str(),
190 InputFileName.c_str()};
191
192 auto Diags = CompilerInstance::createDiagnostics(Opts: new DiagnosticOptions());
193 auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
194 std::unique_ptr<clang::ASTUnit> ErrUnit;
195
196 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCommandLine(
197 ArgBegin: &Args[0], ArgEnd: &Args[4], PCHContainerOps, Diags, ResourceFilesPath: "", StorePreamblesInMemory: false, PreambleStoragePath: "", OnlyLocalDecls: false,
198 CaptureDiagnostics: CaptureDiagsKind::All, RemappedFiles: std::nullopt, RemappedFilesKeepOriginalName: true, PrecompilePreambleAfterNParses: 0, TUKind: TU_Complete, CacheCodeCompletionResults: false, IncludeBriefCommentsInCodeCompletion: false,
199 AllowPCHWithCompilerErrors: false, SkipFunctionBodies: SkipFunctionBodiesScope::None, SingleFileParse: false, UserFilesAreVolatile: true, ForSerialization: false, RetainExcludedConditionalBlocks: false,
200 ModuleFormat: std::nullopt, ErrAST: &ErrUnit, VFS: nullptr);
201
202 ASSERT_NE(AST, nullptr);
203 ASSERT_FALSE(Diags->hasErrorOccurred());
204
205 // Make sure '-working-directory' sets both the FileSystemOpts and underlying
206 // VFS working directory.
207 const auto &FM = AST->getFileManager();
208 const auto &VFS = FM.getVirtualFileSystem();
209 ASSERT_EQ(*VFS.getCurrentWorkingDirectory(), WorkingDir.str());
210 ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir.str());
211}
212
213} // anonymous namespace
214

source code of clang/unittests/Frontend/ASTUnitTest.cpp