[clang][lex] Remark on search path usage
For dependency scanning, it would be useful to collect header search paths (provided on command-line via `-I` and friends) that were actually used during preprocessing. This patch adds that feature to `HeaderSearch` along with a new remark that reports such paths as they get used. Previous version of this patch tried to use the existing `LookupFileCache` to report used paths via `HitIdx`. That doesn't work for `ComputeUserEntryUsage` (which is intended to be called *after* preprocessing), because it indexes used search paths by the file name. This means the values get overwritten when the code contains `#include_next`. Note that `HeaderSearch` doesn't use `HeaderSearchOptions::UserEntries` directly. Instead, `InitHeaderSearch` pre-processes them (adds platform-specific paths, removes duplicates, removes paths that don't exist) and creates `DirectoryLookup` instances. This means we need a mechanism for translating between those two. It's not possible to go from `DirectoryLookup` back to the original `HeaderSearch`, so `InitHeaderSearch` now tracks the relationships explicitly. Depends on D111557. Reviewed By: dexonsmith Differential Revision: https://reviews.llvm.org/D102923
This commit is contained in:
parent
f4c1258d56
commit
4445135109
|
@ -751,6 +751,7 @@ def UnusedLocalTypedef : DiagGroup<"unused-local-typedef">;
|
||||||
def UnusedPropertyIvar : DiagGroup<"unused-property-ivar">;
|
def UnusedPropertyIvar : DiagGroup<"unused-property-ivar">;
|
||||||
def UnusedGetterReturnValue : DiagGroup<"unused-getter-return-value">;
|
def UnusedGetterReturnValue : DiagGroup<"unused-getter-return-value">;
|
||||||
def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">;
|
def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">;
|
||||||
|
def UsedSearchPath : DiagGroup<"search-path-usage">;
|
||||||
def UserDefinedLiterals : DiagGroup<"user-defined-literals">;
|
def UserDefinedLiterals : DiagGroup<"user-defined-literals">;
|
||||||
def UserDefinedWarnings : DiagGroup<"user-defined-warnings">;
|
def UserDefinedWarnings : DiagGroup<"user-defined-warnings">;
|
||||||
def ReorderCtor : DiagGroup<"reorder-ctor">;
|
def ReorderCtor : DiagGroup<"reorder-ctor">;
|
||||||
|
|
|
@ -446,6 +446,9 @@ def warn_pp_hdrstop_filename_ignored : Warning<
|
||||||
"#pragma hdrstop filename not supported, "
|
"#pragma hdrstop filename not supported, "
|
||||||
"/Fp can be used to specify precompiled header filename">,
|
"/Fp can be used to specify precompiled header filename">,
|
||||||
InGroup<ClangClPch>;
|
InGroup<ClangClPch>;
|
||||||
|
def remark_pp_search_path_usage : Remark<
|
||||||
|
"search path used: '%0'">,
|
||||||
|
InGroup<UsedSearchPath>;
|
||||||
def err_pp_file_not_found_angled_include_not_fatal : Error<
|
def err_pp_file_not_found_angled_include_not_fatal : Error<
|
||||||
"'%0' file not found with <angled> %select{include|import}1; "
|
"'%0' file not found with <angled> %select{include|import}1; "
|
||||||
"use \"quotes\" instead">;
|
"use \"quotes\" instead">;
|
||||||
|
|
|
@ -77,13 +77,6 @@ public:
|
||||||
static std::unique_ptr<HeaderMap> Create(const FileEntry *FE,
|
static std::unique_ptr<HeaderMap> Create(const FileEntry *FE,
|
||||||
FileManager &FM);
|
FileManager &FM);
|
||||||
|
|
||||||
/// Check to see if the specified relative filename is located in this
|
|
||||||
/// HeaderMap. If so, open it and return its FileEntry. If RawPath is not
|
|
||||||
/// 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".
|
|
||||||
Optional<FileEntryRef> LookupFile(StringRef Filename, FileManager &FM) const;
|
|
||||||
|
|
||||||
using HeaderMapImpl::dump;
|
using HeaderMapImpl::dump;
|
||||||
using HeaderMapImpl::getFileName;
|
using HeaderMapImpl::getFileName;
|
||||||
using HeaderMapImpl::lookupFilename;
|
using HeaderMapImpl::lookupFilename;
|
||||||
|
|
|
@ -172,6 +172,9 @@ class HeaderSearch {
|
||||||
/// Header-search options used to initialize this header search.
|
/// Header-search options used to initialize this header search.
|
||||||
std::shared_ptr<HeaderSearchOptions> HSOpts;
|
std::shared_ptr<HeaderSearchOptions> HSOpts;
|
||||||
|
|
||||||
|
/// Mapping from SearchDir to HeaderSearchOptions::UserEntries indices.
|
||||||
|
llvm::DenseMap<unsigned, unsigned> SearchDirToHSEntry;
|
||||||
|
|
||||||
DiagnosticsEngine &Diags;
|
DiagnosticsEngine &Diags;
|
||||||
FileManager &FileMgr;
|
FileManager &FileMgr;
|
||||||
|
|
||||||
|
@ -182,6 +185,9 @@ class HeaderSearch {
|
||||||
/// NoCurDirSearch is true, then the check for the file in the current
|
/// NoCurDirSearch is true, then the check for the file in the current
|
||||||
/// directory is suppressed.
|
/// directory is suppressed.
|
||||||
std::vector<DirectoryLookup> SearchDirs;
|
std::vector<DirectoryLookup> SearchDirs;
|
||||||
|
/// Whether the DirectoryLookup at the corresponding index in SearchDirs has
|
||||||
|
/// been successfully used to lookup a file.
|
||||||
|
std::vector<bool> SearchDirsUsage;
|
||||||
unsigned AngledDirIdx = 0;
|
unsigned AngledDirIdx = 0;
|
||||||
unsigned SystemDirIdx = 0;
|
unsigned SystemDirIdx = 0;
|
||||||
bool NoCurDirSearch = false;
|
bool NoCurDirSearch = false;
|
||||||
|
@ -280,15 +286,17 @@ public:
|
||||||
DiagnosticsEngine &getDiags() const { return Diags; }
|
DiagnosticsEngine &getDiags() const { return Diags; }
|
||||||
|
|
||||||
/// Interface for setting the file search paths.
|
/// Interface for setting the file search paths.
|
||||||
void SetSearchPaths(const std::vector<DirectoryLookup> &dirs,
|
void SetSearchPaths(std::vector<DirectoryLookup> dirs, unsigned angledDirIdx,
|
||||||
unsigned angledDirIdx, unsigned systemDirIdx,
|
unsigned systemDirIdx, bool noCurDirSearch,
|
||||||
bool noCurDirSearch) {
|
llvm::DenseMap<unsigned, unsigned> searchDirToHSEntry) {
|
||||||
assert(angledDirIdx <= systemDirIdx && systemDirIdx <= dirs.size() &&
|
assert(angledDirIdx <= systemDirIdx && systemDirIdx <= dirs.size() &&
|
||||||
"Directory indices are unordered");
|
"Directory indices are unordered");
|
||||||
SearchDirs = dirs;
|
SearchDirs = std::move(dirs);
|
||||||
|
SearchDirsUsage.assign(SearchDirs.size(), false);
|
||||||
AngledDirIdx = angledDirIdx;
|
AngledDirIdx = angledDirIdx;
|
||||||
SystemDirIdx = systemDirIdx;
|
SystemDirIdx = systemDirIdx;
|
||||||
NoCurDirSearch = noCurDirSearch;
|
NoCurDirSearch = noCurDirSearch;
|
||||||
|
SearchDirToHSEntry = std::move(searchDirToHSEntry);
|
||||||
//LookupFileCache.clear();
|
//LookupFileCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,6 +304,7 @@ public:
|
||||||
void AddSearchPath(const DirectoryLookup &dir, bool isAngled) {
|
void AddSearchPath(const DirectoryLookup &dir, bool isAngled) {
|
||||||
unsigned idx = isAngled ? SystemDirIdx : AngledDirIdx;
|
unsigned idx = isAngled ? SystemDirIdx : AngledDirIdx;
|
||||||
SearchDirs.insert(SearchDirs.begin() + idx, dir);
|
SearchDirs.insert(SearchDirs.begin() + idx, dir);
|
||||||
|
SearchDirsUsage.insert(SearchDirsUsage.begin() + idx, false);
|
||||||
if (!isAngled)
|
if (!isAngled)
|
||||||
AngledDirIdx++;
|
AngledDirIdx++;
|
||||||
SystemDirIdx++;
|
SystemDirIdx++;
|
||||||
|
@ -505,6 +514,10 @@ public:
|
||||||
return FI && FI->isImport;
|
return FI && FI->isImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine which HeaderSearchOptions::UserEntries have been successfully
|
||||||
|
/// used so far and mark their index with 'true' in the resulting bit vector.
|
||||||
|
std::vector<bool> computeUserEntryUsage() const;
|
||||||
|
|
||||||
/// This method returns a HeaderMap for the specified
|
/// This method returns a HeaderMap for the specified
|
||||||
/// FileEntry, uniquing them through the 'HeaderMaps' datastructure.
|
/// FileEntry, uniquing them through the 'HeaderMaps' datastructure.
|
||||||
const HeaderMap *CreateHeaderMap(const FileEntry *FE);
|
const HeaderMap *CreateHeaderMap(const FileEntry *FE);
|
||||||
|
@ -714,6 +727,14 @@ private:
|
||||||
Module *RequestingModule,
|
Module *RequestingModule,
|
||||||
ModuleMap::KnownHeader *SuggestedModule);
|
ModuleMap::KnownHeader *SuggestedModule);
|
||||||
|
|
||||||
|
/// Cache the result of a successful lookup at the given include location
|
||||||
|
/// using the search path at index `HitIdx`.
|
||||||
|
void cacheLookupSuccess(LookupFileCacheInfo &CacheLookup, unsigned HitIdx,
|
||||||
|
SourceLocation IncludeLoc);
|
||||||
|
/// Note that a lookup at the given include location was successful using the
|
||||||
|
/// search path at index `HitIdx`.
|
||||||
|
void noteLookupUsage(unsigned HitIdx, SourceLocation IncludeLoc);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Retrieve the module map.
|
/// Retrieve the module map.
|
||||||
ModuleMap &getModuleMap() { return ModMap; }
|
ModuleMap &getModuleMap() { return ModMap; }
|
||||||
|
@ -763,6 +784,9 @@ public:
|
||||||
|
|
||||||
search_dir_iterator system_dir_end() const { return SearchDirs.end(); }
|
search_dir_iterator system_dir_end() const { return SearchDirs.end(); }
|
||||||
|
|
||||||
|
/// Get the index of the given search directory.
|
||||||
|
Optional<unsigned> searchDirIdx(const DirectoryLookup &DL) const;
|
||||||
|
|
||||||
/// Retrieve a uniqued framework name.
|
/// Retrieve a uniqued framework name.
|
||||||
StringRef getUniqueFrameworkName(StringRef Framework);
|
StringRef getUniqueFrameworkName(StringRef Framework);
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,11 @@ namespace {
|
||||||
struct DirectoryLookupInfo {
|
struct DirectoryLookupInfo {
|
||||||
IncludeDirGroup Group;
|
IncludeDirGroup Group;
|
||||||
DirectoryLookup Lookup;
|
DirectoryLookup Lookup;
|
||||||
|
Optional<unsigned> UserEntryIdx;
|
||||||
|
|
||||||
DirectoryLookupInfo(IncludeDirGroup Group, DirectoryLookup Lookup)
|
DirectoryLookupInfo(IncludeDirGroup Group, DirectoryLookup Lookup,
|
||||||
: Group(Group), Lookup(Lookup) {}
|
Optional<unsigned> UserEntryIdx)
|
||||||
|
: Group(Group), Lookup(Lookup), UserEntryIdx(UserEntryIdx) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// InitHeaderSearch - This class makes it easier to set the search paths of
|
/// InitHeaderSearch - This class makes it easier to set the search paths of
|
||||||
|
@ -60,13 +62,15 @@ public:
|
||||||
/// AddPath - Add the specified path to the specified group list, prefixing
|
/// AddPath - Add the specified path to the specified group list, prefixing
|
||||||
/// the sysroot if used.
|
/// the sysroot if used.
|
||||||
/// Returns true if the path exists, false if it was ignored.
|
/// Returns true if the path exists, false if it was ignored.
|
||||||
bool AddPath(const Twine &Path, IncludeDirGroup Group, bool isFramework);
|
bool AddPath(const Twine &Path, IncludeDirGroup Group, bool isFramework,
|
||||||
|
Optional<unsigned> UserEntryIdx = None);
|
||||||
|
|
||||||
/// AddUnmappedPath - Add the specified path to the specified group list,
|
/// AddUnmappedPath - Add the specified path to the specified group list,
|
||||||
/// without performing any sysroot remapping.
|
/// without performing any sysroot remapping.
|
||||||
/// Returns true if the path exists, false if it was ignored.
|
/// Returns true if the path exists, false if it was ignored.
|
||||||
bool AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,
|
bool AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,
|
||||||
bool isFramework);
|
bool isFramework,
|
||||||
|
Optional<unsigned> UserEntryIdx = None);
|
||||||
|
|
||||||
/// AddSystemHeaderPrefix - Add the specified prefix to the system header
|
/// AddSystemHeaderPrefix - Add the specified prefix to the system header
|
||||||
/// prefix list.
|
/// prefix list.
|
||||||
|
@ -119,22 +123,25 @@ static bool CanPrefixSysroot(StringRef Path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InitHeaderSearch::AddPath(const Twine &Path, IncludeDirGroup Group,
|
bool InitHeaderSearch::AddPath(const Twine &Path, IncludeDirGroup Group,
|
||||||
bool isFramework) {
|
bool isFramework,
|
||||||
|
Optional<unsigned> UserEntryIdx) {
|
||||||
// Add the path with sysroot prepended, if desired and this is a system header
|
// Add the path with sysroot prepended, if desired and this is a system header
|
||||||
// group.
|
// group.
|
||||||
if (HasSysroot) {
|
if (HasSysroot) {
|
||||||
SmallString<256> MappedPathStorage;
|
SmallString<256> MappedPathStorage;
|
||||||
StringRef MappedPathStr = Path.toStringRef(MappedPathStorage);
|
StringRef MappedPathStr = Path.toStringRef(MappedPathStorage);
|
||||||
if (CanPrefixSysroot(MappedPathStr)) {
|
if (CanPrefixSysroot(MappedPathStr)) {
|
||||||
return AddUnmappedPath(IncludeSysroot + Path, Group, isFramework);
|
return AddUnmappedPath(IncludeSysroot + Path, Group, isFramework,
|
||||||
|
UserEntryIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AddUnmappedPath(Path, Group, isFramework);
|
return AddUnmappedPath(Path, Group, isFramework, UserEntryIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,
|
bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,
|
||||||
bool isFramework) {
|
bool isFramework,
|
||||||
|
Optional<unsigned> UserEntryIdx) {
|
||||||
assert(!Path.isTriviallyEmpty() && "can't handle empty path here");
|
assert(!Path.isTriviallyEmpty() && "can't handle empty path here");
|
||||||
|
|
||||||
FileManager &FM = Headers.getFileMgr();
|
FileManager &FM = Headers.getFileMgr();
|
||||||
|
@ -160,7 +167,8 @@ bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,
|
||||||
|
|
||||||
// If the directory exists, add it.
|
// If the directory exists, add it.
|
||||||
if (auto DE = FM.getOptionalDirectoryRef(MappedPathStr)) {
|
if (auto DE = FM.getOptionalDirectoryRef(MappedPathStr)) {
|
||||||
IncludePath.emplace_back(Group, DirectoryLookup(*DE, Type, isFramework));
|
IncludePath.emplace_back(Group, DirectoryLookup(*DE, Type, isFramework),
|
||||||
|
UserEntryIdx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +179,8 @@ bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,
|
||||||
if (const HeaderMap *HM = Headers.CreateHeaderMap(*FE)) {
|
if (const HeaderMap *HM = Headers.CreateHeaderMap(*FE)) {
|
||||||
// It is a headermap, add it to the search path.
|
// It is a headermap, add it to the search path.
|
||||||
IncludePath.emplace_back(
|
IncludePath.emplace_back(
|
||||||
Group, DirectoryLookup(HM, Type, Group == IndexHeaderMap));
|
Group, DirectoryLookup(HM, Type, Group == IndexHeaderMap),
|
||||||
|
UserEntryIdx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -471,7 +480,7 @@ void InitHeaderSearch::AddDefaultIncludePaths(const LangOptions &Lang,
|
||||||
/// RemoveDuplicates - If there are duplicate directory entries in the specified
|
/// RemoveDuplicates - If there are duplicate directory entries in the specified
|
||||||
/// search list, remove the later (dead) ones. Returns the number of non-system
|
/// search list, remove the later (dead) ones. Returns the number of non-system
|
||||||
/// headers removed, which is used to update NumAngled.
|
/// headers removed, which is used to update NumAngled.
|
||||||
static unsigned RemoveDuplicates(std::vector<DirectoryLookup> &SearchList,
|
static unsigned RemoveDuplicates(std::vector<DirectoryLookupInfo> &SearchList,
|
||||||
unsigned First, bool Verbose) {
|
unsigned First, bool Verbose) {
|
||||||
llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs;
|
llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs;
|
||||||
llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs;
|
llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs;
|
||||||
|
@ -480,7 +489,7 @@ static unsigned RemoveDuplicates(std::vector<DirectoryLookup> &SearchList,
|
||||||
for (unsigned i = First; i != SearchList.size(); ++i) {
|
for (unsigned i = First; i != SearchList.size(); ++i) {
|
||||||
unsigned DirToRemove = i;
|
unsigned DirToRemove = i;
|
||||||
|
|
||||||
const DirectoryLookup &CurEntry = SearchList[i];
|
const DirectoryLookup &CurEntry = SearchList[i].Lookup;
|
||||||
|
|
||||||
if (CurEntry.isNormalDir()) {
|
if (CurEntry.isNormalDir()) {
|
||||||
// If this isn't the first time we've seen this dir, remove it.
|
// If this isn't the first time we've seen this dir, remove it.
|
||||||
|
@ -510,7 +519,7 @@ static unsigned RemoveDuplicates(std::vector<DirectoryLookup> &SearchList,
|
||||||
for (FirstDir = First;; ++FirstDir) {
|
for (FirstDir = First;; ++FirstDir) {
|
||||||
assert(FirstDir != i && "Didn't find dupe?");
|
assert(FirstDir != i && "Didn't find dupe?");
|
||||||
|
|
||||||
const DirectoryLookup &SearchEntry = SearchList[FirstDir];
|
const DirectoryLookup &SearchEntry = SearchList[FirstDir].Lookup;
|
||||||
|
|
||||||
// If these are different lookup types, then they can't be the dupe.
|
// If these are different lookup types, then they can't be the dupe.
|
||||||
if (SearchEntry.getLookupType() != CurEntry.getLookupType())
|
if (SearchEntry.getLookupType() != CurEntry.getLookupType())
|
||||||
|
@ -532,7 +541,7 @@ static unsigned RemoveDuplicates(std::vector<DirectoryLookup> &SearchList,
|
||||||
|
|
||||||
// If the first dir in the search path is a non-system dir, zap it
|
// If the first dir in the search path is a non-system dir, zap it
|
||||||
// instead of the system one.
|
// instead of the system one.
|
||||||
if (SearchList[FirstDir].getDirCharacteristic() == SrcMgr::C_User)
|
if (SearchList[FirstDir].Lookup.getDirCharacteristic() == SrcMgr::C_User)
|
||||||
DirToRemove = FirstDir;
|
DirToRemove = FirstDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,16 +563,37 @@ static unsigned RemoveDuplicates(std::vector<DirectoryLookup> &SearchList,
|
||||||
return NonSystemRemoved;
|
return NonSystemRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extract DirectoryLookups from DirectoryLookupInfos.
|
||||||
|
static std::vector<DirectoryLookup>
|
||||||
|
extractLookups(const std::vector<DirectoryLookupInfo> &Infos) {
|
||||||
|
std::vector<DirectoryLookup> Lookups;
|
||||||
|
Lookups.reserve(Infos.size());
|
||||||
|
llvm::transform(Infos, std::back_inserter(Lookups),
|
||||||
|
[](const DirectoryLookupInfo &Info) { return Info.Lookup; });
|
||||||
|
return Lookups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collect the mapping between indices of DirectoryLookups and UserEntries.
|
||||||
|
static llvm::DenseMap<unsigned, unsigned>
|
||||||
|
mapToUserEntries(const std::vector<DirectoryLookupInfo> &Infos) {
|
||||||
|
llvm::DenseMap<unsigned, unsigned> LookupsToUserEntries;
|
||||||
|
for (unsigned I = 0, E = Infos.size(); I < E; ++I) {
|
||||||
|
// Check whether this DirectoryLookup maps to a HeaderSearch::UserEntry.
|
||||||
|
if (Infos[I].UserEntryIdx)
|
||||||
|
LookupsToUserEntries.insert({I, *Infos[I].UserEntryIdx});
|
||||||
|
}
|
||||||
|
return LookupsToUserEntries;
|
||||||
|
}
|
||||||
|
|
||||||
void InitHeaderSearch::Realize(const LangOptions &Lang) {
|
void InitHeaderSearch::Realize(const LangOptions &Lang) {
|
||||||
// Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList.
|
// Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList.
|
||||||
std::vector<DirectoryLookup> SearchList;
|
std::vector<DirectoryLookupInfo> SearchList;
|
||||||
SearchList.reserve(IncludePath.size());
|
SearchList.reserve(IncludePath.size());
|
||||||
|
|
||||||
// Quoted arguments go first.
|
// Quoted arguments go first.
|
||||||
for (auto &Include : IncludePath)
|
for (auto &Include : IncludePath)
|
||||||
if (Include.Group == Quoted)
|
if (Include.Group == Quoted)
|
||||||
SearchList.push_back(Include.Lookup);
|
SearchList.push_back(Include);
|
||||||
|
|
||||||
// Deduplicate and remember index.
|
// Deduplicate and remember index.
|
||||||
RemoveDuplicates(SearchList, 0, Verbose);
|
RemoveDuplicates(SearchList, 0, Verbose);
|
||||||
|
@ -571,7 +601,7 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
|
||||||
|
|
||||||
for (auto &Include : IncludePath)
|
for (auto &Include : IncludePath)
|
||||||
if (Include.Group == Angled || Include.Group == IndexHeaderMap)
|
if (Include.Group == Angled || Include.Group == IndexHeaderMap)
|
||||||
SearchList.push_back(Include.Lookup);
|
SearchList.push_back(Include);
|
||||||
|
|
||||||
RemoveDuplicates(SearchList, NumQuoted, Verbose);
|
RemoveDuplicates(SearchList, NumQuoted, Verbose);
|
||||||
unsigned NumAngled = SearchList.size();
|
unsigned NumAngled = SearchList.size();
|
||||||
|
@ -583,11 +613,11 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
|
||||||
Include.Group == CXXSystem) ||
|
Include.Group == CXXSystem) ||
|
||||||
(Lang.ObjC && !Lang.CPlusPlus && Include.Group == ObjCSystem) ||
|
(Lang.ObjC && !Lang.CPlusPlus && Include.Group == ObjCSystem) ||
|
||||||
(Lang.ObjC && Lang.CPlusPlus && Include.Group == ObjCXXSystem))
|
(Lang.ObjC && Lang.CPlusPlus && Include.Group == ObjCXXSystem))
|
||||||
SearchList.push_back(Include.Lookup);
|
SearchList.push_back(Include);
|
||||||
|
|
||||||
for (auto &Include : IncludePath)
|
for (auto &Include : IncludePath)
|
||||||
if (Include.Group == After)
|
if (Include.Group == After)
|
||||||
SearchList.push_back(Include.Lookup);
|
SearchList.push_back(Include);
|
||||||
|
|
||||||
// Remove duplicates across both the Angled and System directories. GCC does
|
// Remove duplicates across both the Angled and System directories. GCC does
|
||||||
// this and failing to remove duplicates across these two groups breaks
|
// this and failing to remove duplicates across these two groups breaks
|
||||||
|
@ -596,7 +626,8 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
|
||||||
NumAngled -= NonSystemRemoved;
|
NumAngled -= NonSystemRemoved;
|
||||||
|
|
||||||
bool DontSearchCurDir = false; // TODO: set to true if -I- is set?
|
bool DontSearchCurDir = false; // TODO: set to true if -I- is set?
|
||||||
Headers.SetSearchPaths(SearchList, NumQuoted, NumAngled, DontSearchCurDir);
|
Headers.SetSearchPaths(extractLookups(SearchList), NumQuoted, NumAngled,
|
||||||
|
DontSearchCurDir, mapToUserEntries(SearchList));
|
||||||
|
|
||||||
Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes);
|
Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes);
|
||||||
|
|
||||||
|
@ -606,14 +637,14 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
|
||||||
for (unsigned i = 0, e = SearchList.size(); i != e; ++i) {
|
for (unsigned i = 0, e = SearchList.size(); i != e; ++i) {
|
||||||
if (i == NumQuoted)
|
if (i == NumQuoted)
|
||||||
llvm::errs() << "#include <...> search starts here:\n";
|
llvm::errs() << "#include <...> search starts here:\n";
|
||||||
StringRef Name = SearchList[i].getName();
|
StringRef Name = SearchList[i].Lookup.getName();
|
||||||
const char *Suffix;
|
const char *Suffix;
|
||||||
if (SearchList[i].isNormalDir())
|
if (SearchList[i].Lookup.isNormalDir())
|
||||||
Suffix = "";
|
Suffix = "";
|
||||||
else if (SearchList[i].isFramework())
|
else if (SearchList[i].Lookup.isFramework())
|
||||||
Suffix = " (framework directory)";
|
Suffix = " (framework directory)";
|
||||||
else {
|
else {
|
||||||
assert(SearchList[i].isHeaderMap() && "Unknown DirectoryLookup");
|
assert(SearchList[i].Lookup.isHeaderMap() && "Unknown DirectoryLookup");
|
||||||
Suffix = " (headermap)";
|
Suffix = " (headermap)";
|
||||||
}
|
}
|
||||||
llvm::errs() << " " << Name << Suffix << "\n";
|
llvm::errs() << " " << Name << Suffix << "\n";
|
||||||
|
@ -632,9 +663,9 @@ void clang::ApplyHeaderSearchOptions(HeaderSearch &HS,
|
||||||
for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) {
|
for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) {
|
||||||
const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i];
|
const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i];
|
||||||
if (E.IgnoreSysRoot) {
|
if (E.IgnoreSysRoot) {
|
||||||
Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework);
|
Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework, i);
|
||||||
} else {
|
} else {
|
||||||
Init.AddPath(E.Path, E.Group, E.IsFramework);
|
Init.AddPath(E.Path, E.Group, E.IsFramework, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -194,19 +194,6 @@ LLVM_DUMP_METHOD void HeaderMapImpl::dump() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// LookupFile - Check to see if the specified relative filename is located in
|
|
||||||
/// this HeaderMap. If so, open it and return its FileEntry.
|
|
||||||
Optional<FileEntryRef> HeaderMap::LookupFile(StringRef Filename,
|
|
||||||
FileManager &FM) const {
|
|
||||||
|
|
||||||
SmallString<1024> Path;
|
|
||||||
StringRef Dest = HeaderMapImpl::lookupFilename(Filename, Path);
|
|
||||||
if (Dest.empty())
|
|
||||||
return None;
|
|
||||||
|
|
||||||
return FM.getOptionalFileRef(Dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringRef HeaderMapImpl::lookupFilename(StringRef Filename,
|
StringRef HeaderMapImpl::lookupFilename(StringRef Filename,
|
||||||
SmallVectorImpl<char> &DestPath) const {
|
SmallVectorImpl<char> &DestPath) const {
|
||||||
const HMapHeader &Hdr = getHeader();
|
const HMapHeader &Hdr = getHeader();
|
||||||
|
|
|
@ -108,6 +108,20 @@ void HeaderSearch::PrintStats() {
|
||||||
<< NumSubFrameworkLookups << " subframework lookups.\n";
|
<< NumSubFrameworkLookups << " subframework lookups.\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<bool> HeaderSearch::computeUserEntryUsage() const {
|
||||||
|
std::vector<bool> UserEntryUsage(HSOpts->UserEntries.size());
|
||||||
|
for (unsigned I = 0, E = SearchDirsUsage.size(); I < E; ++I) {
|
||||||
|
// Check whether this DirectoryLookup has been successfully used.
|
||||||
|
if (SearchDirsUsage[I]) {
|
||||||
|
auto UserEntryIdxIt = SearchDirToHSEntry.find(I);
|
||||||
|
// Check whether this DirectoryLookup maps to a HeaderSearch::UserEntry.
|
||||||
|
if (UserEntryIdxIt != SearchDirToHSEntry.end())
|
||||||
|
UserEntryUsage[UserEntryIdxIt->second] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UserEntryUsage;
|
||||||
|
}
|
||||||
|
|
||||||
/// CreateHeaderMap - This method returns a HeaderMap for the specified
|
/// CreateHeaderMap - This method returns a HeaderMap for the specified
|
||||||
/// FileEntry, uniquing them through the 'HeaderMaps' datastructure.
|
/// FileEntry, uniquing them through the 'HeaderMaps' datastructure.
|
||||||
const HeaderMap *HeaderSearch::CreateHeaderMap(const FileEntry *FE) {
|
const HeaderMap *HeaderSearch::CreateHeaderMap(const FileEntry *FE) {
|
||||||
|
@ -262,10 +276,11 @@ Module *HeaderSearch::lookupModule(StringRef ModuleName, StringRef SearchName,
|
||||||
SourceLocation ImportLoc,
|
SourceLocation ImportLoc,
|
||||||
bool AllowExtraModuleMapSearch) {
|
bool AllowExtraModuleMapSearch) {
|
||||||
Module *Module = nullptr;
|
Module *Module = nullptr;
|
||||||
|
unsigned Idx;
|
||||||
|
|
||||||
// Look through the various header search paths to load any available module
|
// Look through the various header search paths to load any available module
|
||||||
// maps, searching for a module map that describes this module.
|
// maps, searching for a module map that describes this module.
|
||||||
for (unsigned Idx = 0, N = SearchDirs.size(); Idx != N; ++Idx) {
|
for (Idx = 0; Idx != SearchDirs.size(); ++Idx) {
|
||||||
if (SearchDirs[Idx].isFramework()) {
|
if (SearchDirs[Idx].isFramework()) {
|
||||||
// Search for or infer a module map for a framework. Here we use
|
// Search for or infer a module map for a framework. Here we use
|
||||||
// SearchName rather than ModuleName, to permit finding private modules
|
// SearchName rather than ModuleName, to permit finding private modules
|
||||||
|
@ -328,6 +343,9 @@ Module *HeaderSearch::lookupModule(StringRef ModuleName, StringRef SearchName,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Module)
|
||||||
|
noteLookupUsage(Idx, ImportLoc);
|
||||||
|
|
||||||
return Module;
|
return Module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,16 +458,19 @@ Optional<FileEntryRef> DirectoryLookup::LookupFile(
|
||||||
if (llvm::sys::path::is_relative(Dest)) {
|
if (llvm::sys::path::is_relative(Dest)) {
|
||||||
MappedName.append(Dest.begin(), Dest.end());
|
MappedName.append(Dest.begin(), Dest.end());
|
||||||
Filename = StringRef(MappedName.begin(), MappedName.size());
|
Filename = StringRef(MappedName.begin(), MappedName.size());
|
||||||
Optional<FileEntryRef> Result = HM->LookupFile(Filename, HS.getFileMgr());
|
Dest = HM->lookupFilename(Filename, Path);
|
||||||
if (Result) {
|
}
|
||||||
FixupSearchPath();
|
|
||||||
return *Result;
|
if (auto Res = HS.getFileMgr().getOptionalFileRef(Dest)) {
|
||||||
}
|
|
||||||
} else if (auto Res = HS.getFileMgr().getOptionalFileRef(Dest)) {
|
|
||||||
FixupSearchPath();
|
FixupSearchPath();
|
||||||
return *Res;
|
return *Res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Header maps need to be marked as used whenever the filename matches.
|
||||||
|
// The case where the target file **exists** is handled by callee of this
|
||||||
|
// function as part of the regular logic that applies to include search paths.
|
||||||
|
// The case where the target file **does not exist** is handled here:
|
||||||
|
HS.noteLookupUsage(*HS.searchDirIdx(*this), IncludeLoc);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,6 +675,21 @@ Optional<FileEntryRef> DirectoryLookup::DoFrameworkLookup(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HeaderSearch::cacheLookupSuccess(LookupFileCacheInfo &CacheLookup,
|
||||||
|
unsigned HitIdx, SourceLocation Loc) {
|
||||||
|
CacheLookup.HitIdx = HitIdx;
|
||||||
|
noteLookupUsage(HitIdx, Loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeaderSearch::noteLookupUsage(unsigned HitIdx, SourceLocation Loc) {
|
||||||
|
SearchDirsUsage[HitIdx] = true;
|
||||||
|
|
||||||
|
auto UserEntryIdxIt = SearchDirToHSEntry.find(HitIdx);
|
||||||
|
if (UserEntryIdxIt != SearchDirToHSEntry.end())
|
||||||
|
Diags.Report(Loc, diag::remark_pp_search_path_usage)
|
||||||
|
<< HSOpts->UserEntries[UserEntryIdxIt->second].Path;
|
||||||
|
}
|
||||||
|
|
||||||
void HeaderSearch::setTarget(const TargetInfo &Target) {
|
void HeaderSearch::setTarget(const TargetInfo &Target) {
|
||||||
ModMap.setTarget(Target);
|
ModMap.setTarget(Target);
|
||||||
}
|
}
|
||||||
|
@ -992,7 +1028,7 @@ Optional<FileEntryRef> HeaderSearch::LookupFile(
|
||||||
&File->getFileEntry(), isAngled, FoundByHeaderMap);
|
&File->getFileEntry(), isAngled, FoundByHeaderMap);
|
||||||
|
|
||||||
// Remember this location for the next lookup we do.
|
// Remember this location for the next lookup we do.
|
||||||
CacheLookup.HitIdx = i;
|
cacheLookupSuccess(CacheLookup, i, IncludeLoc);
|
||||||
return File;
|
return File;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1022,8 +1058,8 @@ Optional<FileEntryRef> HeaderSearch::LookupFile(
|
||||||
return MSFE;
|
return MSFE;
|
||||||
}
|
}
|
||||||
|
|
||||||
LookupFileCacheInfo &CacheLookup = LookupFileCache[Filename];
|
cacheLookupSuccess(LookupFileCache[Filename],
|
||||||
CacheLookup.HitIdx = LookupFileCache[ScratchFilename].HitIdx;
|
LookupFileCache[ScratchFilename].HitIdx, IncludeLoc);
|
||||||
// FIXME: SuggestedModule.
|
// FIXME: SuggestedModule.
|
||||||
return File;
|
return File;
|
||||||
}
|
}
|
||||||
|
@ -1362,6 +1398,13 @@ size_t HeaderSearch::getTotalMemory() const {
|
||||||
+ FrameworkMap.getAllocator().getTotalMemory();
|
+ FrameworkMap.getAllocator().getTotalMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<unsigned> HeaderSearch::searchDirIdx(const DirectoryLookup &DL) const {
|
||||||
|
for (unsigned I = 0; I < SearchDirs.size(); ++I)
|
||||||
|
if (&SearchDirs[I] == &DL)
|
||||||
|
return I;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
StringRef HeaderSearch::getUniqueFrameworkName(StringRef Framework) {
|
StringRef HeaderSearch::getUniqueFrameworkName(StringRef Framework) {
|
||||||
return FrameworkNames.insert(Framework).first->first();
|
return FrameworkNames.insert(Framework).first->first();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
framework module FrameworkA {
|
||||||
|
header "FrameworkA.h"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
framework module FrameworkB {
|
||||||
|
header "FrameworkB.h"
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
#include_next "a.h" // #a-include-next
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"mappings": {
|
||||||
|
"b.h": "DIR/b/b.h"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
module b {
|
||||||
|
header "DIR/b/b.h"
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
// RUN: rm -rf %t && mkdir %t
|
||||||
|
|
||||||
|
// Check that search paths used by `#include` and `#include_next` are reported.
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/a \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/a_next \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/b \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/c \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/d \
|
||||||
|
// RUN: -DINCLUDE -verify
|
||||||
|
#ifdef INCLUDE
|
||||||
|
#include "a.h" // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/search-path-usage/a'}} \
|
||||||
|
// expected-remark-re@#a-include-next {{search path used: '{{.*}}/search-path-usage/a_next'}}
|
||||||
|
#include "d.h" // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/search-path-usage/d'}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that framework search paths are reported.
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -F%S/Inputs/search-path-usage/FwA \
|
||||||
|
// RUN: -F%S/Inputs/search-path-usage/FwB \
|
||||||
|
// RUN: -DFRAMEWORK -verify
|
||||||
|
#ifdef FRAMEWORK
|
||||||
|
#include "FrameworkA/FrameworkA.h" // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/search-path-usage/FwA'}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that system search paths are reported.
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -isystem %S/Inputs/search-path-usage/b \
|
||||||
|
// RUN: -isystem %S/Inputs/search-path-usage/d \
|
||||||
|
// RUN: -DSYSTEM -verify
|
||||||
|
#ifdef SYSTEM
|
||||||
|
#include "b.h" // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/search-path-usage/b'}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that sysroot-based search paths are reported.
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -isysroot %S/Inputs/search-path-usage \
|
||||||
|
// RUN: -iwithsysroot /b \
|
||||||
|
// RUN: -iwithsysroot /d \
|
||||||
|
// RUN: -DSYSROOT -verify
|
||||||
|
#ifdef SYSROOT
|
||||||
|
#include "d.h" // \
|
||||||
|
// expected-remark {{search path used: '/d'}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that search paths used by `__has_include()` are reported.
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/b \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/d \
|
||||||
|
// RUN: -DHAS_INCLUDE -verify
|
||||||
|
#ifdef HAS_INCLUDE
|
||||||
|
#if __has_include("b.h") // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/search-path-usage/b'}}
|
||||||
|
#endif
|
||||||
|
#if __has_include("x.h")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that search paths used by `#import` are reported.
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/b \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/d \
|
||||||
|
// RUN: -DIMPORT -verify
|
||||||
|
#ifdef IMPORT
|
||||||
|
#import "d.h" // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/search-path-usage/d'}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that used header maps are reported when the target file exists.
|
||||||
|
//
|
||||||
|
// RUN: sed "s|DIR|%/S/Inputs/search-path-usage|g" \
|
||||||
|
// RUN: %S/Inputs/search-path-usage/b.hmap.json.template > %t/b.hmap.json
|
||||||
|
// RUN: %hmaptool write %t/b.hmap.json %t/b.hmap
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -I %t/b.hmap \
|
||||||
|
// RUN: -I b \
|
||||||
|
// RUN: -DHMAP -verify
|
||||||
|
#ifdef HMAP
|
||||||
|
#include "b.h" // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/b.hmap'}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that unused header map are not reported.
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -I%t/b.hmap \
|
||||||
|
// RUN: -I%S/Inputs/search-path-usage/d \
|
||||||
|
// RUN: -DHMAP_NO_MATCH -verify
|
||||||
|
#ifdef HMAP_NO_MATCH
|
||||||
|
#include "d.h" // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/search-path-usage/d'}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that used header map is reported even when the target file is missing.
|
||||||
|
//
|
||||||
|
// RUN: sed "s|DIR|%/S/Inputs/search-path-usage/missing-subdir|g" \
|
||||||
|
// RUN: %S/Inputs/search-path-usage/b.hmap.json.template > %t/b-missing.hmap.json
|
||||||
|
// RUN: %hmaptool write %t/b-missing.hmap.json %t/b-missing.hmap
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -I %t/b-missing.hmap \
|
||||||
|
// RUN: -I b \
|
||||||
|
// RUN: -DHMAP_MATCHED_BUT_MISSING -verify
|
||||||
|
#ifdef HMAP_MATCHED_BUT_MISSING
|
||||||
|
#include "b.h" // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/b-missing.hmap'}} \
|
||||||
|
// expected-error {{'b.h' file not found}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that used header map is reported even when the target file is missing
|
||||||
|
// and the lookup is initiated by __has_include.
|
||||||
|
//
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -I %t/b-missing.hmap \
|
||||||
|
// RUN: -I b \
|
||||||
|
// RUN: -DHMAP_MATCHED_BUT_MISSING_IN_HAS_INCLUDE -verify
|
||||||
|
#ifdef HMAP_MATCHED_BUT_MISSING_IN_HAS_INCLUDE
|
||||||
|
#if __has_include("b.h") // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/b-missing.hmap'}}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check that search paths with module maps are reported.
|
||||||
|
//
|
||||||
|
// RUN: mkdir %t/modulemap_abs
|
||||||
|
// RUN: sed "s|DIR|%/S/Inputs/search-path-usage|g" \
|
||||||
|
// RUN: %S/Inputs/search-path-usage/modulemap_abs/module.modulemap.template \
|
||||||
|
// RUN: > %t/modulemap_abs/module.modulemap
|
||||||
|
// RUN: %clang_cc1 -Eonly %s -Rsearch-path-usage \
|
||||||
|
// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/modules \
|
||||||
|
// RUN: -I %t/modulemap_abs \
|
||||||
|
// RUN: -I %S/Inputs/search-path-usage/a \
|
||||||
|
// RUN: -DMODMAP_ABS -verify
|
||||||
|
#ifdef MODMAP_ABS
|
||||||
|
@import b; // \
|
||||||
|
// expected-remark-re {{search path used: '{{.*}}/modulemap_abs'}}
|
||||||
|
#endif
|
Loading…
Reference in New Issue