1 | //====-- unittests/Frontend/ReparseWorkingDirTest.cpp - FrontendAction 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 "clang/Basic/Diagnostic.h" |
10 | #include "clang/Basic/FileManager.h" |
11 | #include "clang/Frontend/ASTUnit.h" |
12 | #include "clang/Frontend/CompilerInstance.h" |
13 | #include "clang/Frontend/CompilerInvocation.h" |
14 | #include "clang/Frontend/FrontendActions.h" |
15 | #include "clang/Frontend/FrontendOptions.h" |
16 | #include "clang/Lex/PreprocessorOptions.h" |
17 | #include "llvm/Support/FileSystem.h" |
18 | #include "llvm/Support/MemoryBuffer.h" |
19 | #include "llvm/Support/Path.h" |
20 | #include "gtest/gtest.h" |
21 | |
22 | using namespace llvm; |
23 | using namespace clang; |
24 | |
25 | namespace { |
26 | class ReparseWorkingDirTest : public ::testing::Test { |
27 | IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS; |
28 | std::shared_ptr<PCHContainerOperations> PCHContainerOpts; |
29 | |
30 | public: |
31 | void SetUp() override { VFS = new vfs::InMemoryFileSystem(); } |
32 | void TearDown() override {} |
33 | |
34 | void setWorkingDirectory(StringRef Path) { |
35 | VFS->setCurrentWorkingDirectory(Path); |
36 | } |
37 | |
38 | void AddFile(const std::string &Filename, const std::string &Contents) { |
39 | ::time_t now; |
40 | ::time(timer: &now); |
41 | VFS->addFile(Path: Filename, ModificationTime: now, |
42 | Buffer: MemoryBuffer::getMemBufferCopy(InputData: Contents, BufferName: Filename)); |
43 | } |
44 | |
45 | std::unique_ptr<ASTUnit> ParseAST(StringRef EntryFile) { |
46 | PCHContainerOpts = std::make_shared<PCHContainerOperations>(); |
47 | auto CI = std::make_shared<CompilerInvocation>(); |
48 | CI->getFrontendOpts().Inputs.push_back(Elt: FrontendInputFile( |
49 | EntryFile, FrontendOptions::getInputKindForExtension( |
50 | Extension: llvm::sys::path::extension(path: EntryFile).substr(Start: 1)))); |
51 | |
52 | CI->getHeaderSearchOpts().AddPath(Path: "headers" , |
53 | Group: frontend::IncludeDirGroup::Quoted, |
54 | /*isFramework*/ IsFramework: false, |
55 | /*IgnoreSysRoot*/ false); |
56 | |
57 | CI->getFileSystemOpts().WorkingDir = *VFS->getCurrentWorkingDirectory(); |
58 | CI->getTargetOpts().Triple = "i386-unknown-linux-gnu" ; |
59 | |
60 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
61 | CompilerInstance::createDiagnostics(Opts: new DiagnosticOptions, |
62 | Client: new DiagnosticConsumer)); |
63 | |
64 | FileManager *FileMgr = new FileManager(CI->getFileSystemOpts(), VFS); |
65 | |
66 | std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation( |
67 | CI, PCHContainerOps: PCHContainerOpts, Diags, FileMgr, OnlyLocalDecls: false, CaptureDiagnostics: CaptureDiagsKind::None, |
68 | /*PrecompilePreambleAfterNParses=*/1); |
69 | return AST; |
70 | } |
71 | |
72 | bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) { |
73 | bool reparseFailed = |
74 | AST->Reparse(PCHContainerOps: PCHContainerOpts, /*RemappedFiles*/ {}, VFS); |
75 | return !reparseFailed; |
76 | } |
77 | }; |
78 | |
79 | TEST_F(ReparseWorkingDirTest, ReparseWorkingDir) { |
80 | // Setup the working directory path. |
81 | SmallString<16> WorkingDir; |
82 | #ifdef _WIN32 |
83 | WorkingDir = "C:\\" ; |
84 | #else |
85 | WorkingDir = "/" ; |
86 | #endif |
87 | llvm::sys::path::append(path&: WorkingDir, a: "root" ); |
88 | setWorkingDirectory(WorkingDir); |
89 | |
90 | SmallString<32> ; |
91 | llvm::sys::path::append(path&: Header, a: WorkingDir, b: "headers" , c: "header.h" ); |
92 | |
93 | SmallString<32> MainName; |
94 | llvm::sys::path::append(path&: MainName, a: WorkingDir, b: "main.cpp" ); |
95 | |
96 | AddFile(Filename: MainName.str().str(), Contents: R"cpp( |
97 | #include "header.h" |
98 | int main() { return foo(); } |
99 | )cpp" ); |
100 | AddFile(Filename: Header.str().str(), Contents: R"h( |
101 | static int foo() { return 0; } |
102 | )h" ); |
103 | |
104 | // Parse the main file, ensuring we can include the header. |
105 | std::unique_ptr<ASTUnit> AST(ParseAST(EntryFile: MainName.str())); |
106 | ASSERT_TRUE(AST.get()); |
107 | ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
108 | |
109 | // Reparse and check that the working directory was preserved. |
110 | ASSERT_TRUE(ReparseAST(AST)); |
111 | |
112 | const auto &FM = AST->getFileManager(); |
113 | const auto &FS = FM.getVirtualFileSystem(); |
114 | ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir); |
115 | ASSERT_EQ(*FS.getCurrentWorkingDirectory(), WorkingDir); |
116 | } |
117 | |
118 | } // end anonymous namespace |
119 | |