diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 3c1aff7cfb54df8d9c678597c42791db4c22db51..1cd6d385b6f01135f7193c368cc60f667bce5f60 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -175,6 +175,13 @@ private: /// resolved. Module *resolveModuleId(const ModuleId &Id, Module *Mod, bool Complain) const; + /// \brief Looks up the modules that \p File corresponds to. + /// + /// If \p File represents a builtin header within Clang's builtin include + /// directory, this also loads all of the module maps to see if it will get + /// associated with a specific module (e.g. in /usr/include). + HeadersMap::iterator findKnownHeader(const FileEntry *File); + public: /// \brief Construct a new module map. /// @@ -212,15 +219,24 @@ public: /// used from. Used to disambiguate if a header is present in multiple /// modules. /// - /// \param FoundInModule If not null will be set to \c true if the header was - /// found in non-exclude header declaration of a module. - /// /// \returns The module KnownHeader, which provides the module that owns the /// given header file. The KnownHeader is default constructed to indicate /// that no module owns this header file. KnownHeader findModuleForHeader(const FileEntry *File, - Module *RequestingModule = NULL, - bool *FoundInModule = NULL); + Module *RequestingModule = NULL); + + /// \brief Reports errors if a module must not include a specific file. + /// + /// \param RequestingModule The module including a file. + /// + /// \param FilenameLoc The location of the inclusion's filename. + /// + /// \param Filename The included filename as written. + /// + /// \param File The included file. + void diagnoseHeaderInclusion(Module *RequestingModule, + SourceLocation FilenameLoc, StringRef Filename, + const FileEntry *File); /// \brief Determine whether the given header is part of a module /// marked 'unavailable'. diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 2491011aa6d9dfbd713e7de8d851e46afd4061f1..7862ed3109c3eb5c0bcc2ab446a1b6f7de3ddee3 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -1506,25 +1506,6 @@ private: /// points to. Module *getModuleForLocation(SourceLocation FilenameLoc); - /// \brief Verify that a private header is included only from within its - /// module. - bool violatesPrivateInclude(Module *RequestingModule, - const FileEntry *IncFileEnt, - ModuleMap::ModuleHeaderRole Role, - Module *RequestedModule); - - /// \brief Verify that a module includes headers only from modules that it - /// has declared that it uses. - bool violatesUseDeclarations(Module *RequestingModule, - Module *RequestedModule); - - /// \brief Verify that it is legal for the source file that \p FilenameLoc - /// points to to include the file \p Filename. - /// - /// Tries to reuse \p IncFileEnt. - void verifyModuleInclude(SourceLocation FilenameLoc, StringRef Filename, - const FileEntry *IncFileEnt); - // Macro handling. void HandleDefineDirective(Token &Tok, bool ImmediatelyAfterTopLevelIfndef); void HandleUndefDirective(Token &Tok); diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index b9a4703128956aec4d69dfd1e138faaf73b563a9..05238278bad93ba99fc1e795039187c8a93ea50f 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -159,21 +159,115 @@ static bool isBuiltinHeader(StringRef FileName) { .Default(false); } -ModuleMap::KnownHeader -ModuleMap::findModuleForHeader(const FileEntry *File, - Module *RequestingModule, - bool *FoundInModule) { +ModuleMap::HeadersMap::iterator +ModuleMap::findKnownHeader(const FileEntry *File) { HeadersMap::iterator Known = Headers.find(File); - - // If we've found a builtin header within Clang's builtin include directory, - // load all of the module maps to see if it will get associated with a - // specific module (e.g., in /usr/include). if (Known == Headers.end() && File->getDir() == BuiltinIncludeDir && isBuiltinHeader(llvm::sys::path::filename(File->getName()))) { HeaderInfo.loadTopLevelSystemModules(); - Known = Headers.find(File); + return Headers.find(File); + } + return Known; +} + +// Returns 'true' if 'RequestingModule directly uses 'RequestedModule'. +static bool directlyUses(const Module *RequestingModule, + const Module *RequestedModule) { + return std::find(RequestingModule->DirectUses.begin(), + RequestingModule->DirectUses.end(), + RequestedModule) != RequestingModule->DirectUses.end(); +} + +static bool violatesPrivateInclude(Module *RequestingModule, + const FileEntry *IncFileEnt, + ModuleMap::ModuleHeaderRole Role, + Module *RequestedModule) { + #ifndef NDEBUG + // Check for consistency between the module header role + // as obtained from the lookup and as obtained from the module. + // This check is not cheap, so enable it only for debugging. + SmallVectorImpl<const FileEntry *> &PvtHdrs + = RequestedModule->PrivateHeaders; + SmallVectorImpl<const FileEntry *>::iterator Look + = std::find(PvtHdrs.begin(), PvtHdrs.end(), IncFileEnt); + bool IsPrivate = Look != PvtHdrs.end(); + assert((IsPrivate && Role == ModuleMap::PrivateHeader) + || (!IsPrivate && Role != ModuleMap::PrivateHeader)); + #endif + return Role == ModuleMap::PrivateHeader && + RequestedModule->getTopLevelModule() != RequestingModule; +} + +void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule, + SourceLocation FilenameLoc, + StringRef Filename, + const FileEntry *File) { + // No errors for indirect modules. This may be a bit of a problem for modules + // with no source files. + if (RequestingModule != SourceModule) + return; + + if (RequestingModule) + resolveUses(RequestingModule, /*Complain=*/false); + + HeadersMap::iterator Known = findKnownHeader(File); + if (Known == Headers.end()) + return; + + Module *Private = NULL; + Module *NotUsed = NULL; + for (SmallVectorImpl<KnownHeader>::iterator I = Known->second.begin(), + E = Known->second.end(); + I != E; ++I) { + // Excluded headers don't really belong to a module. + if (I->getRole() == ModuleMap::ExcludedHeader) + continue; + + // If 'File' is part of 'RequestingModule' we can definitely include it. + if (I->getModule() == RequestingModule) + return; + + // Remember private headers for later printing of a diagnostic. + if (violatesPrivateInclude(RequestingModule, File, I->getRole(), + I->getModule())) { + Private = I->getModule(); + continue; + } + + // If uses need to be specified explicitly, we are only allowed to return + // modules that are explicitly used by the requesting module. + if (RequestingModule && LangOpts.ModulesDeclUse && + !directlyUses(RequestingModule, I->getModule())) { + NotUsed = I->getModule(); + continue; + } + + // We have found a module that we can happily use. + return; + } + + // We have found a header, but it is private. + if (Private != NULL) { + Diags.Report(FilenameLoc, diag::error_use_of_private_header_outside_module) + << Filename; + return; } + // We have found a module, but we don't use it. + if (NotUsed != NULL) { + Diags.Report(FilenameLoc, diag::error_undeclared_use_of_module) + << RequestingModule->getFullModuleName() << Filename; + return; + } + + // Headers for which we have not found a module are fine to include. +} + +ModuleMap::KnownHeader +ModuleMap::findModuleForHeader(const FileEntry *File, + Module *RequestingModule) { + HeadersMap::iterator Known = findKnownHeader(File); + if (Known != Headers.end()) { ModuleMap::KnownHeader Result = KnownHeader(); @@ -185,9 +279,6 @@ ModuleMap::findModuleForHeader(const FileEntry *File, if (I->getRole() == ModuleMap::ExcludedHeader) continue; - if (FoundInModule) - *FoundInModule = true; - // Cannot use a module if it is unavailable. if (!I->getModule()->isAvailable()) continue; @@ -200,9 +291,7 @@ ModuleMap::findModuleForHeader(const FileEntry *File, // If uses need to be specified explicitly, we are only allowed to return // modules that are explicitly used by the requesting module. if (RequestingModule && LangOpts.ModulesDeclUse && - std::find(RequestingModule->DirectUses.begin(), - RequestingModule->DirectUses.end(), - I->getModule()) == RequestingModule->DirectUses.end()) + !directlyUses(RequestingModule, I->getModule())) continue; Result = *I; diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index 1b33f45f4daf15552a1f69e5093e571e7e85b969..b93a39188ac6c38e368b8cba39d014dda974689e 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -550,73 +550,6 @@ Module *Preprocessor::getModuleForLocation(SourceLocation FilenameLoc) { } } -bool Preprocessor::violatesPrivateInclude( - Module *RequestingModule, - const FileEntry *IncFileEnt, - ModuleMap::ModuleHeaderRole Role, - Module *RequestedModule) { - #ifndef NDEBUG - // Check for consistency between the module header role - // as obtained from the lookup and as obtained from the module. - // This check is not cheap, so enable it only for debugging. - SmallVectorImpl<const FileEntry *> &PvtHdrs - = RequestedModule->PrivateHeaders; - SmallVectorImpl<const FileEntry *>::iterator Look - = std::find(PvtHdrs.begin(), PvtHdrs.end(), IncFileEnt); - bool IsPrivate = Look != PvtHdrs.end(); - assert((IsPrivate && Role == ModuleMap::PrivateHeader) - || (!IsPrivate && Role != ModuleMap::PrivateHeader)); - #endif - return Role == ModuleMap::PrivateHeader && - RequestedModule->getTopLevelModule() != RequestingModule; -} - -bool Preprocessor::violatesUseDeclarations( - Module *RequestingModule, - Module *RequestedModule) { - ModuleMap &ModMap = HeaderInfo.getModuleMap(); - ModMap.resolveUses(RequestingModule, /*Complain=*/false); - const SmallVectorImpl<Module *> &AllowedUses = RequestingModule->DirectUses; - SmallVectorImpl<Module *>::const_iterator Declared = - std::find(AllowedUses.begin(), AllowedUses.end(), RequestedModule); - return Declared == AllowedUses.end(); -} - -void Preprocessor::verifyModuleInclude(SourceLocation FilenameLoc, - StringRef Filename, - const FileEntry *IncFileEnt) { - Module *RequestingModule = getModuleForLocation(FilenameLoc); - if (RequestingModule) - HeaderInfo.getModuleMap().resolveUses(RequestingModule, /*Complain=*/false); - bool FoundInModule = false; - ModuleMap::KnownHeader RequestedModule = - HeaderInfo.getModuleMap().findModuleForHeader( - IncFileEnt, RequestingModule, &FoundInModule); - - if (!FoundInModule) - return; // The header is not part of a module. - - if (RequestingModule == RequestedModule.getModule()) - return; // No faults wihin a module, or between files both not in modules. - - if (RequestingModule != HeaderInfo.getModuleMap().SourceModule) - return; // No errors for indirect modules. - // This may be a bit of a problem for modules with no source files. - - if (RequestedModule && violatesPrivateInclude(RequestingModule, IncFileEnt, - RequestedModule.getRole(), - RequestedModule.getModule())) - Diag(FilenameLoc, diag::error_use_of_private_header_outside_module) - << Filename; - - // FIXME: Add support for FixIts in module map files and offer adding the - // required use declaration. - if (RequestingModule && getLangOpts().ModulesDeclUse && - violatesUseDeclarations(RequestingModule, RequestedModule.getModule())) - Diag(FilenameLoc, diag::error_undeclared_use_of_module) - << RequestingModule->getFullModuleName() << Filename; -} - const FileEntry *Preprocessor::LookupFile( SourceLocation FilenameLoc, StringRef Filename, @@ -653,7 +586,8 @@ const FileEntry *Preprocessor::LookupFile( SearchPath, RelativePath, SuggestedModule, SkipCache); if (FE) { if (SuggestedModule) - verifyModuleInclude(FilenameLoc, Filename, FE); + HeaderInfo.getModuleMap().diagnoseHeaderInclusion( + getModuleForLocation(FilenameLoc), FilenameLoc, Filename, FE); return FE; } diff --git a/test/Modules/Inputs/declare-use/module.map b/test/Modules/Inputs/declare-use/module.map index ba615b67144ac182826b447b62a3fac23260f806..40f071289a78db20a521ff9644f701256d07dab5 100644 --- a/test/Modules/Inputs/declare-use/module.map +++ b/test/Modules/Inputs/declare-use/module.map @@ -20,12 +20,14 @@ module XD { module XE { header "e.h" + header "unavailable.h" use XA use XB } module XF { header "f.h" + header "unavailable.h" use XA use XB }