aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Basic/SourceManager.cpp
diff options
context:
space:
mode:
authorSirraide <aeternalmail@gmail.com>2025-07-08 01:02:19 +0200
committerGitHub <noreply@github.com>2025-07-08 01:02:19 +0200
commite3e7393c4681f31cd3820fdda2831a3e004a48f5 (patch)
tree2ffdcd5a104c9d3c39ed763a33eab7f18c8ea775 /clang/lib/Basic/SourceManager.cpp
parentc8850051c2414b899416e16222f5d96e854be563 (diff)
downloadllvm-e3e7393c4681f31cd3820fdda2831a3e004a48f5.zip
llvm-e3e7393c4681f31cd3820fdda2831a3e004a48f5.tar.gz
llvm-e3e7393c4681f31cd3820fdda2831a3e004a48f5.tar.bz2
[Clang] [Diagnostics] Simplify filenames that contain '..' (#143520)
This can significantly shorten file paths to standard library headers, e.g. on my system, `<ranges>` is currently printed as ```console /usr/lib/gcc/x86_64-redhat-linux/15/../../../../include/c++/15/ranges ``` but with this change, we instead print ```console /usr/include/c++/15/ranges ``` This is of course just a heuristic; there are paths that would get longer as a result of this, so we use whichever path ends up being shorter. @AaronBallman pointed out that this might be problematic for network file systems since path resolution might take a while, so this is enabled only for paths that are part of a local filesystem—though not on Windows since there we noticed that the check itself is slow. The file names are cached in `SourceManager`.
Diffstat (limited to 'clang/lib/Basic/SourceManager.cpp')
-rw-r--r--clang/lib/Basic/SourceManager.cpp72
1 files changed, 72 insertions, 0 deletions
diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp
index b2b1488..e6b6130 100644
--- a/clang/lib/Basic/SourceManager.cpp
+++ b/clang/lib/Basic/SourceManager.cpp
@@ -2390,3 +2390,75 @@ SourceManagerForFile::SourceManagerForFile(StringRef FileName,
assert(ID.isValid());
SourceMgr->setMainFileID(ID);
}
+
+StringRef
+SourceManager::getNameForDiagnostic(StringRef Filename,
+ const DiagnosticOptions &Opts) const {
+ OptionalFileEntryRef File = getFileManager().getOptionalFileRef(Filename);
+ if (!File)
+ return Filename;
+
+ bool SimplifyPath = [&] {
+ if (Opts.AbsolutePath)
+ return true;
+
+ // Try to simplify paths that contain '..' in any case since paths to
+ // standard library headers especially tend to get quite long otherwise.
+ // Only do that for local filesystems though to avoid slowing down
+ // compilation too much.
+ if (!File->getName().contains(".."))
+ return false;
+
+ // If we're not on Windows, check if we're on a network file system and
+ // avoid simplifying the path in that case since that can be slow. On
+ // Windows, the check for a local filesystem is already slow, so skip it.
+#ifndef _WIN32
+ if (!llvm::sys::fs::is_local(File->getName()))
+ return false;
+#endif
+
+ return true;
+ }();
+
+ if (!SimplifyPath)
+ return Filename;
+
+ // This may involve computing canonical names, so cache the result.
+ StringRef &CacheEntry = DiagNames[Filename];
+ if (!CacheEntry.empty())
+ return CacheEntry;
+
+ // We want to print a simplified absolute path, i. e. without "dots".
+ //
+ // The hardest part here are the paths like "<part1>/<link>/../<part2>".
+ // On Unix-like systems, we cannot just collapse "<link>/..", because
+ // paths are resolved sequentially, and, thereby, the path
+ // "<part1>/<part2>" may point to a different location. That is why
+ // we use FileManager::getCanonicalName(), which expands all indirections
+ // with llvm::sys::fs::real_path() and caches the result.
+ //
+ // On the other hand, it would be better to preserve as much of the
+ // original path as possible, because that helps a user to recognize it.
+ // real_path() expands all links, which sometimes too much. Luckily,
+ // on Windows we can just use llvm::sys::path::remove_dots(), because,
+ // on that system, both aforementioned paths point to the same place.
+ SmallString<256> TempBuf;
+#ifdef _WIN32
+ TempBuf = File->getName();
+ llvm::sys::fs::make_absolute(TempBuf);
+ llvm::sys::path::native(TempBuf);
+ llvm::sys::path::remove_dots(TempBuf, /* remove_dot_dot */ true);
+#else
+ TempBuf = getFileManager().getCanonicalName(*File);
+#endif
+
+ // In some cases, the resolved path may actually end up being longer (e.g.
+ // if it was originally a relative path), so just retain whichever one
+ // ends up being shorter.
+ if (!Opts.AbsolutePath && TempBuf.size() > Filename.size())
+ CacheEntry = Filename;
+ else
+ CacheEntry = TempBuf.str().copy(DiagNameAlloc);
+
+ return CacheEntry;
+}