[clang] [clang][modules] Diagnose headers owned by multiple modules (PR #188538)
Michael Spencer via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 9 14:00:42 PDT 2026
https://github.com/Bigcheese updated https://github.com/llvm/llvm-project/pull/188538
>From fd727ce5ad3c60eb63deb22aa99c088e61e4f1ad Mon Sep 17 00:00:00 2001
From: Michael Spencer <bigcheesegs at gmail.com>
Date: Mon, 23 Mar 2026 16:15:33 +0000
Subject: [PATCH 1/3] [clang][modules] Diagnose headers owned by multiple
modules
Add -Wduplicate-header-ownership, an off by default warning that fires
at include time when a header is owned by multiple top-level modules.
This helps catch overlapping module maps that can cause confusing
module resolution.
Assisted-by: claude-opus-4.6
---
.../include/clang/Basic/DiagnosticLexKinds.td | 7 +
clang/include/clang/Basic/Module.h | 3 +
clang/include/clang/Lex/ModuleMap.h | 26 +++-
clang/lib/Lex/ModuleMap.cpp | 118 +++++++++++++--
.../test/Modules/duplicate-header-ownership.c | 138 ++++++++++++++++++
5 files changed, 277 insertions(+), 15 deletions(-)
create mode 100644 clang/test/Modules/duplicate-header-ownership.c
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 5eceeced311f2..291d6391dfb1f 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -875,6 +875,13 @@ def err_mmap_module_redefinition : Error<
def note_mmap_prev_definition : Note<"previously defined here">;
def err_mmap_umbrella_clash : Error<
"umbrella for module '%0' already covers this directory">;
+def warn_mmap_duplicate_header_ownership : Warning<
+ "header '%0' is owned by multiple modules">,
+ InGroup<DiagGroup<"duplicate-header-ownership">>, DefaultIgnore;
+def note_mmap_header_owned_by : Note<
+ "header owned by module '%0' here">;
+def note_mmap_header_covered_by_umbrella : Note<
+ "header covered by umbrella for module '%0' here">;
def err_mmap_module_id : Error<
"expected a module name or '*'">;
def err_mmap_expected_library_name : Error<
diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h
index 016cc2ce684b8..94eb877d0a4e9 100644
--- a/clang/include/clang/Basic/Module.h
+++ b/clang/include/clang/Basic/Module.h
@@ -311,6 +311,9 @@ class alignas(8) Module {
/// The umbrella header or directory.
std::variant<std::monostate, FileEntryRef, DirectoryEntryRef> Umbrella;
+ /// The location of the umbrella header or directory declaration.
+ SourceLocation UmbrellaDeclLoc;
+
/// The module signature.
ASTFileSignature Signature;
diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h
index 570a68c37fac4..46655c3aa6565 100644
--- a/clang/include/clang/Lex/ModuleMap.h
+++ b/clang/include/clang/Lex/ModuleMap.h
@@ -219,6 +219,18 @@ class ModuleMap {
/// header.
llvm::DenseMap<const DirectoryEntry *, Module *> UmbrellaDirs;
+ /// Mapping from (header, (sub)module) pairs to the source location where
+ /// the header was added to the module (the header directive location).
+ /// TODO: Consider moving this into Module::Header and serializing it into
+ /// PCMs so that locations are available for headers deserialized from
+ /// modules. Need to evaluate size/perf overhead of adding a SourceLocation
+ /// to the serialization format for this diagnostic.
+ llvm::DenseMap<std::pair<const FileEntry *, const Module *>, SourceLocation>
+ HeaderOwnerLocs;
+
+ /// Headers for which we've already diagnosed duplicate ownership.
+ llvm::DenseSet<const FileEntry *> DiagnosedDuplicateHeaders;
+
/// A generation counter that is used to test whether modules of the
/// same name may shadow or are illegal redefinitions.
///
@@ -355,6 +367,11 @@ class ModuleMap {
/// associated with a specific module (e.g. in /usr/include).
HeadersMap::iterator findKnownHeader(FileEntryRef File);
+ /// Warn if a header is owned by multiple top-level modules.
+ void diagnoseDuplicateHeaderOwnership(SourceLocation FilenameLoc,
+ StringRef Filename, FileEntryRef File,
+ HeadersMap::iterator Known);
+
/// Searches for a module whose umbrella directory contains \p File.
///
/// \param File The header to search for.
@@ -712,17 +729,20 @@ class ModuleMap {
void
setUmbrellaHeaderAsWritten(Module *Mod, FileEntryRef UmbrellaHeader,
const Twine &NameAsWritten,
- const Twine &PathRelativeToRootModuleDirectory);
+ const Twine &PathRelativeToRootModuleDirectory,
+ SourceLocation Loc = SourceLocation());
/// Sets the umbrella directory of the given module to the given directory.
void setUmbrellaDirAsWritten(Module *Mod, DirectoryEntryRef UmbrellaDir,
const Twine &NameAsWritten,
- const Twine &PathRelativeToRootModuleDirectory);
+ const Twine &PathRelativeToRootModuleDirectory,
+ SourceLocation Loc = SourceLocation());
/// Adds this header to the given module.
/// \param Role The role of the header wrt the module.
void addHeader(Module *Mod, Module::Header Header,
- ModuleHeaderRole Role, bool Imported = false);
+ ModuleHeaderRole Role, bool Imported = false,
+ SourceLocation Loc = SourceLocation());
/// Parse a module map without creating `clang::Module` instances.
bool parseModuleMapFile(FileEntryRef File, bool IsSystem,
diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp
index 6c991430cb08b..a8016cf8afa1d 100644
--- a/clang/lib/Lex/ModuleMap.cpp
+++ b/clang/lib/Lex/ModuleMap.cpp
@@ -301,11 +301,13 @@ void ModuleMap::resolveHeader(Module *Mod,
else
// Record this umbrella header.
setUmbrellaHeaderAsWritten(Mod, *File, Header.FileName,
- RelativePathName.str());
+ RelativePathName.str(),
+ Header.FileNameLoc);
} else {
Module::Header H = {Header.FileName, std::string(RelativePathName),
*File};
- addHeader(Mod, H, headerKindToRole(Header.Kind));
+ addHeader(Mod, H, headerKindToRole(Header.Kind), /*Imported=*/false,
+ Header.FileNameLoc);
}
} else if (Header.HasBuiltinHeader && !Header.Size && !Header.ModTime) {
// There's a builtin header but no corresponding on-disk header. Assume
@@ -486,21 +488,24 @@ void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule,
bool RequestingModuleIsModuleInterface,
SourceLocation FilenameLoc,
StringRef Filename, FileEntryRef File) {
- // No errors for indirect modules. This may be a bit of a problem for modules
- // with no source files.
- if (getTopLevelOrNull(RequestingModule) != getTopLevelOrNull(SourceModule))
- return;
-
if (RequestingModule) {
resolveUses(RequestingModule, /*Complain=*/false);
resolveHeaderDirectives(RequestingModule, /*File=*/std::nullopt);
}
+ HeadersMap::iterator Known = findKnownHeader(File);
+
+ diagnoseDuplicateHeaderOwnership(FilenameLoc, Filename, File, Known);
+
+ // No errors for indirect modules. This may be a bit of a problem for modules
+ // with no source files.
+ if (getTopLevelOrNull(RequestingModule) != getTopLevelOrNull(SourceModule))
+ return;
+
bool Excluded = false;
Module *Private = nullptr;
Module *NotUsed = nullptr;
- HeadersMap::iterator Known = findKnownHeader(File);
if (Known != Headers.end()) {
for (const KnownHeader &Header : Known->second) {
// Excluded headers don't really belong to a module.
@@ -564,6 +569,86 @@ void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule,
}
}
+void ModuleMap::diagnoseDuplicateHeaderOwnership(SourceLocation FilenameLoc,
+ StringRef Filename,
+ FileEntryRef File,
+ HeadersMap::iterator Known) {
+ if (Known == Headers.end())
+ return;
+
+ if (Diags.isIgnored(diag::warn_mmap_duplicate_header_ownership,
+ FilenameLoc))
+ return;
+
+ // Only diagnose each header once.
+ if (!DiagnosedDuplicateHeaders.insert(&File.getFileEntry()).second)
+ return;
+
+ struct OwnerInfo {
+ Module *Mod;
+ SourceLocation Loc;
+ bool IsUmbrella;
+ };
+
+ // Collect distinct top-level modules that explicitly own this header with
+ // a modular (non-textual, non-excluded) role.
+ SmallVector<OwnerInfo, 2> OwningModules;
+ llvm::SmallPtrSet<Module *, 2> SeenTopLevel;
+ for (const KnownHeader &H : Known->second) {
+ if (!isModular(H.getRole()))
+ continue;
+ Module *TopLevel = H.getModule()->getTopLevelModule();
+ if (!SeenTopLevel.insert(TopLevel).second)
+ continue;
+ auto It = HeaderOwnerLocs.find({&File.getFileEntry(), H.getModule()});
+ SourceLocation OwnerLoc =
+ It != HeaderOwnerLocs.end() ? It->second : SourceLocation();
+ OwningModules.push_back({TopLevel, OwnerLoc, /*IsUmbrella=*/false});
+ }
+
+ // Need at least one explicit owner for there to be a conflict, since
+ // umbrella coverage can only add one more.
+ if (OwningModules.empty())
+ return;
+
+ // Also check umbrella directory coverage for additional owners from different
+ // top-level modules — but only if the header isn't excluded from the umbrella
+ // module. Explicit headers take precedence over umbrella dirs in module
+ // resolution, but a header owned by one module that another module's umbrella
+ // covers can still create problems.
+ SmallVector<DirectoryEntryRef, 2> IntermediateDirs;
+ if (KnownHeader UmbrellaOwner =
+ findHeaderInUmbrellaDirs(File, IntermediateDirs)) {
+ Module *TopLevel = UmbrellaOwner.getModule()->getTopLevelModule();
+ // Only add if it's a different top-level module and the header isn't
+ // excluded from the umbrella module.
+ if (SeenTopLevel.insert(TopLevel).second) {
+ // Check that the header isn't excluded in the umbrella module.
+ bool IsExcluded = llvm::any_of(Known->second, [TopLevel](const KnownHeader &H) {
+ return H.getModule()->getTopLevelModule() == TopLevel &&
+ H.getRole() == ExcludedHeader;
+ });
+ if (!IsExcluded) {
+ OwningModules.push_back(
+ {TopLevel, UmbrellaOwner.getModule()->UmbrellaDeclLoc,
+ /*IsUmbrella=*/true});
+ }
+ }
+ }
+
+ if (OwningModules.size() < 2)
+ return;
+
+ Diags.Report(FilenameLoc, diag::warn_mmap_duplicate_header_ownership)
+ << Filename;
+ for (const auto &Owner : OwningModules) {
+ unsigned NoteID = Owner.IsUmbrella
+ ? diag::note_mmap_header_covered_by_umbrella
+ : diag::note_mmap_header_owned_by;
+ Diags.Report(Owner.Loc, NoteID) << Owner.Mod->getFullModuleName();
+ }
+}
+
static bool isBetterKnownHeader(const ModuleMap::KnownHeader &New,
const ModuleMap::KnownHeader &Old) {
// Prefer available modules.
@@ -1212,9 +1297,12 @@ Module *ModuleMap::createShadowedModule(StringRef Name, bool IsFramework,
void ModuleMap::setUmbrellaHeaderAsWritten(
Module *Mod, FileEntryRef UmbrellaHeader, const Twine &NameAsWritten,
- const Twine &PathRelativeToRootModuleDirectory) {
+ const Twine &PathRelativeToRootModuleDirectory, SourceLocation Loc) {
Headers[UmbrellaHeader].push_back(KnownHeader(Mod, NormalHeader));
+ if (Loc.isValid())
+ HeaderOwnerLocs[{&UmbrellaHeader.getFileEntry(), Mod}] = Loc;
Mod->Umbrella = UmbrellaHeader;
+ Mod->UmbrellaDeclLoc = Loc;
Mod->UmbrellaAsWritten = NameAsWritten.str();
Mod->UmbrellaRelativeToRootModuleDirectory =
PathRelativeToRootModuleDirectory.str();
@@ -1227,8 +1315,9 @@ void ModuleMap::setUmbrellaHeaderAsWritten(
void ModuleMap::setUmbrellaDirAsWritten(
Module *Mod, DirectoryEntryRef UmbrellaDir, const Twine &NameAsWritten,
- const Twine &PathRelativeToRootModuleDirectory) {
+ const Twine &PathRelativeToRootModuleDirectory, SourceLocation Loc) {
Mod->Umbrella = UmbrellaDir;
+ Mod->UmbrellaDeclLoc = Loc;
Mod->UmbrellaAsWritten = NameAsWritten.str();
Mod->UmbrellaRelativeToRootModuleDirectory =
PathRelativeToRootModuleDirectory.str();
@@ -1307,7 +1396,8 @@ void ModuleMap::resolveHeaderDirectives(
}
void ModuleMap::addHeader(Module *Mod, Module::Header Header,
- ModuleHeaderRole Role, bool Imported) {
+ ModuleHeaderRole Role, bool Imported,
+ SourceLocation Loc) {
KnownHeader KH(Mod, Role);
FileEntryRef HeaderEntry = Header.Entry;
@@ -1319,6 +1409,9 @@ void ModuleMap::addHeader(Module *Mod, Module::Header Header,
if (llvm::is_contained(HeaderList, KH))
return;
+ if (Loc.isValid())
+ HeaderOwnerLocs[{&HeaderEntry.getFileEntry(), Mod}] = Loc;
+
HeaderList.push_back(KH);
Mod->addHeader(headerRoleToKind(Role), std::move(Header));
@@ -2092,7 +2185,8 @@ void ModuleMapLoader::handleUmbrellaDirDecl(
}
// Record this umbrella directory.
- Map.setUmbrellaDirAsWritten(ActiveModule, *Dir, DirNameAsWritten, DirName);
+ Map.setUmbrellaDirAsWritten(ActiveModule, *Dir, DirNameAsWritten, DirName,
+ UDD.Location);
}
void ModuleMapLoader::handleExportDecl(const modulemap::ExportDecl &ED) {
diff --git a/clang/test/Modules/duplicate-header-ownership.c b/clang/test/Modules/duplicate-header-ownership.c
new file mode 100644
index 0000000000000..26bad14f4bc44
--- /dev/null
+++ b/clang/test/Modules/duplicate-header-ownership.c
@@ -0,0 +1,138 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// Test that we warn when two modules own the same header (at include time).
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -I%t/headers \
+// RUN: -fmodules-cache-path=%t/cache %t/tu.c -fsyntax-only \
+// RUN: -Wduplicate-header-ownership -verify
+
+// Test that the warning is off by default.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -I%t/headers \
+// RUN: -fmodules-cache-path=%t/cache2 %t/tu-no-warn.c -fsyntax-only -verify
+
+// Test that no warning fires if the conflicting header is not included.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -I%t/headers \
+// RUN: -fmodules-cache-path=%t/cache3 %t/tu-no-include.c -fsyntax-only \
+// RUN: -Wduplicate-header-ownership -verify
+
+// Test umbrella header in different module conflicts with explicit header.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -I%t/umbrella \
+// RUN: -fmodules-cache-path=%t/cache4 %t/tu-umbrella.c -fsyntax-only \
+// RUN: -Wduplicate-header-ownership -verify
+
+// Test umbrella dir in different module conflicts with explicit header.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -I%t/umbrella-dir \
+// RUN: -fmodules-cache-path=%t/cache5 %t/tu-umbrella-dir.c -fsyntax-only \
+// RUN: -Wduplicate-header-ownership -verify
+
+// Test that explicit header + umbrella in the SAME module is fine.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -I%t/same-module \
+// RUN: -fmodules-cache-path=%t/cache6 %t/tu-same-module.c -fsyntax-only \
+// RUN: -Wduplicate-header-ownership -verify
+
+// Test that excluded header under another module's umbrella is fine.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -I%t/excluded \
+// RUN: -fmodules-cache-path=%t/cache7 %t/tu-excluded.c -fsyntax-only \
+// RUN: -Wduplicate-header-ownership -verify
+
+//--- headers/a.h
+void a(void);
+
+//--- headers/b.h
+void b(void);
+
+//--- headers/module.modulemap
+module A {
+ header "a.h"
+}
+
+module B {
+ header "a.h"
+}
+
+module C {
+ header "b.h"
+}
+
+//--- tu.c
+#include "a.h"
+// expected-warning at -1 {{header 'a.h' is owned by multiple modules}}
+// expected-note@* {{header owned by module 'A' here}}
+// expected-note@* {{header owned by module 'B' here}}
+
+//--- tu-no-warn.c
+// expected-no-diagnostics
+#include "a.h"
+
+//--- tu-no-include.c
+// expected-no-diagnostics
+#include "b.h"
+
+//--- umbrella/umbrella.h
+#include "d.h"
+
+//--- umbrella/d.h
+void d(void);
+
+//--- umbrella/module.modulemap
+module D {
+ umbrella header "umbrella.h"
+}
+
+module E {
+ header "d.h"
+}
+
+//--- tu-umbrella.c
+#include "d.h"
+// expected-warning at -1 {{header 'd.h' is owned by multiple modules}}
+// expected-note@* {{header owned by module 'E' here}}
+// expected-note@* {{header covered by umbrella for module 'D' here}}
+
+//--- umbrella-dir/sub/e.h
+void e(void);
+
+//--- umbrella-dir/module.modulemap
+module F {
+ umbrella "sub"
+}
+
+module G {
+ header "sub/e.h"
+}
+
+//--- tu-umbrella-dir.c
+#include "sub/e.h"
+// expected-warning at -1 {{header 'sub/e.h' is owned by multiple modules}}
+// expected-note@* {{header owned by module 'G' here}}
+// expected-note@* {{header covered by umbrella for module 'F' here}}
+
+//--- same-module/sub/f.h
+void f(void);
+
+//--- same-module/module.modulemap
+module H {
+ umbrella "sub"
+ header "sub/f.h"
+}
+
+//--- tu-same-module.c
+// expected-no-diagnostics
+#include "sub/f.h"
+
+//--- excluded/sub/g.h
+void g(void);
+
+//--- excluded/module.modulemap
+module I {
+ umbrella "sub"
+ exclude header "sub/g.h"
+}
+
+module J {
+ header "sub/g.h"
+}
+
+//--- tu-excluded.c
+// expected-no-diagnostics
+#include "sub/g.h"
>From 8d2cc37ae707b3cf78885bb55bc4fcb65c09a5d0 Mon Sep 17 00:00:00 2001
From: Michael Spencer <bigcheesegs at gmail.com>
Date: Wed, 1 Apr 2026 11:19:11 -0700
Subject: [PATCH 2/3] Fix formatting
---
clang/include/clang/Lex/ModuleMap.h | 5 ++---
clang/lib/Lex/ModuleMap.cpp | 21 ++++++++++-----------
2 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h
index 46655c3aa6565..ed326a7fd545b 100644
--- a/clang/include/clang/Lex/ModuleMap.h
+++ b/clang/include/clang/Lex/ModuleMap.h
@@ -740,9 +740,8 @@ class ModuleMap {
/// Adds this header to the given module.
/// \param Role The role of the header wrt the module.
- void addHeader(Module *Mod, Module::Header Header,
- ModuleHeaderRole Role, bool Imported = false,
- SourceLocation Loc = SourceLocation());
+ void addHeader(Module *Mod, Module::Header Header, ModuleHeaderRole Role,
+ bool Imported = false, SourceLocation Loc = SourceLocation());
/// Parse a module map without creating `clang::Module` instances.
bool parseModuleMapFile(FileEntryRef File, bool IsSystem,
diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp
index a8016cf8afa1d..9b98048d0fb85 100644
--- a/clang/lib/Lex/ModuleMap.cpp
+++ b/clang/lib/Lex/ModuleMap.cpp
@@ -301,8 +301,7 @@ void ModuleMap::resolveHeader(Module *Mod,
else
// Record this umbrella header.
setUmbrellaHeaderAsWritten(Mod, *File, Header.FileName,
- RelativePathName.str(),
- Header.FileNameLoc);
+ RelativePathName.str(), Header.FileNameLoc);
} else {
Module::Header H = {Header.FileName, std::string(RelativePathName),
*File};
@@ -576,8 +575,7 @@ void ModuleMap::diagnoseDuplicateHeaderOwnership(SourceLocation FilenameLoc,
if (Known == Headers.end())
return;
- if (Diags.isIgnored(diag::warn_mmap_duplicate_header_ownership,
- FilenameLoc))
+ if (Diags.isIgnored(diag::warn_mmap_duplicate_header_ownership, FilenameLoc))
return;
// Only diagnose each header once.
@@ -624,14 +622,15 @@ void ModuleMap::diagnoseDuplicateHeaderOwnership(SourceLocation FilenameLoc,
// excluded from the umbrella module.
if (SeenTopLevel.insert(TopLevel).second) {
// Check that the header isn't excluded in the umbrella module.
- bool IsExcluded = llvm::any_of(Known->second, [TopLevel](const KnownHeader &H) {
- return H.getModule()->getTopLevelModule() == TopLevel &&
- H.getRole() == ExcludedHeader;
- });
+ bool IsExcluded =
+ llvm::any_of(Known->second, [TopLevel](const KnownHeader &H) {
+ return H.getModule()->getTopLevelModule() == TopLevel &&
+ H.getRole() == ExcludedHeader;
+ });
if (!IsExcluded) {
- OwningModules.push_back(
- {TopLevel, UmbrellaOwner.getModule()->UmbrellaDeclLoc,
- /*IsUmbrella=*/true});
+ OwningModules.push_back({TopLevel,
+ UmbrellaOwner.getModule()->UmbrellaDeclLoc,
+ /*IsUmbrella=*/true});
}
}
}
>From ba336791e890c88d52509b4f6d9d1e43c91dacb8 Mon Sep 17 00:00:00 2001
From: Michael Spencer <bigcheesegs at gmail.com>
Date: Tue, 7 Apr 2026 16:12:46 -0700
Subject: [PATCH 3/3] Add test.
---
.../test/Modules/duplicate-header-ownership.c | 33 +++++++++++++++----
1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/clang/test/Modules/duplicate-header-ownership.c b/clang/test/Modules/duplicate-header-ownership.c
index 26bad14f4bc44..8d41c72ed8492 100644
--- a/clang/test/Modules/duplicate-header-ownership.c
+++ b/clang/test/Modules/duplicate-header-ownership.c
@@ -35,11 +35,15 @@
// RUN: -fmodules-cache-path=%t/cache7 %t/tu-excluded.c -fsyntax-only \
// RUN: -Wduplicate-header-ownership -verify
+// Test umbrella header in subdirectory claiming parent dir conflicts with
+// explicit header in parent dir.
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -I%t/parent-umbrella \
+// RUN: -fmodules-cache-path=%t/cache8 %t/tu-parent-umbrella.c -fsyntax-only \
+// RUN: -Wduplicate-header-ownership -verify
+
//--- headers/a.h
-void a(void);
//--- headers/b.h
-void b(void);
//--- headers/module.modulemap
module A {
@@ -72,7 +76,6 @@ module C {
#include "d.h"
//--- umbrella/d.h
-void d(void);
//--- umbrella/module.modulemap
module D {
@@ -90,7 +93,6 @@ module E {
// expected-note@* {{header covered by umbrella for module 'D' here}}
//--- umbrella-dir/sub/e.h
-void e(void);
//--- umbrella-dir/module.modulemap
module F {
@@ -108,7 +110,6 @@ module G {
// expected-note@* {{header covered by umbrella for module 'F' here}}
//--- same-module/sub/f.h
-void f(void);
//--- same-module/module.modulemap
module H {
@@ -121,7 +122,6 @@ module H {
#include "sub/f.h"
//--- excluded/sub/g.h
-void g(void);
//--- excluded/module.modulemap
module I {
@@ -136,3 +136,24 @@ module J {
//--- tu-excluded.c
// expected-no-diagnostics
#include "sub/g.h"
+
+//--- parent-umbrella/umb.h
+//--- parent-umbrella/h.h
+//--- parent-umbrella/sub/s.h
+
+//--- parent-umbrella/sub/module.modulemap
+module Umb {
+ umbrella header "../umb.h"
+}
+
+//--- parent-umbrella/module.modulemap
+module K {
+ header "h.h"
+}
+
+//--- tu-parent-umbrella.c
+#include "sub/s.h"
+#include "h.h"
+// expected-warning at -1 {{header 'h.h' is owned by multiple modules}}
+// expected-note@* {{header owned by module 'K' here}}
+// expected-note@* {{header covered by umbrella for module 'Umb' here}}
More information about the cfe-commits
mailing list