diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -751,6 +751,7 @@ def UnusedPropertyIvar : DiagGroup<"unused-property-ivar">; def UnusedGetterReturnValue : DiagGroup<"unused-getter-return-value">; def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">; +def UsedSearchPath : DiagGroup<"search-path-usage">; def UserDefinedLiterals : DiagGroup<"user-defined-literals">; def UserDefinedWarnings : DiagGroup<"user-defined-warnings">; def ReorderCtor : DiagGroup<"reorder-ctor">; diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -446,6 +446,9 @@ "#pragma hdrstop filename not supported, " "/Fp can be used to specify precompiled header filename">, InGroup; +def remark_pp_search_path_usage : Remark< + "search path used: '%0'">, + InGroup; def err_pp_file_not_found_angled_include_not_fatal : Error< "'%0' file not found with %select{include|import}1; " "use \"quotes\" instead">; diff --git a/clang/include/clang/Lex/HeaderMap.h b/clang/include/clang/Lex/HeaderMap.h --- a/clang/include/clang/Lex/HeaderMap.h +++ b/clang/include/clang/Lex/HeaderMap.h @@ -77,13 +77,6 @@ static std::unique_ptr Create(const FileEntry *FE, 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 LookupFile(StringRef Filename, FileManager &FM) const; - using HeaderMapImpl::dump; using HeaderMapImpl::getFileName; using HeaderMapImpl::lookupFilename; diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -172,6 +172,9 @@ /// Header-search options used to initialize this header search. std::shared_ptr HSOpts; + /// Mapping from SearchDir to HeaderSearchOptions::UserEntries indices. + llvm::DenseMap SearchDirToHSEntry; + DiagnosticsEngine &Diags; FileManager &FileMgr; @@ -182,6 +185,9 @@ /// NoCurDirSearch is true, then the check for the file in the current /// directory is suppressed. std::vector SearchDirs; + /// Whether the DirectoryLookup at the corresponding index in SearchDirs has + /// been successfully used to lookup a file. + std::vector SearchDirsUsage; unsigned AngledDirIdx = 0; unsigned SystemDirIdx = 0; bool NoCurDirSearch = false; @@ -280,15 +286,17 @@ DiagnosticsEngine &getDiags() const { return Diags; } /// Interface for setting the file search paths. - void SetSearchPaths(const std::vector &dirs, - unsigned angledDirIdx, unsigned systemDirIdx, - bool noCurDirSearch) { + void SetSearchPaths(std::vector dirs, unsigned angledDirIdx, + unsigned systemDirIdx, bool noCurDirSearch, + llvm::DenseMap searchDirToHSEntry) { assert(angledDirIdx <= systemDirIdx && systemDirIdx <= dirs.size() && "Directory indices are unordered"); - SearchDirs = dirs; + SearchDirs = std::move(dirs); + SearchDirsUsage.assign(SearchDirs.size(), false); AngledDirIdx = angledDirIdx; SystemDirIdx = systemDirIdx; NoCurDirSearch = noCurDirSearch; + SearchDirToHSEntry = std::move(searchDirToHSEntry); //LookupFileCache.clear(); } @@ -296,6 +304,7 @@ void AddSearchPath(const DirectoryLookup &dir, bool isAngled) { unsigned idx = isAngled ? SystemDirIdx : AngledDirIdx; SearchDirs.insert(SearchDirs.begin() + idx, dir); + SearchDirsUsage.insert(SearchDirsUsage.begin() + idx, false); if (!isAngled) AngledDirIdx++; SystemDirIdx++; @@ -505,6 +514,10 @@ 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 computeUserEntryUsage() const; + /// This method returns a HeaderMap for the specified /// FileEntry, uniquing them through the 'HeaderMaps' datastructure. const HeaderMap *CreateHeaderMap(const FileEntry *FE); @@ -714,6 +727,14 @@ Module *RequestingModule, 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: /// Retrieve the module map. ModuleMap &getModuleMap() { return ModMap; } @@ -763,6 +784,9 @@ search_dir_iterator system_dir_end() const { return SearchDirs.end(); } + /// Get the index of the given search directory. + Optional searchDirIdx(const DirectoryLookup &DL) const; + /// Retrieve a uniqued framework name. StringRef getUniqueFrameworkName(StringRef Framework); diff --git a/clang/lib/Frontend/InitHeaderSearch.cpp b/clang/lib/Frontend/InitHeaderSearch.cpp --- a/clang/lib/Frontend/InitHeaderSearch.cpp +++ b/clang/lib/Frontend/InitHeaderSearch.cpp @@ -36,9 +36,11 @@ struct DirectoryLookupInfo { IncludeDirGroup Group; DirectoryLookup Lookup; + Optional UserEntryIdx; - DirectoryLookupInfo(IncludeDirGroup Group, DirectoryLookup Lookup) - : Group(Group), Lookup(Lookup) {} + DirectoryLookupInfo(IncludeDirGroup Group, DirectoryLookup Lookup, + Optional UserEntryIdx) + : Group(Group), Lookup(Lookup), UserEntryIdx(UserEntryIdx) {} }; /// InitHeaderSearch - This class makes it easier to set the search paths of @@ -60,13 +62,15 @@ /// AddPath - Add the specified path to the specified group list, prefixing /// the sysroot if used. /// 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 UserEntryIdx = None); /// AddUnmappedPath - Add the specified path to the specified group list, /// without performing any sysroot remapping. /// Returns true if the path exists, false if it was ignored. bool AddUnmappedPath(const Twine &Path, IncludeDirGroup Group, - bool isFramework); + bool isFramework, + Optional UserEntryIdx = None); /// AddSystemHeaderPrefix - Add the specified prefix to the system header /// prefix list. @@ -119,22 +123,25 @@ } bool InitHeaderSearch::AddPath(const Twine &Path, IncludeDirGroup Group, - bool isFramework) { + bool isFramework, + Optional UserEntryIdx) { // Add the path with sysroot prepended, if desired and this is a system header // group. if (HasSysroot) { SmallString<256> MappedPathStorage; StringRef MappedPathStr = Path.toStringRef(MappedPathStorage); 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 isFramework) { + bool isFramework, + Optional UserEntryIdx) { assert(!Path.isTriviallyEmpty() && "can't handle empty path here"); FileManager &FM = Headers.getFileMgr(); @@ -160,7 +167,8 @@ // If the directory exists, add it. 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; } @@ -171,7 +179,8 @@ if (const HeaderMap *HM = Headers.CreateHeaderMap(*FE)) { // It is a headermap, add it to the search path. IncludePath.emplace_back( - Group, DirectoryLookup(HM, Type, Group == IndexHeaderMap)); + Group, DirectoryLookup(HM, Type, Group == IndexHeaderMap), + UserEntryIdx); return true; } } @@ -471,7 +480,7 @@ /// RemoveDuplicates - If there are duplicate directory entries in the specified /// search list, remove the later (dead) ones. Returns the number of non-system /// headers removed, which is used to update NumAngled. -static unsigned RemoveDuplicates(std::vector &SearchList, +static unsigned RemoveDuplicates(std::vector &SearchList, unsigned First, bool Verbose) { llvm::SmallPtrSet SeenDirs; llvm::SmallPtrSet SeenFrameworkDirs; @@ -480,7 +489,7 @@ for (unsigned i = First; i != SearchList.size(); ++i) { unsigned DirToRemove = i; - const DirectoryLookup &CurEntry = SearchList[i]; + const DirectoryLookup &CurEntry = SearchList[i].Lookup; if (CurEntry.isNormalDir()) { // If this isn't the first time we've seen this dir, remove it. @@ -510,7 +519,7 @@ for (FirstDir = First;; ++FirstDir) { 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 (SearchEntry.getLookupType() != CurEntry.getLookupType()) @@ -532,7 +541,7 @@ // If the first dir in the search path is a non-system dir, zap it // instead of the system one. - if (SearchList[FirstDir].getDirCharacteristic() == SrcMgr::C_User) + if (SearchList[FirstDir].Lookup.getDirCharacteristic() == SrcMgr::C_User) DirToRemove = FirstDir; } @@ -554,16 +563,37 @@ return NonSystemRemoved; } +/// Extract DirectoryLookups from DirectoryLookupInfos. +static std::vector +extractLookups(const std::vector &Infos) { + std::vector 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 +mapToUserEntries(const std::vector &Infos) { + llvm::DenseMap 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) { // Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList. - std::vector SearchList; + std::vector SearchList; SearchList.reserve(IncludePath.size()); // Quoted arguments go first. for (auto &Include : IncludePath) if (Include.Group == Quoted) - SearchList.push_back(Include.Lookup); + SearchList.push_back(Include); // Deduplicate and remember index. RemoveDuplicates(SearchList, 0, Verbose); @@ -571,7 +601,7 @@ for (auto &Include : IncludePath) if (Include.Group == Angled || Include.Group == IndexHeaderMap) - SearchList.push_back(Include.Lookup); + SearchList.push_back(Include); RemoveDuplicates(SearchList, NumQuoted, Verbose); unsigned NumAngled = SearchList.size(); @@ -583,11 +613,11 @@ Include.Group == CXXSystem) || (Lang.ObjC && !Lang.CPlusPlus && Include.Group == ObjCSystem) || (Lang.ObjC && Lang.CPlusPlus && Include.Group == ObjCXXSystem)) - SearchList.push_back(Include.Lookup); + SearchList.push_back(Include); for (auto &Include : IncludePath) if (Include.Group == After) - SearchList.push_back(Include.Lookup); + SearchList.push_back(Include); // Remove duplicates across both the Angled and System directories. GCC does // this and failing to remove duplicates across these two groups breaks @@ -596,7 +626,8 @@ NumAngled -= NonSystemRemoved; 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); @@ -606,14 +637,14 @@ for (unsigned i = 0, e = SearchList.size(); i != e; ++i) { if (i == NumQuoted) llvm::errs() << "#include <...> search starts here:\n"; - StringRef Name = SearchList[i].getName(); + StringRef Name = SearchList[i].Lookup.getName(); const char *Suffix; - if (SearchList[i].isNormalDir()) + if (SearchList[i].Lookup.isNormalDir()) Suffix = ""; - else if (SearchList[i].isFramework()) + else if (SearchList[i].Lookup.isFramework()) Suffix = " (framework directory)"; else { - assert(SearchList[i].isHeaderMap() && "Unknown DirectoryLookup"); + assert(SearchList[i].Lookup.isHeaderMap() && "Unknown DirectoryLookup"); Suffix = " (headermap)"; } llvm::errs() << " " << Name << Suffix << "\n"; @@ -632,9 +663,9 @@ for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) { const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i]; if (E.IgnoreSysRoot) { - Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework); + Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework, i); } else { - Init.AddPath(E.Path, E.Group, E.IsFramework); + Init.AddPath(E.Path, E.Group, E.IsFramework, i); } } diff --git a/clang/lib/Lex/HeaderMap.cpp b/clang/lib/Lex/HeaderMap.cpp --- a/clang/lib/Lex/HeaderMap.cpp +++ b/clang/lib/Lex/HeaderMap.cpp @@ -194,19 +194,6 @@ } } -/// LookupFile - Check to see if the specified relative filename is located in -/// this HeaderMap. If so, open it and return its FileEntry. -Optional 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, SmallVectorImpl &DestPath) const { const HMapHeader &Hdr = getHeader(); diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -108,6 +108,20 @@ << NumSubFrameworkLookups << " subframework lookups.\n"; } +std::vector HeaderSearch::computeUserEntryUsage() const { + std::vector 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 /// FileEntry, uniquing them through the 'HeaderMaps' datastructure. const HeaderMap *HeaderSearch::CreateHeaderMap(const FileEntry *FE) { @@ -262,10 +276,11 @@ SourceLocation ImportLoc, bool AllowExtraModuleMapSearch) { Module *Module = nullptr; + unsigned Idx; // Look through the various header search paths to load any available 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()) { // Search for or infer a module map for a framework. Here we use // SearchName rather than ModuleName, to permit finding private modules @@ -328,6 +343,9 @@ break; } + if (Module) + noteLookupUsage(Idx, ImportLoc); + return Module; } @@ -440,16 +458,19 @@ if (llvm::sys::path::is_relative(Dest)) { MappedName.append(Dest.begin(), Dest.end()); Filename = StringRef(MappedName.begin(), MappedName.size()); - Optional Result = HM->LookupFile(Filename, HS.getFileMgr()); - if (Result) { - FixupSearchPath(); - return *Result; - } - } else if (auto Res = HS.getFileMgr().getOptionalFileRef(Dest)) { + Dest = HM->lookupFilename(Filename, Path); + } + + if (auto Res = HS.getFileMgr().getOptionalFileRef(Dest)) { FixupSearchPath(); 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; } @@ -654,6 +675,21 @@ 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) { ModMap.setTarget(Target); } @@ -992,7 +1028,7 @@ &File->getFileEntry(), isAngled, FoundByHeaderMap); // Remember this location for the next lookup we do. - CacheLookup.HitIdx = i; + cacheLookupSuccess(CacheLookup, i, IncludeLoc); return File; } @@ -1022,8 +1058,8 @@ return MSFE; } - LookupFileCacheInfo &CacheLookup = LookupFileCache[Filename]; - CacheLookup.HitIdx = LookupFileCache[ScratchFilename].HitIdx; + cacheLookupSuccess(LookupFileCache[Filename], + LookupFileCache[ScratchFilename].HitIdx, IncludeLoc); // FIXME: SuggestedModule. return File; } @@ -1362,6 +1398,13 @@ + FrameworkMap.getAllocator().getTotalMemory(); } +Optional 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) { return FrameworkNames.insert(Framework).first->first(); } diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/FwA/FrameworkA.framework/Headers/FrameworkA.h b/clang/test/Preprocessor/Inputs/search-path-usage/FwA/FrameworkA.framework/Headers/FrameworkA.h new file mode 100644 diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/FwA/FrameworkA.framework/Modules/module.modulemap b/clang/test/Preprocessor/Inputs/search-path-usage/FwA/FrameworkA.framework/Modules/module.modulemap new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/Inputs/search-path-usage/FwA/FrameworkA.framework/Modules/module.modulemap @@ -0,0 +1,3 @@ +framework module FrameworkA { + header "FrameworkA.h" +} diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/FwB/FrameworkB.framework/Headers/FrameworkB.h b/clang/test/Preprocessor/Inputs/search-path-usage/FwB/FrameworkB.framework/Headers/FrameworkB.h new file mode 100644 diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/FwB/FrameworkB.framework/Modules/module.modulemap b/clang/test/Preprocessor/Inputs/search-path-usage/FwB/FrameworkB.framework/Modules/module.modulemap new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/Inputs/search-path-usage/FwB/FrameworkB.framework/Modules/module.modulemap @@ -0,0 +1,3 @@ +framework module FrameworkB { + header "FrameworkB.h" +} diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/a/a.h b/clang/test/Preprocessor/Inputs/search-path-usage/a/a.h new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/Inputs/search-path-usage/a/a.h @@ -0,0 +1 @@ +#include_next "a.h" // #a-include-next diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/a_next/a.h b/clang/test/Preprocessor/Inputs/search-path-usage/a_next/a.h new file mode 100644 diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/b.hmap.json.template b/clang/test/Preprocessor/Inputs/search-path-usage/b.hmap.json.template new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/Inputs/search-path-usage/b.hmap.json.template @@ -0,0 +1,5 @@ +{ + "mappings": { + "b.h": "DIR/b/b.h" + } +} diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/b/b.h b/clang/test/Preprocessor/Inputs/search-path-usage/b/b.h new file mode 100644 diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/d/d.h b/clang/test/Preprocessor/Inputs/search-path-usage/d/d.h new file mode 100644 diff --git a/clang/test/Preprocessor/Inputs/search-path-usage/modulemap_abs/module.modulemap.template b/clang/test/Preprocessor/Inputs/search-path-usage/modulemap_abs/module.modulemap.template new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/Inputs/search-path-usage/modulemap_abs/module.modulemap.template @@ -0,0 +1,3 @@ +module b { + header "DIR/b/b.h" +} diff --git a/clang/test/Preprocessor/search-path-usage.m b/clang/test/Preprocessor/search-path-usage.m new file mode 100644 --- /dev/null +++ b/clang/test/Preprocessor/search-path-usage.m @@ -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