From 2972991969b6841b361085d8c0dd0b964ebfd04f Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Fri, 22 Dec 2017 02:53:30 +0000 Subject: [Modules] Change private modules rules and warnings We used to advertise private modules to be declared as submodules (Foo.Private). This has proven to not scale well since private headers might carry several dependencies, introducing unwanted content into the main module and often causing dep cycles. Change the canonical way to name it to Foo_Private, forcing private modules as top level ones, and provide warnings under -Wprivate-module to suggest fixes for other private naming. Update documentation to reflect that. rdar://problem/31173501 llvm-svn: 321337 --- clang/lib/Lex/ModuleMap.cpp | 98 +++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 35 deletions(-) (limited to 'clang/lib/Lex/ModuleMap.cpp') diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index fbbae7a..b3ac10c 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -1608,6 +1608,54 @@ namespace { } // namespace +/// Private modules are canonicalized as Foo_Private. Clang provides extra +/// module map search logic to find the appropriate private module when PCH +/// is used with implicit module maps. Warn when private modules are written +/// in other ways (FooPrivate and Foo.Private), providing notes and fixits. +static void diagnosePrivateModules(const ModuleMap &Map, + DiagnosticsEngine &Diags, + const Module *ActiveModule) { + + auto GenNoteAndFixIt = [&](StringRef BadName, StringRef Canonical, + const Module *M) { + auto D = Diags.Report(ActiveModule->DefinitionLoc, + diag::note_mmap_rename_top_level_private_module); + D << BadName << M->Name; + D << FixItHint::CreateReplacement(ActiveModule->DefinitionLoc, Canonical); + }; + + for (auto E = Map.module_begin(); E != Map.module_end(); ++E) { + auto const *M = E->getValue(); + if (M->Directory != ActiveModule->Directory) + continue; + + SmallString<128> FullName(ActiveModule->getFullModuleName()); + if (!FullName.startswith(M->Name) && !FullName.endswith("Private")) + continue; + SmallString<128> Canonical(M->Name); + Canonical.append("_Private"); + + // Foo.Private -> Foo_Private + if (ActiveModule->Parent && ActiveModule->Name == "Private" && !M->Parent && + M->Name == ActiveModule->Parent->Name) { + Diags.Report(ActiveModule->DefinitionLoc, + diag::warn_mmap_mismatched_private_submodule) + << FullName; + GenNoteAndFixIt(FullName, Canonical, M); + continue; + } + + // FooPrivate and whatnots -> Foo_Private + if (!ActiveModule->Parent && !M->Parent && M->Name != ActiveModule->Name && + ActiveModule->Name != Canonical) { + Diags.Report(ActiveModule->DefinitionLoc, + diag::warn_mmap_mismatched_private_module_name) + << ActiveModule->Name; + GenNoteAndFixIt(ActiveModule->Name, Canonical, M); + } + } +} + /// \brief Parse a module declaration. /// /// module-declaration: @@ -1791,41 +1839,21 @@ void ModuleMapParser::parseModuleDecl() { ActiveModule->NoUndeclaredIncludes = true; ActiveModule->Directory = Directory; - if (!ActiveModule->Parent) { - StringRef MapFileName(ModuleMapFile->getName()); - if (MapFileName.endswith("module.private.modulemap") || - MapFileName.endswith("module_private.map")) { - // Adding a top-level module from a private modulemap is likely a - // user error; we check to see if there's another top-level module - // defined in the non-private map in the same dir, and if so emit a - // warning. - for (auto E = Map.module_begin(); E != Map.module_end(); ++E) { - auto const *M = E->getValue(); - if (!M->Parent && - M->Directory == ActiveModule->Directory && - M->Name != ActiveModule->Name) { - Diags.Report(ActiveModule->DefinitionLoc, - diag::warn_mmap_mismatched_top_level_private) - << ActiveModule->Name << M->Name; - // The pattern we're defending against here is typically due to - // a module named FooPrivate which is supposed to be a submodule - // called Foo.Private. Emit a fixit in that case. - auto D = - Diags.Report(ActiveModule->DefinitionLoc, - diag::note_mmap_rename_top_level_private_as_submodule); - D << ActiveModule->Name << M->Name; - StringRef Bad(ActiveModule->Name); - if (Bad.consume_back("Private")) { - SmallString<128> Fixed = Bad; - Fixed.append(".Private"); - D << FixItHint::CreateReplacement(ActiveModule->DefinitionLoc, - Fixed); - } - break; - } - } - } - } + + // Private modules named as FooPrivate, Foo.Private or similar are likely a + // user error; provide warnings, notes and fixits to direct users to use + // Foo_Private instead. + SourceLocation StartLoc = + SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()); + StringRef MapFileName(ModuleMapFile->getName()); + if (Map.HeaderInfo.getHeaderSearchOpts().ImplicitModuleMaps && + !Diags.isIgnored(diag::warn_mmap_mismatched_private_submodule, + StartLoc) && + !Diags.isIgnored(diag::warn_mmap_mismatched_private_module_name, + StartLoc) && + (MapFileName.endswith("module.private.modulemap") || + MapFileName.endswith("module_private.map"))) + diagnosePrivateModules(Map, Diags, ActiveModule); bool Done = false; do { -- cgit v1.1