[clang] 62e4c22 - [clang] Fix ASTUnit working directory handling

Ben Barham via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 30 09:56:59 PDT 2023


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


        


More information about the cfe-commits mailing list