Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions clang/lib/Frontend/ASTUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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([&]() {
Expand All @@ -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());

Expand Down Expand Up @@ -1752,6 +1752,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;
Expand Down Expand Up @@ -1801,8 +1807,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->ModuleCache = new InMemoryModuleCache;
Expand Down
32 changes: 32 additions & 0 deletions clang/unittests/Frontend/ASTUnitTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,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,
CaptureDiagsKind::All, None, true, 0, TU_Complete, false, false, false,
SkipFunctionBodiesScope::None, false, true, false, false, None, &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
1 change: 1 addition & 0 deletions clang/unittests/Frontend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_clang_unittest(FrontendTests
CodeGenActionTest.cpp
ParsedSourceLocationTest.cpp
PCHPreambleTest.cpp
ReparseWorkingDirTest.cpp
OutputStreamTest.cpp
TextDiagnosticTest.cpp
UtilsTest.cpp
Expand Down
116 changes: 116 additions & 0 deletions clang/unittests/Frontend/ReparseWorkingDirTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//====-- 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. We use '//root/' to allow the path to be
// valid on both Windows and Unix. We need the trailing slash for the path
// to be treated as absolute.
SmallString<16> WorkingDir;
llvm::sys::path::append(WorkingDir, "//root",
llvm::sys::path::get_separator());
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