Index: clang/include/clang/Basic/FileManager.h =================================================================== --- clang/include/clang/Basic/FileManager.h +++ clang/include/clang/Basic/FileManager.h @@ -106,6 +106,34 @@ bool isOpenForTests() const { return File != nullptr; } }; +/// A reference to a \c FileEntry that includes the name of the file as it was +/// accessed by the FileManager's client. +class FileEntryRef { +public: + FileEntryRef(StringRef Name, const FileEntry &Entry) + : Name(Name), Entry(Entry) {} + + const StringRef getName() const { return Name; } + + const FileEntry &getFileEntry() const { return Entry; } + + off_t getSize() const { return Entry.getSize(); } + + unsigned getUID() const { return Entry.getUID(); } + + const llvm::sys::fs::UniqueID &getUniqueID() const { + return Entry.getUniqueID(); + } + + time_t getModificationTime() const { return Entry.getModificationTime(); } + + const DirectoryEntry *getDir() const { return Entry.getDir(); } + +private: + StringRef Name; + const FileEntry &Entry; +}; + /// Implements support for file system lookup, file system caching, /// and directory search management. /// @@ -200,6 +228,9 @@ /// Removes the FileSystemStatCache object from the manager. void clearStatCache(); + /// Returns the number of unique real file entries cached by the file manager. + size_t getNumUniqueRealFiles() const { return UniqueRealFiles.size(); } + /// Lookup, cache, and verify the specified directory (real or /// virtual). /// @@ -215,6 +246,10 @@ /// Lookup, cache, and verify the specified file (real or /// virtual). /// + /// This function is deprecated and will be removed at some point in the + /// future, new clients should use + /// \c getFileRef. + /// /// This returns a \c std::error_code if there was an error loading the file. /// If there is no error, the FileEntry is guaranteed to be non-NULL. /// @@ -225,6 +260,21 @@ llvm::ErrorOr getFile(StringRef Filename, bool OpenFile = false, bool CacheFailure = true); + /// Lookup, cache, and verify the specified file (real or virtual). Return the + /// reference to the file entry together with the exact path that was used to + /// access a file by a particular call to getFileRef. + /// + /// This returns a \c std::error_code if there was an error loading the file, + /// or a \c FileEntryRef otherwise. + /// + /// \param OpenFile if true and the file exists, it will be opened. + /// + /// \param CacheFailure If true and the file does not exist, we'll cache + /// the failure to find this file. + llvm::ErrorOr getFileRef(StringRef Filename, + bool OpenFile = false, + bool CacheFailure = true); + /// Returns the current file system options FileSystemOptions &getFileSystemOpts() { return FileSystemOpts; } const FileSystemOptions &getFileSystemOpts() const { return FileSystemOpts; } Index: clang/include/clang/Basic/SourceManager.h =================================================================== --- clang/include/clang/Basic/SourceManager.h +++ clang/include/clang/Basic/SourceManager.h @@ -265,16 +265,21 @@ llvm::PointerIntPair ContentAndKind; + /// The filename that is used to access the file entry represented by the + /// content cache. + StringRef Filename; + public: /// Return a FileInfo object. static FileInfo get(SourceLocation IL, const ContentCache *Con, - CharacteristicKind FileCharacter) { + CharacteristicKind FileCharacter, StringRef Filename) { FileInfo X; X.IncludeLoc = IL.getRawEncoding(); X.NumCreatedFIDs = 0; X.HasLineDirectives = false; X.ContentAndKind.setPointer(Con); X.ContentAndKind.setInt(FileCharacter); + X.Filename = Filename; return X; } @@ -299,6 +304,10 @@ void setHasLineDirectives() { HasLineDirectives = true; } + + /// Returns the name of the file that was used when the file was loaded from + /// the underlying file system. + StringRef getName() const { return Filename; } }; /// Each ExpansionInfo encodes the expansion location - where @@ -821,7 +830,18 @@ const SrcMgr::ContentCache *IR = getOrCreateContentCache(SourceFile, isSystem(FileCharacter)); assert(IR && "getOrCreateContentCache() cannot return NULL"); - return createFileID(IR, IncludePos, FileCharacter, LoadedID, LoadedOffset); + return createFileID(IR, SourceFile->getName(), IncludePos, FileCharacter, + LoadedID, LoadedOffset); + } + + FileID createFileID(FileEntryRef SourceFile, SourceLocation IncludePos, + SrcMgr::CharacteristicKind FileCharacter, + int LoadedID = 0, unsigned LoadedOffset = 0) { + const SrcMgr::ContentCache *IR = getOrCreateContentCache( + &SourceFile.getFileEntry(), isSystem(FileCharacter)); + assert(IR && "getOrCreateContentCache() cannot return NULL"); + return createFileID(IR, SourceFile.getName(), IncludePos, FileCharacter, + LoadedID, LoadedOffset); } /// Create a new FileID that represents the specified memory buffer. @@ -832,9 +852,10 @@ SrcMgr::CharacteristicKind FileCharacter = SrcMgr::C_User, int LoadedID = 0, unsigned LoadedOffset = 0, SourceLocation IncludeLoc = SourceLocation()) { + StringRef Name = Buffer->getBufferIdentifier(); return createFileID( createMemBufferContentCache(Buffer.release(), /*DoNotFree*/ false), - IncludeLoc, FileCharacter, LoadedID, LoadedOffset); + Name, IncludeLoc, FileCharacter, LoadedID, LoadedOffset); } enum UnownedTag { Unowned }; @@ -847,8 +868,9 @@ SrcMgr::CharacteristicKind FileCharacter = SrcMgr::C_User, int LoadedID = 0, unsigned LoadedOffset = 0, SourceLocation IncludeLoc = SourceLocation()) { - return createFileID(createMemBufferContentCache(Buffer, /*DoNotFree*/true), - IncludeLoc, FileCharacter, LoadedID, LoadedOffset); + return createFileID(createMemBufferContentCache(Buffer, /*DoNotFree*/ true), + Buffer->getBufferIdentifier(), IncludeLoc, + FileCharacter, LoadedID, LoadedOffset); } /// Get the FileID for \p SourceFile if it exists. Otherwise, create a @@ -997,6 +1019,19 @@ return Content->OrigEntry; } + /// Returns the FileEntryRef for the provided FileID. + Optional getFileEntryRefForID(FileID FID) const { + bool MyInvalid = false; + const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &MyInvalid); + if (MyInvalid || !Entry.isFile()) + return None; + + const SrcMgr::ContentCache *Content = Entry.getFile().getContentCache(); + if (!Content || !Content->OrigEntry) + return None; + return FileEntryRef(Entry.getFile().getName(), *Content->OrigEntry); + } + /// Returns the FileEntry record for the provided SLocEntry. const FileEntry *getFileEntryForSLocEntry(const SrcMgr::SLocEntry &sloc) const { @@ -1785,10 +1820,10 @@ /// /// This works regardless of whether the ContentCache corresponds to a /// file or some other input source. - FileID createFileID(const SrcMgr::ContentCache* File, + FileID createFileID(const SrcMgr::ContentCache *File, StringRef Filename, SourceLocation IncludePos, - SrcMgr::CharacteristicKind DirCharacter, - int LoadedID, unsigned LoadedOffset); + SrcMgr::CharacteristicKind DirCharacter, int LoadedID, + unsigned LoadedOffset); const SrcMgr::ContentCache * getOrCreateContentCache(const FileEntry *SourceFile, Index: clang/include/clang/Lex/DirectoryLookup.h =================================================================== --- clang/include/clang/Lex/DirectoryLookup.h +++ clang/include/clang/Lex/DirectoryLookup.h @@ -176,27 +176,20 @@ /// \param [out] MappedName if this is a headermap which maps the filename to /// 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 *SearchPath, - SmallVectorImpl *RelativePath, - Module *RequestingModule, - ModuleMap::KnownHeader *SuggestedModule, - bool &InUserSpecifiedSystemFramework, - bool &IsFrameworkFound, - bool &HasBeenMapped, - SmallVectorImpl &MappedName) const; + Optional + LookupFile(StringRef &Filename, HeaderSearch &HS, SourceLocation IncludeLoc, + SmallVectorImpl *SearchPath, + SmallVectorImpl *RelativePath, Module *RequestingModule, + ModuleMap::KnownHeader *SuggestedModule, + bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound, + bool &HasBeenMapped, SmallVectorImpl &MappedName) const; private: - const FileEntry *DoFrameworkLookup( - StringRef Filename, HeaderSearch &HS, - SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath, - Module *RequestingModule, + Optional DoFrameworkLookup( + StringRef Filename, HeaderSearch &HS, SmallVectorImpl *SearchPath, + SmallVectorImpl *RelativePath, Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule, - bool &InUserSpecifiedSystemFramework, - bool &IsFrameworkFound) const; - + bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound) const; }; } // end namespace clang Index: clang/include/clang/Lex/HeaderMap.h =================================================================== --- clang/include/clang/Lex/HeaderMap.h +++ clang/include/clang/Lex/HeaderMap.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_LEX_HEADERMAP_H #define LLVM_CLANG_LEX_HEADERMAP_H +#include "clang/Basic/FileManager.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" @@ -21,8 +22,6 @@ namespace clang { -class FileEntry; -class FileManager; struct HMapBucket; struct HMapHeader; @@ -78,7 +77,7 @@ /// NULL and the file is found, RawPath will be set to the raw path at which /// the file was found in the file system. For example, for a search path /// ".." and a filename "../file.h" this would be "../../file.h". - const FileEntry *LookupFile(StringRef Filename, FileManager &FM) const; + Optional LookupFile(StringRef Filename, FileManager &FM) const; using HeaderMapImpl::lookupFilename; using HeaderMapImpl::getFileName; Index: clang/include/clang/Lex/HeaderSearch.h =================================================================== --- clang/include/clang/Lex/HeaderSearch.h +++ clang/include/clang/Lex/HeaderSearch.h @@ -395,7 +395,7 @@ /// found in any of searched SearchDirs. Will be set to false if a framework /// is found only through header maps. Doesn't guarantee the requested file is /// found. - const FileEntry *LookupFile( + Optional LookupFile( StringRef Filename, SourceLocation IncludeLoc, bool isAngled, const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir, ArrayRef> Includers, @@ -410,7 +410,7 @@ /// within ".../Carbon.framework/Headers/Carbon.h", check to see if /// HIToolbox is a subframework within Carbon.framework. If so, return /// the FileEntry for the designated file, otherwise return null. - const FileEntry *LookupSubframeworkHeader( + Optional LookupSubframeworkHeader( StringRef Filename, const FileEntry *ContextFileEnt, SmallVectorImpl *SearchPath, SmallVectorImpl *RelativePath, Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule); @@ -649,7 +649,7 @@ /// Look up the file with the specified name and determine its owning /// module. - const FileEntry * + Optional getFileAndSuggestModule(StringRef FileName, SourceLocation IncludeLoc, const DirectoryEntry *Dir, bool IsSystemHeaderDir, Module *RequestingModule, Index: clang/include/clang/Lex/Preprocessor.h =================================================================== --- clang/include/clang/Lex/Preprocessor.h +++ clang/include/clang/Lex/Preprocessor.h @@ -1949,17 +1949,15 @@ /// Given a "foo" or \ reference, look up the indicated file. /// - /// Returns null on failure. \p isAngled indicates whether the file + /// Returns None on failure. \p isAngled indicates whether the file /// reference is for system \#include's or not (i.e. using <> instead of ""). - const FileEntry *LookupFile(SourceLocation FilenameLoc, StringRef Filename, - bool isAngled, const DirectoryLookup *FromDir, - const FileEntry *FromFile, - const DirectoryLookup *&CurDir, - SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath, - ModuleMap::KnownHeader *SuggestedModule, - bool *IsMapped, bool *IsFrameworkFound, - bool SkipCache = false); + Optional + LookupFile(SourceLocation FilenameLoc, StringRef Filename, bool isAngled, + const DirectoryLookup *FromDir, const FileEntry *FromFile, + const DirectoryLookup *&CurDir, SmallVectorImpl *SearchPath, + SmallVectorImpl *RelativePath, + ModuleMap::KnownHeader *SuggestedModule, bool *IsMapped, + bool *IsFrameworkFound, bool SkipCache = false); /// Get the DirectoryLookup structure used to find the current /// FileEntry, if CurLexer is non-null and if applicable. Index: clang/lib/Basic/FileManager.cpp =================================================================== --- clang/lib/Basic/FileManager.cpp +++ clang/lib/Basic/FileManager.cpp @@ -184,13 +184,29 @@ llvm::ErrorOr FileManager::getFile(StringRef Filename, bool openFile, bool CacheFailure) { + auto Result = getFileRef(Filename, openFile, CacheFailure); + if (Result) + return &Result->getFileEntry(); + return Result.getError(); +} + +static llvm::ErrorOr +promoteFileRef(StringRef name, llvm::ErrorOr value) { + if (value) + return FileEntryRef(name, *value); + return value.getError(); +} + +llvm::ErrorOr +FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) { ++NumFileLookups; // See if there is already an entry in the map. auto SeenFileInsertResult = SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory}); if (!SeenFileInsertResult.second) - return promoteInnerReference(SeenFileInsertResult.first->second); + return promoteFileRef(SeenFileInsertResult.first->first(), + SeenFileInsertResult.first->second); // We've not seen this before. Fill it in. ++NumFileCacheMisses; @@ -269,9 +285,11 @@ // to switch towards a design where we return a FileName object that // encapsulates both the name by which the file was accessed and the // corresponding FileEntry. + // FIXME: The Name should be removed from FileEntry once all clients + // adopt FileEntryRef. UFE.Name = InterndFileName; - return &UFE; + return FileEntryRef(InterndFileName, UFE); } // Otherwise, we don't have this file yet, add it. @@ -292,7 +310,7 @@ // We should still fill the path even if we aren't opening the file. fillRealPathName(&UFE, InterndFileName); } - return &UFE; + return FileEntryRef(InterndFileName, UFE); } const FileEntry * Index: clang/lib/Basic/SourceManager.cpp =================================================================== --- clang/lib/Basic/SourceManager.cpp +++ clang/lib/Basic/SourceManager.cpp @@ -466,10 +466,9 @@ // If the file of the SLocEntry changed we could still have loaded it. if (!SLocEntryLoaded[Index]) { // Try to recover; create a SLocEntry so the rest of clang can handle it. - LoadedSLocEntryTable[Index] = SLocEntry::get(0, - FileInfo::get(SourceLocation(), - getFakeContentCacheForRecovery(), - SrcMgr::C_User)); + LoadedSLocEntryTable[Index] = SLocEntry::get( + 0, FileInfo::get(SourceLocation(), getFakeContentCacheForRecovery(), + SrcMgr::C_User, "")); } } @@ -556,7 +555,7 @@ /// createFileID - Create a new FileID for the specified ContentCache and /// include position. This works regardless of whether the ContentCache /// corresponds to a file or some other input source. -FileID SourceManager::createFileID(const ContentCache *File, +FileID SourceManager::createFileID(const ContentCache *File, StringRef Filename, SourceLocation IncludePos, SrcMgr::CharacteristicKind FileCharacter, int LoadedID, unsigned LoadedOffset) { @@ -565,14 +564,14 @@ unsigned Index = unsigned(-LoadedID) - 2; assert(Index < LoadedSLocEntryTable.size() && "FileID out of range"); assert(!SLocEntryLoaded[Index] && "FileID already loaded"); - LoadedSLocEntryTable[Index] = SLocEntry::get(LoadedOffset, - FileInfo::get(IncludePos, File, FileCharacter)); + LoadedSLocEntryTable[Index] = SLocEntry::get( + LoadedOffset, FileInfo::get(IncludePos, File, FileCharacter, Filename)); SLocEntryLoaded[Index] = true; return FileID::get(LoadedID); } - LocalSLocEntryTable.push_back(SLocEntry::get(NextLocalOffset, - FileInfo::get(IncludePos, File, - FileCharacter))); + LocalSLocEntryTable.push_back( + SLocEntry::get(NextLocalOffset, + FileInfo::get(IncludePos, File, FileCharacter, Filename))); unsigned FileSize = File->getSize(); assert(NextLocalOffset + FileSize + 1 > NextLocalOffset && NextLocalOffset + FileSize + 1 <= CurrentLoadedOffset && Index: clang/lib/Frontend/CompilerInstance.cpp =================================================================== --- clang/lib/Frontend/CompilerInstance.cpp +++ clang/lib/Frontend/CompilerInstance.cpp @@ -831,33 +831,37 @@ // Figure out where to get and map in the main file. if (InputFile != "-") { - auto FileOrErr = FileMgr.getFile(InputFile, /*OpenFile=*/true); + auto FileOrErr = FileMgr.getFileRef(InputFile, /*OpenFile=*/true); if (!FileOrErr) { Diags.Report(diag::err_fe_error_reading) << InputFile; return false; } - auto File = *FileOrErr; + FileEntryRef File = *FileOrErr; // The natural SourceManager infrastructure can't currently handle named // pipes, but we would at least like to accept them for the main // file. Detect them here, read them with the volatile flag so FileMgr will // pick up the correct size, and simply override their contents as we do for // STDIN. - if (File->isNamedPipe()) { - auto MB = FileMgr.getBufferForFile(File, /*isVolatile=*/true); + if (File.getFileEntry().isNamedPipe()) { + auto MB = + FileMgr.getBufferForFile(&File.getFileEntry(), /*isVolatile=*/true); if (MB) { // Create a new virtual file that will have the correct size. - File = FileMgr.getVirtualFile(InputFile, (*MB)->getBufferSize(), 0); - SourceMgr.overrideFileContents(File, std::move(*MB)); + const FileEntry *FE = + FileMgr.getVirtualFile(InputFile, (*MB)->getBufferSize(), 0); + SourceMgr.overrideFileContents(FE, std::move(*MB)); + SourceMgr.setMainFileID( + SourceMgr.createFileID(FE, SourceLocation(), Kind)); } else { Diags.Report(diag::err_cannot_open_file) << InputFile << MB.getError().message(); return false; } + } else { + SourceMgr.setMainFileID( + SourceMgr.createFileID(File, SourceLocation(), Kind)); } - - SourceMgr.setMainFileID( - SourceMgr.createFileID(File, SourceLocation(), Kind)); } else { llvm::ErrorOr> SBOrErr = llvm::MemoryBuffer::getSTDIN(); Index: clang/lib/Frontend/DependencyFile.cpp =================================================================== --- clang/lib/Frontend/DependencyFile.cpp +++ clang/lib/Frontend/DependencyFile.cpp @@ -46,13 +46,13 @@ // Dependency generation really does want to go all the way to the // file entry for a source location to find out what is depended on. // We do not want #line markers to affect dependency generation! - const FileEntry *FE = - SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc))); - if (!FE) + Optional File = + SM.getFileEntryRefForID(SM.getFileID(SM.getExpansionLoc(Loc))); + if (!File) return; StringRef Filename = - llvm::sys::path::remove_leading_dotslash(FE->getName()); + llvm::sys::path::remove_leading_dotslash(File->getName()); DepCollector.maybeAddDependency(Filename, /*FromModule*/false, isSystem(FileType), Index: clang/lib/Frontend/FrontendActions.cpp =================================================================== --- clang/lib/Frontend/FrontendActions.cpp +++ clang/lib/Frontend/FrontendActions.cpp @@ -287,15 +287,15 @@ SmallVector Headers; for (StringRef Name : ModuleHeaders) { const DirectoryLookup *CurDir = nullptr; - const FileEntry *FE = HS.LookupFile( - Name, SourceLocation(), /*Angled*/ false, nullptr, CurDir, - None, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + Optional FE = HS.LookupFile( + Name, SourceLocation(), /*Angled*/ false, nullptr, CurDir, None, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); if (!FE) { CI.getDiagnostics().Report(diag::err_module_header_file_not_found) << Name; continue; } - Headers.push_back({Name, FE}); + Headers.push_back({Name, &FE->getFileEntry()}); } HS.getModuleMap().createHeaderModule(CI.getLangOpts().CurrentModule, Headers); Index: clang/lib/Frontend/Rewrite/InclusionRewriter.cpp =================================================================== --- clang/lib/Frontend/Rewrite/InclusionRewriter.cpp +++ clang/lib/Frontend/Rewrite/InclusionRewriter.cpp @@ -412,11 +412,11 @@ Includers; Includers.push_back(std::make_pair(FileEnt, FileEnt->getDir())); // FIXME: Why don't we call PP.LookupFile here? - const FileEntry *File = PP.getHeaderSearchInfo().LookupFile( + Optional File = PP.getHeaderSearchInfo().LookupFile( Filename, SourceLocation(), isAngled, Lookup, CurDir, Includers, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); - FileExists = File != nullptr; + FileExists = File.hasValue(); return true; } Index: clang/lib/Frontend/VerifyDiagnosticConsumer.cpp =================================================================== --- clang/lib/Frontend/VerifyDiagnosticConsumer.cpp +++ clang/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -528,15 +528,16 @@ // Lookup file via Preprocessor, like a #include. const DirectoryLookup *CurDir; - const FileEntry *FE = + Optional File = PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir, nullptr, nullptr, nullptr, nullptr, nullptr); - if (!FE) { + if (!File) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_file) << Filename << KindStr; continue; } + const FileEntry *FE = &File->getFileEntry(); if (SM.translateFile(FE).isInvalid()) SM.createFileID(FE, Pos, SrcMgr::C_User); Index: clang/lib/Lex/HeaderMap.cpp =================================================================== --- clang/lib/Lex/HeaderMap.cpp +++ clang/lib/Lex/HeaderMap.cpp @@ -196,17 +196,17 @@ /// LookupFile - Check to see if the specified relative filename is located in /// this HeaderMap. If so, open it and return its FileEntry. -const FileEntry *HeaderMap::LookupFile( - StringRef Filename, FileManager &FM) const { +Optional HeaderMap::LookupFile(StringRef Filename, + FileManager &FM) const { SmallString<1024> Path; StringRef Dest = HeaderMapImpl::lookupFilename(Filename, Path); if (Dest.empty()) - return nullptr; + return None; - if (auto File = FM.getFile(Dest)) + if (auto File = FM.getFileRef(Dest)) return *File; - return nullptr; + return None; } StringRef HeaderMapImpl::lookupFilename(StringRef Filename, Index: clang/lib/Lex/HeaderSearch.cpp =================================================================== --- clang/lib/Lex/HeaderSearch.cpp +++ clang/lib/Lex/HeaderSearch.cpp @@ -303,39 +303,33 @@ return getHeaderMap()->getFileName(); } -const FileEntry *HeaderSearch::getFileAndSuggestModule( +Optional HeaderSearch::getFileAndSuggestModule( 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. - auto File = getFileMgr().getFile(FileName, /*OpenFile=*/true); + auto File = getFileMgr().getFileRef(FileName, /*OpenFile=*/true); if (!File) - return nullptr; + return None; // If there is a module that corresponds to this header, suggest it. - if (!findUsableModuleForHeader(*File, Dir ? Dir : (*File)->getDir(), - RequestingModule, SuggestedModule, - IsSystemHeaderDir)) - return nullptr; + if (!findUsableModuleForHeader( + &File->getFileEntry(), Dir ? Dir : File->getFileEntry().getDir(), + RequestingModule, SuggestedModule, IsSystemHeaderDir)) + return None; return *File; } /// LookupFile - Lookup the specified file in this search path, returning it /// if it exists or returning null if not. -const FileEntry *DirectoryLookup::LookupFile( - StringRef &Filename, - HeaderSearch &HS, - SourceLocation IncludeLoc, - SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath, - Module *RequestingModule, - ModuleMap::KnownHeader *SuggestedModule, - bool &InUserSpecifiedSystemFramework, - bool &IsFrameworkFound, - bool &HasBeenMapped, - SmallVectorImpl &MappedName) const { +Optional DirectoryLookup::LookupFile( + StringRef &Filename, HeaderSearch &HS, SourceLocation IncludeLoc, + SmallVectorImpl *SearchPath, SmallVectorImpl *RelativePath, + Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule, + bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound, + bool &HasBeenMapped, SmallVectorImpl &MappedName) const { InUserSpecifiedSystemFramework = false; HasBeenMapped = false; @@ -369,9 +363,19 @@ SmallString<1024> Path; StringRef Dest = HM->lookupFilename(Filename, Path); if (Dest.empty()) - return nullptr; + return None; - const FileEntry *Result; + auto FixupSearchPath = [&]() { + if (SearchPath) { + StringRef SearchPathRef(getName()); + SearchPath->clear(); + SearchPath->append(SearchPathRef.begin(), SearchPathRef.end()); + } + if (RelativePath) { + RelativePath->clear(); + RelativePath->append(Filename.begin(), Filename.end()); + } + }; // Check if the headermap maps the filename to a framework include // ("Foo.h" -> "Foo/Foo.h"), in which case continue header lookup using the @@ -381,25 +385,17 @@ MappedName.append(Dest.begin(), Dest.end()); Filename = StringRef(MappedName.begin(), MappedName.size()); HasBeenMapped = true; - Result = HM->LookupFile(Filename, HS.getFileMgr()); - } else if (auto Res = HS.getFileMgr().getFile(Dest)) { - Result = *Res; - } else { - Result = nullptr; - } - - if (Result) { - if (SearchPath) { - StringRef SearchPathRef(getName()); - SearchPath->clear(); - SearchPath->append(SearchPathRef.begin(), SearchPathRef.end()); - } - if (RelativePath) { - RelativePath->clear(); - RelativePath->append(Filename.begin(), Filename.end()); + Optional Result = HM->LookupFile(Filename, HS.getFileMgr()); + if (Result) { + FixupSearchPath(); + return *Result; } + } else if (auto Res = HS.getFileMgr().getFileRef(Dest)) { + FixupSearchPath(); + return *Res; } - return Result; + + return None; } /// Given a framework directory, find the top-most framework directory. @@ -464,7 +460,7 @@ /// DoFrameworkLookup - Do a lookup of the specified file in the current /// DirectoryLookup, which is a framework directory. -const FileEntry *DirectoryLookup::DoFrameworkLookup( +Optional DirectoryLookup::DoFrameworkLookup( StringRef Filename, HeaderSearch &HS, SmallVectorImpl *SearchPath, SmallVectorImpl *RelativePath, Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule, @@ -473,7 +469,8 @@ // Framework names must have a '/' in the filename. size_t SlashPos = Filename.find('/'); - if (SlashPos == StringRef::npos) return nullptr; + if (SlashPos == StringRef::npos) + return None; // Find out if this is the home for the specified framework, by checking // HeaderSearch. Possible answers are yes/no and unknown. @@ -482,7 +479,7 @@ // If it is known and in some other directory, fail. if (CacheEntry.Directory && CacheEntry.Directory != getFrameworkDir()) - return nullptr; + return None; // Otherwise, construct the path to this framework dir. @@ -505,7 +502,8 @@ // If the framework dir doesn't exist, we fail. auto Dir = FileMgr.getDirectory(FrameworkName); - if (!Dir) return nullptr; + if (!Dir) + return None; // Otherwise, if it does, remember that this is the right direntry for this // framework. @@ -544,11 +542,10 @@ FrameworkName.append(Filename.begin()+SlashPos+1, Filename.end()); - const FileEntry *FE = nullptr; - if (auto File = FileMgr.getFile(FrameworkName, /*OpenFile=*/!SuggestedModule)) - FE = *File; + llvm::ErrorOr File = + FileMgr.getFileRef(FrameworkName, /*OpenFile=*/!SuggestedModule); - if (!FE) { + if (!File) { // Check "/System/Library/Frameworks/Cocoa.framework/PrivateHeaders/file.h" const char *Private = "Private"; FrameworkName.insert(FrameworkName.begin()+OrigSize, Private, @@ -557,15 +554,13 @@ SearchPath->insert(SearchPath->begin()+OrigSize, Private, Private+strlen(Private)); - if (auto File = FileMgr.getFile(FrameworkName, - /*OpenFile=*/!SuggestedModule)) - FE = *File; + File = FileMgr.getFileRef(FrameworkName, /*OpenFile=*/!SuggestedModule); } // If we found the header and are allowed to suggest a module, do so now. - if (FE && needModuleLookup(RequestingModule, SuggestedModule)) { + if (File && needModuleLookup(RequestingModule, SuggestedModule)) { // Find the framework in which this header occurs. - StringRef FrameworkPath = FE->getDir()->getName(); + StringRef FrameworkPath = File->getFileEntry().getDir()->getName(); bool FoundFramework = false; do { // Determine whether this directory exists. @@ -589,15 +584,19 @@ bool IsSystem = getDirCharacteristic() != SrcMgr::C_User; if (FoundFramework) { if (!HS.findUsableModuleForFrameworkHeader( - FE, FrameworkPath, RequestingModule, SuggestedModule, IsSystem)) - return nullptr; + &File->getFileEntry(), FrameworkPath, RequestingModule, + SuggestedModule, IsSystem)) + return None; } else { - if (!HS.findUsableModuleForHeader(FE, getDir(), RequestingModule, - SuggestedModule, IsSystem)) - return nullptr; + if (!HS.findUsableModuleForHeader(&File->getFileEntry(), getDir(), + RequestingModule, SuggestedModule, + IsSystem)) + return None; } } - return FE; + if (File) + return *File; + return None; } void HeaderSearch::setTarget(const TargetInfo &Target) { @@ -702,7 +701,7 @@ /// for system \#include's or not (i.e. using <> instead of ""). Includers, if /// non-empty, indicates where the \#including file(s) are, in case a relative /// search is needed. Microsoft mode will pass all \#including files. -const FileEntry *HeaderSearch::LookupFile( +Optional HeaderSearch::LookupFile( StringRef Filename, SourceLocation IncludeLoc, bool isAngled, const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir, ArrayRef> Includers, @@ -724,7 +723,8 @@ CurDir = nullptr; // If this was an #include_next "/absolute/file", fail. - if (FromDir) return nullptr; + if (FromDir) + return None; if (SearchPath) SearchPath->clear(); @@ -739,8 +739,9 @@ } // This is the header that MSVC's header search would have found. - const FileEntry *MSFE = nullptr; ModuleMap::KnownHeader MSSuggestedModule; + const FileEntry *MSFE_FE = nullptr; + StringRef MSFE_Name; // Unless disabled, check to see if the file is in the #includer's // directory. This cannot be based on CurDir, because each includer could be @@ -769,7 +770,7 @@ bool IncluderIsSystemHeader = Includer ? getFileInfo(Includer).DirInfo != SrcMgr::C_User : BuildSystemModule; - if (const FileEntry *FE = getFileAndSuggestModule( + if (Optional FE = getFileAndSuggestModule( TmpDir, IncludeLoc, IncluderAndDir.second, IncluderIsSystemHeader, RequestingModule, SuggestedModule)) { if (!Includer) { @@ -788,7 +789,7 @@ bool IndexHeaderMapHeader = FromHFI.IndexHeaderMapHeader; StringRef Framework = FromHFI.Framework; - HeaderFileInfo &ToHFI = getFileInfo(FE); + HeaderFileInfo &ToHFI = getFileInfo(&FE->getFileEntry()); ToHFI.DirInfo = DirInfo; ToHFI.IndexHeaderMapHeader = IndexHeaderMapHeader; ToHFI.Framework = Framework; @@ -805,7 +806,7 @@ if (First) { diagnoseFrameworkInclude(Diags, IncludeLoc, IncluderAndDir.second->getName(), Filename, - FE); + &FE->getFileEntry()); return FE; } @@ -815,7 +816,8 @@ if (Diags.isIgnored(diag::ext_pp_include_search_ms, IncludeLoc)) { return FE; } else { - MSFE = FE; + MSFE_FE = &FE->getFileEntry(); + MSFE_Name = FE->getName(); if (SuggestedModule) { MSSuggestedModule = *SuggestedModule; *SuggestedModule = ModuleMap::KnownHeader(); @@ -827,6 +829,9 @@ } } + Optional MSFE(MSFE_FE ? FileEntryRef(MSFE_Name, *MSFE_FE) + : Optional()); + CurDir = nullptr; // If this is a system #include, ignore the user #include locs. @@ -868,7 +873,7 @@ bool InUserSpecifiedSystemFramework = false; bool HasBeenMapped = false; bool IsFrameworkFoundInDir = false; - const FileEntry *FE = SearchDirs[i].LookupFile( + Optional File = SearchDirs[i].LookupFile( Filename, *this, IncludeLoc, SearchPath, RelativePath, RequestingModule, SuggestedModule, InUserSpecifiedSystemFramework, IsFrameworkFoundInDir, HasBeenMapped, MappedName); @@ -883,12 +888,13 @@ // lookups, ignore IsFrameworkFoundInDir after the first remapping and not // just for remapping in a current search directory. *IsFrameworkFound |= (IsFrameworkFoundInDir && !CacheLookup.MappedName); - if (!FE) continue; + if (!File) + continue; CurDir = &SearchDirs[i]; // This file is a system header or C++ unfriendly if the dir is. - HeaderFileInfo &HFI = getFileInfo(FE); + HeaderFileInfo &HFI = getFileInfo(&File->getFileEntry()); HFI.DirInfo = CurDir->getDirCharacteristic(); // If the directory characteristic is User but this framework was @@ -918,7 +924,8 @@ } } - if (checkMSVCHeaderSearch(Diags, MSFE, FE, IncludeLoc)) { + if (checkMSVCHeaderSearch(Diags, MSFE ? &MSFE->getFileEntry() : nullptr, + &File->getFileEntry(), IncludeLoc)) { if (SuggestedModule) *SuggestedModule = MSSuggestedModule; return MSFE; @@ -926,13 +933,13 @@ bool FoundByHeaderMap = !IsMapped ? false : *IsMapped; if (!Includers.empty()) - diagnoseFrameworkInclude(Diags, IncludeLoc, - Includers.front().second->getName(), Filename, - FE, isAngled, FoundByHeaderMap); + diagnoseFrameworkInclude( + Diags, IncludeLoc, Includers.front().second->getName(), Filename, + &File->getFileEntry(), isAngled, FoundByHeaderMap); // Remember this location for the next lookup we do. CacheLookup.HitIdx = i; - return FE; + return File; } // If we are including a file with a quoted include "foo.h" from inside @@ -948,12 +955,14 @@ ScratchFilename += '/'; ScratchFilename += Filename; - const FileEntry *FE = LookupFile( + Optional File = LookupFile( ScratchFilename, IncludeLoc, /*isAngled=*/true, FromDir, CurDir, Includers.front(), SearchPath, RelativePath, RequestingModule, SuggestedModule, IsMapped, /*IsFrameworkFound=*/nullptr); - if (checkMSVCHeaderSearch(Diags, MSFE, FE, IncludeLoc)) { + if (checkMSVCHeaderSearch(Diags, MSFE ? &MSFE->getFileEntry() : nullptr, + File ? &File->getFileEntry() : nullptr, + IncludeLoc)) { if (SuggestedModule) *SuggestedModule = MSSuggestedModule; return MSFE; @@ -962,11 +971,12 @@ LookupFileCacheInfo &CacheLookup = LookupFileCache[Filename]; CacheLookup.HitIdx = LookupFileCache[ScratchFilename].HitIdx; // FIXME: SuggestedModule. - return FE; + return File; } } - if (checkMSVCHeaderSearch(Diags, MSFE, nullptr, IncludeLoc)) { + if (checkMSVCHeaderSearch(Diags, MSFE ? &MSFE->getFileEntry() : nullptr, + nullptr, IncludeLoc)) { if (SuggestedModule) *SuggestedModule = MSSuggestedModule; return MSFE; @@ -974,7 +984,7 @@ // Otherwise, didn't find it. Remember we didn't find this. CacheLookup.HitIdx = SearchDirs.size(); - return nullptr; + return None; } /// LookupSubframeworkHeader - Look up a subframework for the specified @@ -982,19 +992,17 @@ /// within ".../Carbon.framework/Headers/Carbon.h", check to see if HIToolbox /// is a subframework within Carbon.framework. If so, return the FileEntry /// for the designated file, otherwise return null. -const FileEntry *HeaderSearch:: -LookupSubframeworkHeader(StringRef Filename, - const FileEntry *ContextFileEnt, - SmallVectorImpl *SearchPath, - SmallVectorImpl *RelativePath, - Module *RequestingModule, - ModuleMap::KnownHeader *SuggestedModule) { +Optional HeaderSearch::LookupSubframeworkHeader( + StringRef Filename, const FileEntry *ContextFileEnt, + SmallVectorImpl *SearchPath, SmallVectorImpl *RelativePath, + Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule) { assert(ContextFileEnt && "No context file?"); // Framework names must have a '/' in the filename. Find it. // FIXME: Should we permit '\' on Windows? size_t SlashPos = Filename.find('/'); - if (SlashPos == StringRef::npos) return nullptr; + if (SlashPos == StringRef::npos) + return None; // Look up the base framework name of the ContextFileEnt. StringRef ContextName = ContextFileEnt->getName(); @@ -1005,7 +1013,7 @@ if (FrameworkPos == StringRef::npos || (ContextName[FrameworkPos + DotFrameworkLen] != '/' && ContextName[FrameworkPos + DotFrameworkLen] != '\\')) - return nullptr; + return None; SmallString<1024> FrameworkName(ContextName.data(), ContextName.data() + FrameworkPos + @@ -1025,7 +1033,7 @@ CacheLookup.first().size() == FrameworkName.size() && memcmp(CacheLookup.first().data(), &FrameworkName[0], CacheLookup.first().size()) != 0) - return nullptr; + return None; // Cache subframework. if (!CacheLookup.second.Directory) { @@ -1033,14 +1041,14 @@ // If the framework dir doesn't exist, we fail. auto Dir = FileMgr.getDirectory(FrameworkName); - if (!Dir) return nullptr; + if (!Dir) + return None; // Otherwise, if it does, remember that this is the right direntry for this // framework. CacheLookup.second.Directory = *Dir; } - const FileEntry *FE = nullptr; if (RelativePath) { RelativePath->clear(); @@ -1057,10 +1065,10 @@ } HeadersFilename.append(Filename.begin()+SlashPos+1, Filename.end()); - if (auto File = FileMgr.getFile(HeadersFilename, /*OpenFile=*/true)) - FE = *File; + llvm::ErrorOr File = + FileMgr.getFileRef(HeadersFilename, /*OpenFile=*/true); - if (!FE) { + if (!File) { // Check ".../Frameworks/HIToolbox.framework/PrivateHeaders/HIToolbox.h" HeadersFilename = FrameworkName; HeadersFilename += "PrivateHeaders/"; @@ -1071,11 +1079,10 @@ } HeadersFilename.append(Filename.begin()+SlashPos+1, Filename.end()); - if (auto File = FileMgr.getFile(HeadersFilename, /*OpenFile=*/true)) - FE = *File; - - if (!FE) - return nullptr; + File = FileMgr.getFileRef(HeadersFilename, /*OpenFile=*/true); + + if (!File) + return None; } // This file is a system header or C++ unfriendly if the old file is. @@ -1084,14 +1091,15 @@ // getFileInfo could resize the vector and we don't want to rely on order // of evaluation. unsigned DirInfo = getFileInfo(ContextFileEnt).DirInfo; - getFileInfo(FE).DirInfo = DirInfo; + getFileInfo(&File->getFileEntry()).DirInfo = DirInfo; FrameworkName.pop_back(); // remove the trailing '/' - if (!findUsableModuleForFrameworkHeader(FE, FrameworkName, RequestingModule, - SuggestedModule, /*IsSystem*/ false)) - return nullptr; + if (!findUsableModuleForFrameworkHeader(&File->getFileEntry(), FrameworkName, + RequestingModule, SuggestedModule, + /*IsSystem*/ false)) + return None; - return FE; + return *File; } //===----------------------------------------------------------------------===// Index: clang/lib/Lex/PPDirectives.cpp =================================================================== --- clang/lib/Lex/PPDirectives.cpp +++ clang/lib/Lex/PPDirectives.cpp @@ -679,7 +679,7 @@ return nullptr; } -const FileEntry *Preprocessor::LookupFile( +Optional Preprocessor::LookupFile( SourceLocation FilenameLoc, StringRef Filename, bool isAngled, const DirectoryLookup *FromDir, const FileEntry *FromFile, const DirectoryLookup *&CurDir, SmallVectorImpl *SearchPath, @@ -740,7 +740,7 @@ // the include path until we find that file or run out of files. const DirectoryLookup *TmpCurDir = CurDir; const DirectoryLookup *TmpFromDir = nullptr; - while (const FileEntry *FE = HeaderInfo.LookupFile( + while (Optional FE = HeaderInfo.LookupFile( Filename, FilenameLoc, isAngled, TmpFromDir, TmpCurDir, Includers, SearchPath, RelativePath, RequestingModule, SuggestedModule, /*IsMapped=*/nullptr, @@ -748,7 +748,7 @@ // Keep looking as if this file did a #include_next. TmpFromDir = TmpCurDir; ++TmpFromDir; - if (FE == FromFile) { + if (&FE->getFileEntry() == FromFile) { // Found it. FromDir = TmpFromDir; CurDir = TmpCurDir; @@ -758,7 +758,7 @@ } // Do a standard file entry lookup. - const FileEntry *FE = HeaderInfo.LookupFile( + Optional FE = HeaderInfo.LookupFile( Filename, FilenameLoc, isAngled, FromDir, CurDir, Includers, SearchPath, RelativePath, RequestingModule, SuggestedModule, IsMapped, IsFrameworkFound, SkipCache, BuildSystemModule); @@ -766,7 +766,7 @@ if (SuggestedModule && !LangOpts.AsmPreprocessor) HeaderInfo.getModuleMap().diagnoseHeaderInclusion( RequestingModule, RequestingModuleIsModuleInterface, FilenameLoc, - Filename, FE); + Filename, &FE->getFileEntry()); return FE; } @@ -776,14 +776,13 @@ // headers on the #include stack and pass them to HeaderInfo. if (IsFileLexer()) { if ((CurFileEnt = CurPPLexer->getFileEntry())) { - if ((FE = HeaderInfo.LookupSubframeworkHeader(Filename, CurFileEnt, - SearchPath, RelativePath, - RequestingModule, - SuggestedModule))) { + if (Optional FE = HeaderInfo.LookupSubframeworkHeader( + Filename, CurFileEnt, SearchPath, RelativePath, RequestingModule, + SuggestedModule)) { if (SuggestedModule && !LangOpts.AsmPreprocessor) HeaderInfo.getModuleMap().diagnoseHeaderInclusion( RequestingModule, RequestingModuleIsModuleInterface, FilenameLoc, - Filename, FE); + Filename, &FE->getFileEntry()); return FE; } } @@ -792,13 +791,13 @@ for (IncludeStackInfo &ISEntry : llvm::reverse(IncludeMacroStack)) { if (IsFileLexer(ISEntry)) { if ((CurFileEnt = ISEntry.ThePPLexer->getFileEntry())) { - if ((FE = HeaderInfo.LookupSubframeworkHeader( + if (Optional FE = HeaderInfo.LookupSubframeworkHeader( Filename, CurFileEnt, SearchPath, RelativePath, - RequestingModule, SuggestedModule))) { + RequestingModule, SuggestedModule)) { if (SuggestedModule && !LangOpts.AsmPreprocessor) HeaderInfo.getModuleMap().diagnoseHeaderInclusion( RequestingModule, RequestingModuleIsModuleInterface, - FilenameLoc, Filename, FE); + FilenameLoc, Filename, &FE->getFileEntry()); return FE; } } @@ -806,7 +805,7 @@ } // Otherwise, we really couldn't find the file. - return nullptr; + return None; } //===----------------------------------------------------------------------===// @@ -1754,13 +1753,16 @@ llvm::sys::path::native(NormalizedPath); #endif } - const FileEntry *File = LookupFile( - FilenameLoc, LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, - isAngled, LookupFrom, LookupFromFile, CurDir, - Callbacks ? &SearchPath : nullptr, Callbacks ? &RelativePath : nullptr, - &SuggestedModule, &IsMapped, &IsFrameworkFound); - if (!File) { + auto LookupHeaderFile = [&]() -> Optional { + Optional File = LookupFile( + FilenameLoc, LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, + isAngled, LookupFrom, LookupFromFile, CurDir, + Callbacks ? &SearchPath : nullptr, Callbacks ? &RelativePath : nullptr, + &SuggestedModule, &IsMapped, &IsFrameworkFound); + if (File) + return File; + if (Callbacks) { // Give the clients a chance to recover. SmallString<128> RecoveryPath; @@ -1771,103 +1773,109 @@ HeaderInfo.AddSearchPath(DL, isAngled); // Try the lookup again, skipping the cache. - File = LookupFile( + Optional File = LookupFile( FilenameLoc, LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, isAngled, LookupFrom, LookupFromFile, CurDir, nullptr, nullptr, &SuggestedModule, &IsMapped, /*IsFrameworkFound=*/nullptr, /*SkipCache*/ true); + if (File) + return File; } } } - if (!SuppressIncludeNotFoundError) { - // If the file could not be located and it was included via angle - // brackets, we can attempt a lookup as though it were a quoted path to - // provide the user with a possible fixit. - if (isAngled) { - File = LookupFile( - FilenameLoc, - LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, false, - LookupFrom, LookupFromFile, CurDir, - Callbacks ? &SearchPath : nullptr, - Callbacks ? &RelativePath : nullptr, &SuggestedModule, &IsMapped, - /*IsFrameworkFound=*/nullptr); - if (File) { - Diag(FilenameTok, - diag::err_pp_file_not_found_angled_include_not_fatal) - << Filename << IsImportDecl - << FixItHint::CreateReplacement(FilenameRange, - "\"" + Filename.str() + "\""); - } + if (SuppressIncludeNotFoundError) + return None; + + // If the file could not be located and it was included via angle + // brackets, we can attempt a lookup as though it were a quoted path to + // provide the user with a possible fixit. + if (isAngled) { + Optional File = LookupFile( + FilenameLoc, LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, + false, LookupFrom, LookupFromFile, CurDir, + Callbacks ? &SearchPath : nullptr, + Callbacks ? &RelativePath : nullptr, &SuggestedModule, &IsMapped, + /*IsFrameworkFound=*/nullptr); + if (File) { + Diag(FilenameTok, diag::err_pp_file_not_found_angled_include_not_fatal) + << Filename << IsImportDecl + << FixItHint::CreateReplacement(FilenameRange, + "\"" + Filename.str() + "\""); + return File; } + } - // Check for likely typos due to leading or trailing non-isAlphanumeric - // characters - StringRef OriginalFilename = Filename; - if (LangOpts.SpellChecking && !File) { - // A heuristic to correct a typo file name by removing leading and - // trailing non-isAlphanumeric characters. - auto CorrectTypoFilename = [](llvm::StringRef Filename) { - Filename = Filename.drop_until(isAlphanumeric); - while (!Filename.empty() && !isAlphanumeric(Filename.back())) { - Filename = Filename.drop_back(); - } - return Filename; - }; - StringRef TypoCorrectionName = CorrectTypoFilename(Filename); - SmallString<128> NormalizedTypoCorrectionPath; - if (LangOpts.MSVCCompat) { - NormalizedTypoCorrectionPath = TypoCorrectionName.str(); + // Check for likely typos due to leading or trailing non-isAlphanumeric + // characters + StringRef OriginalFilename = Filename; + if (LangOpts.SpellChecking) { + // A heuristic to correct a typo file name by removing leading and + // trailing non-isAlphanumeric characters. + auto CorrectTypoFilename = [](llvm::StringRef Filename) { + Filename = Filename.drop_until(isAlphanumeric); + while (!Filename.empty() && !isAlphanumeric(Filename.back())) { + Filename = Filename.drop_back(); + } + return Filename; + }; + StringRef TypoCorrectionName = CorrectTypoFilename(Filename); + SmallString<128> NormalizedTypoCorrectionPath; + if (LangOpts.MSVCCompat) { + NormalizedTypoCorrectionPath = TypoCorrectionName.str(); #ifndef _WIN32 - llvm::sys::path::native(NormalizedTypoCorrectionPath); + llvm::sys::path::native(NormalizedTypoCorrectionPath); #endif - } - File = LookupFile( - FilenameLoc, - LangOpts.MSVCCompat ? NormalizedTypoCorrectionPath.c_str() - : TypoCorrectionName, - isAngled, LookupFrom, LookupFromFile, CurDir, - Callbacks ? &SearchPath : nullptr, - Callbacks ? &RelativePath : nullptr, &SuggestedModule, &IsMapped, - /*IsFrameworkFound=*/nullptr); - if (File) { - auto Hint = - isAngled - ? FixItHint::CreateReplacement( - FilenameRange, "<" + TypoCorrectionName.str() + ">") - : FixItHint::CreateReplacement( - FilenameRange, "\"" + TypoCorrectionName.str() + "\""); - Diag(FilenameTok, diag::err_pp_file_not_found_typo_not_fatal) - << OriginalFilename << TypoCorrectionName << Hint; - // We found the file, so set the Filename to the name after typo - // correction. - Filename = TypoCorrectionName; - } } - - // If the file is still not found, just go with the vanilla diagnostic - if (!File) { - Diag(FilenameTok, diag::err_pp_file_not_found) << OriginalFilename - << FilenameRange; - if (IsFrameworkFound) { - size_t SlashPos = OriginalFilename.find('/'); - assert(SlashPos != StringRef::npos && - "Include with framework name should have '/' in the filename"); - StringRef FrameworkName = OriginalFilename.substr(0, SlashPos); - FrameworkCacheEntry &CacheEntry = - HeaderInfo.LookupFrameworkCache(FrameworkName); - assert(CacheEntry.Directory && "Found framework should be in cache"); - Diag(FilenameTok, diag::note_pp_framework_without_header) - << OriginalFilename.substr(SlashPos + 1) << FrameworkName - << CacheEntry.Directory->getName(); - } + Optional File = LookupFile( + FilenameLoc, + LangOpts.MSVCCompat ? NormalizedTypoCorrectionPath.c_str() + : TypoCorrectionName, + isAngled, LookupFrom, LookupFromFile, CurDir, + Callbacks ? &SearchPath : nullptr, + Callbacks ? &RelativePath : nullptr, &SuggestedModule, &IsMapped, + /*IsFrameworkFound=*/nullptr); + if (File) { + auto Hint = + isAngled + ? FixItHint::CreateReplacement( + FilenameRange, "<" + TypoCorrectionName.str() + ">") + : FixItHint::CreateReplacement( + FilenameRange, "\"" + TypoCorrectionName.str() + "\""); + Diag(FilenameTok, diag::err_pp_file_not_found_typo_not_fatal) + << OriginalFilename << TypoCorrectionName << Hint; + // We found the file, so set the Filename to the name after typo + // correction. + Filename = TypoCorrectionName; + return File; } } - } + + // If the file is still not found, just go with the vanilla diagnostic + assert(!File.hasValue() && "expected missing file"); + Diag(FilenameTok, diag::err_pp_file_not_found) + << OriginalFilename << FilenameRange; + if (IsFrameworkFound) { + size_t SlashPos = OriginalFilename.find('/'); + assert(SlashPos != StringRef::npos && + "Include with framework name should have '/' in the filename"); + StringRef FrameworkName = OriginalFilename.substr(0, SlashPos); + FrameworkCacheEntry &CacheEntry = + HeaderInfo.LookupFrameworkCache(FrameworkName); + assert(CacheEntry.Directory && "Found framework should be in cache"); + Diag(FilenameTok, diag::note_pp_framework_without_header) + << OriginalFilename.substr(SlashPos + 1) << FrameworkName + << CacheEntry.Directory->getName(); + } + + return None; + }; + + Optional File = LookupHeaderFile(); if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) { - if (isPCHThroughHeader(File)) + if (File && isPCHThroughHeader(&File->getFileEntry())) SkippingUntilPCHThroughHeader = false; return {ImportAction::None}; } @@ -1878,7 +1886,8 @@ // some directives (e.g. #endif of a header guard) will never be seen. // Since this will lead to confusing errors, avoid the inclusion. if (File && PreambleConditionalStack.isRecording() && - SourceMgr.translateFile(File) == SourceMgr.getMainFileID()) { + SourceMgr.translateFile(&File->getFileEntry()) == + SourceMgr.getMainFileID()) { Diag(FilenameTok.getLocation(), diag::err_pp_including_mainfile_in_preamble); return {ImportAction::None}; @@ -1897,7 +1906,7 @@ // include cycle. Don't enter already processed files again as it can lead to // reaching the max allowed include depth again. if (Action == Enter && HasReachedMaxIncludeDepth && File && - HeaderInfo.getFileInfo(File).NumIncludes) + HeaderInfo.getFileInfo(&File->getFileEntry()).NumIncludes) Action = IncludeLimitReached; // Determine whether we should try to import the module for this #include, if @@ -1973,7 +1982,8 @@ SrcMgr::CharacteristicKind FileCharacter = SourceMgr.getFileCharacteristic(FilenameTok.getLocation()); if (File) - FileCharacter = std::max(HeaderInfo.getFileDirFlavor(File), FileCharacter); + FileCharacter = std::max(HeaderInfo.getFileDirFlavor(&File->getFileEntry()), + FileCharacter); // If this is a '#import' or an import-declaration, don't re-enter the file. // @@ -1987,8 +1997,8 @@ // Ask HeaderInfo if we should enter this #include file. If not, #including // this file will have no effect. if (Action == Enter && File && - !HeaderInfo.ShouldEnterIncludeFile(*this, File, EnterOnce, - getLangOpts().Modules, + !HeaderInfo.ShouldEnterIncludeFile(*this, &File->getFileEntry(), + EnterOnce, getLangOpts().Modules, SuggestedModule.getModule())) { // Even if we've already preprocessed this header once and know that we // don't need to see its contents again, we still need to import it if it's @@ -2006,11 +2016,11 @@ Callbacks->InclusionDirective( HashLoc, IncludeTok, LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, isAngled, - FilenameRange, File, SearchPath, RelativePath, - Action == Import ? SuggestedModule.getModule() : nullptr, + FilenameRange, File ? &File->getFileEntry() : nullptr, SearchPath, + RelativePath, Action == Import ? SuggestedModule.getModule() : nullptr, FileCharacter); - if (Action == Skip) - Callbacks->FileSkipped(*File, FilenameTok, FileCharacter); + if (Action == Skip && File) + Callbacks->FileSkipped(File->getFileEntry(), FilenameTok, FileCharacter); } if (!File) @@ -2027,11 +2037,11 @@ // 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 = - !IsMapped && File && !File->tryGetRealPathName().empty(); + !IsMapped && !File->getFileEntry().tryGetRealPathName().empty(); if (CheckIncludePathPortability) { StringRef Name = LangOpts.MSVCCompat ? NormalizedPath.str() : Filename; - StringRef RealPathName = File->tryGetRealPathName(); + StringRef RealPathName = File->getFileEntry().tryGetRealPathName(); SmallVector Components(llvm::sys::path::begin(Name), llvm::sys::path::end(Name)); @@ -2102,7 +2112,7 @@ // position on the file where it will be included and after the expansions. if (IncludePos.isMacroID()) IncludePos = SourceMgr.getExpansionRange(IncludePos).getEnd(); - FileID FID = SourceMgr.createFileID(File, IncludePos, FileCharacter); + FileID FID = SourceMgr.createFileID(*File, IncludePos, FileCharacter); assert(FID.isValid() && "Expected valid file ID"); // If all is good, enter the new file! Index: clang/lib/Lex/PPMacroExpansion.cpp =================================================================== --- clang/lib/Lex/PPMacroExpansion.cpp +++ clang/lib/Lex/PPMacroExpansion.cpp @@ -1210,19 +1210,21 @@ // Search include directories. const DirectoryLookup *CurDir; - const FileEntry *File = + Optional File = PP.LookupFile(FilenameLoc, Filename, isAngled, LookupFrom, LookupFromFile, CurDir, nullptr, nullptr, nullptr, nullptr, nullptr); if (PPCallbacks *Callbacks = PP.getPPCallbacks()) { SrcMgr::CharacteristicKind FileType = SrcMgr::C_User; if (File) - FileType = PP.getHeaderSearchInfo().getFileDirFlavor(File); - Callbacks->HasInclude(FilenameLoc, Filename, isAngled, File, FileType); + FileType = + PP.getHeaderSearchInfo().getFileDirFlavor(&File->getFileEntry()); + Callbacks->HasInclude(FilenameLoc, Filename, isAngled, + File ? &File->getFileEntry() : nullptr, FileType); } // Get the result value. A result of true means the file exists. - return File != nullptr; + return File.hasValue(); } /// EvaluateHasInclude - Process a '__has_include("path")' expression. Index: clang/lib/Lex/Pragma.cpp =================================================================== --- clang/lib/Lex/Pragma.cpp +++ clang/lib/Lex/Pragma.cpp @@ -498,7 +498,7 @@ // Search include directories for this file. const DirectoryLookup *CurDir; - const FileEntry *File = + Optional File = LookupFile(FilenameTok.getLocation(), Filename, isAngled, nullptr, nullptr, CurDir, nullptr, nullptr, nullptr, nullptr, nullptr); if (!File) { Index: clang/lib/Lex/Preprocessor.cpp =================================================================== --- clang/lib/Lex/Preprocessor.cpp +++ clang/lib/Lex/Preprocessor.cpp @@ -563,7 +563,7 @@ // Lookup and save the FileID for the through header. If it isn't found // in the search path, it's a fatal error. const DirectoryLookup *CurDir; - const FileEntry *File = LookupFile( + Optional File = LookupFile( SourceLocation(), PPOpts->PCHThroughHeader, /*isAngled=*/false, /*FromDir=*/nullptr, /*FromFile=*/nullptr, CurDir, /*SearchPath=*/nullptr, /*RelativePath=*/nullptr, @@ -575,7 +575,7 @@ return; } setPCHThroughHeaderFileID( - SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User)); + SourceMgr.createFileID(*File, SourceLocation(), SrcMgr::C_User)); } // Skip tokens from the Predefines and if needed the main file. Index: clang/lib/Serialization/ASTReader.cpp =================================================================== --- clang/lib/Serialization/ASTReader.cpp +++ clang/lib/Serialization/ASTReader.cpp @@ -1514,6 +1514,7 @@ } SrcMgr::CharacteristicKind FileCharacter = (SrcMgr::CharacteristicKind)Record[2]; + // FIXME: The FileID should be created from the FileEntryRef. FileID FID = SourceMgr.createFileID(File, IncludeLoc, FileCharacter, ID, BaseOffset + Record[0]); SrcMgr::FileInfo &FileInfo = Index: clang/lib/Serialization/ASTWriter.cpp =================================================================== --- clang/lib/Serialization/ASTWriter.cpp +++ clang/lib/Serialization/ASTWriter.cpp @@ -1842,6 +1842,7 @@ Entry.IsTransient, Entry.IsTopLevelModuleMap}; + // FIXME: The path should be taken from the FileEntryRef. EmitRecordWithPath(IFAbbrevCode, Record, Entry.File->getName()); } Index: clang/unittests/Tooling/CMakeLists.txt =================================================================== --- clang/unittests/Tooling/CMakeLists.txt +++ clang/unittests/Tooling/CMakeLists.txt @@ -15,6 +15,7 @@ CastExprTest.cpp CommentHandlerTest.cpp CompilationDatabaseTest.cpp + DependencyScannerTest.cpp DiagnosticsYamlTest.cpp ExecutionTest.cpp FixItTest.cpp Index: clang/unittests/Tooling/DependencyScannerTest.cpp =================================================================== --- /dev/null +++ clang/unittests/Tooling/DependencyScannerTest.cpp @@ -0,0 +1,118 @@ +//===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "gtest/gtest.h" +#include +#include + +namespace clang { +namespace tooling { + +namespace { + +/// Prints out all of the gathered dependencies into a string. +class TestFileCollector : public DependencyFileGenerator { +public: + TestFileCollector(DependencyOutputOptions &Opts, + std::vector &Deps) + : DependencyFileGenerator(Opts), Deps(Deps) {} + + void finishedMainFile(DiagnosticsEngine &Diags) override { + Deps = getDependencies(); + } + +private: + std::vector &Deps; +}; + +class TestDependencyScanningAction : public tooling::ToolAction { +public: + TestDependencyScanningAction(std::vector &Deps) : Deps(Deps) {} + + bool runInvocation(std::shared_ptr Invocation, + FileManager *FileMgr, + std::shared_ptr PCHContainerOps, + DiagnosticConsumer *DiagConsumer) override { + CompilerInstance Compiler(std::move(PCHContainerOps)); + Compiler.setInvocation(std::move(Invocation)); + Compiler.setFileManager(FileMgr); + + Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false); + if (!Compiler.hasDiagnostics()) + return false; + + Compiler.createSourceManager(*FileMgr); + Compiler.addDependencyCollector(std::make_shared( + Compiler.getInvocation().getDependencyOutputOpts(), Deps)); + + auto Action = llvm::make_unique(); + return Compiler.ExecuteAction(*Action); + } + +private: + std::vector &Deps; +}; + +} // namespace + +TEST(DependencyScanner, ScanDepsReuseFilemanager) { + std::vector Compilation = {"-c", "-E", "-MT", "test.cpp.o"}; + StringRef CWD = "/root"; + FixedCompilationDatabase CDB(CWD, Compilation); + + auto VFS = new llvm::vfs::InMemoryFileSystem(); + VFS->setCurrentWorkingDirectory(CWD); + VFS->addFile("/root/header.h", 0, llvm::MemoryBuffer::getMemBuffer("\n")); + VFS->addHardLink("/root/symlink.h", "/root/header.h"); + VFS->addFile("/root/test.cpp", 0, + llvm::MemoryBuffer::getMemBuffer( + "#include \"symlink.h\"\n#include \"header.h\"\n")); + + ClangTool Tool(CDB, {"test.cpp"}, std::make_shared(), + VFS); + Tool.clearArgumentsAdjusters(); + std::vector Deps; + TestDependencyScanningAction Action(Deps); + Tool.run(&Action); + // The first invocation should return dependencies in order of access. + ASSERT_EQ(Deps.size(), 3u); + EXPECT_EQ(Deps[0], "/root/test.cpp"); + EXPECT_EQ(Deps[1], "/root/symlink.h"); + EXPECT_EQ(Deps[2], "/root/header.h"); + + // The file manager should still have two FileEntries, as one file is a + // hardlink. + FileManager &Files = Tool.getFiles(); + EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u); + + Deps.clear(); + Tool.run(&Action); + // The second invocation should have the same order of dependencies. + ASSERT_EQ(Deps.size(), 3u); + EXPECT_EQ(Deps[0], "/root/test.cpp"); + EXPECT_EQ(Deps[1], "/root/symlink.h"); + EXPECT_EQ(Deps[2], "/root/header.h"); + + EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u); +} + +} // end namespace tooling +} // end namespace clang