Author: Hamish Knight Date: 2023-06-30T09:56:42-07:00 New Revision: 62e4c22c95a9c5fbbebded43388006d5fed3b613
URL: https://github.com/llvm/llvm-project/commit/62e4c22c95a9c5fbbebded43388006d5fed3b613 DIFF: https://github.com/llvm/llvm-project/commit/62e4c22c95a9c5fbbebded43388006d5fed3b613.diff LOG: [clang] Fix ASTUnit working directory handling Fix a couple of issues with the handling of the current working directory in ASTUnit: - Use `createPhysicalFileSystem` instead of `getRealFileSystem` to avoid affecting the process' current working directory, and set it at the top of `ASTUnit::LoadFromCommandLine` such that the driver used for argument parsing and the ASTUnit share the same VFS. This ensures that '-working-directory' correctly sets the VFS working directory in addition to the FileManager working directory. - Ensure we preserve the FileSystemOptions set on the FileManager when re-creating it (as `ASTUnit::Reparse` will clear the currently set FileManager). rdar://110697657 Reviewed By: bnbarham, benlangmuir Differential Revision: https://reviews.llvm.org/D154134 Added: clang/unittests/Frontend/ReparseWorkingDirTest.cpp Modified: clang/lib/Frontend/ASTUnit.cpp clang/unittests/Frontend/ASTUnitTest.cpp clang/unittests/Frontend/CMakeLists.txt Removed: ################################################################################ diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index aac449bfbc4061..30ddfb2e84cf93 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -1145,6 +1145,7 @@ bool ASTUnit::Parse(std::shared_ptr<PCHContainerOperations> PCHContainerOps, // Create the compiler instance to use for building the AST. std::unique_ptr<CompilerInstance> Clang( new CompilerInstance(std::move(PCHContainerOps))); + Clang->setInvocation(CCInvocation); // Clean up on error, disengage it if the function returns successfully. auto CleanOnError = llvm::make_scope_exit([&]() { @@ -1171,7 +1172,6 @@ bool ASTUnit::Parse(std::shared_ptr<PCHContainerOperations> PCHContainerOps, llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(Clang.get()); - Clang->setInvocation(CCInvocation); OriginalSourceFile = std::string(Clang->getFrontendOpts().Inputs[0].getFile()); @@ -1754,6 +1754,12 @@ ASTUnit *ASTUnit::LoadFromCommandLine( IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { assert(Diags.get() && "no DiagnosticsEngine was provided"); + // If no VFS was provided, create one that tracks the physical file system. + // If '-working-directory' was passed as an argument, 'createInvocation' will + // set this as the current working directory of the VFS. + if (!VFS) + VFS = llvm::vfs::createPhysicalFileSystem(); + SmallVector<StoredDiagnostic, 4> StoredDiagnostics; std::shared_ptr<CompilerInvocation> CI; @@ -1799,8 +1805,6 @@ ASTUnit *ASTUnit::LoadFromCommandLine( ConfigureDiags(Diags, *AST, CaptureDiagnostics); AST->Diagnostics = Diags; AST->FileSystemOpts = CI->getFileSystemOpts(); - if (!VFS) - VFS = llvm::vfs::getRealFileSystem(); VFS = createVFSFromCompilerInvocation(*CI, *Diags, VFS); AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); AST->StorePreamblesInMemory = StorePreamblesInMemory; diff --git a/clang/unittests/Frontend/ASTUnitTest.cpp b/clang/unittests/Frontend/ASTUnitTest.cpp index bb3466575d8a7b..852cfc7118b23a 100644 --- a/clang/unittests/Frontend/ASTUnitTest.cpp +++ b/clang/unittests/Frontend/ASTUnitTest.cpp @@ -179,4 +179,36 @@ TEST_F(ASTUnitTest, LoadFromCommandLineEarlyError) { ASSERT_NE(ErrUnit->stored_diag_size(), 0U); } +TEST_F(ASTUnitTest, LoadFromCommandLineWorkingDirectory) { + EXPECT_FALSE( + llvm::sys::fs::createTemporaryFile("bar", "c", FD, InputFileName)); + auto Input = std::make_unique<ToolOutputFile>(InputFileName, FD); + Input->os() << ""; + + SmallString<128> WorkingDir; + ASSERT_FALSE(sys::fs::createUniqueDirectory("foo", WorkingDir)); + const char *Args[] = {"clang", "-working-directory", WorkingDir.c_str(), + InputFileName.c_str()}; + + auto Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions()); + auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); + std::unique_ptr<clang::ASTUnit> ErrUnit; + + auto *AST = ASTUnit::LoadFromCommandLine( + &Args[0], &Args[4], PCHContainerOps, Diags, "", false, "", false, + CaptureDiagsKind::All, std::nullopt, true, 0, TU_Complete, false, false, + false, SkipFunctionBodiesScope::None, false, true, false, false, + std::nullopt, &ErrUnit, nullptr); + + ASSERT_NE(AST, nullptr); + ASSERT_FALSE(Diags->hasErrorOccurred()); + + // Make sure '-working-directory' sets both the FileSystemOpts and underlying + // VFS working directory. + const auto &FM = AST->getFileManager(); + const auto &VFS = FM.getVirtualFileSystem(); + ASSERT_EQ(*VFS.getCurrentWorkingDirectory(), WorkingDir.str()); + ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir.str()); +} + } // anonymous namespace diff --git a/clang/unittests/Frontend/CMakeLists.txt b/clang/unittests/Frontend/CMakeLists.txt index e11790d05abb9c..0f05813338f2ad 100644 --- a/clang/unittests/Frontend/CMakeLists.txt +++ b/clang/unittests/Frontend/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_unittest(FrontendTests CodeGenActionTest.cpp ParsedSourceLocationTest.cpp PCHPreambleTest.cpp + ReparseWorkingDirTest.cpp OutputStreamTest.cpp TextDiagnosticTest.cpp UtilsTest.cpp diff --git a/clang/unittests/Frontend/ReparseWorkingDirTest.cpp b/clang/unittests/Frontend/ReparseWorkingDirTest.cpp new file mode 100644 index 00000000000000..ca7ce23dd64b22 --- /dev/null +++ b/clang/unittests/Frontend/ReparseWorkingDirTest.cpp @@ -0,0 +1,118 @@ +//====-- unittests/Frontend/ReparseWorkingDirTest.cpp - FrontendAction tests =// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { +class ReparseWorkingDirTest : public ::testing::Test { + IntrusiveRefCntPtr<vfs::InMemoryFileSystem> VFS; + std::shared_ptr<PCHContainerOperations> PCHContainerOpts; + +public: + void SetUp() override { VFS = new vfs::InMemoryFileSystem(); } + void TearDown() override {} + + void setWorkingDirectory(StringRef Path) { + VFS->setCurrentWorkingDirectory(Path); + } + + void AddFile(const std::string &Filename, const std::string &Contents) { + ::time_t now; + ::time(&now); + VFS->addFile(Filename, now, + MemoryBuffer::getMemBufferCopy(Contents, Filename)); + } + + std::unique_ptr<ASTUnit> ParseAST(StringRef EntryFile) { + PCHContainerOpts = std::make_shared<PCHContainerOperations>(); + auto CI = std::make_shared<CompilerInvocation>(); + CI->getFrontendOpts().Inputs.push_back(FrontendInputFile( + EntryFile, FrontendOptions::getInputKindForExtension( + llvm::sys::path::extension(EntryFile).substr(1)))); + + CI->getHeaderSearchOpts().AddPath("headers", + frontend::IncludeDirGroup::Quoted, + /*isFramework*/ false, + /*IgnoreSysRoot*/ false); + + CI->getFileSystemOpts().WorkingDir = *VFS->getCurrentWorkingDirectory(); + CI->getTargetOpts().Triple = "i386-unknown-linux-gnu"; + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + CompilerInstance::createDiagnostics(new DiagnosticOptions, + new DiagnosticConsumer)); + + FileManager *FileMgr = new FileManager(CI->getFileSystemOpts(), VFS); + + std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation( + CI, PCHContainerOpts, Diags, FileMgr, false, CaptureDiagsKind::None, + /*PrecompilePreambleAfterNParses=*/1); + return AST; + } + + bool ReparseAST(const std::unique_ptr<ASTUnit> &AST) { + bool reparseFailed = + AST->Reparse(PCHContainerOpts, /*RemappedFiles*/ {}, VFS); + return !reparseFailed; + } +}; + +TEST_F(ReparseWorkingDirTest, ReparseWorkingDir) { + // Setup the working directory path. + SmallString<16> WorkingDir; +#ifdef _WIN32 + WorkingDir = "C:\\"; +#else + WorkingDir = "/"; +#endif + llvm::sys::path::append(WorkingDir, "root"); + setWorkingDirectory(WorkingDir); + + SmallString<32> Header; + llvm::sys::path::append(Header, WorkingDir, "headers", "header.h"); + + SmallString<32> MainName; + llvm::sys::path::append(MainName, WorkingDir, "main.cpp"); + + AddFile(MainName.str().str(), R"cpp( +#include "header.h" +int main() { return foo(); } +)cpp"); + AddFile(Header.str().str(), R"h( +static int foo() { return 0; } +)h"); + + // Parse the main file, ensuring we can include the header. + std::unique_ptr<ASTUnit> AST(ParseAST(MainName.str())); + ASSERT_TRUE(AST.get()); + ASSERT_FALSE(AST->getDiagnostics().hasErrorOccurred()); + + // Reparse and check that the working directory was preserved. + ASSERT_TRUE(ReparseAST(AST)); + + const auto &FM = AST->getFileManager(); + const auto &FS = FM.getVirtualFileSystem(); + ASSERT_EQ(FM.getFileSystemOpts().WorkingDir, WorkingDir); + ASSERT_EQ(*FS.getCurrentWorkingDirectory(), WorkingDir); +} + +} // end anonymous namespace _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits