Skip to content

Commit 2663a53

Browse files
committed
support header shadowing detection
1 parent f53b624 commit 2663a53

File tree

6 files changed

+137
-61
lines changed

6 files changed

+137
-61
lines changed

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,8 @@ def ShadowFieldInConstructor : DiagGroup<"shadow-field-in-constructor",
789789
def ShadowIvar : DiagGroup<"shadow-ivar">;
790790
def ShadowUncapturedLocal : DiagGroup<"shadow-uncaptured-local">;
791791

792+
def HeaderShadowing : DiagGroup<"header-shadowing">;
793+
792794
// -Wshadow-all is a catch-all for all shadowing. -Wshadow is just the
793795
// shadowing that we think is unsafe.
794796
def Shadow : DiagGroup<"shadow", [ShadowFieldInConstructorModified,

clang/include/clang/Basic/DiagnosticLexKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,10 @@ def warn_quoted_include_in_framework_header : Warning<
951951
def warn_framework_include_private_from_public : Warning<
952952
"public framework header includes private framework header '%0'"
953953
>, InGroup<FrameworkIncludePrivateFromPublic>;
954+
def warn_header_shadowed
955+
: Warning<"multiple candidates for header '%0' found; "
956+
"using the one from '%1', shadowed by '%2'">,
957+
InGroup<HeaderShadowing>, DefaultIgnore;
954958
def warn_deprecated_module_dot_map : Warning<
955959
"'%0' as a module map name is deprecated, rename it to %select{module.modulemap|module.private.modulemap}1%select{| in the 'Modules' directory of the framework}2">,
956960
InGroup<DeprecatedModuleDotMap>;

clang/include/clang/Lex/DirectoryLookup.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ class DirectoryLookup {
181181
ModuleMap::KnownHeader *SuggestedModule,
182182
bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound,
183183
bool &IsInHeaderMap, SmallVectorImpl<char> &MappedName,
184-
bool OpenFile = true) const;
184+
bool NeedSuggest, bool OpenFile = true) const;
185185

