diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 64f90c493c105..0fec806f714d8 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -348,7 +348,8 @@ void VerifyPCHAction::ExecuteAction() { DisableValidationForModuleKind::None, /*AllowASTWithCompilerErrors*/ false, /*AllowConfigurationMismatch*/ true, - /*ValidateSystemInputs*/ true)); + /*ValidateSystemInputs*/ true, + CI.getHeaderSearchOpts().ValidateASTInputFilesContent)); Reader->ReadAST(getCurrentFile(), Preamble ? serialization::MK_Preamble diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 008bf571f847d..28787f3ee8828 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4781,6 +4781,24 @@ bool ASTWriter::PreparePathForOutput(SmallVectorImpl &Path) { Changed = true; } + // If we are generating a normal PCH (EG. not a C++ module). + if (!WritingModule) { + // Use the vfs overlay if it exists to translate paths. + auto &FileSys = + Context->getSourceManager().getFileManager().getVirtualFileSystem(); + if (auto *RFS = dyn_cast(&FileSys)) { + if (auto Result = RFS->lookupPath(StringRef(Path.data(), Path.size()))) { + if (std::optional Redirect = Result->getExternalRedirect()) { + const char *data = Redirect->data(); + const size_t size = Redirect->size(); + Path.resize(size); + Path.assign(data, data + size); + Changed = true; + } + } + } + } + return Changed; } diff --git a/clang/test/PCH/verify_no_timestamp.c b/clang/test/PCH/verify_no_timestamp.c new file mode 100644 index 0000000000000..8aca76cf77c44 --- /dev/null +++ b/clang/test/PCH/verify_no_timestamp.c @@ -0,0 +1,8 @@ +// RUN: echo 'int SomeFunc() { return 42; }' > %t.h +// RUN: %clang_cc1 -Werror -fno-pch-timestamp -fvalidate-ast-input-files-content -emit-pch -o "%t.pch" %t.h + +// Now change the source file, which should cause the verifier to fail with content mismatch. +// RUN: echo 'int SomeFunc() { return 13; }' > %t.h +// RUN: not %clang_cc1 -fno-pch-timestamp -fvalidate-ast-input-files-content -verify-pch %t.pch 2>&1 | FileCheck %s -DT=%t + +// CHECK: fatal error: file '[[T]].h' has been modified since the precompiled header '[[T]].pch' was built: content changed diff --git a/clang/test/VFS/Inputs/vfsoverlay-directory-remap.yaml b/clang/test/VFS/Inputs/vfsoverlay-directory-remap.yaml new file mode 100644 index 0000000000000..3cb279a08fae1 --- /dev/null +++ b/clang/test/VFS/Inputs/vfsoverlay-directory-remap.yaml @@ -0,0 +1,9 @@ +{ + 'version': 0, + 'roots': [ + { 'name': 'FROM_DIR', + 'type': 'directory-remap', + 'external-contents': 'TO_DIR' + } + ] +} diff --git a/clang/test/VFS/remap-to-fake.c b/clang/test/VFS/remap-to-fake.c new file mode 100644 index 0000000000000..85572e0120ae6 --- /dev/null +++ b/clang/test/VFS/remap-to-fake.c @@ -0,0 +1,27 @@ +// RUN: split-file %s %t +// RUN: sed -e "s@FROM_DIR@%{/t:regex_replacement}/From@g" -e "s@TO_DIR@%{/t:regex_replacement}/Fake@g" %S/Inputs/vfsoverlay-directory-remap.yaml > %t/to-fake.yaml +// RUN: sed -e "s@FROM_DIR@%{/t:regex_replacement}/Fake@g" -e "s@TO_DIR@%{/t:regex_replacement}/To@g" %S/Inputs/vfsoverlay-directory-remap.yaml > %t/from-fake.yaml + +// RUN: %clang_cc1 -Werror -fno-pch-timestamp -fvalidate-ast-input-files-content -ivfsoverlay %t/to-fake.yaml -emit-pch -o "%t.pch" %t/From/../From/B.h + +// Remove the `From` directory as we don't want to accidentally find that source if the PCH hasn't remapped using the VFS! +// RUN: rm -rf %t/From + +// The PCH will be invalid because the `Fake` directory does not exist. +// RUN: not %clang_cc1 -fno-pch-timestamp -fvalidate-ast-input-files-content -verify-pch %t.pch + +// But if we specify the correct VFS overlay it'll verify clean. +// RUN: %clang_cc1 -fno-pch-timestamp -fvalidate-ast-input-files-content -verify-pch -ivfsoverlay %t/from-fake.yaml %t.pch + +// RUN: %clang_cc1 -fno-pch-timestamp -fvalidate-ast-input-files-content -Werror -I %t/To -ivfsoverlay %t/from-fake.yaml -include-pch "%t.pch" -emit-llvm -C %t/Test.c -o %t.o + +//--- From/B.h +#pragma once +int SomeFunc() { return 13; } +//--- To/B.h +#pragma once +int SomeFunc() { return 13; } +//--- Test.c +#include "B.h" + +int UseSomeFunc() { return SomeFunc(); }