-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[Clang] [Diagnostics] Simplify filenames that contain '..' #143520
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
15c0a79
97c0acc
529aac1
5d30887
c0f56c3
a91e711
bd40f82
7cf1829
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
Comment on lines
+2446
to
+2453
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sort of pre-existing, but I am not sure doing something different for windows actually make sense. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I was interpreting the comment above this to mean that Windows itself doesn’t care about symlinks when resolving |
||
|
||
// 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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// REQUIRES: shell | ||
|
||
// RUN: rm -rf %t | ||
// RUN: mkdir -p %t/a/b/ | ||
// RUN: echo 'void x;' > %t/test.h | ||
// RUN: echo 'const void x;' > %t/header_with_a_really_long_name.h | ||
// RUN: ln -s %t/header_with_a_really_long_name.h %t/a/shorter_name.h | ||
// | ||
// RUN: %clang_cc1 -fsyntax-only -I %t %s 2> %t/output.txt || true | ||
// RUN: cat %t/output.txt | FileCheck %s | ||
|
||
// Check that we strip '..' by canonicalising the path... | ||
#include "a/b/../../test.h" | ||
// CHECK: simplify-paths.c.tmp/test.h:1:6: error: variable has incomplete type 'void' | ||
|
||
// ... but only if the resulting path is actually shorter. | ||
#include "a/b/../shorter_name.h" | ||
// CHECK: simplify-paths.c.tmp/a/b/../shorter_name.h:1:12: error: variable has incomplete type 'const void' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if it would be more efficient to check the map first
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, not sure. This is a
perf/
branch anyway so when I’m done fixing the tests (I think it’s just a clang-tidy test that’s left at this point?) we can just check if there are any regressions and try doing this instead if so.