[clang] 4445135 - [clang][lex] Remark on search path usage

Jan Svoboda via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 12 03:21:02 PDT 2021


Author: Jan Svoboda
Date: 2021-10-12T12:20:55+02:00
New Revision: 444513510999e4c1ea23253654196793834d53bf

URL: https://github.com/llvm/llvm-project/commit/444513510999e4c1ea23253654196793834d53bf
DIFF: https://github.com/llvm/llvm-project/commit/444513510999e4c1ea23253654196793834d53bf.diff

LOG: [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

Added: 
    clang/test/Preprocessor/Inputs/search-path-usage/FwA/FrameworkA.framework/Headers/FrameworkA.h
    clang/test/Preprocessor/Inputs/search-path-usage/FwA/FrameworkA.framework/Modules/module.modulemap
    clang/test/Preprocessor/Inputs/search-path-usage/FwB/FrameworkB.framework/Headers/FrameworkB.h
    clang/test/Preprocessor/Inputs/search-path-usage/FwB/FrameworkB.framework/Modules/module.modulemap
    clang/test/Preprocessor/Inputs/search-path-usage/a/a.h
    clang/test/Preprocessor/Inputs/search-path-usage/a_next/a.h
    clang/test/Preprocessor/Inputs/search-path-usage/b.hmap.json.template
    clang/test/Preprocessor/Inputs/search-path-usage/b/b.h
    clang/test/Preprocessor/Inputs/search-path-usage/d/d.h
    clang/test/Preprocessor/Inputs/search-path-usage/modulemap_abs/module.modulemap.template
    clang/test/Preprocessor/search-path-usage.m

Modified: 
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticLexKinds.td
    clang/include/clang/Lex/HeaderMap.h
    clang/include/clang/Lex/HeaderSearch.h
    clang/lib/Frontend/InitHeaderSearch.cpp
    clang/lib/Lex/HeaderMap.cpp
    clang/lib/Lex/HeaderSearch.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index d9db3482dbda7..0d8c2cb5b67d4 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -751,6 +751,7 @@ def UnusedLocalTypedef : DiagGroup<"unused-local-typedef">;
 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
index 7fbf19ed6cb3f..92c6b0bbffc30 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -446,6 +446,9 @@ def warn_pp_hdrstop_filename_ignored : Warning<
   "#pragma hdrstop filename not supported, "
   "/Fp can be used to specify precompiled header filename">,
   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<
   "'%0' file not found with <angled> %select{include|import}1; "
   "use \"quotes\" instead">;

diff  --git a/clang/include/clang/Lex/HeaderMap.h b/clang/include/clang/Lex/HeaderMap.h
index 53108b00bd16d..ca6a49bae3bfe 100644
--- a/clang/include/clang/Lex/HeaderMap.h
+++ b/clang/include/clang/Lex/HeaderMap.h
@@ -77,13 +77,6 @@ class HeaderMap : private HeaderMapImpl {
   static std::unique_ptr<HeaderMap> 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<FileEntryRef> 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
index 372589d75d965..1ab1b4f235745 100644
--- a/clang/include/clang/Lex/HeaderSearch.h
+++ b/clang/include/clang/Lex/HeaderSearch.h
@@ -172,6 +172,9 @@ class HeaderSearch {
   /// Header-search options used to initialize this header search.
   std::shared_ptr<HeaderSearchOptions> HSOpts;
 
+  /// Mapping from SearchDir to HeaderSearchOptions::UserEntries indices.
+  llvm::DenseMap<unsigned, unsigned> SearchDirToHSEntry;
+
   DiagnosticsEngine &Diags;
   FileManager &FileMgr;
 
@@ -182,6 +185,9 @@ class HeaderSearch {
   /// NoCurDirSearch is true, then the check for the file in the current
   /// directory is suppressed.
   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 SystemDirIdx = 0;
   bool NoCurDirSearch = false;
@@ -280,15 +286,17 @@ class HeaderSearch {
   DiagnosticsEngine &getDiags() const { return Diags; }
 
   /// Interface for setting the file search paths.
-  void SetSearchPaths(const std::vector<DirectoryLookup> &dirs,
-                      unsigned angledDirIdx, unsigned systemDirIdx,
-                      bool noCurDirSearch) {
+  void SetSearchPaths(std::vector<DirectoryLookup> dirs, unsigned angledDirIdx,
+                      unsigned systemDirIdx, bool noCurDirSearch,
+                      llvm::DenseMap<unsigned, unsigned> 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 @@ class HeaderSearch {
   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 @@ class HeaderSearch {
     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
   /// FileEntry, uniquing them through the 'HeaderMaps' datastructure.
   const HeaderMap *CreateHeaderMap(const FileEntry *FE);
@@ -714,6 +727,14 @@ class HeaderSearch {
                           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 @@ class HeaderSearch {
 
   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.
   StringRef getUniqueFrameworkName(StringRef Framework);
 

diff  --git a/clang/lib/Frontend/InitHeaderSearch.cpp b/clang/lib/Frontend/InitHeaderSearch.cpp
index ba9f96384f819..ed1314f3b03db 100644
--- a/clang/lib/Frontend/InitHeaderSearch.cpp
+++ b/clang/lib/Frontend/InitHeaderSearch.cpp
@@ -36,9 +36,11 @@ namespace {
 struct DirectoryLookupInfo {
   IncludeDirGroup Group;
   DirectoryLookup Lookup;
+  Optional<unsigned> UserEntryIdx;
 
-  DirectoryLookupInfo(IncludeDirGroup Group, DirectoryLookup Lookup)
-      : Group(Group), Lookup(Lookup) {}
+  DirectoryLookupInfo(IncludeDirGroup Group, DirectoryLookup Lookup,
+                      Optional<unsigned> UserEntryIdx)
+      : Group(Group), Lookup(Lookup), UserEntryIdx(UserEntryIdx) {}
 };
 
 /// InitHeaderSearch - This class makes it easier to set the search paths of
@@ -60,13 +62,15 @@ class InitHeaderSearch {
   /// 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<unsigned> 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<unsigned> UserEntryIdx = None);
 
   /// AddSystemHeaderPrefix - Add the specified prefix to the system header
   /// prefix list.
@@ -119,22 +123,25 @@ static bool CanPrefixSysroot(StringRef Path) {
 }
 
 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
   // 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<unsigned> UserEntryIdx) {
   assert(!Path.isTriviallyEmpty() && "can't handle empty path here");
 
   FileManager &FM = Headers.getFileMgr();
@@ -160,7 +167,8 @@ bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,
 
   // 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 @@ bool InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group,
       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 @@ void InitHeaderSearch::AddDefaultIncludePaths(const LangOptions &Lang,
 /// 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<DirectoryLookup> &SearchList,
+static unsigned RemoveDuplicates(std::vector<DirectoryLookupInfo> &SearchList,
                                  unsigned First, bool Verbose) {
   llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs;
   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) {
     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 @@ static unsigned RemoveDuplicates(std::vector<DirectoryLookup> &SearchList,
       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 
diff erent lookup types, then they can't be the dupe.
         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
       // 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 @@ static unsigned RemoveDuplicates(std::vector<DirectoryLookup> &SearchList,
   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) {
   // Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList.
-  std::vector<DirectoryLookup> SearchList;
+  std::vector<DirectoryLookupInfo> 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 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
 
   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 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
          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 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
   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 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) {
     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 @@ void clang::ApplyHeaderSearchOptions(HeaderSearch &HS,
   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
index ae5e6b221953f..0001fc348eda6 100644
--- a/clang/lib/Lex/HeaderMap.cpp
+++ b/clang/lib/Lex/HeaderMap.cpp
@@ -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,
                                         SmallVectorImpl<char> &DestPath) const {
   const HMapHeader &Hdr = getHeader();

diff  --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp
index 539e0d880fe3b..b2fc2d4eb1a98 100644
--- a/clang/lib/Lex/HeaderSearch.cpp
+++ b/clang/lib/Lex/HeaderSearch.cpp
@@ -108,6 +108,20 @@ void HeaderSearch::PrintStats() {
                << 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
 /// FileEntry, uniquing them through the 'HeaderMaps' datastructure.
 const HeaderMap *HeaderSearch::CreateHeaderMap(const FileEntry *FE) {
@@ -262,10 +276,11 @@ Module *HeaderSearch::lookupModule(StringRef ModuleName, StringRef SearchName,
                                    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 @@ Module *HeaderSearch::lookupModule(StringRef ModuleName, StringRef SearchName,
       break;
   }
 
+  if (Module)
+    noteLookupUsage(Idx, ImportLoc);
+
   return Module;
 }
 
@@ -440,16 +458,19 @@ Optional<FileEntryRef> DirectoryLookup::LookupFile(
   if (llvm::sys::path::is_relative(Dest)) {
     MappedName.append(Dest.begin(), Dest.end());
     Filename = StringRef(MappedName.begin(), MappedName.size());
-    Optional<FileEntryRef> 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 @@ Optional<FileEntryRef> DirectoryLookup::DoFrameworkLookup(
   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 @@ Optional<FileEntryRef> HeaderSearch::LookupFile(
           &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 @@ Optional<FileEntryRef> HeaderSearch::LookupFile(
         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 @@ size_t HeaderSearch::getTotalMemory() const {
     + 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) {
   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
index 0000000000000..e69de29bb2d1d

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
index 0000000000000..578d1753b8e3c
--- /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
index 0000000000000..e69de29bb2d1d

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
index 0000000000000..887d191a0cac6
--- /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
index 0000000000000..db7ad0f06bb7a
--- /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
index 0000000000000..e69de29bb2d1d

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
index 0000000000000..e31a9a8efe285
--- /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
index 0000000000000..e69de29bb2d1d

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
index 0000000000000..e69de29bb2d1d

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
index 0000000000000..6a370780a1cea
--- /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
index 0000000000000..66f626808cf6f
--- /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
+ at import b; // \
+// expected-remark-re {{search path used: '{{.*}}/modulemap_abs'}}
+#endif


        


More information about the cfe-commits mailing list