Index: include/clang/Lex/Preprocessor.h =================================================================== --- include/clang/Lex/Preprocessor.h +++ include/clang/Lex/Preprocessor.h @@ -1602,6 +1602,7 @@ *Ident_AbnormalTermination; const char *getCurLexerEndPos(); + void diagnoseMissingHeaderInUmbrellaDir(Module *Mod); public: void PoisonSEHIdentifiers(bool Poison = true); // Borland Index: lib/Lex/PPLexerChange.cpp =================================================================== --- lib/Lex/PPLexerChange.cpp +++ lib/Lex/PPLexerChange.cpp @@ -287,6 +287,49 @@ return EndPos; } +static void +collectAllSubModulesWithUmbrellaHeader(Module *Mod, + SmallVectorImpl &SubMods) { + if (Mod->getUmbrellaHeader()) + SubMods.push_back(Mod); + for (auto *M : Mod->submodules()) + collectAllSubModulesWithUmbrellaHeader(M, SubMods); +} + +void Preprocessor::diagnoseMissingHeaderInUmbrellaDir(Module *Mod) { + assert(Mod->getUmbrellaHeader() && "Module must use umbrella header"); + SourceLocation StartLoc = + SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()); + if (getDiagnostics().isIgnored(diag::warn_uncovered_module_header, StartLoc)) + return; + + ModuleMap &ModMap = getHeaderSearchInfo().getModuleMap(); + const DirectoryEntry *Dir = Mod->getUmbrellaDir().Entry; + vfs::FileSystem &FS = *FileMgr.getVirtualFileSystem(); + std::error_code EC; + for (vfs::recursive_directory_iterator Entry(FS, Dir->getName(), EC), End; + Entry != End && !EC; Entry.increment(EC)) { + using llvm::StringSwitch; + + // Check whether this entry has an extension typically associated with + // headers. + if (!StringSwitch(llvm::sys::path::extension(Entry->getName())) + .Cases(".h", ".H", ".hh", ".hpp", true) + .Default(false)) + continue; + + if (const FileEntry *Header = getFileManager().getFile(Entry->getName())) + if (!getSourceManager().hasFileInfo(Header)) { + if (!ModMap.isHeaderInUnavailableModule(Header)) { + // Find the relative path that would access this header. + SmallString<128> RelativePath; + computeRelativePath(FileMgr, Dir, Header, RelativePath); + Diag(StartLoc, diag::warn_uncovered_module_header) + << Mod->getFullModuleName() << RelativePath; + } + } + } +} /// HandleEndOfFile - This callback is invoked when the lexer hits the end of /// the current file. This either returns the EOF token or pops a level off @@ -473,44 +516,14 @@ } // If we are building a module that has an umbrella header, make sure that - // each of the headers within the directory covered by the umbrella header - // was actually included by the umbrella header. + // each of the headers within the directory, including all submodules, is + // covered by the umbrella header was actually included by the umbrella + // header. if (Module *Mod = getCurrentModule()) { - if (Mod->getUmbrellaHeader()) { - SourceLocation StartLoc - = SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()); - - if (!getDiagnostics().isIgnored(diag::warn_uncovered_module_header, - StartLoc)) { - ModuleMap &ModMap = getHeaderSearchInfo().getModuleMap(); - const DirectoryEntry *Dir = Mod->getUmbrellaDir().Entry; - vfs::FileSystem &FS = *FileMgr.getVirtualFileSystem(); - std::error_code EC; - for (vfs::recursive_directory_iterator Entry(FS, Dir->getName(), EC), End; - Entry != End && !EC; Entry.increment(EC)) { - using llvm::StringSwitch; - - // Check whether this entry has an extension typically associated with - // headers. - if (!StringSwitch(llvm::sys::path::extension(Entry->getName())) - .Cases(".h", ".H", ".hh", ".hpp", true) - .Default(false)) - continue; - - if (const FileEntry *Header = - getFileManager().getFile(Entry->getName())) - if (!getSourceManager().hasFileInfo(Header)) { - if (!ModMap.isHeaderInUnavailableModule(Header)) { - // Find the relative path that would access this header. - SmallString<128> RelativePath; - computeRelativePath(FileMgr, Dir, Header, RelativePath); - Diag(StartLoc, diag::warn_uncovered_module_header) - << Mod->getFullModuleName() << RelativePath; - } - } - } - } - } + llvm::SmallVector AllMods; + collectAllSubModulesWithUmbrellaHeader(Mod, AllMods); + for (auto *M : AllMods) + diagnoseMissingHeaderInUmbrellaDir(M); } return true; Index: test/Modules/Inputs/incomplete-umbrella/Foo.framework/Headers/Bar.h =================================================================== --- /dev/null +++ test/Modules/Inputs/incomplete-umbrella/Foo.framework/Headers/Bar.h @@ -0,0 +1 @@ +#define BAR_PUBLIC 1 Index: test/Modules/Inputs/incomplete-umbrella/Foo.framework/Headers/FooPublic.h =================================================================== --- /dev/null +++ test/Modules/Inputs/incomplete-umbrella/Foo.framework/Headers/FooPublic.h @@ -0,0 +1 @@ +// FooPublic.h Index: test/Modules/Inputs/incomplete-umbrella/Foo.framework/Modules/module.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/incomplete-umbrella/Foo.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module Foo { + umbrella header "FooPublic.h" + requires objc + export * +} Index: test/Modules/Inputs/incomplete-umbrella/Foo.framework/Modules/module.private.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/incomplete-umbrella/Foo.framework/Modules/module.private.modulemap @@ -0,0 +1,5 @@ +explicit module Foo.Private { + umbrella header "Foo.h" + requires objc + export * +} Index: test/Modules/Inputs/incomplete-umbrella/Foo.framework/PrivateHeaders/Baz.h =================================================================== --- /dev/null +++ test/Modules/Inputs/incomplete-umbrella/Foo.framework/PrivateHeaders/Baz.h @@ -0,0 +1 @@ +#define BAZ_PRIVATE 1 Index: test/Modules/Inputs/incomplete-umbrella/Foo.framework/PrivateHeaders/Foo.h =================================================================== --- /dev/null +++ test/Modules/Inputs/incomplete-umbrella/Foo.framework/PrivateHeaders/Foo.h @@ -0,0 +1 @@ +// Foo.h Index: test/Modules/incomplete-umbrella.m =================================================================== --- /dev/null +++ test/Modules/incomplete-umbrella.m @@ -0,0 +1,15 @@ +// RUN: rm -rf %t +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -F%S/Inputs/incomplete-umbrella -fsyntax-only %s 2>&1 | FileCheck %s + +#import +#import +#import +@import Foo.Private; + +// CHECK: warning: umbrella header for module 'Foo' does not include header 'Bar.h' +// CHECK: warning: umbrella header for module 'Foo.Private' does not include header 'Baz.h' +int foo() { + int a = BAR_PUBLIC; + int b = BAZ_PRIVATE; + return 0; +}