Author: twoh Date: Fri Jun 3 13:52:51 2016 New Revision: 271708 URL: http://llvm.org/viewvc/llvm-project?rev=271708&view=rev Log: Use the name of the file on disk to issue a new diagnostic about non-portable #include and #import paths.
Differential Revision: http://reviews.llvm.org/D19843 Corresponding LLVM change: http://reviews.llvm.org/D19842 Patch by Eric Niebler Added: cfe/trunk/test/Lexer/Inputs/case-insensitive-include.h cfe/trunk/test/Lexer/case-insensitive-include-ms.c cfe/trunk/test/Lexer/case-insensitive-include.c Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td cfe/trunk/include/clang/Basic/FileManager.h cfe/trunk/include/clang/Basic/VirtualFileSystem.h cfe/trunk/include/clang/Lex/DirectoryLookup.h cfe/trunk/include/clang/Lex/HeaderSearch.h cfe/trunk/lib/Basic/FileManager.cpp cfe/trunk/lib/Basic/VirtualFileSystem.cpp cfe/trunk/lib/Lex/HeaderSearch.cpp cfe/trunk/lib/Lex/PPDirectives.cpp cfe/trunk/test/PCH/case-insensitive-include.c Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Fri Jun 3 13:52:51 2016 @@ -390,6 +390,7 @@ def : DiagGroup<"sequence-point", [Unseq def AmbiguousMacro : DiagGroup<"ambiguous-macro">; def KeywordAsMacro : DiagGroup<"keyword-macro">; def ReservedIdAsMacro : DiagGroup<"reserved-id-macro">; +def NonportableIncludePath : DiagGroup<"nonportable-include-path">; // Just silence warnings about -Wstrict-aliasing for now. def : DiagGroup<"strict-aliasing=0">; Modified: cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td Fri Jun 3 13:52:51 2016 @@ -274,6 +274,10 @@ def ext_missing_whitespace_after_macro_n "whitespace required after macro name">; def warn_missing_whitespace_after_macro_name : Warning< "whitespace recommended after macro name">; +def pp_nonportable_path : Warning< + "non-portable path to file '%0'; specified path differs in case from file" + " name on disk">, + InGroup<NonportableIncludePath>; def pp_pragma_once_in_main_file : Warning<"#pragma once in main file">, InGroup<DiagGroup<"pragma-once-outside-header">>; Modified: cfe/trunk/include/clang/Basic/FileManager.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/FileManager.h?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/FileManager.h (original) +++ cfe/trunk/include/clang/Basic/FileManager.h Fri Jun 3 13:52:51 2016 @@ -52,6 +52,7 @@ public: /// descriptor for the file. class FileEntry { const char *Name; // Name of the file. + std::string RealPathName; // Real path to the file; could be empty. off_t Size; // File size in bytes. time_t ModTime; // Modification time of file. const DirectoryEntry *Dir; // Directory file lives in. @@ -82,6 +83,7 @@ public: } const char *getName() const { return Name; } + StringRef tryGetRealPathName() const { return RealPathName; } bool isValid() const { return IsValid; } off_t getSize() const { return Size; } unsigned getUID() const { return UID; } Modified: cfe/trunk/include/clang/Basic/VirtualFileSystem.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/VirtualFileSystem.h?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/VirtualFileSystem.h (original) +++ cfe/trunk/include/clang/Basic/VirtualFileSystem.h Fri Jun 3 13:52:51 2016 @@ -91,6 +91,13 @@ public: virtual ~File(); /// \brief Get the status of the file. virtual llvm::ErrorOr<Status> status() = 0; + /// \brief Get the name of the file + virtual llvm::ErrorOr<StringRef> getName() { + if (auto Status = status()) + return Status->getName(); + else + return Status.getError(); + } /// \brief Get the contents of the file as a \p MemoryBuffer. virtual llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> getBuffer(const Twine &Name, int64_t FileSize = -1, Modified: cfe/trunk/include/clang/Lex/DirectoryLookup.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/DirectoryLookup.h?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/include/clang/Lex/DirectoryLookup.h (original) +++ cfe/trunk/include/clang/Lex/DirectoryLookup.h Fri Jun 3 13:52:51 2016 @@ -151,6 +151,9 @@ public: /// /// \param HS The header search instance to search with. /// + /// \param IncludeLoc the source location of the #include or #import + /// directive. + /// /// \param SearchPath If not NULL, will be set to the search path relative /// to which the file was found. /// @@ -172,6 +175,7 @@ public: /// a framework include ("Foo.h" -> "Foo/Foo.h"), set the new name to this /// vector and point Filename to it. const FileEntry *LookupFile(StringRef &Filename, HeaderSearch &HS, + SourceLocation IncludeLoc, SmallVectorImpl<char> *SearchPath, SmallVectorImpl<char> *RelativePath, Module *RequestingModule, Modified: cfe/trunk/include/clang/Lex/HeaderSearch.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/HeaderSearch.h?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/include/clang/Lex/HeaderSearch.h (original) +++ cfe/trunk/include/clang/Lex/HeaderSearch.h Fri Jun 3 13:52:51 2016 @@ -580,8 +580,9 @@ private: /// \brief Look up the file with the specified name and determine its owning /// module. const FileEntry * - getFileAndSuggestModule(StringRef FileName, const DirectoryEntry *Dir, - bool IsSystemHeaderDir, Module *RequestingModule, + getFileAndSuggestModule(StringRef FileName, SourceLocation IncludeLoc, + const DirectoryEntry *Dir, bool IsSystemHeaderDir, + Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule); public: Modified: cfe/trunk/lib/Basic/FileManager.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/FileManager.cpp?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/lib/Basic/FileManager.cpp (original) +++ cfe/trunk/lib/Basic/FileManager.cpp Fri Jun 3 13:52:51 2016 @@ -312,6 +312,9 @@ const FileEntry *FileManager::getFile(St UFE.InPCH = Data.InPCH; UFE.File = std::move(F); UFE.IsValid = true; + if (UFE.File) + if (auto RealPathName = UFE.File->getName()) + UFE.RealPathName = RealPathName->str(); return &UFE; } Modified: cfe/trunk/lib/Basic/VirtualFileSystem.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/VirtualFileSystem.cpp?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/lib/Basic/VirtualFileSystem.cpp (original) +++ cfe/trunk/lib/Basic/VirtualFileSystem.cpp Fri Jun 3 13:52:51 2016 @@ -140,16 +140,19 @@ namespace { class RealFile : public File { int FD; Status S; + std::string RealName; friend class RealFileSystem; - RealFile(int FD, StringRef NewName) + RealFile(int FD, StringRef NewName, StringRef NewRealPathName) : FD(FD), S(NewName, {}, {}, {}, {}, {}, - llvm::sys::fs::file_type::status_error, {}) { + llvm::sys::fs::file_type::status_error, {}), + RealName(NewRealPathName.str()) { assert(FD >= 0 && "Invalid or inactive file descriptor"); } public: ~RealFile() override; ErrorOr<Status> status() override; + ErrorOr<StringRef> getName() override; ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, @@ -170,6 +173,10 @@ ErrorOr<Status> RealFile::status() { return S; } +ErrorOr<StringRef> RealFile::getName() { + return RealName.empty() ? S.getName() : StringRef(RealName); +} + ErrorOr<std::unique_ptr<MemoryBuffer>> RealFile::getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) { @@ -207,9 +214,10 @@ ErrorOr<Status> RealFileSystem::status(c ErrorOr<std::unique_ptr<File>> RealFileSystem::openFileForRead(const Twine &Name) { int FD; - if (std::error_code EC = sys::fs::openFileForRead(Name, FD)) + SmallString<256> RealName; + if (std::error_code EC = sys::fs::openFileForRead(Name, FD, &RealName)) return EC; - return std::unique_ptr<File>(new RealFile(FD, Name.str())); + return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str())); } llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const { Modified: cfe/trunk/lib/Lex/HeaderSearch.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/HeaderSearch.cpp?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/lib/Lex/HeaderSearch.cpp (original) +++ cfe/trunk/lib/Lex/HeaderSearch.cpp Fri Jun 3 13:52:51 2016 @@ -250,8 +250,9 @@ const char *DirectoryLookup::getName() c } 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::getFileAn 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::Lookup 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::LookupFil 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::LookupFil 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::LookupFil 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) { Modified: cfe/trunk/lib/Lex/PPDirectives.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPDirectives.cpp?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/lib/Lex/PPDirectives.cpp (original) +++ cfe/trunk/lib/Lex/PPDirectives.cpp Fri Jun 3 13:52:51 2016 @@ -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::HandleIncludeDirectiv } } + // 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 Added: cfe/trunk/test/Lexer/Inputs/case-insensitive-include.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Lexer/Inputs/case-insensitive-include.h?rev=271708&view=auto ============================================================================== --- cfe/trunk/test/Lexer/Inputs/case-insensitive-include.h (added) +++ cfe/trunk/test/Lexer/Inputs/case-insensitive-include.h Fri Jun 3 13:52:51 2016 @@ -0,0 +1,5 @@ +#pragma once + +struct S { + int x; +}; Added: cfe/trunk/test/Lexer/case-insensitive-include-ms.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Lexer/case-insensitive-include-ms.c?rev=271708&view=auto ============================================================================== --- cfe/trunk/test/Lexer/case-insensitive-include-ms.c (added) +++ cfe/trunk/test/Lexer/case-insensitive-include-ms.c Fri Jun 3 13:52:51 2016 @@ -0,0 +1,18 @@ +// REQUIRES: case-insensitive-filesystem + +// RUN: mkdir -p %T/apath +// RUN: cp %S/Inputs/case-insensitive-include.h %T +// RUN: cd %T +// RUN: %clang_cc1 -fsyntax-only -fms-compatibility %s -include %s -I %T -verify +// RUN: %clang_cc1 -fsyntax-only -fms-compatibility -fdiagnostics-parseable-fixits %s -include %s -I %T 2>&1 | FileCheck %s + +#include "..\Output\.\case-insensitive-include.h" +#include "..\Output\.\Case-Insensitive-Include.h" // expected-warning {{non-portable path}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:50}:"\"..\\Output\\.\\case-insensitive-include.h\"" +#include "..\output\.\case-insensitive-include.h" // expected-warning {{non-portable path}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:50}:"\"..\\Output\\.\\case-insensitive-include.h\"" + +#include "apath\..\.\case-insensitive-include.h" +#include "apath\..\.\Case-Insensitive-Include.h" // expected-warning {{non-portable path}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:49}:"\"apath\\..\\.\\case-insensitive-include.h\"" +#include "APath\..\.\case-insensitive-include.h" // For the sake of efficiency, this case is not diagnosed. :-( Added: cfe/trunk/test/Lexer/case-insensitive-include.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Lexer/case-insensitive-include.c?rev=271708&view=auto ============================================================================== --- cfe/trunk/test/Lexer/case-insensitive-include.c (added) +++ cfe/trunk/test/Lexer/case-insensitive-include.c Fri Jun 3 13:52:51 2016 @@ -0,0 +1,27 @@ +// REQUIRES: case-insensitive-filesystem + +// RUN: mkdir -p %T/apath +// RUN: cp %S/Inputs/case-insensitive-include.h %T +// RUN: cd %T +// RUN: %clang_cc1 -fsyntax-only %s -include %s -I %T -verify +// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s -include %s -I %T 2>&1 | FileCheck %s + +#include "case-insensitive-include.h" +#include "Case-Insensitive-Include.h" // expected-warning {{non-portable path}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:38}:"\"case-insensitive-include.h\"" + +#include "../Output/./case-insensitive-include.h" +#include "../Output/./Case-Insensitive-Include.h" // expected-warning {{non-portable path}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:50}:"\"../Output/./case-insensitive-include.h\"" +#include "../output/./case-insensitive-include.h" // expected-warning {{non-portable path}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:50}:"\"../Output/./case-insensitive-include.h\"" + +#include "apath/.././case-insensitive-include.h" +#include "apath/.././Case-Insensitive-Include.h" // expected-warning {{non-portable path}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:49}:"\"apath/.././case-insensitive-include.h\"" +#include "APath/.././case-insensitive-include.h" // For the sake of efficiency, this case is not diagnosed. :-( + +#include "../Output/./apath/.././case-insensitive-include.h" +#include "../Output/./APath/.././case-insensitive-include.h" // For the sake of efficiency, this case is not diagnosed. :-( +#include "../output/./apath/.././case-insensitive-include.h" // expected-warning {{non-portable path}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:61}:"\"../Output/./apath/.././case-insensitive-include.h\"" Modified: cfe/trunk/test/PCH/case-insensitive-include.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/case-insensitive-include.c?rev=271708&r1=271707&r2=271708&view=diff ============================================================================== --- cfe/trunk/test/PCH/case-insensitive-include.c (original) +++ cfe/trunk/test/PCH/case-insensitive-include.c Fri Jun 3 13:52:51 2016 @@ -2,7 +2,7 @@ // Test this without pch. // RUN: cp %S/Inputs/case-insensitive-include.h %T -// RUN: %clang_cc1 -fsyntax-only %s -include %s -I %T -verify +// RUN: %clang_cc1 -Wno-nonportable-include-path -fsyntax-only %s -include %s -I %T -verify // Test with pch. // RUN: %clang_cc1 -emit-pch -o %t.pch %s -I %T _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits