From 966a992d69e2c6cb8acfc20b0e64227034fbbff8 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Fri, 19 Dec 2025 15:09:39 -0800 Subject: [PATCH 1/2] [clang][cas] Fix caching builds with -fdiagnostics-absolute-paths Add a fallback path to diagnostic handling for -fdiagnostics-absolute-paths to try the real filesystem, since the include tree filesystem will not be able to resolve the path. This only affects diagonstic formatting, not the cached result, so it is safe. rdar://166408406 --- .../include/clang/Basic/DiagnosticOptions.def | 3 ++ clang/lib/Frontend/CompileJobCache.cpp | 6 +++ clang/lib/Frontend/TextDiagnostic.cpp | 19 ++++++++-- ...include-tree-fdiagnostics-absolute-paths.c | 38 +++++++++++++++++++ clang/test/CAS/libclang-replay-job.c | 19 ++++++++++ clang/test/Modules/feature-availability.c | 1 + 6 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 clang/test/CAS/include-tree-fdiagnostics-absolute-paths.c diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def index ba8ce8f2e6d4f..11047a36b869d 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.def +++ b/clang/include/clang/Basic/DiagnosticOptions.def @@ -101,6 +101,9 @@ DIAGOPT(ShowSafeBufferUsageSuggestions, 1, 0) // TO_UPSTREAM(BoundsSafety) DIAGOPT(BoundsSafetyAdoptionMode, 1, 0) +// TO_UPSTREAM(CAS) +DIAGOPT(FallbackRealFileSystemAbsolutePaths, 1, 0) + #undef DIAGOPT #undef ENUM_DIAGOPT #undef VALUE_DIAGOPT diff --git a/clang/lib/Frontend/CompileJobCache.cpp b/clang/lib/Frontend/CompileJobCache.cpp index 82e7b56260f39..05d152ce9ad83 100644 --- a/clang/lib/Frontend/CompileJobCache.cpp +++ b/clang/lib/Frontend/CompileJobCache.cpp @@ -328,6 +328,9 @@ std::optional CompileJobCache::initialize(CompilerInstance &Clang) { return Error::success(); }; + // Ensure path canonicalization in text diagnostics. + Invocation.getDiagnosticOpts().FallbackRealFileSystemAbsolutePaths = true; + llvm::PrefixMapper PrefixMapper; llvm::SmallVector Split; llvm::MappedPrefix::transformPairs(CacheOpts.PathPrefixMappings, Split); @@ -638,6 +641,9 @@ Expected> CompileJobCache::replayCachedResult( assert(!Clang.getDiagnostics().hasErrorOccurred()); + // Ensure path canonicalization in text diagnostics. + Clang.getDiagnosticOpts().FallbackRealFileSystemAbsolutePaths = true; + std::optional MCOutputID; ObjectStoreCachingOutputs CachingOutputs( Clang, WorkingDir, std::move(PrefixMapper), WriteOutputAsCASID, diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp index e6f592f9ca0c3..da12b4044ee0a 100644 --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -16,7 +16,10 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/IOSandbox.h" #include "llvm/Support/Locale.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/VirtualFileSystem.h" #include #include @@ -813,9 +816,7 @@ void TextDiagnostic::printDiagnosticMessage(raw_ostream &OS, } void TextDiagnostic::emitFilename(StringRef Filename, const SourceManager &SM) { -#ifdef _WIN32 - SmallString<4096> TmpFilename; -#endif + SmallString<256> TmpFilename; if (DiagOpts.AbsolutePath) { auto File = SM.getFileManager().getOptionalFileRef(Filename); if (File) { @@ -843,6 +844,18 @@ void TextDiagnostic::emitFilename(StringRef Filename, const SourceManager &SM) { Filename = SM.getFileManager().getCanonicalName(*File); #endif } + + // TO_UPSTREAM(CAS) + if (DiagOpts.FallbackRealFileSystemAbsolutePaths && + !llvm::sys::path::is_absolute(Filename)) { + // When caching, the above may fail to canonicalize due to using the + // IncludeTreeFileSystem. In that case, we can safely fallback to the real + // filesytem since the text diagnostic output is not captured directly. + auto BypassSandbox = llvm::sys::sandbox::scopedDisable(); + auto FS = llvm::vfs::getRealFileSystem(); + if (FS->getRealPath(Filename, TmpFilename) == std::error_code()) + Filename = TmpFilename; + } } OS << Filename; diff --git a/clang/test/CAS/include-tree-fdiagnostics-absolute-paths.c b/clang/test/CAS/include-tree-fdiagnostics-absolute-paths.c new file mode 100644 index 0000000000000..9a2911c112d4b --- /dev/null +++ b/clang/test/CAS/include-tree-fdiagnostics-absolute-paths.c @@ -0,0 +1,38 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang subdir/tu.c -I. -fdiagnostics-absolute-paths -E \ +// RUN: -Rcompile-job-cache > %t/stdout-miss 2> %t/stderr-miss +// RUN: FileCheck %s -DPREFIX=%/t -check-prefix=PP -input-file=%t/stdout-miss +// RUN: FileCheck %s -DPREFIX=%/t -check-prefix=DIAG -input-file=%t/stderr-miss +// RUN: FileCheck %s -DPREFIX=%/t -check-prefix=MISS -input-file=%t/stderr-miss + +// Again, but with a cache hit. +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang subdir/tu.c -I. -fdiagnostics-absolute-paths -E \ +// RUN: -Rcompile-job-cache > %t/stdout-hit 2> %t/stderr-hit +// RUN: FileCheck %s -DPREFIX=%/t -check-prefix=PP -input-file=%t/stdout-hit +// RUN: FileCheck %s -DPREFIX=%/t -check-prefix=DIAG -input-file=%t/stderr-hit +// RUN: FileCheck %s -DPREFIX=%/t -check-prefix=HIT -input-file=%t/stderr-hit + +// Preprocessor does not force absolute paths. +// PP: # 1 "subdir/tu.c" +// PP: # 1 "./h1.h" +// PP: const char *filename = "./h1.h"; + +// Diagnostics force absolute paths. +// DIAG: [[PREFIX]]/subdir/tu.c:1:2: warning: "from tu" +// DIAG: In file included from [[PREFIX]]/subdir/tu.c +// DIAG: [[PREFIX]]/h1.h:1:2: warning: "from h1" + +// MISS: remark: compile job cache miss +// HIT: remark: compile job cache hit + +//--- subdir/tu.c +#warning "from tu" +#include "h1.h" + +//--- h1.h +#warning "from h1" +const char *filename = __FILE__; diff --git a/clang/test/CAS/libclang-replay-job.c b/clang/test/CAS/libclang-replay-job.c index e81c07bd94add..1ba1ad6f7bf9c 100644 --- a/clang/test/CAS/libclang-replay-job.c +++ b/clang/test/CAS/libclang-replay-job.c @@ -78,6 +78,25 @@ // RUN: diff -u %t/t1.d %t/a/b/rel.d // FIXME: Get clang's `-working-directory` to affect relative path for serialized diagnostics. +// Check with -fdiagnostics-absolute-path +// RUN: cd %t +// RUN: c-index-test core -replay-cached-job -cas-path %t/cas @%t/cache-key \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option no-logging \ +// RUN: -working-dir %t \ +// RUN: -- @%t/cc1.rsp \ +// RUN: -fdiagnostics-absolute-paths \ +// RUN: -Rcompile-job-cache-hit \ +// RUN: -dependency-file %t/t3.d -o %t/output3.o 2> %t/output3.txt +// RUN: FileCheck %s -input-file=%t/output2.txt -check-prefix=REL_DIAG +// RUN: FileCheck %s -input-file=%t/output3.txt -check-prefix=ABS_DIAG +// RUN: diff %t/output1.o %t/output3.o +// RUN: diff -u %t/t1.d %t/t3.d +// REL_DIAG: remark: compile job cache hit for +// REL_DIAG: {{^}}main.c:1:2: warning: +// ABS_DIAG: remark: compile job cache hit for +// ABS_DIAG: libclang-replay-job.c.tmp{{.}}main.c:1:2: warning: + // Use relative path to inputs and outputs. //--- cdb.json.template [{ diff --git a/clang/test/Modules/feature-availability.c b/clang/test/Modules/feature-availability.c index e21522facc9d7..d9fdd1120d49a 100644 --- a/clang/test/Modules/feature-availability.c +++ b/clang/test/Modules/feature-availability.c @@ -1,3 +1,4 @@ +// RUN: rm -rf %t // RUN: %clang_cc1 -triple arm64-apple-macosx -fmodules %S/Inputs/feature-availability/module.modulemap -fmodule-name=Feature1 -emit-module -o %t/feature1.pcm // RUN: %clang_cc1 -triple arm64-apple-macosx -fmodules %S/Inputs/feature-availability/module.modulemap -fmodule-name=Feature2 -fmodule-file=%t/feature1.pcm -emit-module -o %t/feature2.pcm // RUN: %clang_cc1 -triple arm64-apple-macosx -fmodules -fmodule-file=%t/feature2.pcm -I %S/Inputs/feature-availability -emit-llvm -o - %s | FileCheck %s From 392f68cbba4bf8c4b58d029419113114603af912 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Fri, 19 Dec 2025 15:53:37 -0800 Subject: [PATCH 2/2] Add empty filename check --- clang/lib/Frontend/TextDiagnostic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp index da12b4044ee0a..007c5167fdb44 100644 --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -846,7 +846,7 @@ void TextDiagnostic::emitFilename(StringRef Filename, const SourceManager &SM) { } // TO_UPSTREAM(CAS) - if (DiagOpts.FallbackRealFileSystemAbsolutePaths && + if (DiagOpts.FallbackRealFileSystemAbsolutePaths && !Filename.empty() && !llvm::sys::path::is_absolute(Filename)) { // When caching, the above may fail to canonicalize due to using the // IncludeTreeFileSystem. In that case, we can safely fallback to the real