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(); }
+
+private:
+ StringRef Name;
+ const FileEntry &Entry;
+};
+
/// Implements support for file system lookup, file system caching,
/// and directory search management.
///
llvm::StringMap<llvm::ErrorOr<DirectoryEntry &>, llvm::BumpPtrAllocator>
SeenDirEntries;
+ /// A reference to the file entry that is associated with a particular
+ /// filename, or a reference to another filename that should be looked up
+ /// instead of the accessed filename.
+ ///
+ /// The reference to another filename is specifically useful for Redirecting
+ /// VFSs that use external names. In that case, the \c FileEntryRef returned
+ /// by the \c FileManager will have the external name, and not the name that
+ /// was used to lookup the file.
+ using SeenFileEntryOrRedirect =
+ llvm::PointerUnion<FileEntry *, const StringRef *>;
+
/// A cache that maps paths to file entries (either real or
/// virtual) we have looked up, or an error that occurred when we looked up
/// the file.
///
/// \see SeenDirEntries
- llvm::StringMap<llvm::ErrorOr<FileEntry &>, llvm::BumpPtrAllocator>
- SeenFileEntries;
+ llvm::StringMap<llvm::ErrorOr<SeenFileEntryOrRedirect>,
+ llvm::BumpPtrAllocator>
+ SeenFileEntries;
/// The canonical names of directories.
llvm::DenseMap<const DirectoryEntry *, llvm::StringRef> CanonicalDirNames;
/// 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).
///
/// 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.
///
llvm::ErrorOr<const FileEntry *>
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. If the underlying VFS is
+ /// a redirecting VFS that uses external file names, the returned FileEntryRef
+ /// will use the external name instead of the filename that was passed to this
+ /// method.
+ ///
+ /// 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<FileEntryRef> 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; }
llvm::PointerIntPair<const ContentCache*, 3, CharacteristicKind>
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;
}
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
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.
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 };
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
return Content->OrigEntry;
}
+ /// Returns the FileEntryRef for the provided FileID.
+ Optional<FileEntryRef> getFileEntryRefForID(FileID FID) const {
+ bool Invalid = false;
+ const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &Invalid);
+ if (Invalid || !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
{
///
/// 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,
/// \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<char> *SearchPath,
- SmallVectorImpl<char> *RelativePath,
- Module *RequestingModule,
- ModuleMap::KnownHeader *SuggestedModule,
- bool &InUserSpecifiedSystemFramework,
- bool &IsFrameworkFound,
- bool &HasBeenMapped,
- SmallVectorImpl<char> &MappedName) const;
+ Optional<FileEntryRef>
+ LookupFile(StringRef &Filename, HeaderSearch &HS, SourceLocation IncludeLoc,
+ SmallVectorImpl<char> *SearchPath,
+ SmallVectorImpl<char> *RelativePath, Module *RequestingModule,
+ ModuleMap::KnownHeader *SuggestedModule,
+ bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound,
+ bool &HasBeenMapped, SmallVectorImpl<char> &MappedName) const;
private:
- const FileEntry *DoFrameworkLookup(
- StringRef Filename, HeaderSearch &HS,
- SmallVectorImpl<char> *SearchPath,
- SmallVectorImpl<char> *RelativePath,
- Module *RequestingModule,
+ Optional<FileEntryRef> DoFrameworkLookup(
+ StringRef Filename, HeaderSearch &HS, SmallVectorImpl<char> *SearchPath,
+ SmallVectorImpl<char> *RelativePath, Module *RequestingModule,
ModuleMap::KnownHeader *SuggestedModule,
- bool &InUserSpecifiedSystemFramework,
- bool &IsFrameworkFound) const;
-
+ bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound) const;
};
} // end namespace clang
#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"
namespace clang {
-class FileEntry;
-class FileManager;
struct HMapBucket;
struct HMapHeader;
/// 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<FileEntryRef> LookupFile(StringRef Filename, FileManager &FM) const;
using HeaderMapImpl::lookupFilename;
using HeaderMapImpl::getFileName;
/// 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<FileEntryRef> LookupFile(
StringRef Filename, SourceLocation IncludeLoc, bool isAngled,
const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir,
ArrayRef<std::pair<const FileEntry *, const DirectoryEntry *>> Includers,
/// 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<FileEntryRef> LookupSubframeworkHeader(
StringRef Filename, const FileEntry *ContextFileEnt,
SmallVectorImpl<char> *SearchPath, SmallVectorImpl<char> *RelativePath,
Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule);
/// Look up the file with the specified name and determine its owning
/// module.
- const FileEntry *
+ Optional<FileEntryRef>
getFileAndSuggestModule(StringRef FileName, SourceLocation IncludeLoc,
const DirectoryEntry *Dir, bool IsSystemHeaderDir,
Module *RequestingModule,
/// Given a "foo" or \<foo> 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<char> *SearchPath,
- SmallVectorImpl<char> *RelativePath,
- ModuleMap::KnownHeader *SuggestedModule,
- bool *IsMapped, bool *IsFrameworkFound,
- bool SkipCache = false);
+ Optional<FileEntryRef>
+ LookupFile(SourceLocation FilenameLoc, StringRef Filename, bool isAngled,
+ const DirectoryLookup *FromDir, const FileEntry *FromFile,
+ const DirectoryLookup *&CurDir, SmallVectorImpl<char> *SearchPath,
+ SmallVectorImpl<char> *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.
}
};
+ Optional<FileEntryRef> LookupHeaderIncludeOrImport(
+ const DirectoryLookup *&CurDir, StringRef Filename,
+ SourceLocation FilenameLoc, CharSourceRange FilenameRange,
+ const Token &FilenameTok, bool &IsFrameworkFound, bool IsImportDecl,
+ bool &IsMapped, const DirectoryLookup *LookupFrom,
+ const FileEntry *LookupFromFile, SmallString<128> &NormalizedPath,
+ SmallVectorImpl<char> &RelativePath, SmallVectorImpl<char> &SearchPath,
+ ModuleMap::KnownHeader &SuggestedModule, bool isAngled);
+
// File inclusion.
void HandleIncludeDirective(SourceLocation HashLoc, Token &Tok,
const DirectoryLookup *LookupFrom = nullptr,
llvm::ErrorOr<const FileEntry *>
FileManager::getFile(StringRef Filename, bool openFile, bool CacheFailure) {
+ auto Result = getFileRef(Filename, openFile, CacheFailure);
+ if (Result)
+ return &Result->getFileEntry();
+ return Result.getError();
+}
+
+llvm::ErrorOr<FileEntryRef>
+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);
+ if (!SeenFileInsertResult.second) {
+ if (!SeenFileInsertResult.first->second)
+ return SeenFileInsertResult.first->second.getError();
+ // Construct and return and FileEntryRef, unless it's a redirect to another
+ // filename.
+ SeenFileEntryOrRedirect Value = *SeenFileInsertResult.first->second;
+ FileEntry *FE;
+ if (LLVM_LIKELY(FE = Value.dyn_cast<FileEntry *>()))
+ return FileEntryRef(SeenFileInsertResult.first->first(), *FE);
+ return getFileRef(*Value.get<const StringRef *>(), openFile, CacheFailure);
+ }
// We've not seen this before. Fill it in.
++NumFileCacheMisses;
// This occurs when one dir is symlinked to another, for example.
FileEntry &UFE = UniqueRealFiles[Status.getUniqueID()];
- NamedFileEnt.second = UFE;
+ NamedFileEnt.second = &UFE;
// If the name returned by getStatValue is different than Filename, re-intern
// the name.
if (Status.getName() != Filename) {
auto &NamedFileEnt =
- *SeenFileEntries.insert({Status.getName(), UFE}).first;
- assert(&*NamedFileEnt.second == &UFE &&
+ *SeenFileEntries.insert({Status.getName(), &UFE}).first;
+ assert((*NamedFileEnt.second).get<FileEntry *>() == &UFE &&
"filename from getStatValue() refers to wrong file");
InterndFileName = NamedFileEnt.first().data();
+ // In addition to re-interning the name, construct a redirecting seen file
+ // entry, that will point to the name the filesystem actually wants to use.
+ StringRef *Redirect = new (CanonicalNameStorage) StringRef(InterndFileName);
+ SeenFileInsertResult.first->second = Redirect;
}
if (UFE.isValid()) { // Already have an entry with this inode, return it.
// 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.
// 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 *
// See if there is already an entry in the map for an existing file.
auto &NamedFileEnt = *SeenFileEntries.insert(
{Filename, std::errc::no_such_file_or_directory}).first;
- if (NamedFileEnt.second)
- return &*NamedFileEnt.second;
+ if (NamedFileEnt.second) {
+ SeenFileEntryOrRedirect Value = *NamedFileEnt.second;
+ FileEntry *FE;
+ if (LLVM_LIKELY(FE = Value.dyn_cast<FileEntry *>()))
+ return FE;
+ return getVirtualFile(*Value.get<const StringRef *>(), Size,
+ ModificationTime);
+ }
// We've not seen this before, or the file is cached as non-existent.
++NumFileCacheMisses;
Status.getUser(), Status.getGroup(), Size,
Status.getType(), Status.getPermissions());
- NamedFileEnt.second = *UFE;
+ NamedFileEnt.second = UFE;
// If we had already opened this file, close it now so we don't
// leak the descriptor. We're not going to use the file
} else {
VirtualFileEntries.push_back(std::make_unique<FileEntry>());
UFE = VirtualFileEntries.back().get();
- NamedFileEnt.second = *UFE;
+ NamedFileEnt.second = UFE;
}
UFE->Name = InterndFileName;
UIDToFiles.resize(NextFileUID);
// Map file entries
- for (llvm::StringMap<llvm::ErrorOr<FileEntry &>,
+ for (llvm::StringMap<llvm::ErrorOr<SeenFileEntryOrRedirect>,
llvm::BumpPtrAllocator>::const_iterator
- FE = SeenFileEntries.begin(), FEEnd = SeenFileEntries.end();
+ FE = SeenFileEntries.begin(),
+ FEEnd = SeenFileEntries.end();
FE != FEEnd; ++FE)
- if (auto Entry = FE->getValue()) {
- UIDToFiles[Entry->getUID()] = &*Entry;
+ if (llvm::ErrorOr<SeenFileEntryOrRedirect> Entry = FE->getValue()) {
+ if (const auto *FE = (*Entry).dyn_cast<FileEntry *>())
+ UIDToFiles[FE->getUID()] = FE;
}
// Map virtual file entries
// 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, ""));
}
}
/// 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) {
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 &&
// 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<std::unique_ptr<llvm::MemoryBuffer>> SBOrErr =
llvm::MemoryBuffer::getSTDIN();
// 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<FileEntryRef> 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),
SmallVector<Module::Header, 16> 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<FileEntryRef> 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);
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<FileEntryRef> File = PP.getHeaderSearchInfo().LookupFile(
Filename, SourceLocation(), isAngled, Lookup, CurDir, Includers, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr);
- FileExists = File != nullptr;
+ FileExists = File.hasValue();
return true;
}
// Lookup file via Preprocessor, like a #include.
const DirectoryLookup *CurDir;
- const FileEntry *FE =
+ Optional<FileEntryRef> 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);
/// 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<FileEntryRef> 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,
return getHeaderMap()->getFileName();
}
-const FileEntry *HeaderSearch::getFileAndSuggestModule(
+Optional<FileEntryRef> 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.
- llvm::ErrorOr<const FileEntry *> File =
- getFileMgr().getFile(FileName, /*OpenFile=*/true);
+ auto File = getFileMgr().getFileRef(FileName, /*OpenFile=*/true);
if (!File) {
// For rare, surprising errors (e.g. "out of file handles"), diag the EC
// message.
Diags.Report(IncludeLoc, diag::err_cannot_open_file)
<< FileName << EC.message();
}
- 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<char> *SearchPath,
- SmallVectorImpl<char> *RelativePath,
- Module *RequestingModule,
- ModuleMap::KnownHeader *SuggestedModule,
- bool &InUserSpecifiedSystemFramework,
- bool &IsFrameworkFound,
- bool &HasBeenMapped,
- SmallVectorImpl<char> &MappedName) const {
+Optional<FileEntryRef> DirectoryLookup::LookupFile(
+ StringRef &Filename, HeaderSearch &HS, SourceLocation IncludeLoc,
+ SmallVectorImpl<char> *SearchPath, SmallVectorImpl<char> *RelativePath,
+ Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule,
+ bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound,
+ bool &HasBeenMapped, SmallVectorImpl<char> &MappedName) const {
InUserSpecifiedSystemFramework = false;
HasBeenMapped = false;
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
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<FileEntryRef> 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.
/// DoFrameworkLookup - Do a lookup of the specified file in the current
/// DirectoryLookup, which is a framework directory.
-const FileEntry *DirectoryLookup::DoFrameworkLookup(
+Optional<FileEntryRef> DirectoryLookup::DoFrameworkLookup(
StringRef Filename, HeaderSearch &HS, SmallVectorImpl<char> *SearchPath,
SmallVectorImpl<char> *RelativePath, Module *RequestingModule,
ModuleMap::KnownHeader *SuggestedModule,
// 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.
// 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.
// 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.
FrameworkName.append(Filename.begin()+SlashPos+1, Filename.end());
- const FileEntry *FE = nullptr;
- if (auto File = FileMgr.getFile(FrameworkName, /*OpenFile=*/!SuggestedModule))
- FE = *File;
+ llvm::ErrorOr<FileEntryRef> 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,
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.
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) {
/// 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<FileEntryRef> HeaderSearch::LookupFile(
StringRef Filename, SourceLocation IncludeLoc, bool isAngled,
const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir,
ArrayRef<std::pair<const FileEntry *, const DirectoryEntry *>> Includers,
CurDir = nullptr;
// If this was an #include_next "/absolute/file", fail.
- if (FromDir) return nullptr;
+ if (FromDir)
+ return None;
if (SearchPath)
SearchPath->clear();
}
// 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
bool IncluderIsSystemHeader =
Includer ? getFileInfo(Includer).DirInfo != SrcMgr::C_User :
BuildSystemModule;
- if (const FileEntry *FE = getFileAndSuggestModule(
+ if (Optional<FileEntryRef> FE = getFileAndSuggestModule(
TmpDir, IncludeLoc, IncluderAndDir.second, IncluderIsSystemHeader,
RequestingModule, SuggestedModule)) {
if (!Includer) {
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;
if (First) {
diagnoseFrameworkInclude(Diags, IncludeLoc,
IncluderAndDir.second->getName(), Filename,
- FE);
+ &FE->getFileEntry());
return FE;
}
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();
}
}
+ Optional<FileEntryRef> MSFE(MSFE_FE ? FileEntryRef(MSFE_Name, *MSFE_FE)
+ : Optional<FileEntryRef>());
+
CurDir = nullptr;
// If this is a system #include, ignore the user #include locs.
bool InUserSpecifiedSystemFramework = false;
bool HasBeenMapped = false;
bool IsFrameworkFoundInDir = false;
- const FileEntry *FE = SearchDirs[i].LookupFile(
+ Optional<FileEntryRef> File = SearchDirs[i].LookupFile(
Filename, *this, IncludeLoc, SearchPath, RelativePath, RequestingModule,
SuggestedModule, InUserSpecifiedSystemFramework, IsFrameworkFoundInDir,
HasBeenMapped, MappedName);
// 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
}
}
- if (checkMSVCHeaderSearch(Diags, MSFE, FE, IncludeLoc)) {
+ if (checkMSVCHeaderSearch(Diags, MSFE ? &MSFE->getFileEntry() : nullptr,
+ &File->getFileEntry(), IncludeLoc)) {
if (SuggestedModule)
*SuggestedModule = MSSuggestedModule;
return MSFE;
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
ScratchFilename += '/';
ScratchFilename += Filename;
- const FileEntry *FE = LookupFile(
+ Optional<FileEntryRef> 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;
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;
// 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
/// 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<char> *SearchPath,
- SmallVectorImpl<char> *RelativePath,
- Module *RequestingModule,
- ModuleMap::KnownHeader *SuggestedModule) {
+Optional<FileEntryRef> HeaderSearch::LookupSubframeworkHeader(
+ StringRef Filename, const FileEntry *ContextFileEnt,
+ SmallVectorImpl<char> *SearchPath, SmallVectorImpl<char> *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();
if (FrameworkPos == StringRef::npos ||
(ContextName[FrameworkPos + DotFrameworkLen] != '/' &&
ContextName[FrameworkPos + DotFrameworkLen] != '\\'))
- return nullptr;
+ return None;
SmallString<1024> FrameworkName(ContextName.data(), ContextName.data() +
FrameworkPos +
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) {
// 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();
}
HeadersFilename.append(Filename.begin()+SlashPos+1, Filename.end());
- if (auto File = FileMgr.getFile(HeadersFilename, /*OpenFile=*/true))
- FE = *File;
+ llvm::ErrorOr<FileEntryRef> File =
+ FileMgr.getFileRef(HeadersFilename, /*OpenFile=*/true);
- if (!FE) {
+ if (!File) {
// Check ".../Frameworks/HIToolbox.framework/PrivateHeaders/HIToolbox.h"
HeadersFilename = FrameworkName;
HeadersFilename += "PrivateHeaders/";
}
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.
// 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;
}
//===----------------------------------------------------------------------===//
return nullptr;
}
-const FileEntry *Preprocessor::LookupFile(
+Optional<FileEntryRef> Preprocessor::LookupFile(
SourceLocation FilenameLoc, StringRef Filename, bool isAngled,
const DirectoryLookup *FromDir, const FileEntry *FromFile,
const DirectoryLookup *&CurDir, SmallVectorImpl<char> *SearchPath,
// 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<FileEntryRef> FE = HeaderInfo.LookupFile(
Filename, FilenameLoc, isAngled, TmpFromDir, TmpCurDir,
Includers, SearchPath, RelativePath, RequestingModule,
SuggestedModule, /*IsMapped=*/nullptr,
// 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;
}
// Do a standard file entry lookup.
- const FileEntry *FE = HeaderInfo.LookupFile(
+ Optional<FileEntryRef> FE = HeaderInfo.LookupFile(
Filename, FilenameLoc, isAngled, FromDir, CurDir, Includers, SearchPath,
RelativePath, RequestingModule, SuggestedModule, IsMapped,
IsFrameworkFound, SkipCache, BuildSystemModule);
if (SuggestedModule && !LangOpts.AsmPreprocessor)
HeaderInfo.getModuleMap().diagnoseHeaderInclusion(
RequestingModule, RequestingModuleIsModuleInterface, FilenameLoc,
- Filename, FE);
+ Filename, &FE->getFileEntry());
return FE;
}
// 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<FileEntryRef> 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;
}
}
for (IncludeStackInfo &ISEntry : llvm::reverse(IncludeMacroStack)) {
if (IsFileLexer(ISEntry)) {
if ((CurFileEnt = ISEntry.ThePPLexer->getFileEntry())) {
- if ((FE = HeaderInfo.LookupSubframeworkHeader(
+ if (Optional<FileEntryRef> 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;
}
}
}
// Otherwise, we really couldn't find the file.
- return nullptr;
+ return None;
}
//===----------------------------------------------------------------------===//
}
}
+Optional<FileEntryRef> Preprocessor::LookupHeaderIncludeOrImport(
+ const DirectoryLookup *&CurDir, StringRef Filename,
+ SourceLocation FilenameLoc, CharSourceRange FilenameRange,
+ const Token &FilenameTok, bool &IsFrameworkFound, bool IsImportDecl,
+ bool &IsMapped, const DirectoryLookup *LookupFrom,
+ const FileEntry *LookupFromFile, SmallString<128> &NormalizedPath,
+ SmallVectorImpl<char> &RelativePath, SmallVectorImpl<char> &SearchPath,
+ ModuleMap::KnownHeader &SuggestedModule, bool isAngled) {
+ Optional<FileEntryRef> 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;
+ if (Callbacks->FileNotFound(Filename, RecoveryPath)) {
+ if (auto DE = FileMgr.getDirectory(RecoveryPath)) {
+ // Add the recovery path to the list of search paths.
+ DirectoryLookup DL(*DE, SrcMgr::C_User, false);
+ HeaderInfo.AddSearchPath(DL, isAngled);
+
+ // Try the lookup again, skipping the cache.
+ Optional<FileEntryRef> 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)
+ 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<FileEntryRef> 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) {
+ // 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);
+#endif
+ }
+ Optional<FileEntryRef> 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;
+}
+
/// Handle either a #include-like directive or an import declaration that names
/// a header file.
///
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) {
- if (Callbacks) {
- // Give the clients a chance to recover.
- SmallString<128> RecoveryPath;
- if (Callbacks->FileNotFound(Filename, RecoveryPath)) {
- if (auto DE = FileMgr.getDirectory(RecoveryPath)) {
- // Add the recovery path to the list of search paths.
- DirectoryLookup DL(*DE, SrcMgr::C_User, false);
- HeaderInfo.AddSearchPath(DL, isAngled);
-
- // Try the lookup again, skipping the cache.
- File = LookupFile(
- FilenameLoc,
- LangOpts.MSVCCompat ? NormalizedPath.c_str() : Filename, isAngled,
- LookupFrom, LookupFromFile, CurDir, nullptr, nullptr,
- &SuggestedModule, &IsMapped, /*IsFrameworkFound=*/nullptr,
- /*SkipCache*/ true);
- }
- }
- }
-
- 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() + "\"");
- }
- }
- // 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();
-#ifndef _WIN32
- 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<FileEntryRef> File = LookupHeaderIncludeOrImport(
+ CurDir, Filename, FilenameLoc, FilenameRange, FilenameTok,
+ IsFrameworkFound, IsImportDecl, IsMapped, LookupFrom, LookupFromFile,
+ NormalizedPath, RelativePath, SearchPath, SuggestedModule, isAngled);
if (usingPCHWithThroughHeader() && SkippingUntilPCHThroughHeader) {
- if (isPCHThroughHeader(File))
+ if (File && isPCHThroughHeader(&File->getFileEntry()))
SkippingUntilPCHThroughHeader = false;
return {ImportAction::None};
}
// 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};
// 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
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.
//
// 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
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)
// 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<StringRef, 16> Components(llvm::sys::path::begin(Name),
llvm::sys::path::end(Name));
// 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!
// Search include directories.
const DirectoryLookup *CurDir;
- const FileEntry *File =
+ Optional<FileEntryRef> 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.
// Search include directories for this file.
const DirectoryLookup *CurDir;
- const FileEntry *File =
+ Optional<FileEntryRef> File =
LookupFile(FilenameTok.getLocation(), Filename, isAngled, nullptr,
nullptr, CurDir, nullptr, nullptr, nullptr, nullptr, nullptr);
if (!File) {
// 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<FileEntryRef> File = LookupFile(
SourceLocation(), PPOpts->PCHThroughHeader,
/*isAngled=*/false, /*FromDir=*/nullptr, /*FromFile=*/nullptr, CurDir,
/*SearchPath=*/nullptr, /*RelativePath=*/nullptr,
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.
}
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 =
Entry.IsTransient,
Entry.IsTopLevelModuleMap};
+ // FIXME: The path should be taken from the FileEntryRef.
EmitRecordWithPath(IFAbbrevCode, Record, Entry.File->getName());
}
// REQUIRES: shell
#include "external-names.h"
+#ifdef REINCLUDE
+#include "external-names.h"
+#endif
////
// Preprocessor (__FILE__ macro and # directives):
// RUN: %clang_cc1 -I %t -ivfsoverlay %t.yaml -triple %itanium_abi_triple -debug-info-kind=limited -emit-llvm %s -o - | FileCheck -check-prefix=CHECK-DEBUG %s
// CHECK-DEBUG-NOT: Inputs
+
+////
+// Dependency file
+
+// RUN: %clang_cc1 -D REINCLUDE -I %t -ivfsoverlay %t.external.yaml -Eonly %s -MTfoo -dependency-file %t.external.dep
+// RUN: echo "EOF" >> %t.external.dep
+// RUN: cat %t.external.dep | FileCheck --check-prefix=CHECK-DEP-EXTERNAL %s
+// CHECK-DEP-EXTERNAL: Inputs{{.}}external-names.h
+// CHECK-DEP-EXTERNAL-NEXT: EOF
+
+// RUN: %clang_cc1 -D REINCLUDE -I %t -ivfsoverlay %t.yaml -Eonly %s -MTfoo -dependency-file %t.dep
+// RUN: cat %t.dep | FileCheck --check-prefix=CHECK-DEP %s
+// CHECK-DEP-NOT: Inputs
CastExprTest.cpp
CommentHandlerTest.cpp
CompilationDatabaseTest.cpp
+ DependencyScannerTest.cpp
DiagnosticsYamlTest.cpp
ExecutionTest.cpp
FixItTest.cpp
--- /dev/null
+//===- 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 <algorithm>
+#include <string>
+
+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<std::string> &Deps)
+ : DependencyFileGenerator(Opts), Deps(Deps) {}
+
+ void finishedMainFile(DiagnosticsEngine &Diags) override {
+ Deps = getDependencies();
+ }
+
+private:
+ std::vector<std::string> &Deps;
+};
+
+class TestDependencyScanningAction : public tooling::ToolAction {
+public:
+ TestDependencyScanningAction(std::vector<std::string> &Deps) : Deps(Deps) {}
+
+ bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
+ FileManager *FileMgr,
+ std::shared_ptr<PCHContainerOperations> 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<TestFileCollector>(
+ Compiler.getInvocation().getDependencyOutputOpts(), Deps));
+
+ auto Action = std::make_unique<PreprocessOnlyAction>();
+ return Compiler.ExecuteAction(*Action);
+ }
+
+private:
+ std::vector<std::string> &Deps;
+};
+
+} // namespace
+
+TEST(DependencyScanner, ScanDepsReuseFilemanager) {
+ std::vector<std::string> 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<PCHContainerOperations>(),
+ VFS);
+ Tool.clearArgumentsAdjusters();
+ std::vector<std::string> 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