| 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 | auto DiagOpts = std::make_shared<DiagnosticOptions>(); |
| 61 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
| 62 | CompilerInstance::createDiagnostics(VFS&: *VFS, Opts&: *DiagOpts, |
| 63 | Client: new DiagnosticConsumer)); |
| 64 | |
| 65 | FileManager *FileMgr = new FileManager(CI->getFileSystemOpts(), VFS); |
| 66 | |
| 67 | std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation( |
| 68 | CI, PCHContainerOps: PCHContainerOpts, DiagOpts, Diags, FileMgr, OnlyLocalDecls: false, |
| 69 | CaptureDiagnostics: CaptureDiagsKind::None, |
| 70 | /*PrecompilePreambleAfterNParses=*/1); |
| 71 | return AST; |
| 72 | } |
| 73 | |
| 74 | bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) { |
| 75 | bool reparseFailed = |
| 76 | AST->Reparse(PCHContainerOps: PCHContainerOpts, /*RemappedFiles*/ {}, VFS); |
| 77 | return !reparseFailed; |
| 78 | } |
| 79 | }; |
| 80 | |
| 81 | TEST_F(ReparseWorkingDirTest, ReparseWorkingDir) { |
| 82 | // Setup the working directory path. |
| 83 | SmallString<16> WorkingDir; |
| 84 | #ifdef _WIN32 |
| 85 | WorkingDir = "C:\\" ; |
| 86 | #else |
| 87 | WorkingDir = "/" ; |
| 88 | #endif |
| 89 | llvm::sys::path::append(path&: WorkingDir, a: "root" ); |
| 90 | setWorkingDirectory(WorkingDir); |
| 91 | |
| 92 | SmallString<32> ; |
| 93 | llvm::sys::path::append(path&: Header, a: WorkingDir, b: "headers" , c: "header.h" ); |
| 94 | |
| 95 | SmallString<32> MainName; |
| 96 | llvm::sys::path::append(path&: MainName, a: WorkingDir, b: "main.cpp" ); |
| 97 | |
| 98 | AddFile(Filename: MainName.str().str(), Contents: R"cpp( |
| 99 | #include "header.h" |
| 100 | int main() { return foo(); } |
| 101 | )cpp" ); |
| 102 | AddFile(Filename: Header.str().str(), Contents: R"h( |
| 103 | static int foo() { return 0; } |
| 104 | )h" ); |
| 105 | |
| 106 | // Parse the main file, ensuring we can include the header. |
| 107 | std::unique_ptr<ASTUnit> AST(ParseAST(EntryFile: MainName.str())); |
| 108 | ASSERT_TRUE(AST.get()); |
| 109 | ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 110 | |
| 111 | // Reparse and check that the working directory was preserved. |
| 112 | ASSERT_TRUE(ReparseAST(AST)); |
| 113 | |
| 114 | const auto &FM = AST->getFileManager(); |
| 115 | const auto &FS = FM.getVirtualFileSystem(); |
| 116 | ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir); |
| 117 | ASSERT_EQ(*FS.getCurrentWorkingDirectory(), WorkingDir); |
| 118 | } |
| 119 | |
| 120 | } // end anonymous namespace |
| 121 | |