//===----------------------------------------------------------------------===//
#include "clang/Lex/HeaderSearch.h"
-#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/IdentifierTable.h"
+#include "clang/Lex/ExternalPreprocessorSource.h"
#include "clang/Lex/HeaderMap.h"
#include "clang/Lex/HeaderSearchOptions.h"
+#include "clang/Lex/LexDiagnostic.h"
#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Capacity.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
#include <cstdio>
+#include <utility>
+#if defined(LLVM_ON_UNIX)
+#include <limits.h>
+#endif
using namespace clang;
const IdentifierInfo *
-HeaderFileInfo::getControllingMacro(ExternalIdentifierLookup *External) {
- if (ControllingMacro)
+HeaderFileInfo::getControllingMacro(ExternalPreprocessorSource *External) {
+ if (ControllingMacro) {
+ if (ControllingMacro->isOutOfDate())
+ External->updateOutOfDateIdentifier(
+ *const_cast<IdentifierInfo *>(ControllingMacro));
return ControllingMacro;
+ }
if (!ControllingMacroID || !External)
- return 0;
+ return nullptr;
ControllingMacro = External->GetIdentifier(ControllingMacroID);
return ControllingMacro;
ExternalHeaderFileInfoSource::~ExternalHeaderFileInfoSource() {}
HeaderSearch::HeaderSearch(IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts,
- FileManager &FM, DiagnosticsEngine &Diags,
- const LangOptions &LangOpts,
+ SourceManager &SourceMgr, DiagnosticsEngine &Diags,
+ const LangOptions &LangOpts,
const TargetInfo *Target)
- : HSOpts(HSOpts), FileMgr(FM), FrameworkMap(64),
- ModMap(FileMgr, *Diags.getClient(), LangOpts, Target)
-{
+ : HSOpts(std::move(HSOpts)), Diags(Diags),
+ FileMgr(SourceMgr.getFileManager()), FrameworkMap(64),
+ ModMap(SourceMgr, Diags, LangOpts, Target, *this) {
AngledDirIdx = 0;
SystemDirIdx = 0;
NoCurDirSearch = false;
- ExternalLookup = 0;
- ExternalSource = 0;
+ ExternalLookup = nullptr;
+ ExternalSource = nullptr;
NumIncluded = 0;
NumMultiIncludeFileOptzn = 0;
NumFrameworkLookups = NumSubFrameworkLookups = 0;
return HM;
}
- return 0;
+ return nullptr;
}
std::string HeaderSearch::getModuleFileName(Module *Module) {
- // If we don't have a module cache path, we can't do anything.
- if (ModuleCachePath.empty())
+ const FileEntry *ModuleMap =
+ getModuleMap().getModuleMapFileForUniquing(Module);
+ return getModuleFileName(Module->Name, ModuleMap->getName());
+}
+
+std::string HeaderSearch::getModuleFileName(StringRef ModuleName,
+ StringRef ModuleMapPath) {
+ // If we don't have a module cache path or aren't supposed to use one, we
+ // can't do anything.
+ if (getModuleCachePath().empty())
return std::string();
+ SmallString<256> Result(getModuleCachePath());
+ llvm::sys::fs::make_absolute(Result);
- SmallString<256> Result(ModuleCachePath);
- llvm::sys::path::append(Result, Module->getTopLevelModule()->Name + ".pcm");
- return Result.str().str();
-}
+ if (HSOpts->DisableModuleHash) {
+ llvm::sys::path::append(Result, ModuleName + ".pcm");
+ } else {
+ // Construct the name <ModuleName>-<hash of ModuleMapPath>.pcm which should
+ // ideally be globally unique to this particular module. Name collisions
+ // in the hash are safe (because any translation unit can only import one
+ // module with each name), but result in a loss of caching.
+ //
+ // To avoid false-negatives, we form as canonical a path as we can, and map
+ // to lower-case in case we're on a case-insensitive file system.
+ auto *Dir =
+ FileMgr.getDirectory(llvm::sys::path::parent_path(ModuleMapPath));
+ if (!Dir)
+ return std::string();
+ auto DirName = FileMgr.getCanonicalName(Dir);
+ auto FileName = llvm::sys::path::filename(ModuleMapPath);
-std::string HeaderSearch::getModuleFileName(StringRef ModuleName) {
- // If we don't have a module cache path, we can't do anything.
- if (ModuleCachePath.empty())
- return std::string();
-
-
- SmallString<256> Result(ModuleCachePath);
- llvm::sys::path::append(Result, ModuleName + ".pcm");
+ llvm::hash_code Hash =
+ llvm::hash_combine(DirName.lower(), FileName.lower());
+
+ SmallString<128> HashStr;
+ llvm::APInt(64, size_t(Hash)).toStringUnsigned(HashStr, /*Radix*/36);
+ llvm::sys::path::append(Result, ModuleName + "-" + HashStr + ".pcm");
+ }
return Result.str().str();
}
Module *HeaderSearch::lookupModule(StringRef ModuleName, bool AllowSearch) {
// Look in the module map to determine if there is a module by this name.
Module *Module = ModMap.findModule(ModuleName);
- if (Module || !AllowSearch)
+ if (Module || !AllowSearch || !HSOpts->ImplicitModuleMaps)
return Module;
// Look through the various header search paths to load any available module
// Only deal with normal search directories.
if (!SearchDirs[Idx].isNormalDir())
continue;
-
+
+ bool IsSystem = SearchDirs[Idx].isSystemHeaderDirectory();
// Search for a module map file in this directory.
- if (loadModuleMapFile(SearchDirs[Idx].getDir()) == LMM_NewlyLoaded) {
+ if (loadModuleMapFile(SearchDirs[Idx].getDir(), IsSystem,
+ /*IsFramework*/false) == LMM_NewlyLoaded) {
// We just loaded a module map file; check whether the module is
// available now.
Module = ModMap.findModule(ModuleName);
SmallString<128> NestedModuleMapDirName;
NestedModuleMapDirName = SearchDirs[Idx].getDir()->getName();
llvm::sys::path::append(NestedModuleMapDirName, ModuleName);
- if (loadModuleMapFile(NestedModuleMapDirName) == LMM_NewlyLoaded) {
+ if (loadModuleMapFile(NestedModuleMapDirName, IsSystem,
+ /*IsFramework*/false) == LMM_NewlyLoaded){
// If we just loaded a module map file, look for the module again.
Module = ModMap.findModule(ModuleName);
if (Module)
break;
}
+
+ // If we've already performed the exhaustive search for module maps in this
+ // search directory, don't do it again.
+ if (SearchDirs[Idx].haveSearchedAllModuleMaps())
+ continue;
+
+ // Load all module maps in the immediate subdirectories of this search
+ // directory.
+ loadSubdirectoryModuleMaps(SearchDirs[Idx]);
+
+ // Look again for the module.
+ Module = ModMap.findModule(ModuleName);
+ if (Module)
+ break;
}
-
+
return Module;
}
return getHeaderMap()->getFileName();
}
+const FileEntry *HeaderSearch::getFileAndSuggestModule(
+ StringRef FileName, const DirectoryEntry *Dir, bool IsSystemHeaderDir,
+ Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule) {
+ // If we have a module map that might map this header, load it and
+ // check whether we'll have a suggestion for a module.
+ const FileEntry *File = getFileMgr().getFile(FileName, /*OpenFile=*/true);
+ if (!File)
+ return nullptr;
+
+ // If there is a module that corresponds to this header, suggest it.
+ if (!findUsableModuleForHeader(File, Dir ? Dir : File->getDir(),
+ RequestingModule, SuggestedModule,
+ IsSystemHeaderDir))
+ return nullptr;
+
+ 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,
+ StringRef &Filename,
HeaderSearch &HS,
SmallVectorImpl<char> *SearchPath,
SmallVectorImpl<char> *RelativePath,
- Module **SuggestedModule,
- bool &InUserSpecifiedSystemFramework) const {
+ Module *RequestingModule,
+ ModuleMap::KnownHeader *SuggestedModule,
+ bool &InUserSpecifiedSystemFramework,
+ bool &HasBeenMapped,
+ SmallVectorImpl<char> &MappedName) const {
InUserSpecifiedSystemFramework = false;
+ HasBeenMapped = false;
SmallString<1024> TmpDir;
if (isNormalDir()) {
// Concatenate the requested file onto the directory.
TmpDir = getDir()->getName();
llvm::sys::path::append(TmpDir, Filename);
- if (SearchPath != NULL) {
+ if (SearchPath) {
StringRef SearchPathRef(getDir()->getName());
SearchPath->clear();
SearchPath->append(SearchPathRef.begin(), SearchPathRef.end());
}
- if (RelativePath != NULL) {
+ if (RelativePath) {
RelativePath->clear();
RelativePath->append(Filename.begin(), Filename.end());
}
-
- // If we have a module map that might map this header, load it and
- // check whether we'll have a suggestion for a module.
- if (SuggestedModule && HS.hasModuleMap(TmpDir, getDir())) {
- const FileEntry *File = HS.getFileMgr().getFile(TmpDir.str(),
- /*openFile=*/false);
- if (!File)
- return File;
-
- // If there is a module that corresponds to this header,
- // suggest it.
- *SuggestedModule = HS.findModuleForHeader(File);
- return File;
- }
-
- return HS.getFileMgr().getFile(TmpDir.str(), /*openFile=*/true);
+
+ return HS.getFileAndSuggestModule(TmpDir, getDir(),
+ isSystemHeaderDirectory(),
+ RequestingModule, SuggestedModule);
}
if (isFramework())
return DoFrameworkLookup(Filename, HS, SearchPath, RelativePath,
- SuggestedModule, InUserSpecifiedSystemFramework);
+ RequestingModule, SuggestedModule,
+ InUserSpecifiedSystemFramework);
assert(isHeaderMap() && "Unknown directory lookup");
- const FileEntry * const Result = getHeaderMap()->LookupFile(
- Filename, HS.getFileMgr());
+ const HeaderMap *HM = getHeaderMap();
+ SmallString<1024> Path;
+ StringRef Dest = HM->lookupFilename(Filename, Path);
+ if (Dest.empty())
+ return nullptr;
+
+ const FileEntry *Result;
+
+ // Check if the headermap maps the filename to a framework include
+ // ("Foo.h" -> "Foo/Foo.h"), in which case continue header lookup using the
+ // framework include.
+ if (llvm::sys::path::is_relative(Dest)) {
+ MappedName.clear();
+ MappedName.append(Dest.begin(), Dest.end());
+ Filename = StringRef(MappedName.begin(), MappedName.size());
+ HasBeenMapped = true;
+ Result = HM->LookupFile(Filename, HS.getFileMgr());
+
+ } else {
+ Result = HS.getFileMgr().getFile(Dest);
+ }
+
if (Result) {
- if (SearchPath != NULL) {
+ if (SearchPath) {
StringRef SearchPathRef(getName());
SearchPath->clear();
SearchPath->append(SearchPathRef.begin(), SearchPathRef.end());
}
- if (RelativePath != NULL) {
+ if (RelativePath) {
RelativePath->clear();
RelativePath->append(Filename.begin(), Filename.end());
}
assert(llvm::sys::path::extension(DirName) == ".framework" &&
"Not a framework directory");
-#ifdef LLVM_ON_UNIX
// Note: as an egregious but useful hack we use the real path here, because
// frameworks moving between top-level frameworks to embedded frameworks tend
// to be symlinked, and we base the logical structure of modules on the
//
// Similar issues occur when a top-level framework has moved into an
// embedded framework.
- char RealDirName[PATH_MAX];
- if (realpath(DirName.str().c_str(), RealDirName))
- DirName = RealDirName;
-#endif
-
const DirectoryEntry *TopFrameworkDir = FileMgr.getDirectory(DirName);
+ DirName = FileMgr.getCanonicalName(TopFrameworkDir);
do {
// Get the parent directory name.
DirName = llvm::sys::path::parent_path(DirName);
/// DoFrameworkLookup - Do a lookup of the specified file in the current
/// DirectoryLookup, which is a framework directory.
const FileEntry *DirectoryLookup::DoFrameworkLookup(
- StringRef Filename,
- HeaderSearch &HS,
- SmallVectorImpl<char> *SearchPath,
- SmallVectorImpl<char> *RelativePath,
- Module **SuggestedModule,
- bool &InUserSpecifiedSystemFramework) const
-{
+ StringRef Filename, HeaderSearch &HS, SmallVectorImpl<char> *SearchPath,
+ SmallVectorImpl<char> *RelativePath, Module *RequestingModule,
+ ModuleMap::KnownHeader *SuggestedModule,
+ bool &InUserSpecifiedSystemFramework) const {
FileManager &FileMgr = HS.getFileMgr();
// Framework names must have a '/' in the filename.
size_t SlashPos = Filename.find('/');
- if (SlashPos == StringRef::npos) return 0;
+ if (SlashPos == StringRef::npos) return nullptr;
// 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 0;
+ return nullptr;
// Otherwise, construct the path to this framework dir.
FrameworkName += ".framework/";
// If the cache entry was unresolved, populate it now.
- if (CacheEntry.Directory == 0) {
+ if (!CacheEntry.Directory) {
HS.IncrementFrameworkLookupCount();
// If the framework dir doesn't exist, we fail.
- const DirectoryEntry *Dir = FileMgr.getDirectory(FrameworkName.str());
- if (Dir == 0) return 0;
+ const DirectoryEntry *Dir = FileMgr.getDirectory(FrameworkName);
+ if (!Dir) return nullptr;
// Otherwise, if it does, remember that this is the right direntry for this
// framework.
if (getDirCharacteristic() == SrcMgr::C_User) {
SmallString<1024> SystemFrameworkMarker(FrameworkName);
SystemFrameworkMarker += ".system_framework";
- if (llvm::sys::fs::exists(SystemFrameworkMarker.str())) {
+ if (llvm::sys::fs::exists(SystemFrameworkMarker)) {
CacheEntry.IsUserSpecifiedSystemFramework = true;
}
}
// Set the 'user-specified system framework' flag.
InUserSpecifiedSystemFramework = CacheEntry.IsUserSpecifiedSystemFramework;
- if (RelativePath != NULL) {
+ if (RelativePath) {
RelativePath->clear();
RelativePath->append(Filename.begin()+SlashPos+1, Filename.end());
}
FrameworkName += "Headers/";
- if (SearchPath != NULL) {
+ if (SearchPath) {
SearchPath->clear();
// Without trailing '/'.
SearchPath->append(FrameworkName.begin(), FrameworkName.end()-1);
}
FrameworkName.append(Filename.begin()+SlashPos+1, Filename.end());
- const FileEntry *FE = FileMgr.getFile(FrameworkName.str(),
+ const FileEntry *FE = FileMgr.getFile(FrameworkName,
/*openFile=*/!SuggestedModule);
if (!FE) {
// Check "/System/Library/Frameworks/Cocoa.framework/PrivateHeaders/file.h"
const char *Private = "Private";
FrameworkName.insert(FrameworkName.begin()+OrigSize, Private,
Private+strlen(Private));
- if (SearchPath != NULL)
+ if (SearchPath)
SearchPath->insert(SearchPath->begin()+OrigSize, Private,
Private+strlen(Private));
- FE = FileMgr.getFile(FrameworkName.str(), /*openFile=*/!SuggestedModule);
+ FE = FileMgr.getFile(FrameworkName, /*openFile=*/!SuggestedModule);
}
// If we found the header and are allowed to suggest a module, do so now.
if (FE && SuggestedModule) {
// Find the framework in which this header occurs.
- StringRef FrameworkPath = FE->getName();
+ StringRef FrameworkPath = FE->getDir()->getName();
bool FoundFramework = false;
do {
- // Get the parent directory name.
- FrameworkPath = llvm::sys::path::parent_path(FrameworkPath);
- if (FrameworkPath.empty())
- break;
-
// Determine whether this directory exists.
const DirectoryEntry *Dir = FileMgr.getDirectory(FrameworkPath);
if (!Dir)
FoundFramework = true;
break;
}
+
+ // Get the parent directory name.
+ FrameworkPath = llvm::sys::path::parent_path(FrameworkPath);
+ if (FrameworkPath.empty())
+ break;
} while (true);
+ bool IsSystem = getDirCharacteristic() != SrcMgr::C_User;
if (FoundFramework) {
- // Find the top-level framework based on this framework.
- SmallVector<std::string, 4> SubmodulePath;
- const DirectoryEntry *TopFrameworkDir
- = ::getTopFrameworkDir(FileMgr, FrameworkPath, SubmodulePath);
-
- // Determine the name of the top-level framework.
- StringRef ModuleName = llvm::sys::path::stem(TopFrameworkDir->getName());
-
- // Load this framework module. If that succeeds, find the suggested module
- // for this header, if any.
- bool IsSystem = getDirCharacteristic() != SrcMgr::C_User;
- if (HS.loadFrameworkModule(ModuleName, TopFrameworkDir, IsSystem)) {
- *SuggestedModule = HS.findModuleForHeader(FE);
- }
+ if (!HS.findUsableModuleForFrameworkHeader(
+ FE, FrameworkPath, RequestingModule, SuggestedModule, IsSystem))
+ return nullptr;
} else {
- *SuggestedModule = HS.findModuleForHeader(FE);
+ if (!HS.findUsableModuleForHeader(FE, getDir(), RequestingModule,
+ SuggestedModule, IsSystem))
+ return nullptr;
}
}
return FE;
// Header File Location.
//===----------------------------------------------------------------------===//
+/// \brief Return true with a diagnostic if the file that MSVC would have found
+/// fails to match the one that Clang would have found with MSVC header search
+/// disabled.
+static bool checkMSVCHeaderSearch(DiagnosticsEngine &Diags,
+ const FileEntry *MSFE, const FileEntry *FE,
+ SourceLocation IncludeLoc) {
+ if (MSFE && FE != MSFE) {
+ Diags.Report(IncludeLoc, diag::ext_pp_include_search_ms) << MSFE->getName();
+ return true;
+ }
+ return false;
+}
+
+static const char *copyString(StringRef Str, llvm::BumpPtrAllocator &Alloc) {
+ assert(!Str.empty());
+ char *CopyStr = Alloc.Allocate<char>(Str.size()+1);
+ std::copy(Str.begin(), Str.end(), CopyStr);
+ CopyStr[Str.size()] = '\0';
+ return CopyStr;
+}
/// LookupFile - Given a "foo" or \<foo> reference, look up the indicated file,
/// return null on failure. isAngled indicates whether the file reference is
-/// for system \#include's or not (i.e. using <> instead of ""). CurFileEnt, if
-/// non-null, indicates where the \#including file is, in case a relative search
-/// is needed.
+/// 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(
- StringRef Filename,
- bool isAngled,
- const DirectoryLookup *FromDir,
- const DirectoryLookup *&CurDir,
- const FileEntry *CurFileEnt,
- SmallVectorImpl<char> *SearchPath,
- SmallVectorImpl<char> *RelativePath,
- Module **SuggestedModule,
- bool SkipCache)
-{
+ StringRef Filename, SourceLocation IncludeLoc, bool isAngled,
+ const DirectoryLookup *FromDir, const DirectoryLookup *&CurDir,
+ ArrayRef<std::pair<const FileEntry *, const DirectoryEntry *>> Includers,
+ SmallVectorImpl<char> *SearchPath, SmallVectorImpl<char> *RelativePath,
+ Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule,
+ bool SkipCache, bool BuildSystemModule) {
if (SuggestedModule)
- *SuggestedModule = 0;
+ *SuggestedModule = ModuleMap::KnownHeader();
// If 'Filename' is absolute, check to see if it exists and no searching.
if (llvm::sys::path::is_absolute(Filename)) {
- CurDir = 0;
+ CurDir = nullptr;
// If this was an #include_next "/absolute/file", fail.
- if (FromDir) return 0;
+ if (FromDir) return nullptr;
- if (SearchPath != NULL)
+ if (SearchPath)
SearchPath->clear();
- if (RelativePath != NULL) {
+ if (RelativePath) {
RelativePath->clear();
RelativePath->append(Filename.begin(), Filename.end());
}
// Otherwise, just return the file.
- return FileMgr.getFile(Filename, /*openFile=*/true);
+ return getFileAndSuggestModule(Filename, nullptr,
+ /*IsSystemHeaderDir*/false,
+ RequestingModule, SuggestedModule);
}
+ // This is the header that MSVC's header search would have found.
+ const FileEntry *MSFE = nullptr;
+ ModuleMap::KnownHeader MSSuggestedModule;
+
// Unless disabled, check to see if the file is in the #includer's
- // directory. This has to be based on CurFileEnt, not CurDir, because
- // CurFileEnt could be a #include of a subdirectory (#include "foo/bar.h") and
- // a subsequent include of "baz.h" should resolve to "whatever/foo/baz.h".
+ // directory. This cannot be based on CurDir, because each includer could be
+ // a #include of a subdirectory (#include "foo/bar.h") and a subsequent
+ // include of "baz.h" should resolve to "whatever/foo/baz.h".
// This search is not done for <> headers.
- if (CurFileEnt && !isAngled && !NoCurDirSearch) {
+ if (!Includers.empty() && !isAngled && !NoCurDirSearch) {
SmallString<1024> TmpDir;
- // Concatenate the requested file onto the directory.
- // FIXME: Portability. Filename concatenation should be in sys::Path.
- TmpDir += CurFileEnt->getDir()->getName();
- TmpDir.push_back('/');
- TmpDir.append(Filename.begin(), Filename.end());
- if (const FileEntry *FE = FileMgr.getFile(TmpDir.str(),/*openFile=*/true)) {
- // Leave CurDir unset.
- // This file is a system header or C++ unfriendly if the old file is.
+ bool First = true;
+ for (const auto &IncluderAndDir : Includers) {
+ const FileEntry *Includer = IncluderAndDir.first;
+
+ // Concatenate the requested file onto the directory.
+ // FIXME: Portability. Filename concatenation should be in sys::Path.
+ TmpDir = IncluderAndDir.second->getName();
+ TmpDir.push_back('/');
+ TmpDir.append(Filename.begin(), Filename.end());
+
+ // FIXME: We don't cache the result of getFileInfo across the call to
+ // getFileAndSuggestModule, because it's a reference to an element of
+ // a container that could be reallocated across this call.
//
- // Note that we only use one of FromHFI/ToHFI at once, due to potential
- // reallocation of the underlying vector potentially making the first
- // reference binding dangling.
- HeaderFileInfo &FromHFI = getFileInfo(CurFileEnt);
- unsigned DirInfo = FromHFI.DirInfo;
- bool IndexHeaderMapHeader = FromHFI.IndexHeaderMapHeader;
- StringRef Framework = FromHFI.Framework;
-
- HeaderFileInfo &ToHFI = getFileInfo(FE);
- ToHFI.DirInfo = DirInfo;
- ToHFI.IndexHeaderMapHeader = IndexHeaderMapHeader;
- ToHFI.Framework = Framework;
-
- if (SearchPath != NULL) {
- StringRef SearchPathRef(CurFileEnt->getDir()->getName());
- SearchPath->clear();
- SearchPath->append(SearchPathRef.begin(), SearchPathRef.end());
- }
- if (RelativePath != NULL) {
- RelativePath->clear();
- RelativePath->append(Filename.begin(), Filename.end());
+ // If we have no includer, that means we're processing a #include
+ // from a module build. We should treat this as a system header if we're
+ // building a [system] module.
+ bool IncluderIsSystemHeader =
+ Includer ? getFileInfo(Includer).DirInfo != SrcMgr::C_User :
+ BuildSystemModule;
+ if (const FileEntry *FE = getFileAndSuggestModule(
+ TmpDir, IncluderAndDir.second, IncluderIsSystemHeader,
+ RequestingModule, SuggestedModule)) {
+ if (!Includer) {
+ assert(First && "only first includer can have no file");
+ return FE;
+ }
+
+ // Leave CurDir unset.
+ // This file is a system header or C++ unfriendly if the old file is.
+ //
+ // Note that we only use one of FromHFI/ToHFI at once, due to potential
+ // reallocation of the underlying vector potentially making the first
+ // reference binding dangling.
+ HeaderFileInfo &FromHFI = getFileInfo(Includer);
+ unsigned DirInfo = FromHFI.DirInfo;
+ bool IndexHeaderMapHeader = FromHFI.IndexHeaderMapHeader;
+ StringRef Framework = FromHFI.Framework;
+
+ HeaderFileInfo &ToHFI = getFileInfo(FE);
+ ToHFI.DirInfo = DirInfo;
+ ToHFI.IndexHeaderMapHeader = IndexHeaderMapHeader;
+ ToHFI.Framework = Framework;
+
+ if (SearchPath) {
+ StringRef SearchPathRef(IncluderAndDir.second->getName());
+ SearchPath->clear();
+ SearchPath->append(SearchPathRef.begin(), SearchPathRef.end());
+ }
+ if (RelativePath) {
+ RelativePath->clear();
+ RelativePath->append(Filename.begin(), Filename.end());
+ }
+ if (First)
+ return FE;
+
+ // Otherwise, we found the path via MSVC header search rules. If
+ // -Wmsvc-include is enabled, we have to keep searching to see if we
+ // would've found this header in -I or -isystem directories.
+ if (Diags.isIgnored(diag::ext_pp_include_search_ms, IncludeLoc)) {
+ return FE;
+ } else {
+ MSFE = FE;
+ if (SuggestedModule) {
+ MSSuggestedModule = *SuggestedModule;
+ *SuggestedModule = ModuleMap::KnownHeader();
+ }
+ break;
+ }
}
- return FE;
+ First = false;
}
}
- CurDir = 0;
+ CurDir = nullptr;
// If this is a system #include, ignore the user #include locs.
unsigned i = isAngled ? AngledDirIdx : 0;
// multiply included, and the "pragma once" optimization prevents them from
// being relex/pp'd, but they would still have to search through a
// (potentially huge) series of SearchDirs to find it.
- std::pair<unsigned, unsigned> &CacheLookup =
- LookupFileCache.GetOrCreateValue(Filename).getValue();
+ LookupFileCacheInfo &CacheLookup = LookupFileCache[Filename];
// If the entry has been previously looked up, the first value will be
// non-zero. If the value is equal to i (the start point of our search), then
// this is a matching hit.
- if (!SkipCache && CacheLookup.first == i+1) {
+ if (!SkipCache && CacheLookup.StartIdx == i+1) {
// Skip querying potentially lots of directories for this lookup.
- i = CacheLookup.second;
+ i = CacheLookup.HitIdx;
+ if (CacheLookup.MappedName)
+ Filename = CacheLookup.MappedName;
} else {
// Otherwise, this is the first query, or the previous query didn't match
// our search start. We will fill in our found location below, so prime the
// start point value.
- CacheLookup.first = i+1;
+ CacheLookup.reset(/*StartIdx=*/i+1);
}
+ SmallString<64> MappedName;
+
// Check each directory in sequence to see if it contains this file.
for (; i != SearchDirs.size(); ++i) {
bool InUserSpecifiedSystemFramework = false;
- const FileEntry *FE =
- SearchDirs[i].LookupFile(Filename, *this, SearchPath, RelativePath,
- SuggestedModule, InUserSpecifiedSystemFramework);
+ bool HasBeenMapped = false;
+ const FileEntry *FE = SearchDirs[i].LookupFile(
+ Filename, *this, SearchPath, RelativePath, RequestingModule,
+ SuggestedModule, InUserSpecifiedSystemFramework, HasBeenMapped,
+ MappedName);
+ if (HasBeenMapped) {
+ CacheLookup.MappedName =
+ copyString(Filename, LookupFileCache.getAllocator());
+ }
if (!FE) continue;
CurDir = &SearchDirs[i];
SlashPos));
}
}
-
+
+ if (checkMSVCHeaderSearch(Diags, MSFE, FE, IncludeLoc)) {
+ if (SuggestedModule)
+ *SuggestedModule = MSSuggestedModule;
+ return MSFE;
+ }
+
// Remember this location for the next lookup we do.
- CacheLookup.second = i;
+ CacheLookup.HitIdx = i;
return FE;
}
// a header in a framework that is currently being built, and we couldn't
// resolve "foo.h" any other way, change the include to <Foo/foo.h>, where
// "Foo" is the name of the framework in which the including header was found.
- if (CurFileEnt && !isAngled && Filename.find('/') == StringRef::npos) {
- HeaderFileInfo &IncludingHFI = getFileInfo(CurFileEnt);
+ if (!Includers.empty() && Includers.front().first && !isAngled &&
+ Filename.find('/') == StringRef::npos) {
+ HeaderFileInfo &IncludingHFI = getFileInfo(Includers.front().first);
if (IncludingHFI.IndexHeaderMapHeader) {
SmallString<128> ScratchFilename;
ScratchFilename += IncludingHFI.Framework;
ScratchFilename += '/';
ScratchFilename += Filename;
-
- const FileEntry *Result = LookupFile(ScratchFilename, /*isAngled=*/true,
- FromDir, CurDir, CurFileEnt,
- SearchPath, RelativePath,
- SuggestedModule);
- std::pair<unsigned, unsigned> &CacheLookup
- = LookupFileCache.GetOrCreateValue(Filename).getValue();
- CacheLookup.second
- = LookupFileCache.GetOrCreateValue(ScratchFilename).getValue().second;
- return Result;
+
+ const FileEntry *FE =
+ LookupFile(ScratchFilename, IncludeLoc, /*isAngled=*/true, FromDir,
+ CurDir, Includers.front(), SearchPath, RelativePath,
+ RequestingModule, SuggestedModule);
+
+ if (checkMSVCHeaderSearch(Diags, MSFE, FE, IncludeLoc)) {
+ if (SuggestedModule)
+ *SuggestedModule = MSSuggestedModule;
+ return MSFE;
+ }
+
+ LookupFileCacheInfo &CacheLookup = LookupFileCache[Filename];
+ CacheLookup.HitIdx = LookupFileCache[ScratchFilename].HitIdx;
+ // FIXME: SuggestedModule.
+ return FE;
}
}
+ if (checkMSVCHeaderSearch(Diags, MSFE, nullptr, IncludeLoc)) {
+ if (SuggestedModule)
+ *SuggestedModule = MSSuggestedModule;
+ return MSFE;
+ }
+
// Otherwise, didn't find it. Remember we didn't find this.
- CacheLookup.second = SearchDirs.size();
- return 0;
+ CacheLookup.HitIdx = SearchDirs.size();
+ return nullptr;
}
/// LookupSubframeworkHeader - Look up a subframework for the specified
LookupSubframeworkHeader(StringRef Filename,
const FileEntry *ContextFileEnt,
SmallVectorImpl<char> *SearchPath,
- SmallVectorImpl<char> *RelativePath) {
+ 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 0;
+ if (SlashPos == StringRef::npos) return nullptr;
// Look up the base framework name of the ContextFileEnt.
const char *ContextName = ContextFileEnt->getName();
// If the context info wasn't a framework, couldn't be a subframework.
const unsigned DotFrameworkLen = 10;
const char *FrameworkPos = strstr(ContextName, ".framework");
- if (FrameworkPos == 0 ||
+ if (FrameworkPos == nullptr ||
(FrameworkPos[DotFrameworkLen] != '/' &&
FrameworkPos[DotFrameworkLen] != '\\'))
- return 0;
+ return nullptr;
SmallString<1024> FrameworkName(ContextName, FrameworkPos+DotFrameworkLen+1);
FrameworkName.append(Filename.begin(), Filename.begin()+SlashPos);
FrameworkName += ".framework/";
- llvm::StringMapEntry<FrameworkCacheEntry> &CacheLookup =
- FrameworkMap.GetOrCreateValue(Filename.substr(0, SlashPos));
+ auto &CacheLookup =
+ *FrameworkMap.insert(std::make_pair(Filename.substr(0, SlashPos),
+ FrameworkCacheEntry())).first;
// Some other location?
- if (CacheLookup.getValue().Directory &&
- CacheLookup.getKeyLength() == FrameworkName.size() &&
- memcmp(CacheLookup.getKeyData(), &FrameworkName[0],
- CacheLookup.getKeyLength()) != 0)
- return 0;
+ if (CacheLookup.second.Directory &&
+ CacheLookup.first().size() == FrameworkName.size() &&
+ memcmp(CacheLookup.first().data(), &FrameworkName[0],
+ CacheLookup.first().size()) != 0)
+ return nullptr;
// Cache subframework.
- if (CacheLookup.getValue().Directory == 0) {
+ if (!CacheLookup.second.Directory) {
++NumSubFrameworkLookups;
// If the framework dir doesn't exist, we fail.
- const DirectoryEntry *Dir = FileMgr.getDirectory(FrameworkName.str());
- if (Dir == 0) return 0;
+ const DirectoryEntry *Dir = FileMgr.getDirectory(FrameworkName);
+ if (!Dir) return nullptr;
// Otherwise, if it does, remember that this is the right direntry for this
// framework.
- CacheLookup.getValue().Directory = Dir;
+ CacheLookup.second.Directory = Dir;
}
- const FileEntry *FE = 0;
+ const FileEntry *FE = nullptr;
- if (RelativePath != NULL) {
+ if (RelativePath) {
RelativePath->clear();
RelativePath->append(Filename.begin()+SlashPos+1, Filename.end());
}
// Check ".../Frameworks/HIToolbox.framework/Headers/HIToolbox.h"
SmallString<1024> HeadersFilename(FrameworkName);
HeadersFilename += "Headers/";
- if (SearchPath != NULL) {
+ if (SearchPath) {
SearchPath->clear();
// Without trailing '/'.
SearchPath->append(HeadersFilename.begin(), HeadersFilename.end()-1);
}
HeadersFilename.append(Filename.begin()+SlashPos+1, Filename.end());
- if (!(FE = FileMgr.getFile(HeadersFilename.str(), /*openFile=*/true))) {
+ if (!(FE = FileMgr.getFile(HeadersFilename, /*openFile=*/true))) {
// Check ".../Frameworks/HIToolbox.framework/PrivateHeaders/HIToolbox.h"
HeadersFilename = FrameworkName;
HeadersFilename += "PrivateHeaders/";
- if (SearchPath != NULL) {
+ if (SearchPath) {
SearchPath->clear();
// Without trailing '/'.
SearchPath->append(HeadersFilename.begin(), HeadersFilename.end()-1);
}
HeadersFilename.append(Filename.begin()+SlashPos+1, Filename.end());
- if (!(FE = FileMgr.getFile(HeadersFilename.str(), /*openFile=*/true)))
- return 0;
+ if (!(FE = FileMgr.getFile(HeadersFilename, /*openFile=*/true)))
+ return nullptr;
}
// This file is a system header or C++ unfriendly if the old file is.
// of evaluation.
unsigned DirInfo = getFileInfo(ContextFileEnt).DirInfo;
getFileInfo(FE).DirInfo = DirInfo;
- return FE;
-}
-/// \brief Helper static function to normalize a path for injection into
-/// a synthetic header.
-/*static*/ std::string
-HeaderSearch::NormalizeDashIncludePath(StringRef File, FileManager &FileMgr) {
- // Implicit include paths should be resolved relative to the current
- // working directory first, and then use the regular header search
- // mechanism. The proper way to handle this is to have the
- // predefines buffer located at the current working directory, but
- // it has no file entry. For now, workaround this by using an
- // absolute path if we find the file here, and otherwise letting
- // header search handle it.
- SmallString<128> Path(File);
- llvm::sys::fs::make_absolute(Path);
- bool exists;
- if (llvm::sys::fs::exists(Path.str(), exists) || !exists)
- Path = File;
- else if (exists)
- FileMgr.getFile(File);
-
- return Lexer::Stringify(Path.str());
+ FrameworkName.pop_back(); // remove the trailing '/'
+ if (!findUsableModuleForFrameworkHeader(FE, FrameworkName, RequestingModule,
+ SuggestedModule, /*IsSystem*/ false))
+ return nullptr;
+
+ return FE;
}
//===----------------------------------------------------------------------===//
/// header file info (\p HFI)
static void mergeHeaderFileInfo(HeaderFileInfo &HFI,
const HeaderFileInfo &OtherHFI) {
+ assert(OtherHFI.External && "expected to merge external HFI");
+
HFI.isImport |= OtherHFI.isImport;
HFI.isPragmaOnce |= OtherHFI.isPragmaOnce;
+ HFI.isModuleHeader |= OtherHFI.isModuleHeader;
HFI.NumIncludes += OtherHFI.NumIncludes;
-
+
if (!HFI.ControllingMacro && !HFI.ControllingMacroID) {
HFI.ControllingMacro = OtherHFI.ControllingMacro;
HFI.ControllingMacroID = OtherHFI.ControllingMacroID;
}
-
- if (OtherHFI.External) {
- HFI.DirInfo = OtherHFI.DirInfo;
- HFI.External = OtherHFI.External;
- HFI.IndexHeaderMapHeader = OtherHFI.IndexHeaderMapHeader;
- }
+
+ HFI.DirInfo = OtherHFI.DirInfo;
+ HFI.External = (!HFI.IsValid || HFI.External);
+ HFI.IsValid = true;
+ HFI.IndexHeaderMapHeader = OtherHFI.IndexHeaderMapHeader;
if (HFI.Framework.empty())
HFI.Framework = OtherHFI.Framework;
-
- HFI.Resolved = true;
}
/// getFileInfo - Return the HeaderFileInfo structure for the specified
/// FileEntry.
HeaderFileInfo &HeaderSearch::getFileInfo(const FileEntry *FE) {
if (FE->getUID() >= FileInfo.size())
- FileInfo.resize(FE->getUID()+1);
-
- HeaderFileInfo &HFI = FileInfo[FE->getUID()];
- if (ExternalSource && !HFI.Resolved)
- mergeHeaderFileInfo(HFI, ExternalSource->GetHeaderFileInfo(FE));
+ FileInfo.resize(FE->getUID() + 1);
+
+ HeaderFileInfo *HFI = &FileInfo[FE->getUID()];
+ // FIXME: Use a generation count to check whether this is really up to date.
+ if (ExternalSource && !HFI->Resolved) {
+ HFI->Resolved = true;
+ auto ExternalHFI = ExternalSource->GetHeaderFileInfo(FE);
+
+ HFI = &FileInfo[FE->getUID()];
+ if (ExternalHFI.External)
+ mergeHeaderFileInfo(*HFI, ExternalHFI);
+ }
+
+ HFI->IsValid = true;
+ // We have local information about this header file, so it's no longer
+ // strictly external.
+ HFI->External = false;
+ return *HFI;
+}
+
+const HeaderFileInfo *
+HeaderSearch::getExistingFileInfo(const FileEntry *FE,
+ bool WantExternal) const {
+ // If we have an external source, ensure we have the latest information.
+ // FIXME: Use a generation count to check whether this is really up to date.
+ HeaderFileInfo *HFI;
+ if (ExternalSource) {
+ if (FE->getUID() >= FileInfo.size()) {
+ if (!WantExternal)
+ return nullptr;
+ FileInfo.resize(FE->getUID() + 1);
+ }
+
+ HFI = &FileInfo[FE->getUID()];
+ if (!WantExternal && (!HFI->IsValid || HFI->External))
+ return nullptr;
+ if (!HFI->Resolved) {
+ HFI->Resolved = true;
+ auto ExternalHFI = ExternalSource->GetHeaderFileInfo(FE);
+
+ HFI = &FileInfo[FE->getUID()];
+ if (ExternalHFI.External)
+ mergeHeaderFileInfo(*HFI, ExternalHFI);
+ }
+ } else if (FE->getUID() >= FileInfo.size()) {
+ return nullptr;
+ } else {
+ HFI = &FileInfo[FE->getUID()];
+ }
+
+ if (!HFI->IsValid || (HFI->External && !WantExternal))
+ return nullptr;
+
return HFI;
}
bool HeaderSearch::isFileMultipleIncludeGuarded(const FileEntry *File) {
// Check if we've ever seen this file as a header.
- if (File->getUID() >= FileInfo.size())
- return false;
-
- // Resolve header file info from the external source, if needed.
- HeaderFileInfo &HFI = FileInfo[File->getUID()];
- if (ExternalSource && !HFI.Resolved)
- mergeHeaderFileInfo(HFI, ExternalSource->GetHeaderFileInfo(File));
-
- return HFI.isPragmaOnce || HFI.isImport ||
- HFI.ControllingMacro || HFI.ControllingMacroID;
+ if (auto *HFI = getExistingFileInfo(File))
+ return HFI->isPragmaOnce || HFI->isImport || HFI->ControllingMacro ||
+ HFI->ControllingMacroID;
+ return false;
}
-void HeaderSearch::setHeaderFileInfoForUID(HeaderFileInfo HFI, unsigned UID) {
- if (UID >= FileInfo.size())
- FileInfo.resize(UID+1);
- HFI.Resolved = true;
- FileInfo[UID] = HFI;
+void HeaderSearch::MarkFileModuleHeader(const FileEntry *FE,
+ ModuleMap::ModuleHeaderRole Role,
+ bool isCompilingModuleHeader) {
+ bool isModularHeader = !(Role & ModuleMap::TextualHeader);
+
+ // Don't mark the file info as non-external if there's nothing to change.
+ if (!isCompilingModuleHeader) {
+ if (!isModularHeader)
+ return;
+ auto *HFI = getExistingFileInfo(FE);
+ if (HFI && HFI->isModuleHeader)
+ return;
+ }
+
+ auto &HFI = getFileInfo(FE);
+ HFI.isModuleHeader |= isModularHeader;
+ HFI.isCompilingModuleHeader |= isCompilingModuleHeader;
}
-bool HeaderSearch::ShouldEnterIncludeFile(const FileEntry *File, bool isImport){
+bool HeaderSearch::ShouldEnterIncludeFile(Preprocessor &PP,
+ const FileEntry *File,
+ bool isImport, Module *M) {
++NumIncluded; // Count # of attempted #includes.
// Get information about this file.
// Next, check to see if the file is wrapped with #ifndef guards. If so, and
// if the macro that guards it is defined, we know the #include has no effect.
if (const IdentifierInfo *ControllingMacro
- = FileInfo.getControllingMacro(ExternalLookup))
- if (ControllingMacro->hasMacroDefinition()) {
+ = FileInfo.getControllingMacro(ExternalLookup)) {
+ // If the header corresponds to a module, check whether the macro is already
+ // defined in that module rather than checking in the current set of visible
+ // modules.
+ if (M ? PP.isMacroDefinedInLocalModule(ControllingMacro, M)
+ : PP.isMacroDefined(ControllingMacro)) {
++NumMultiIncludeFileOptzn;
return false;
}
+ }
// Increment the number of times this file has been included.
++FileInfo.NumIncludes;
}
StringRef HeaderSearch::getUniqueFrameworkName(StringRef Framework) {
- return FrameworkNames.GetOrCreateValue(Framework).getKey();
+ return FrameworkNames.insert(Framework).first->first();
}
bool HeaderSearch::hasModuleMap(StringRef FileName,
- const DirectoryEntry *Root) {
+ const DirectoryEntry *Root,
+ bool IsSystem) {
+ if (!HSOpts->ImplicitModuleMaps)
+ return false;
+
SmallVector<const DirectoryEntry *, 2> FixUpDirectories;
StringRef DirName = FileName;
DirName = llvm::sys::path::parent_path(DirName);
if (DirName.empty())
return false;
-
+
// Determine whether this directory exists.
const DirectoryEntry *Dir = FileMgr.getDirectory(DirName);
if (!Dir)
return false;
-
+
// Try to load the module map file in this directory.
- switch (loadModuleMapFile(Dir)) {
+ switch (loadModuleMapFile(Dir, IsSystem,
+ llvm::sys::path::extension(Dir->getName()) ==
+ ".framework")) {
case LMM_NewlyLoaded:
case LMM_AlreadyLoaded:
// Success. All of the directories we stepped through inherit this module
// map file.
for (unsigned I = 0, N = FixUpDirectories.size(); I != N; ++I)
DirectoryHasModuleMap[FixUpDirectories[I]] = true;
-
return true;
case LMM_NoDirectory:
} while (true);
}
-Module *HeaderSearch::findModuleForHeader(const FileEntry *File) {
- if (Module *Mod = ModMap.findModuleForHeader(File))
- return Mod;
-
- return 0;
+ModuleMap::KnownHeader
+HeaderSearch::findModuleForHeader(const FileEntry *File) const {
+ if (ExternalSource) {
+ // Make sure the external source has handled header info about this file,
+ // which includes whether the file is part of a module.
+ (void)getExistingFileInfo(File);
+ }
+ return ModMap.findModuleForHeader(File);
}
-bool HeaderSearch::loadModuleMapFile(const FileEntry *File) {
- const DirectoryEntry *Dir = File->getDir();
-
- llvm::DenseMap<const DirectoryEntry *, bool>::iterator KnownDir
- = DirectoryHasModuleMap.find(Dir);
- if (KnownDir != DirectoryHasModuleMap.end())
- return !KnownDir->second;
-
- bool Result = ModMap.parseModuleMapFile(File);
- if (!Result && llvm::sys::path::filename(File->getName()) == "module.map") {
- // If the file we loaded was a module.map, look for the corresponding
- // module_private.map.
- SmallString<128> PrivateFilename(Dir->getName());
+bool HeaderSearch::findUsableModuleForHeader(
+ const FileEntry *File, const DirectoryEntry *Root, Module *RequestingModule,
+ ModuleMap::KnownHeader *SuggestedModule, bool IsSystemHeaderDir) {
+ if (File && SuggestedModule) {
+ // If there is a module that corresponds to this header, suggest it.
+ hasModuleMap(File->getName(), Root, IsSystemHeaderDir);
+ *SuggestedModule = findModuleForHeader(File);
+ }
+ return true;
+}
+
+bool HeaderSearch::findUsableModuleForFrameworkHeader(
+ const FileEntry *File, StringRef FrameworkName, Module *RequestingModule,
+ ModuleMap::KnownHeader *SuggestedModule, bool IsSystemFramework) {
+ // If we're supposed to suggest a module, look for one now.
+ if (SuggestedModule) {
+ // Find the top-level framework based on this framework.
+ SmallVector<std::string, 4> SubmodulePath;
+ const DirectoryEntry *TopFrameworkDir
+ = ::getTopFrameworkDir(FileMgr, FrameworkName, SubmodulePath);
+
+ // Determine the name of the top-level framework.
+ StringRef ModuleName = llvm::sys::path::stem(TopFrameworkDir->getName());
+
+ // Load this framework module. If that succeeds, find the suggested module
+ // for this header, if any.
+ loadFrameworkModule(ModuleName, TopFrameworkDir, IsSystemFramework);
+
+ // FIXME: This can find a module not part of ModuleName, which is
+ // important so that we're consistent about whether this header
+ // corresponds to a module. Possibly we should lock down framework modules
+ // so that this is not possible.
+ *SuggestedModule = findModuleForHeader(File);
+ }
+ return true;
+}
+
+static const FileEntry *getPrivateModuleMap(const FileEntry *File,
+ FileManager &FileMgr) {
+ StringRef Filename = llvm::sys::path::filename(File->getName());
+ SmallString<128> PrivateFilename(File->getDir()->getName());
+ if (Filename == "module.map")
llvm::sys::path::append(PrivateFilename, "module_private.map");
- if (const FileEntry *PrivateFile = FileMgr.getFile(PrivateFilename))
- Result = ModMap.parseModuleMapFile(PrivateFile);
+ else if (Filename == "module.modulemap")
+ llvm::sys::path::append(PrivateFilename, "module.private.modulemap");
+ else
+ return nullptr;
+ return FileMgr.getFile(PrivateFilename);
+}
+
+bool HeaderSearch::loadModuleMapFile(const FileEntry *File, bool IsSystem) {
+ // Find the directory for the module. For frameworks, that may require going
+ // up from the 'Modules' directory.
+ const DirectoryEntry *Dir = nullptr;
+ if (getHeaderSearchOpts().ModuleMapFileHomeIsCwd)
+ Dir = FileMgr.getDirectory(".");
+ else {
+ Dir = File->getDir();
+ StringRef DirName(Dir->getName());
+ if (llvm::sys::path::filename(DirName) == "Modules") {
+ DirName = llvm::sys::path::parent_path(DirName);
+ if (DirName.endswith(".framework"))
+ Dir = FileMgr.getDirectory(DirName);
+ // FIXME: This assert can fail if there's a race between the above check
+ // and the removal of the directory.
+ assert(Dir && "parent must exist");
+ }
}
-
- DirectoryHasModuleMap[Dir] = !Result;
- return Result;
+
+ switch (loadModuleMapFileImpl(File, IsSystem, Dir)) {
+ case LMM_AlreadyLoaded:
+ case LMM_NewlyLoaded:
+ return false;
+ case LMM_NoDirectory:
+ case LMM_InvalidModuleMap:
+ return true;
+ }
+ llvm_unreachable("Unknown load module map result");
}
-Module *HeaderSearch::loadFrameworkModule(StringRef Name,
+HeaderSearch::LoadModuleMapResult
+HeaderSearch::loadModuleMapFileImpl(const FileEntry *File, bool IsSystem,
+ const DirectoryEntry *Dir) {
+ assert(File && "expected FileEntry");
+
+ // Check whether we've already loaded this module map, and mark it as being
+ // loaded in case we recursively try to load it from itself.
+ auto AddResult = LoadedModuleMaps.insert(std::make_pair(File, true));
+ if (!AddResult.second)
+ return AddResult.first->second ? LMM_AlreadyLoaded : LMM_InvalidModuleMap;
+
+ if (ModMap.parseModuleMapFile(File, IsSystem, Dir)) {
+ LoadedModuleMaps[File] = false;
+ return LMM_InvalidModuleMap;
+ }
+
+ // Try to load a corresponding private module map.
+ if (const FileEntry *PMMFile = getPrivateModuleMap(File, FileMgr)) {
+ if (ModMap.parseModuleMapFile(PMMFile, IsSystem, Dir)) {
+ LoadedModuleMaps[File] = false;
+ return LMM_InvalidModuleMap;
+ }
+ }
+
+ // This directory has a module map.
+ return LMM_NewlyLoaded;
+}
+
+const FileEntry *
+HeaderSearch::lookupModuleMapFile(const DirectoryEntry *Dir, bool IsFramework) {
+ if (!HSOpts->ImplicitModuleMaps)
+ return nullptr;
+ // For frameworks, the preferred spelling is Modules/module.modulemap, but
+ // module.map at the framework root is also accepted.
+ SmallString<128> ModuleMapFileName(Dir->getName());
+ if (IsFramework)
+ llvm::sys::path::append(ModuleMapFileName, "Modules");
+ llvm::sys::path::append(ModuleMapFileName, "module.modulemap");
+ if (const FileEntry *F = FileMgr.getFile(ModuleMapFileName))
+ return F;
+
+ // Continue to allow module.map
+ ModuleMapFileName = Dir->getName();
+ llvm::sys::path::append(ModuleMapFileName, "module.map");
+ return FileMgr.getFile(ModuleMapFileName);
+}
+
+Module *HeaderSearch::loadFrameworkModule(StringRef Name,
const DirectoryEntry *Dir,
bool IsSystem) {
if (Module *Module = ModMap.findModule(Name))
return Module;
-
+
// Try to load a module map file.
- switch (loadModuleMapFile(Dir)) {
+ switch (loadModuleMapFile(Dir, IsSystem, /*IsFramework*/true)) {
case LMM_InvalidModuleMap:
+ // Try to infer a module map from the framework directory.
+ if (HSOpts->ImplicitModuleMaps)
+ ModMap.inferFrameworkModule(Dir, IsSystem, /*Parent=*/nullptr);
break;
-
+
case LMM_AlreadyLoaded:
case LMM_NoDirectory:
- return 0;
-
+ return nullptr;
+
case LMM_NewlyLoaded:
- return ModMap.findModule(Name);
+ break;
}
- // Figure out the top-level framework directory and the submodule path from
- // that top-level framework to the requested framework.
- SmallVector<std::string, 2> SubmodulePath;
- SubmodulePath.push_back(Name);
- const DirectoryEntry *TopFrameworkDir
- = ::getTopFrameworkDir(FileMgr, Dir->getName(), SubmodulePath);
-
-
- // Try to infer a module map from the top-level framework directory.
- Module *Result = ModMap.inferFrameworkModule(SubmodulePath.back(),
- TopFrameworkDir,
- IsSystem,
- /*Parent=*/0);
- if (!Result)
- return 0;
-
- // Follow the submodule path to find the requested (sub)framework module
- // within the top-level framework module.
- SubmodulePath.pop_back();
- while (!SubmodulePath.empty() && Result) {
- Result = ModMap.lookupModuleQualified(SubmodulePath.back(), Result);
- SubmodulePath.pop_back();
- }
- return Result;
+ return ModMap.findModule(Name);
}
HeaderSearch::LoadModuleMapResult
-HeaderSearch::loadModuleMapFile(StringRef DirName) {
+HeaderSearch::loadModuleMapFile(StringRef DirName, bool IsSystem,
+ bool IsFramework) {
if (const DirectoryEntry *Dir = FileMgr.getDirectory(DirName))
- return loadModuleMapFile(Dir);
+ return loadModuleMapFile(Dir, IsSystem, IsFramework);
return LMM_NoDirectory;
}
HeaderSearch::LoadModuleMapResult
-HeaderSearch::loadModuleMapFile(const DirectoryEntry *Dir) {
- llvm::DenseMap<const DirectoryEntry *, bool>::iterator KnownDir
- = DirectoryHasModuleMap.find(Dir);
+HeaderSearch::loadModuleMapFile(const DirectoryEntry *Dir, bool IsSystem,
+ bool IsFramework) {
+ auto KnownDir = DirectoryHasModuleMap.find(Dir);
if (KnownDir != DirectoryHasModuleMap.end())
- return KnownDir->second? LMM_AlreadyLoaded : LMM_InvalidModuleMap;
-
- SmallString<128> ModuleMapFileName;
- ModuleMapFileName += Dir->getName();
- unsigned ModuleMapDirNameLen = ModuleMapFileName.size();
- llvm::sys::path::append(ModuleMapFileName, "module.map");
- if (const FileEntry *ModuleMapFile = FileMgr.getFile(ModuleMapFileName)) {
- // We have found a module map file. Try to parse it.
- if (ModMap.parseModuleMapFile(ModuleMapFile)) {
- // No suitable module map.
+ return KnownDir->second ? LMM_AlreadyLoaded : LMM_InvalidModuleMap;
+
+ if (const FileEntry *ModuleMapFile = lookupModuleMapFile(Dir, IsFramework)) {
+ LoadModuleMapResult Result =
+ loadModuleMapFileImpl(ModuleMapFile, IsSystem, Dir);
+ // Add Dir explicitly in case ModuleMapFile is in a subdirectory.
+ // E.g. Foo.framework/Modules/module.modulemap
+ // ^Dir ^ModuleMapFile
+ if (Result == LMM_NewlyLoaded)
+ DirectoryHasModuleMap[Dir] = true;
+ else if (Result == LMM_InvalidModuleMap)
DirectoryHasModuleMap[Dir] = false;
- return LMM_InvalidModuleMap;
- }
-
- // This directory has a module map.
- DirectoryHasModuleMap[Dir] = true;
-
- // Check whether there is a private module map that we need to load as well.
- ModuleMapFileName.erase(ModuleMapFileName.begin() + ModuleMapDirNameLen,
- ModuleMapFileName.end());
- llvm::sys::path::append(ModuleMapFileName, "module_private.map");
- if (const FileEntry *PrivateModuleMapFile
- = FileMgr.getFile(ModuleMapFileName)) {
- if (ModMap.parseModuleMapFile(PrivateModuleMapFile)) {
- // No suitable module map.
- DirectoryHasModuleMap[Dir] = false;
- return LMM_InvalidModuleMap;
- }
- }
-
- return LMM_NewlyLoaded;
+ return Result;
}
-
- // No suitable module map.
- DirectoryHasModuleMap[Dir] = false;
return LMM_InvalidModuleMap;
}
void HeaderSearch::collectAllModules(SmallVectorImpl<Module *> &Modules) {
Modules.clear();
-
- // Load module maps for each of the header search directories.
- for (unsigned Idx = 0, N = SearchDirs.size(); Idx != N; ++Idx) {
- if (SearchDirs[Idx].isFramework()) {
- llvm::error_code EC;
- SmallString<128> DirNative;
- llvm::sys::path::native(SearchDirs[Idx].getFrameworkDir()->getName(),
- DirNative);
-
- // Search each of the ".framework" directories to load them as modules.
- bool IsSystem = SearchDirs[Idx].getDirCharacteristic() != SrcMgr::C_User;
- for (llvm::sys::fs::directory_iterator Dir(DirNative.str(), EC), DirEnd;
- Dir != DirEnd && !EC; Dir.increment(EC)) {
- if (llvm::sys::path::extension(Dir->path()) != ".framework")
- continue;
-
- const DirectoryEntry *FrameworkDir = FileMgr.getDirectory(Dir->path());
- if (!FrameworkDir)
- continue;
-
- // Load this framework module.
- loadFrameworkModule(llvm::sys::path::stem(Dir->path()), FrameworkDir,
- IsSystem);
+
+ if (HSOpts->ImplicitModuleMaps) {
+ // Load module maps for each of the header search directories.
+ for (unsigned Idx = 0, N = SearchDirs.size(); Idx != N; ++Idx) {
+ bool IsSystem = SearchDirs[Idx].isSystemHeaderDirectory();
+ if (SearchDirs[Idx].isFramework()) {
+ std::error_code EC;
+ SmallString<128> DirNative;
+ llvm::sys::path::native(SearchDirs[Idx].getFrameworkDir()->getName(),
+ DirNative);
+
+ // Search each of the ".framework" directories to load them as modules.
+ vfs::FileSystem &FS = *FileMgr.getVirtualFileSystem();
+ for (vfs::directory_iterator Dir = FS.dir_begin(DirNative, EC), DirEnd;
+ Dir != DirEnd && !EC; Dir.increment(EC)) {
+ if (llvm::sys::path::extension(Dir->getName()) != ".framework")
+ continue;
+
+ const DirectoryEntry *FrameworkDir =
+ FileMgr.getDirectory(Dir->getName());
+ if (!FrameworkDir)
+ continue;
+
+ // Load this framework module.
+ loadFrameworkModule(llvm::sys::path::stem(Dir->getName()),
+ FrameworkDir, IsSystem);
+ }
+ continue;
}
- continue;
- }
-
- // FIXME: Deal with header maps.
- if (SearchDirs[Idx].isHeaderMap())
- continue;
-
- // Try to load a module map file for the search directory.
- loadModuleMapFile(SearchDirs[Idx].getDir());
-
- // Try to load module map files for immediate subdirectories of this search
- // directory.
- llvm::error_code EC;
- SmallString<128> DirNative;
- llvm::sys::path::native(SearchDirs[Idx].getDir()->getName(), DirNative);
- for (llvm::sys::fs::directory_iterator Dir(DirNative.str(), EC), DirEnd;
- Dir != DirEnd && !EC; Dir.increment(EC)) {
- loadModuleMapFile(Dir->path());
+
+ // FIXME: Deal with header maps.
+ if (SearchDirs[Idx].isHeaderMap())
+ continue;
+
+ // Try to load a module map file for the search directory.
+ loadModuleMapFile(SearchDirs[Idx].getDir(), IsSystem,
+ /*IsFramework*/ false);
+
+ // Try to load module map files for immediate subdirectories of this
+ // search directory.
+ loadSubdirectoryModuleMaps(SearchDirs[Idx]);
}
}
-
+
// Populate the list of modules.
for (ModuleMap::module_iterator M = ModMap.module_begin(),
MEnd = ModMap.module_end();
Modules.push_back(M->getValue());
}
}
+
+void HeaderSearch::loadTopLevelSystemModules() {
+ if (!HSOpts->ImplicitModuleMaps)
+ return;
+
+ // Load module maps for each of the header search directories.
+ for (unsigned Idx = 0, N = SearchDirs.size(); Idx != N; ++Idx) {
+ // We only care about normal header directories.
+ if (!SearchDirs[Idx].isNormalDir()) {
+ continue;
+ }
+
+ // Try to load a module map file for the search directory.
+ loadModuleMapFile(SearchDirs[Idx].getDir(),
+ SearchDirs[Idx].isSystemHeaderDirectory(),
+ SearchDirs[Idx].isFramework());
+ }
+}
+
+void HeaderSearch::loadSubdirectoryModuleMaps(DirectoryLookup &SearchDir) {
+ assert(HSOpts->ImplicitModuleMaps &&
+ "Should not be loading subdirectory module maps");
+
+ if (SearchDir.haveSearchedAllModuleMaps())
+ return;
+
+ std::error_code EC;
+ SmallString<128> DirNative;
+ llvm::sys::path::native(SearchDir.getDir()->getName(), DirNative);
+ vfs::FileSystem &FS = *FileMgr.getVirtualFileSystem();
+ for (vfs::directory_iterator Dir = FS.dir_begin(DirNative, EC), DirEnd;
+ Dir != DirEnd && !EC; Dir.increment(EC)) {
+ bool IsFramework =
+ llvm::sys::path::extension(Dir->getName()) == ".framework";
+ if (IsFramework == SearchDir.isFramework())
+ loadModuleMapFile(Dir->getName(), SearchDir.isSystemHeaderDirectory(),
+ SearchDir.isFramework());
+ }
+
+ SearchDir.setSearchedAllModuleMaps(true);
+}
+
+std::string HeaderSearch::suggestPathToFileForDiagnostics(const FileEntry *File,
+ bool *IsSystem) {
+ // FIXME: We assume that the path name currently cached in the FileEntry is
+ // the most appropriate one for this analysis (and that it's spelled the same
+ // way as the corresponding header search path).
+ const char *Name = File->getName();
+
+ unsigned BestPrefixLength = 0;
+ unsigned BestSearchDir;
+
+ for (unsigned I = 0; I != SearchDirs.size(); ++I) {
+ // FIXME: Support this search within frameworks and header maps.
+ if (!SearchDirs[I].isNormalDir())
+ continue;
+
+ const char *Dir = SearchDirs[I].getDir()->getName();
+ for (auto NI = llvm::sys::path::begin(Name),
+ NE = llvm::sys::path::end(Name),
+ DI = llvm::sys::path::begin(Dir),
+ DE = llvm::sys::path::end(Dir);
+ /*termination condition in loop*/; ++NI, ++DI) {
+ // '.' components in Name are ignored.
+ while (NI != NE && *NI == ".")
+ ++NI;
+ if (NI == NE)
+ break;
+
+ // '.' components in Dir are ignored.
+ while (DI != DE && *DI == ".")
+ ++DI;
+ if (DI == DE) {
+ // Dir is a prefix of Name, up to '.' components and choice of path
+ // separators.
+ unsigned PrefixLength = NI - llvm::sys::path::begin(Name);
+ if (PrefixLength > BestPrefixLength) {
+ BestPrefixLength = PrefixLength;
+ BestSearchDir = I;
+ }
+ break;
+ }
+
+ if (*NI != *DI)
+ break;
+ }
+ }
+
+ if (IsSystem)
+ *IsSystem = BestPrefixLength ? BestSearchDir >= SystemDirIdx : false;
+ return Name + BestPrefixLength;
+}