diff options
author | Sirraide <aeternalmail@gmail.com> | 2025-07-08 01:02:19 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-08 01:02:19 +0200 |
commit | e3e7393c4681f31cd3820fdda2831a3e004a48f5 (patch) | |
tree | 2ffdcd5a104c9d3c39ed763a33eab7f18c8ea775 /clang/lib/Basic/SourceManager.cpp | |
parent | c8850051c2414b899416e16222f5d96e854be563 (diff) | |
download | llvm-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.cpp | 72 |
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; +} |