Index: cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td @@ -94,6 +94,9 @@ "could not acquire lock file for module '%0': %1">, InGroup; def remark_module_lock_timeout : Remark< "timed out waiting to acquire lock file for module '%0'">, InGroup; +def err_module_shadowed : Error<"import of shadowed module '%0'">, DefaultFatal; +def err_module_build_shadowed_submodule : Error< + "build a shadowed submodule '%0'">, DefaultFatal; def err_module_cycle : Error<"cyclic dependency in module '%0': %1">, DefaultFatal; def err_module_prebuilt : Error< Index: cfe/trunk/include/clang/Basic/Module.h =================================================================== --- cfe/trunk/include/clang/Basic/Module.h +++ cfe/trunk/include/clang/Basic/Module.h @@ -197,6 +197,9 @@ /// will be false to indicate that this (sub)module is not available. SmallVector Requirements; + /// \brief A module with the same name that shadows this module. + Module *ShadowingModule = nullptr; + /// \brief Whether this module is missing a feature from \c Requirements. unsigned IsMissingRequirement : 1; @@ -375,13 +378,20 @@ /// /// \param Target The target options used for the current translation unit. /// - /// \param Req If this module is unavailable, this parameter - /// will be set to one of the requirements that is not met for use of - /// this module. + /// \param Req If this module is unavailable because of a missing requirement, + /// this parameter will be set to one of the requirements that is not met for + /// use of this module. + /// + /// \param MissingHeader If this module is unavailable because of a missing + /// header, this parameter will be set to one of the missing headers. + /// + /// \param ShadowingModule If this module is unavailable because it is + /// shadowed, this parameter will be set to the shadowing module. bool isAvailable(const LangOptions &LangOpts, const TargetInfo &Target, Requirement &Req, - UnresolvedHeaderDirective &MissingHeader) const; + UnresolvedHeaderDirective &MissingHeader, + Module *&ShadowingModule) const; /// \brief Determine whether this module is a submodule. bool isSubModule() const { return Parent != nullptr; } Index: cfe/trunk/include/clang/Lex/HeaderSearch.h =================================================================== --- cfe/trunk/include/clang/Lex/HeaderSearch.h +++ cfe/trunk/include/clang/Lex/HeaderSearch.h @@ -726,6 +726,7 @@ LoadModuleMapResult loadModuleMapFileImpl(const FileEntry *File, bool IsSystem, const DirectoryEntry *Dir, + bool IsExplicitlyProvided, FileID ID = FileID(), unsigned *Offset = nullptr); Index: cfe/trunk/include/clang/Lex/ModuleMap.h =================================================================== --- cfe/trunk/include/clang/Lex/ModuleMap.h +++ cfe/trunk/include/clang/Lex/ModuleMap.h @@ -195,6 +195,17 @@ /// header. llvm::DenseMap UmbrellaDirs; + /// \brief The set of modules provided explicitly (e.g. by -fmodule-map-file), + /// which are allowed to shadow other implicitly discovered modules. + llvm::DenseSet ExplicitlyProvidedModules; + + bool mayShadowModuleBeingParsed(Module *ExistingModule, + bool IsExplicitlyProvided) { + assert(!ExistingModule->Parent && "expected top-level module"); + return !IsExplicitlyProvided && + ExplicitlyProvidedModules.count(ExistingModule); + } + /// \brief The set of attributes that can be attached to a module. struct Attributes { /// \brief Whether this is a system module. @@ -475,9 +486,9 @@ /// /// \returns The found or newly-created module, along with a boolean value /// that will be true if the module is newly-created. - std::pair findOrCreateModule(StringRef Name, Module *Parent, - bool IsFramework, - bool IsExplicit); + std::pair + findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, + bool IsExplicit, bool UsesExplicitModuleMapFile = false); /// \brief Create a 'global module' for a C++ Modules TS module interface /// unit. @@ -502,6 +513,11 @@ Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir, bool IsSystem, Module *Parent); + /// \brief Create a new top-level module that is shadowed by + /// \p ShadowingModule. + Module *createShadowedModule(StringRef Name, bool IsFramework, + Module *ShadowingModule); + /// \brief Retrieve the module map file containing the definition of the given /// module. /// @@ -587,6 +603,8 @@ /// \brief Marks this header as being excluded from the given module. void excludeHeader(Module *Mod, Module::Header Header); + void setExplicitlyProvided(Module *Mod); + /// \brief Parse the given module map file, and record any modules we /// encounter. /// @@ -606,10 +624,15 @@ /// \param ExternModuleLoc The location of the "extern module" declaration /// that caused us to load this module map file, if any. /// + /// \param IsExplicitlyProvided Whether this module map file was provided + /// explicitly by the user (e.g. -fmodule-map-file), rather than found + /// implicitly. + /// /// \returns true if an error occurred, false otherwise. bool parseModuleMapFile(const FileEntry *File, bool IsSystem, - const DirectoryEntry *HomeDir, FileID ID = FileID(), - unsigned *Offset = nullptr, + const DirectoryEntry *HomeDir, + bool IsExplicitlyProvided = false, + FileID ID = FileID(), unsigned *Offset = nullptr, SourceLocation ExternModuleLoc = SourceLocation()); /// \brief Dump the contents of the module map, for debugging purposes. Index: cfe/trunk/lib/Basic/Module.cpp =================================================================== --- cfe/trunk/lib/Basic/Module.cpp +++ cfe/trunk/lib/Basic/Module.cpp @@ -95,11 +95,16 @@ bool Module::isAvailable(const LangOptions &LangOpts, const TargetInfo &Target, Requirement &Req, - UnresolvedHeaderDirective &MissingHeader) const { + UnresolvedHeaderDirective &MissingHeader, + Module *&ShadowingModule) const { if (IsAvailable) return true; for (const Module *Current = this; Current; Current = Current->Parent) { + if (Current->ShadowingModule) { + ShadowingModule = Current->ShadowingModule; + return false; + } for (unsigned I = 0, N = Current->Requirements.size(); I != N; ++I) { if (hasFeature(Current->Requirements[I].first, LangOpts, Target) != Current->Requirements[I].second) { Index: cfe/trunk/lib/Lex/HeaderSearch.cpp =================================================================== --- cfe/trunk/lib/Lex/HeaderSearch.cpp +++ cfe/trunk/lib/Lex/HeaderSearch.cpp @@ -1367,7 +1367,8 @@ } } - switch (loadModuleMapFileImpl(File, IsSystem, Dir, ID, Offset)) { + switch (loadModuleMapFileImpl(File, IsSystem, Dir, + /*IsExplictlyProvided=*/true, ID, Offset)) { case LMM_AlreadyLoaded: case LMM_NewlyLoaded: return false; @@ -1378,10 +1379,9 @@ llvm_unreachable("Unknown load module map result"); } -HeaderSearch::LoadModuleMapResult -HeaderSearch::loadModuleMapFileImpl(const FileEntry *File, bool IsSystem, - const DirectoryEntry *Dir, FileID ID, - unsigned *Offset) { +HeaderSearch::LoadModuleMapResult HeaderSearch::loadModuleMapFileImpl( + const FileEntry *File, bool IsSystem, const DirectoryEntry *Dir, + bool IsExplicitlyProvided, FileID ID, unsigned *Offset) { assert(File && "expected FileEntry"); // Check whether we've already loaded this module map, and mark it as being @@ -1390,14 +1390,16 @@ if (!AddResult.second) return AddResult.first->second ? LMM_AlreadyLoaded : LMM_InvalidModuleMap; - if (ModMap.parseModuleMapFile(File, IsSystem, Dir, ID, Offset)) { + if (ModMap.parseModuleMapFile(File, IsSystem, Dir, IsExplicitlyProvided, ID, + Offset)) { LoadedModuleMaps[File] = false; return LMM_InvalidModuleMap; } // Try to load a corresponding private module map. if (const FileEntry *PMMFile = getPrivateModuleMap(File, FileMgr)) { - if (ModMap.parseModuleMapFile(PMMFile, IsSystem, Dir)) { + if (ModMap.parseModuleMapFile(PMMFile, IsSystem, Dir, + IsExplicitlyProvided)) { LoadedModuleMaps[File] = false; return LMM_InvalidModuleMap; } @@ -1468,8 +1470,8 @@ return KnownDir->second ? LMM_AlreadyLoaded : LMM_InvalidModuleMap; if (const FileEntry *ModuleMapFile = lookupModuleMapFile(Dir, IsFramework)) { - LoadModuleMapResult Result = - loadModuleMapFileImpl(ModuleMapFile, IsSystem, Dir); + LoadModuleMapResult Result = loadModuleMapFileImpl( + ModuleMapFile, IsSystem, Dir, /*IsExplicitlyProvided=*/false); // Add Dir explicitly in case ModuleMapFile is in a subdirectory. // E.g. Foo.framework/Modules/module.modulemap // ^Dir ^ModuleMapFile Index: cfe/trunk/lib/Lex/ModuleMap.cpp =================================================================== --- cfe/trunk/lib/Lex/ModuleMap.cpp +++ cfe/trunk/lib/Lex/ModuleMap.cpp @@ -744,14 +744,13 @@ return Context->findSubmodule(Name); } -std::pair ModuleMap::findOrCreateModule(StringRef Name, - Module *Parent, - bool IsFramework, - bool IsExplicit) { +std::pair +ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, + bool IsExplicit, bool UsesExplicitModuleMapFile) { // Try to find an existing module with this name. if (Module *Sub = lookupModuleQualified(Name, Parent)) return std::make_pair(Sub, false); - + // Create a new module with this name. Module *Result = new Module(Name, SourceLocation(), Parent, IsFramework, IsExplicit, NumCreatedModules++); @@ -759,6 +758,8 @@ if (LangOpts.CurrentModule == Name) SourceModule = Result; Modules[Name] = Result; + if (UsesExplicitModuleMapFile) + ExplicitlyProvidedModules.insert(Result); } return std::make_pair(Result, true); } @@ -999,6 +1000,19 @@ return Result; } +Module *ModuleMap::createShadowedModule(StringRef Name, bool IsFramework, + Module *ShadowingModule) { + + // Create a new module with this name. + Module *Result = + new Module(Name, SourceLocation(), /*Parent=*/nullptr, IsFramework, + /*IsExplicit=*/false, NumCreatedModules++); + Result->ShadowingModule = ShadowingModule; + Result->IsAvailable = false; + + return Result; +} + void ModuleMap::setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader, Twine NameAsWritten) { Headers[UmbrellaHeader].push_back(KnownHeader(Mod, NormalHeader)); @@ -1116,6 +1130,11 @@ Mod->Headers[Module::HK_Excluded].push_back(std::move(Header)); } +void ModuleMap::setExplicitlyProvided(Module *Mod) { + assert(Modules[Mod->Name] == Mod && "explicitly provided module is shadowed"); + ExplicitlyProvidedModules.insert(Mod); +} + const FileEntry * ModuleMap::getContainingModuleMapFile(const Module *Module) const { if (Module->DefinitionLoc.isInvalid()) @@ -1319,7 +1338,9 @@ /// \brief Consume the current token and return its location. SourceLocation consumeToken(); - + + bool UsesExplicitModuleMapFile = false; + /// \brief Skip tokens until we reach the a token with the given kind /// (or the end of the file). void skipUntil(MMToken::TokenKind K); @@ -1345,20 +1366,19 @@ bool parseOptionalAttributes(Attributes &Attrs); public: - explicit ModuleMapParser(Lexer &L, SourceManager &SourceMgr, - const TargetInfo *Target, - DiagnosticsEngine &Diags, - ModuleMap &Map, - const FileEntry *ModuleMapFile, - const DirectoryEntry *Directory, - bool IsSystem) + explicit ModuleMapParser(Lexer &L, SourceManager &SourceMgr, + const TargetInfo *Target, DiagnosticsEngine &Diags, + ModuleMap &Map, const FileEntry *ModuleMapFile, + const DirectoryEntry *Directory, bool IsSystem, + bool UsesExplicitModuleMapFile) : L(L), SourceMgr(SourceMgr), Target(Target), Diags(Diags), Map(Map), ModuleMapFile(ModuleMapFile), Directory(Directory), - IsSystem(IsSystem) { + IsSystem(IsSystem), + UsesExplicitModuleMapFile(UsesExplicitModuleMapFile) { Tok.clear(); consumeToken(); } - + bool parseModuleMapFile(); bool terminatedByDirective() { return false; } @@ -1787,6 +1807,7 @@ SourceLocation LBraceLoc = consumeToken(); // Determine whether this (sub)module has already been defined. + Module *ShadowingModule = nullptr; if (Module *Existing = Map.lookupModuleQualified(ModuleName, ActiveModule)) { // We might see a (re)definition of a module that we already have a // definition for in two cases: @@ -1812,23 +1833,36 @@ } return; } - - Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition) - << ModuleName; - Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition); - - // Skip the module definition. - skipUntil(MMToken::RBrace); - if (Tok.is(MMToken::RBrace)) - consumeToken(); - - HadError = true; - return; + + if (!Existing->Parent && + Map.mayShadowModuleBeingParsed(Existing, UsesExplicitModuleMapFile)) { + ShadowingModule = Existing; + } else { + // This is not a shawdowed module decl, it is an illegal redefinition. + Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition) + << ModuleName; + Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition); + + // Skip the module definition. + skipUntil(MMToken::RBrace); + if (Tok.is(MMToken::RBrace)) + consumeToken(); + + HadError = true; + return; + } } // Start defining this module. - ActiveModule = Map.findOrCreateModule(ModuleName, ActiveModule, Framework, - Explicit).first; + if (ShadowingModule) { + ActiveModule = + Map.createShadowedModule(ModuleName, Framework, ShadowingModule); + } else { + ActiveModule = Map.findOrCreateModule(ModuleName, ActiveModule, Framework, + Explicit, UsesExplicitModuleMapFile) + .first; + } + ActiveModule->DefinitionLoc = ModuleNameLoc; if (Attrs.IsSystem || IsSystem) ActiveModule->IsSystem = true; @@ -2004,7 +2038,7 @@ Map.HeaderInfo.getHeaderSearchOpts().ModuleMapFileHomeIsCwd ? Directory : File->getDir(), - FileID(), nullptr, ExternLoc); + false /*IsExplicitlyProvided*/, FileID(), nullptr, ExternLoc); } /// Whether to add the requirement \p Feature to the module \p M. @@ -2811,7 +2845,8 @@ } bool ModuleMap::parseModuleMapFile(const FileEntry *File, bool IsSystem, - const DirectoryEntry *Dir, FileID ID, + const DirectoryEntry *Dir, + bool IsExplicitlyProvided, FileID ID, unsigned *Offset, SourceLocation ExternModuleLoc) { assert(Target && "Missing target information"); @@ -2841,7 +2876,7 @@ Buffer->getBufferEnd()); SourceLocation Start = L.getSourceLocation(); ModuleMapParser Parser(L, SourceMgr, Target, Diags, *this, File, Dir, - IsSystem); + IsSystem, IsExplicitlyProvided); bool Result = Parser.parseModuleMapFile(); ParsedModuleMap[File] = Result; @@ -2854,5 +2889,6 @@ // Notify callbacks that we parsed it. for (const auto &Cb : Callbacks) Cb->moduleMapFileRead(Start, *File, IsSystem); + return Result; } Index: cfe/trunk/lib/Lex/PPDirectives.cpp =================================================================== --- cfe/trunk/lib/Lex/PPDirectives.cpp +++ cfe/trunk/lib/Lex/PPDirectives.cpp @@ -1655,12 +1655,18 @@ DiagnosticsEngine &Diags, Module *M) { Module::Requirement Requirement; Module::UnresolvedHeaderDirective MissingHeader; - if (M->isAvailable(LangOpts, TargetInfo, Requirement, MissingHeader)) + Module *ShadowingModule = nullptr; + if (M->isAvailable(LangOpts, TargetInfo, Requirement, MissingHeader, + ShadowingModule)) return false; if (MissingHeader.FileNameLoc.isValid()) { Diags.Report(MissingHeader.FileNameLoc, diag::err_module_header_missing) << MissingHeader.IsUmbrella << MissingHeader.FileName; + } else if (ShadowingModule) { + Diags.Report(M->DefinitionLoc, diag::err_module_shadowed) << M->Name; + Diags.Report(ShadowingModule->DefinitionLoc, + diag::note_previous_definition); } else { // FIXME: Track the location at which the requirement was specified, and // use it here. @@ -2024,6 +2030,15 @@ // Determine if we're switching to building a new submodule, and which one. if (auto *M = SuggestedModule.getModule()) { + if (M->getTopLevelModule()->ShadowingModule) { + // We are building a submodule that belongs to a shadowed module. This + // means we find header files in the shadowed module. + Diag(M->DefinitionLoc, diag::err_module_build_shadowed_submodule) + << M->getFullModuleName(); + Diag(M->getTopLevelModule()->ShadowingModule->DefinitionLoc, + diag::note_previous_definition); + return; + } // When building a pch, -fmodule-name tells the compiler to textually // include headers in the specified module. We are not building the // specified module. Index: cfe/trunk/test/Modules/Inputs/shadow/A1/A.h =================================================================== --- cfe/trunk/test/Modules/Inputs/shadow/A1/A.h +++ cfe/trunk/test/Modules/Inputs/shadow/A1/A.h @@ -0,0 +1 @@ +#define A1_A_h Index: cfe/trunk/test/Modules/Inputs/shadow/A1/module.modulemap =================================================================== --- cfe/trunk/test/Modules/Inputs/shadow/A1/module.modulemap +++ cfe/trunk/test/Modules/Inputs/shadow/A1/module.modulemap @@ -0,0 +1,5 @@ +module A { + header "A.h" +} + +module A1 {} Index: cfe/trunk/test/Modules/Inputs/shadow/A2/A.h =================================================================== --- cfe/trunk/test/Modules/Inputs/shadow/A2/A.h +++ cfe/trunk/test/Modules/Inputs/shadow/A2/A.h @@ -0,0 +1 @@ +#define A2_A_h Index: cfe/trunk/test/Modules/Inputs/shadow/A2/module.modulemap =================================================================== --- cfe/trunk/test/Modules/Inputs/shadow/A2/module.modulemap +++ cfe/trunk/test/Modules/Inputs/shadow/A2/module.modulemap @@ -0,0 +1,5 @@ +module A { + header "A.h" +} + +module A2 {} Index: cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/Foo.h =================================================================== --- cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/Foo.h +++ cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/Foo.h @@ -0,0 +1 @@ +#include // expected-error {{could not build module 'A'}} Index: cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/module.modulemap =================================================================== --- cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/module.modulemap +++ cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/module.modulemap @@ -0,0 +1,14 @@ +module A [system] { // expected-note {{previous definition is here}} + module sub { + header "sys/A.h" + } + module sub2 { + header "sys/A2.h" + } + module stdarg { + header "stdarg.h" + export * + } +} + +module A2 {} Index: cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/sys/A.h =================================================================== --- cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/sys/A.h +++ cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/sys/A.h @@ -0,0 +1 @@ +#include Index: cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/sys/A2.h =================================================================== --- cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/sys/A2.h +++ cfe/trunk/test/Modules/Inputs/shadowed-submodule/A1/sys/A2.h @@ -0,0 +1 @@ +// nothing Index: cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/Foo.h =================================================================== --- cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/Foo.h +++ cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/Foo.h @@ -0,0 +1 @@ +#include Index: cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/module.modulemap =================================================================== --- cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/module.modulemap +++ cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/module.modulemap @@ -0,0 +1,14 @@ +module A [system] { + module sub { + header "sys/A.h" + } + module sub2 { // expected-error {{build a shadowed submodule 'A.sub2'}} + header "sys/A2.h" + } + module stdarg { + header "stdarg.h" + export * + } +} + +module A2 {} Index: cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/sys/A.h =================================================================== --- cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/sys/A.h +++ cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/sys/A.h @@ -0,0 +1 @@ +#include Index: cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/sys/A2.h =================================================================== --- cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/sys/A2.h +++ cfe/trunk/test/Modules/Inputs/shadowed-submodule/A2/sys/A2.h @@ -0,0 +1 @@ +// nothing Index: cfe/trunk/test/Modules/Inputs/shadowed-submodule/Foo/module.modulemap =================================================================== --- cfe/trunk/test/Modules/Inputs/shadowed-submodule/Foo/module.modulemap +++ cfe/trunk/test/Modules/Inputs/shadowed-submodule/Foo/module.modulemap @@ -0,0 +1,3 @@ +module Foo { + header "../A1/Foo.h" +} Index: cfe/trunk/test/Modules/shadow.m =================================================================== --- cfe/trunk/test/Modules/shadow.m +++ cfe/trunk/test/Modules/shadow.m @@ -0,0 +1,21 @@ +// RUN: rm -rf %t +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/shadow/A1 -I %S/Inputs/shadow/A2 %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -fmodule-map-file=%S/Inputs/shadow/A2/module.modulemap %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION +// REDEFINITION: error: redefinition of module 'A' +// REDEFINITION: note: previously defined + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -I %S/Inputs/shadow %s -verify + +@import A1; +@import A2; +@import A; + +#import "A2/A.h" // expected-note {{implicitly imported}} +// expected-error@A2/module.modulemap:1 {{import of shadowed module 'A'}} +// expected-note@A1/module.modulemap:1 {{previous definition}} + +#if defined(A2_A_h) +#error got the wrong definition of module A +#elif !defined(A1_A_h) +#error missing definition from A1 +#endif Index: cfe/trunk/test/Modules/shadowed-submodule.m =================================================================== --- cfe/trunk/test/Modules/shadowed-submodule.m +++ cfe/trunk/test/Modules/shadowed-submodule.m @@ -0,0 +1,5 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/shadowed-submodule/Foo -I %S/Inputs/shadowed-submodule/A2 %s -verify + +@import Foo; // expected-error {{module 'A' was built in directory}} + // expected-note@shadowed-submodule.m:4 {{imported by module 'Foo'}}