diff options
Diffstat (limited to 'clang/lib/Lex')
-rw-r--r-- | clang/lib/Lex/HeaderSearch.cpp | 14 | ||||
-rw-r--r-- | clang/lib/Lex/PPDirectives.cpp | 67 |
2 files changed, 75 insertions, 6 deletions
diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp index f7fc0b0f65f..e5cc30e41c5 100644 --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -250,8 +250,9 @@ const char *DirectoryLookup::getName() const { } const FileEntry *HeaderSearch::getFileAndSuggestModule( - StringRef FileName, const DirectoryEntry *Dir, bool IsSystemHeaderDir, - Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule) { + StringRef FileName, SourceLocation IncludeLoc, const DirectoryEntry *Dir, + bool IsSystemHeaderDir, Module *RequestingModule, + ModuleMap::KnownHeader *SuggestedModule) { // If we have a module map that might map this header, load it and // check whether we'll have a suggestion for a module. const FileEntry *File = getFileMgr().getFile(FileName, /*OpenFile=*/true); @@ -272,6 +273,7 @@ const FileEntry *HeaderSearch::getFileAndSuggestModule( const FileEntry *DirectoryLookup::LookupFile( StringRef &Filename, HeaderSearch &HS, + SourceLocation IncludeLoc, SmallVectorImpl<char> *SearchPath, SmallVectorImpl<char> *RelativePath, Module *RequestingModule, @@ -297,7 +299,7 @@ const FileEntry *DirectoryLookup::LookupFile( RelativePath->append(Filename.begin(), Filename.end()); } - return HS.getFileAndSuggestModule(TmpDir, getDir(), + return HS.getFileAndSuggestModule(TmpDir, IncludeLoc, getDir(), isSystemHeaderDirectory(), RequestingModule, SuggestedModule); } @@ -585,7 +587,7 @@ const FileEntry *HeaderSearch::LookupFile( RelativePath->append(Filename.begin(), Filename.end()); } // Otherwise, just return the file. - return getFileAndSuggestModule(Filename, nullptr, + return getFileAndSuggestModule(Filename, IncludeLoc, nullptr, /*IsSystemHeaderDir*/false, RequestingModule, SuggestedModule); } @@ -622,7 +624,7 @@ const FileEntry *HeaderSearch::LookupFile( Includer ? getFileInfo(Includer).DirInfo != SrcMgr::C_User : BuildSystemModule; if (const FileEntry *FE = getFileAndSuggestModule( - TmpDir, IncluderAndDir.second, IncluderIsSystemHeader, + TmpDir, IncludeLoc, IncluderAndDir.second, IncluderIsSystemHeader, RequestingModule, SuggestedModule)) { if (!Includer) { assert(First && "only first includer can have no file"); @@ -713,7 +715,7 @@ const FileEntry *HeaderSearch::LookupFile( bool InUserSpecifiedSystemFramework = false; bool HasBeenMapped = false; const FileEntry *FE = SearchDirs[i].LookupFile( - Filename, *this, SearchPath, RelativePath, RequestingModule, + Filename, *this, IncludeLoc, SearchPath, RelativePath, RequestingModule, SuggestedModule, InUserSpecifiedSystemFramework, HasBeenMapped, MappedName); if (HasBeenMapped) { diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 34adf7fac29..dccb588cb2a 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -24,6 +24,9 @@ #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/Pragma.h" #include "llvm/ADT/APInt.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/iterator_range.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Path.h" #include "llvm/Support/SaveAndRestore.h" @@ -1556,6 +1559,41 @@ static void diagnoseAutoModuleImport( ("@import " + PathString + ";").str()); } +namespace { + // Given a vector of path components and a string containing the real + // path to the file, build a properly-cased replacement in the vector, + // and return true if the replacement should be suggested. + bool TrySimplifyPath(SmallVectorImpl<StringRef> &Components, + StringRef RealPathName) { + auto RealPathComponentIter = llvm::sys::path::rbegin(RealPathName); + auto RealPathComponentEnd = llvm::sys::path::rend(RealPathName); + int Cnt = 0; + bool SuggestReplacement = false; + // Below is a best-effort to handle ".." in paths. It is admittedly + // not 100% correct in the presence of symlinks. + for(auto &Component : llvm::reverse(Components)) { + if ("." == Component) { + } else if (".." == Component) { + ++Cnt; + } else if (Cnt) { + --Cnt; + } else if (RealPathComponentIter != RealPathComponentEnd) { + if (Component != *RealPathComponentIter) { + // If these path components differ by more than just case, then we + // may be looking at symlinked paths. Bail on this diagnostic to avoid + // noisy false positives. + SuggestReplacement = RealPathComponentIter->equals_lower(Component); + if (!SuggestReplacement) + break; + Component = *RealPathComponentIter; + } + ++RealPathComponentIter; + } + } + return SuggestReplacement; + } +} + /// HandleIncludeDirective - The "\#include" tokens have just been read, read /// the file to be included from the lexer, then include it! This is a common /// routine with functionality shared between \#include, \#include_next and @@ -1720,6 +1758,35 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, } } + // Issue a diagnostic if the name of the file on disk has a different case + // than the one we're about to open. + const bool CheckIncludePathPortability = + File && !File->tryGetRealPathName().empty(); + + if (CheckIncludePathPortability) { + StringRef Name = LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename; + StringRef RealPathName = File->tryGetRealPathName(); + SmallVector<StringRef, 16> Components(llvm::sys::path::begin(Name), + llvm::sys::path::end(Name)); + + if (TrySimplifyPath(Components, RealPathName)) { + SmallString<128> Path; + Path.reserve(Name.size()+2); + Path.push_back(isAngled ? '<' : '"'); + for (auto Component : Components) { + Path.append(Component); + // Append the separator the user used, or the close quote + Path.push_back( + Path.size() <= Filename.size() ? Filename[Path.size()-1] : + (isAngled ? '>' : '"')); + } + auto Replacement = Path.str().str(); + SourceRange Range(FilenameTok.getLocation(), CharEnd); + Diag(FilenameTok, diag::pp_nonportable_path) << Replacement << + FixItHint::CreateReplacement(Range, Replacement); + } + } + // Should we enter the source file? Set to false if either the source file is // known to have no effect beyond its effect on module visibility -- that is, // if it's got an include guard that is already defined or is a modular header |