Make -Wnonportable-include-path ignore drive case on Windows.
See PR45812 for motivation.
No explicit test since I couldn't figure out how to get the
current disk drive in lower case into a form in lit where I could
mkdir it and cd to it. But the change does have test coverage in
that I can remove the case normalization in lit, and tests failed
on several bots (and for me locally if in a pwd with a lower-case
drive) without that normalization prior to this change.
Differential Revision: https://reviews.llvm.org/D79531
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 8b1d1fb..84c13b2 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -2074,6 +2074,8 @@
if (Callbacks && !IsImportDecl) {
// Notify the callback object that we've seen an inclusion directive.
// FIXME: Use a different callback for a pp-import?
+ // FIXME: Passes wrong filename if LookupHeaderIncludeOrImport() did
+ // typo correction.
Callbacks->InclusionDirective(
HashLoc, IncludeTok, LookupFilename, isAngled, FilenameRange,
File ? &File->getFileEntry() : nullptr, SearchPath, RelativePath,
@@ -2100,10 +2102,41 @@
!IsMapped && !File->getFileEntry().tryGetRealPathName().empty();
if (CheckIncludePathPortability) {
+ // FIXME: Looks at the wrong filename if we did typo correction.
StringRef Name = LookupFilename;
+ StringRef NameWithoriginalSlashes = Filename;
+#if defined(_WIN32)
+ // Skip UNC prefix if present. (tryGetRealPathName() always
+ // returns a path with the prefix skipped.)
+ bool NameWasUNC = Name.consume_front("\\\\?\\");
+ NameWithoriginalSlashes.consume_front("\\\\?\\");
+#endif
StringRef RealPathName = File->getFileEntry().tryGetRealPathName();
SmallVector<StringRef, 16> Components(llvm::sys::path::begin(Name),
llvm::sys::path::end(Name));
+#if defined(_WIN32)
+ // -Wnonportable-include-path is designed to diagnose includes using
+ // case even on systems with a case-insensitive file system.
+ // On Windows, RealPathName always starts with an upper-case drive
+ // letter for absolute paths, but Name might start with either
+ // case depending on if `cd c:\foo` or `cd C:\foo` was used in the shell.
+ // ("foo" will always have on-disk case, no matter which case was
+ // used in the cd command). To not emit this warning solely for
+ // the drive letter, whose case is dependent on if `cd` is used
+ // with upper- or lower-case drive letters, always consider the
+ // given drive letter case as correct for the purpose of this warning.
+ SmallString<128> FixedDriveRealPath;
+ if (llvm::sys::path::is_absolute(Name) &&
+ llvm::sys::path::is_absolute(RealPathName) &&
+ toLowercase(Name[0]) == toLowercase(RealPathName[0]) &&
+ isLowercase(Name[0]) != isLowercase(RealPathName[0])) {
+ assert(Components.size() >= 3 && "should have drive, backslash, name");
+ assert(Components[0].size() == 2 && "should start with drive");
+ assert(Components[0][1] == ':' && "should have colon");
+ FixedDriveRealPath = (Name.substr(0, 1) + RealPathName.substr(1)).str();
+ RealPathName = FixedDriveRealPath;
+ }
+#endif
if (trySimplifyPath(Components, RealPathName)) {
SmallString<128> Path;
@@ -2132,15 +2165,23 @@
continue;
// Append the separator(s) the user used, or the close quote
- if (Path.size() > Filename.size()) {
+ if (Path.size() > NameWithoriginalSlashes.size()) {
Path.push_back(isAngled ? '>' : '"');
continue;
}
- assert(IsSep(Filename[Path.size()-1]));
+ assert(IsSep(NameWithoriginalSlashes[Path.size()-1]));
do
- Path.push_back(Filename[Path.size()-1]);
- while (Path.size() <= Filename.size() && IsSep(Filename[Path.size()-1]));
+ Path.push_back(NameWithoriginalSlashes[Path.size()-1]);
+ while (Path.size() <= NameWithoriginalSlashes.size() &&
+ IsSep(NameWithoriginalSlashes[Path.size()-1]));
}
+
+#if defined(_WIN32)
+ // Restore UNC prefix if it was there.
+ if (NameWasUNC)
+ Path = (Path.substr(0, 1) + "\\\\?\\" + Path.substr(1)).str();
+#endif
+
// For user files and known standard headers, issue a diagnostic.
// For other system headers, don't. They can be controlled separately.
auto DiagId =