186186
private:
187187
OptionalFileEntryRef DoFrameworkLookup(

clang/include/clang/Lex/HeaderSearch.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -809,12 +809,11 @@ class HeaderSearch {
809809

810810
/// Look up the file with the specified name and determine its owning
811811
/// module.
812-
OptionalFileEntryRef
813-
getFileAndSuggestModule(StringRef FileName, SourceLocation IncludeLoc,
814-
const DirectoryEntry *Dir, bool IsSystemHeaderDir,
815-
Module *RequestingModule,
816-
ModuleMap::KnownHeader *SuggestedModule,
817-
bool OpenFile = true, bool CacheFailures = true);
812+
OptionalFileEntryRef getFileAndSuggestModule(
813+
StringRef FileName, SourceLocation IncludeLoc, const DirectoryEntry *Dir,
814+
bool IsSystemHeaderDir, Module *RequestingModule,
815+
ModuleMap::KnownHeader *SuggestedModule, bool NeedSuggest,
816+
bool OpenFile = true, bool CacheFailures = true);
818817

819818
/// Cache the result of a successful lookup at the given include location
820819
/// using the search path at \c HitIt.

clang/lib/Lex/HeaderSearch.cpp

Lines changed: 111 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,8 @@ StringRef DirectoryLookup::getName() const {
444444
OptionalFileEntryRef HeaderSearch::getFileAndSuggestModule(
445445
StringRef FileName, SourceLocation IncludeLoc, const DirectoryEntry *Dir,
446446
bool IsSystemHeaderDir, Module *RequestingModule,
447-
ModuleMap::KnownHeader *SuggestedModule, bool OpenFile /*=true*/,
448-
bool CacheFailures /*=true*/) {
447+
ModuleMap::KnownHeader *SuggestedModule, bool NeedSuggest,
448+
bool OpenFile /*=true*/, bool CacheFailures /*=true*/) {
449449
// If we have a module map that might map this header, load it and
450450
// check whether we'll have a suggestion for a module.
451451
auto File = getFileMgr().getFileRef(FileName, OpenFile, CacheFailures);
@@ -462,6 +462,9 @@ OptionalFileEntryRef HeaderSearch::getFileAndSuggestModule(
462462
return std::nullopt;
463463
}
464464

465+
if (!NeedSuggest)
466+
return *File;
467+
465468
// If there is a module that corresponds to this header, suggest it.
466469
if (!findUsableModuleForHeader(
467470
*File, Dir ? Dir : File->getFileEntry().getDir(), RequestingModule,
@@ -478,7 +481,7 @@ OptionalFileEntryRef DirectoryLookup::LookupFile(
478481
SmallVectorImpl<char> *SearchPath, SmallVectorImpl<char> *RelativePath,
479482
Module *RequestingModule, ModuleMap::KnownHeader *SuggestedModule,
480483
bool &InUserSpecifiedSystemFramework, bool &IsFrameworkFound,
481-
bool &IsInHeaderMap, SmallVectorImpl<char> &MappedName,
484+
bool &IsInHeaderMap, SmallVectorImpl<char> &MappedName, bool NeedSuggest,
482485
bool OpenFile) const {
483486
InUserSpecifiedSystemFramework = false;
484487
IsInHeaderMap = false;
@@ -501,7 +504,7 @@ OptionalFileEntryRef DirectoryLookup::LookupFile(
501504

502505
return HS.getFileAndSuggestModule(
503506
TmpDir, IncludeLoc, getDir(), isSystemHeaderDirectory(),
504-
RequestingModule, SuggestedModule, OpenFile);
507+
RequestingModule, SuggestedModule, NeedSuggest, OpenFile);
505508
}
506509

507510
if (isFramework())
@@ -881,6 +884,35 @@ diagnoseFrameworkInclude(DiagnosticsEngine &Diags, SourceLocation IncludeLoc,
881884
<< IncludeFilename;
882885
}
883886

887+
/// Return true if a shadow has been detected and the caller should
888+
/// stop and return the first-found file and module, false otherwise.
889+
static bool checkAndStoreCandidate(
890+
ModuleMap::KnownHeader *SuggestedModule, OptionalFileEntryRef CandidateFile,
891+
StringRef CandidateDir, DiagnosticsEngine &Diags, StringRef Filename,
892+
SourceLocation IncludeLoc, ModuleMap::KnownHeader &FirstModule,
893+
OptionalFileEntryRef &FirstHeader, SmallString<1024> &FirstDir) {
894+
if (!FirstHeader) {
895+
// Found the first candidate
896+
FirstHeader = CandidateFile;
897+
FirstDir = CandidateDir;
898+
if (SuggestedModule)
899+
FirstModule = *SuggestedModule;
900+
return false;
901+
}
902+
903+
if (FirstDir != CandidateDir) {
904+
// Found a second candidate from a different directory
905+
Diags.Report(IncludeLoc, diag::warn_header_shadowed)
906+
<< Filename << FirstDir << CandidateDir;
907+
if (SuggestedModule)
908+
*SuggestedModule = FirstModule;
909+
return true;
910+
}
911+
912+
// Found a candidate from the same directory as the first one
913+
return false;
914+
}
915+
884916
/// LookupFile - Given a "foo" or \<foo> reference, look up the indicated file,
885917
/// return null on failure. isAngled indicates whether the file reference is
886918
/// for system \#include's or not (i.e. using <> instead of ""). Includers, if
@@ -923,13 +955,18 @@ OptionalFileEntryRef HeaderSearch::LookupFile(
923955
// Otherwise, just return the file.
924956
return getFileAndSuggestModule(Filename, IncludeLoc, nullptr,
925957
/*IsSystemHeaderDir*/ false,
926-
RequestingModule, SuggestedModule, OpenFile,
927-
CacheFailures);
958+
RequestingModule, SuggestedModule, true,
959+
OpenFile, CacheFailures);
928960
}
929961

930962
// This is the header that MSVC's header search would have found.
963+
bool First = true;
964+
bool NeedSuggest = true;
931965
ModuleMap::KnownHeader MSSuggestedModule;
932966
OptionalFileEntryRef MSFE;
967+
ModuleMap::KnownHeader FirstModule;
968+
OptionalFileEntryRef FirstHeader;
969+
SmallString<1024> FirstDir;
933970

934971
// Check to see if the file is in the #includer's directory. This cannot be
935972
// based on CurDir, because each includer could be a #include of a
@@ -938,7 +975,6 @@ OptionalFileEntryRef HeaderSearch::LookupFile(
938975
// headers.
939976
if (!Includers.empty() && !isAngled) {
940977
SmallString<1024> TmpDir;
941-
bool First = true;
942978
for (const auto &IncluderAndDir : Includers) {
943979
OptionalFileEntryRef Includer = IncluderAndDir.first;
944980

@@ -962,10 +998,15 @@ OptionalFileEntryRef HeaderSearch::LookupFile(
962998
}();
963999
if (OptionalFileEntryRef FE = getFileAndSuggestModule(
9641000
TmpDir, IncludeLoc, IncluderAndDir.second, IncluderIsSystemHeader,
965-
RequestingModule, SuggestedModule)) {
1001+
RequestingModule, SuggestedModule, NeedSuggest)) {
1002+
NeedSuggest = false;
1003+
9661004
if (!Includer) {
9671005
assert(First && "only first includer can have no file");
968-
return FE;
1006+
checkAndStoreCandidate(
1007+
SuggestedModule, FE, IncluderAndDir.second.getName(), Diags,
1008+
Filename, IncludeLoc, FirstModule, FirstHeader, FirstDir);
1009+
break;
9691010
}
9701011

9711012
// Leave CurDir unset.
@@ -994,22 +1035,28 @@ OptionalFileEntryRef HeaderSearch::LookupFile(
9941035
diagnoseFrameworkInclude(Diags, IncludeLoc,
9951036
IncluderAndDir.second.getName(), Filename,
9961037
*FE);
997-
return FE;
1038+
checkAndStoreCandidate(
1039+
SuggestedModule, FE, IncluderAndDir.second.getName(), Diags,
1040+
Filename, IncludeLoc, FirstModule, FirstHeader, FirstDir);
1041+
break;
9981042
}
9991043

10001044
// Otherwise, we found the path via MSVC header search rules. If
10011045
// -Wmsvc-include is enabled, we have to keep searching to see if we
10021046
// would've found this header in -I or -isystem directories.
1003-
if (Diags.isIgnored(diag::ext_pp_include_search_ms, IncludeLoc)) {
1004-
return FE;
1005-
} else {
1006-
MSFE = FE;
1007-
if (SuggestedModule) {
1008-
MSSuggestedModule = *SuggestedModule;
1009-
*SuggestedModule = ModuleMap::KnownHeader();
1010-
}
1047+
if (checkAndStoreCandidate(
1048+
SuggestedModule, FE, IncluderAndDir.second.getName(), Diags,
1049+
Filename, IncludeLoc, FirstModule, FirstHeader, FirstDir)) {
1050+
// Found mutiple candidates via MSVC rules
1051+
if (Diags.isIgnored(diag::ext_pp_include_search_ms, IncludeLoc))
1052+
return FirstHeader;
10111053
break;
10121054
}
1055+
MSFE = FE;
1056+
if (SuggestedModule) {
1057+
MSSuggestedModule = *SuggestedModule;
1058+
*SuggestedModule = ModuleMap::KnownHeader();
1059+
}
10131060
}
10141061
First = false;
10151062
}
@@ -1069,6 +1116,7 @@ OptionalFileEntryRef HeaderSearch::LookupFile(
10691116
}
10701117

10711118
SmallString<64> MappedName;
1119+
First = true;
10721120

10731121
// Check each directory in sequence to see if it contains this file.
10741122
for (; It != search_dir_end(); ++It) {
@@ -1078,7 +1126,7 @@ OptionalFileEntryRef HeaderSearch::LookupFile(
10781126
OptionalFileEntryRef File = It->LookupFile(
10791127
Filename, *this, IncludeLoc, SearchPath, RelativePath, RequestingModule,
10801128
SuggestedModule, InUserSpecifiedSystemFramework, IsFrameworkFoundInDir,
1081-
IsInHeaderMap, MappedName, OpenFile);
1129+
IsInHeaderMap, MappedName, NeedSuggest, OpenFile);
10821130
if (!MappedName.empty()) {
10831131
assert(IsInHeaderMap && "MappedName should come from a header map");
10841132
CacheLookup.MappedName =
@@ -1097,53 +1145,62 @@ OptionalFileEntryRef HeaderSearch::LookupFile(
10971145
if (!File)
10981146
continue;
10991147

1100-
CurDir = It;
1101-
1102-
IncludeNames[*File] = Filename;
1103-
1104-
// This file is a system header or C++ unfriendly if the dir is.
1105-
HeaderFileInfo &HFI = getFileInfo(*File);
1106-
HFI.DirInfo = CurDir->getDirCharacteristic();
1107-
1108-
// If the directory characteristic is User but this framework was
1109-
// user-specified to be treated as a system framework, promote the
1110-
// characteristic.
1111-
if (HFI.DirInfo == SrcMgr::C_User && InUserSpecifiedSystemFramework)
1112-
HFI.DirInfo = SrcMgr::C_System;
1113-
1114-
// If the filename matches a known system header prefix, override
1115-
// whether the file is a system header.
1116-
for (unsigned j = SystemHeaderPrefixes.size(); j; --j) {
1117-
if (Filename.starts_with(SystemHeaderPrefixes[j - 1].first)) {
1118-
HFI.DirInfo = SystemHeaderPrefixes[j-1].second ? SrcMgr::C_System
1119-
: SrcMgr::C_User;
1120-
break;
1148+
if (First) {
1149+
First = false;
1150+
NeedSuggest = false;
1151+
CurDir = It;
1152+
IncludeNames[*File] = Filename;
1153+
1154+
// This file is a system header or C++ unfriendly if the dir is.
1155+
HeaderFileInfo &HFI = getFileInfo(*File);
1156+
HFI.DirInfo = CurDir->getDirCharacteristic();
1157+
1158+
// If the directory characteristic is User but this framework was
1159+
// user-specified to be treated as a system framework, promote the
1160+
// characteristic.
1161+
if (HFI.DirInfo == SrcMgr::C_User && InUserSpecifiedSystemFramework)
1162+
HFI.DirInfo = SrcMgr::C_System;
1163+
1164+
// If the filename matches a known system header prefix, override
1165+
// whether the file is a system header.
1166+
for (unsigned j = SystemHeaderPrefixes.size(); j; --j) {
1167+
if (Filename.starts_with(SystemHeaderPrefixes[j - 1].first)) {
1168+
HFI.DirInfo = SystemHeaderPrefixes[j - 1].second ? SrcMgr::C_System
1169+
: SrcMgr::C_User;
1170+
break;
1171+
}
11211172
}
1122-
}
11231173

1124-
if (checkMSVCHeaderSearch(Diags, MSFE, &File->getFileEntry(), IncludeLoc)) {
1125-
if (SuggestedModule)
1174+
if (checkMSVCHeaderSearch(Diags, MSFE, &File->getFileEntry(),
1175+
IncludeLoc) &&
1176+
SuggestedModule)
11261177
*SuggestedModule = MSSuggestedModule;
1127-
return MSFE;
1128-
}
11291178

1130-
bool FoundByHeaderMap = !IsMapped ? false : *IsMapped;
1131-
if (!Includers.empty())
1132-
diagnoseFrameworkInclude(Diags, IncludeLoc,
1133-
Includers.front().second.getName(), Filename,
1134-
*File, isAngled, FoundByHeaderMap);
1179+
bool FoundByHeaderMap = !IsMapped ? false : *IsMapped;
1180+
if (!Includers.empty())
1181+
diagnoseFrameworkInclude(Diags, IncludeLoc,
1182+
Includers.front().second.getName(), Filename,
1183+
*File, isAngled, FoundByHeaderMap);
11351184

1136-
// Remember this location for the next lookup we do.
1137-
cacheLookupSuccess(CacheLookup, It, IncludeLoc);
1138-
return File;
1185+
// Remember this location for the next lookup we do.
1186+
cacheLookupSuccess(CacheLookup, It, IncludeLoc);
1187+
}
1188+
1189+
if (checkAndStoreCandidate(SuggestedModule, File, It->getName(), Diags,
1190+
Filename, IncludeLoc, FirstModule, FirstHeader,
1191+
FirstDir))
1192+
return FirstHeader;
11391193
}
11401194

1141-
if (checkMSVCHeaderSearch(Diags, MSFE, nullptr, IncludeLoc)) {
1195+
if (First && checkMSVCHeaderSearch(Diags, MSFE, nullptr, IncludeLoc)) {
11421196
if (SuggestedModule)
11431197
*SuggestedModule = MSSuggestedModule;
11441198
return MSFE;
11451199
}
11461200

1201+
if (FirstHeader)
1202+
return FirstHeader;
1203+
11471204
// Otherwise, didn't find it. Remember we didn't find this.
11481205
CacheLookup.HitIt = search_dir_end();
11491206
return std::nullopt;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
4+
// RUN: %clang_cc1 -Wheader-shadowing -Eonly %t/main.c -I %t/include 2>&1 | FileCheck %s --check-prefix=SHADOWING
5+
// SHADOWING: {{.*}} warning: multiple candidates for header 'header.h' found;
6+
7+
//--- main.c
8+
#include "header.h"
9+
10+
//--- include/header.h
11+
#pragma once
12+
13+
//--- header.h
14+
#pragma once

0 commit comments

Comments
 (0)