jansvoboda11 created this revision.
jansvoboda11 added reviewers: Bigcheese, dexonsmith.
jansvoboda11 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
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`.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D102923
Files:
clang/include/clang/Basic/DiagnosticLexKinds.td
clang/include/clang/Lex/HeaderSearch.h
clang/lib/Frontend/InitHeaderSearch.cpp
clang/lib/Lex/HeaderSearch.cpp
clang/test/Preprocessor/Inputs/header-search-user-entries/a/a.h
clang/test/Preprocessor/Inputs/header-search-user-entries/a_next/a.h
clang/test/Preprocessor/Inputs/header-search-user-entries/b/b.h
clang/test/Preprocessor/Inputs/header-search-user-entries/d/d.h
clang/test/Preprocessor/header-search-user-entries.c
Index: clang/test/Preprocessor/header-search-user-entries.c
===================================================================
--- /dev/null
+++ clang/test/Preprocessor/header-search-user-entries.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -fsyntax-only %s -Rinclude-header-search-usage \
+// RUN: -I%S/Inputs/header-search-user-entries/a -I%S/Inputs/header-search-user-entries/a_next \
+// RUN: -I%S/Inputs/header-search-user-entries/b -I%S/Inputs/header-search-user-entries/c \
+// RUN: -I%S/Inputs/header-search-user-entries/d 2>&1 | FileCheck %s
+
+#include "a.h"
+#include "d.h"
+
+// CHECK: remark: user-provided search path used '{{.*}}/header-search-user-entries/a'
+// CHECK: remark: user-provided search path used '{{.*}}/header-search-user-entries/a_next'
+// CHECK: remark: user-provided search path used '{{.*}}/header-search-user-entries/d'
Index: clang/test/Preprocessor/Inputs/header-search-user-entries/a/a.h
===================================================================
--- /dev/null
+++ clang/test/Preprocessor/Inputs/header-search-user-entries/a/a.h
@@ -0,0 +1 @@
+#include_next "a.h"
Index: clang/lib/Lex/HeaderSearch.cpp
===================================================================
--- clang/lib/Lex/HeaderSearch.cpp
+++ clang/lib/Lex/HeaderSearch.cpp
@@ -108,6 +108,18 @@
<< 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) {
+ if (SearchDirsUsage[i]) {
+ auto UserEntryIdxIt = SearchDirToHSEntry.find(i);
+ 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) {
@@ -649,6 +661,17 @@
return None;
}
+void HeaderSearch::CacheLookupSuccess(LookupFileCacheInfo &CacheLookup,
+ unsigned HitIdx) {
+ CacheLookup.HitIdx = HitIdx;
+ SearchDirsUsage[HitIdx] = true;
+
+ auto UserEntryIdxIt = SearchDirToHSEntry.find(HitIdx);
+ if (UserEntryIdxIt != SearchDirToHSEntry.end())
+ Diags.Report(diag::remark_pp_include_header_search_usage)
+ << HSOpts->UserEntries[UserEntryIdxIt->second].Path;
+}
+
void HeaderSearch::setTarget(const TargetInfo &Target) {
ModMap.setTarget(Target);
}
@@ -987,7 +1010,7 @@
&File->getFileEntry(), isAngled, FoundByHeaderMap);
// Remember this location for the next lookup we do.
- CacheLookup.HitIdx = i;
+ CacheLookupSuccess(CacheLookup, i);
return File;
}
@@ -1017,8 +1040,8 @@
return MSFE;
}
- LookupFileCacheInfo &CacheLookup = LookupFileCache[Filename];
- CacheLookup.HitIdx = LookupFileCache[ScratchFilename].HitIdx;
+ CacheLookupSuccess(LookupFileCache[Filename],
+ LookupFileCache[ScratchFilename].HitIdx);
// FIXME: SuggestedModule.
return File;
}
Index: clang/lib/Frontend/InitHeaderSearch.cpp
===================================================================
--- clang/lib/Frontend/InitHeaderSearch.cpp
+++ clang/lib/Frontend/InitHeaderSearch.cpp
@@ -36,9 +36,11 @@
struct IncludePathInfo {
IncludeDirGroup Group;
DirectoryLookup Path;
+ Optional<unsigned> UserEntryIdx;
- IncludePathInfo(IncludeDirGroup Group, DirectoryLookup Path)
- : Group(Group), Path(Path) {}
+ IncludePathInfo(IncludeDirGroup Group, DirectoryLookup Path,
+ Optional<unsigned> UserEntryIdx)
+ : Group(Group), Path(Path), 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<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 @@
}
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 @@
// 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<DirectoryLookup> &SearchList,
+static unsigned RemoveDuplicates(std::vector<IncludePathInfo> &SearchList,
unsigned First, bool Verbose) {
llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs;
llvm::SmallPtrSet<const DirectoryEntry *, 8> 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].Path;
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].Path;
// 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].Path.getDirCharacteristic() == SrcMgr::C_User)
DirToRemove = FirstDir;
}
@@ -554,16 +563,35 @@
return NonSystemRemoved;
}
+/// Extract DirectoryLookups from IncludePathInfos.
+static std::vector<DirectoryLookup>
+ExtractLookups(const std::vector<IncludePathInfo> &Infos) {
+ std::vector<DirectoryLookup> Lookups;
+ Lookups.reserve(Infos.size());
+ llvm::transform(Infos, std::back_inserter(Lookups),
+ [](const IncludePathInfo &Info) { return Info.Path; });
+ return Lookups;
+}
+
+/// Collect the mapping between indices of DirectoryLookups and user entries.
+static llvm::DenseMap<unsigned, unsigned>
+MapLookupsToUserEntries(const std::vector<IncludePathInfo> &Infos) {
+ llvm::DenseMap<unsigned, unsigned> LookupsToUserEntries;
+ for (unsigned i = 0, e = Infos.size(); i < e; ++i)
+ 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<IncludePathInfo> SearchList;
SearchList.reserve(IncludePath.size());
// Quoted arguments go first.
for (auto &Include : IncludePath)
if (Include.Group == Quoted)
- SearchList.push_back(Include.Path);
+ SearchList.push_back(Include);
// Deduplicate and remember index.
RemoveDuplicates(SearchList, 0, Verbose);
@@ -571,7 +599,7 @@
for (auto &Include : IncludePath)
if (Include.Group == Angled || Include.Group == IndexHeaderMap)
- SearchList.push_back(Include.Path);
+ SearchList.push_back(Include);
RemoveDuplicates(SearchList, NumQuoted, Verbose);
unsigned NumAngled = SearchList.size();
@@ -583,11 +611,11 @@
Include.Group == CXXSystem) ||
(Lang.ObjC && !Lang.CPlusPlus && Include.Group == ObjCSystem) ||
(Lang.ObjC && Lang.CPlusPlus && Include.Group == ObjCXXSystem))
- SearchList.push_back(Include.Path);
+ SearchList.push_back(Include);
for (auto &Include : IncludePath)
if (Include.Group == After)
- SearchList.push_back(Include.Path);
+ 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 +624,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, MapLookupsToUserEntries(SearchList));
Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes);
@@ -606,14 +635,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].Path.getName();
const char *Suffix;
- if (SearchList[i].isNormalDir())
+ if (SearchList[i].Path.isNormalDir())
Suffix = "";
- else if (SearchList[i].isFramework())
+ else if (SearchList[i].Path.isFramework())
Suffix = " (framework directory)";
else {
- assert(SearchList[i].isHeaderMap() && "Unknown DirectoryLookup");
+ assert(SearchList[i].Path.isHeaderMap() && "Unknown DirectoryLookup");
Suffix = " (headermap)";
}
llvm::errs() << " " << Name << Suffix << "\n";
@@ -632,9 +661,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);
}
}
Index: clang/include/clang/Lex/HeaderSearch.h
===================================================================
--- clang/include/clang/Lex/HeaderSearch.h
+++ clang/include/clang/Lex/HeaderSearch.h
@@ -161,6 +161,9 @@
/// Header-search options used to initialize this header search.
std::shared_ptr<HeaderSearchOptions> HSOpts;
+ /// Mapping from SearchDir to HeaderSearchOptions::Entry indices.
+ llvm::DenseMap<unsigned, unsigned> SearchDirToHSEntry;
+
DiagnosticsEngine &Diags;
FileManager &FileMgr;
@@ -171,6 +174,7 @@
/// NoCurDirSearch is true, then the check for the file in the current
/// directory is suppressed.
std::vector<DirectoryLookup> SearchDirs;
+ std::vector<bool> SearchDirsUsage;
unsigned AngledDirIdx = 0;
unsigned SystemDirIdx = 0;
bool NoCurDirSearch = false;
@@ -271,13 +275,16 @@
/// Interface for setting the file search paths.
void SetSearchPaths(const std::vector<DirectoryLookup> &dirs,
unsigned angledDirIdx, unsigned systemDirIdx,
- bool noCurDirSearch) {
+ bool noCurDirSearch,
+ llvm::DenseMap<unsigned, unsigned> searchDirToHSEntry) {
assert(angledDirIdx <= systemDirIdx && systemDirIdx <= dirs.size() &&
"Directory indices are unordered");
SearchDirs = dirs;
+ SearchDirsUsage.assign(dirs.size(), false);
AngledDirIdx = angledDirIdx;
SystemDirIdx = systemDirIdx;
NoCurDirSearch = noCurDirSearch;
+ SearchDirToHSEntry = std::move(searchDirToHSEntry);
//LookupFileCache.clear();
}
@@ -285,6 +292,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++;
@@ -492,6 +500,10 @@
return FI && FI->isImport;
}
+ /// Determine which user-provided search directories have been used so far and
+ /// mark their index with '1' 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);
@@ -694,6 +706,9 @@
Module *RequestingModule,
ModuleMap::KnownHeader *SuggestedModule);
+ /// Cache a successful lookup.
+ void CacheLookupSuccess(LookupFileCacheInfo &CacheLookup, unsigned HitIdx);
+
public:
/// Retrieve the module map.
ModuleMap &getModuleMap() { return ModMap; }
Index: clang/include/clang/Basic/DiagnosticLexKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticLexKinds.td
+++ clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -423,6 +423,9 @@
"#pragma hdrstop filename not supported, "
"/Fp can be used to specify precompiled header filename">,
InGroup<ClangClPch>;
+def remark_pp_include_header_search_usage : Remark<
+ "user-provided search path used '%0'">,
+ InGroup<DiagGroup<"include-header-search-usage">>;
def err_pp_file_not_found_angled_include_not_fatal : Error<
"'%0' file not found with <angled> %select{include|import}1; "
"use \"quotes\" instead">;
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits