From: Douglas Gregor Date: Wed, 7 Dec 2011 02:23:45 +0000 (+0000) Subject: Implement basic support for private headers in frameworks. In essence, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=587986efc5ca409da3ebf0a4ab7f72ebf50a3ab9;p=clang Implement basic support for private headers in frameworks. In essence, when we load a module map (module.map) from a directory, also load a private module map (module_private.map) for that directory, if present. That private module map can inject a new submodule that captures private headers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146012 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 7c26aed042..2cf115d728 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -413,7 +413,11 @@ def err_mmap_expected_wildcard_member : Error< "expected module export wildcard">; def err_mmap_expected_export_wildcard : Error< "only '*' can be exported from an inferred submodule">; - +def err_mmap_explicit_top_level : Error< + "'explicit' is not permitted on top-level modules">; +def err_mmap_nested_submodule_id : Error< + "qualified module name can only be used to define modules at the top level">; + def warn_auto_module_import : Warning< "treating #%select{include|import|include_next|__include_macros}0 as an " "import of module '%1'">, InGroup, DefaultIgnore; diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index 1b969b93be..7035b9c45d 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -870,15 +870,33 @@ HeaderSearch::loadModuleMapFile(const DirectoryEntry *Dir) { llvm::SmallString<128> ModuleMapFileName; ModuleMapFileName += Dir->getName(); + unsigned ModuleMapDirNameLen = ModuleMapFileName.size(); llvm::sys::path::append(ModuleMapFileName, "module.map"); if (const FileEntry *ModuleMapFile = FileMgr.getFile(ModuleMapFileName)) { // We have found a module map file. Try to parse it. - if (!ModMap.parseModuleMapFile(ModuleMapFile)) { - // This directory has a module map. - DirectoryHasModuleMap[Dir] = true; - - return LMM_NewlyLoaded; + if (ModMap.parseModuleMapFile(ModuleMapFile)) { + // No suitable module map. + DirectoryHasModuleMap[Dir] = false; + return LMM_InvalidModuleMap; } + + // This directory has a module map. + DirectoryHasModuleMap[Dir] = true; + + // Check whether there is a private module map that we need to load as well. + ModuleMapFileName.erase(ModuleMapFileName.begin() + ModuleMapDirNameLen, + ModuleMapFileName.end()); + llvm::sys::path::append(ModuleMapFileName, "module_private.map"); + if (const FileEntry *PrivateModuleMapFile + = FileMgr.getFile(ModuleMapFileName)) { + if (ModMap.parseModuleMapFile(PrivateModuleMapFile)) { + // No suitable module map. + DirectoryHasModuleMap[Dir] = false; + return LMM_InvalidModuleMap; + } + } + + return LMM_NewlyLoaded; } // No suitable module map. diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 9c3a39f4f6..f6df302007 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -435,7 +435,10 @@ namespace clang { /// \brief Skip tokens until we reach the a token with the given kind /// (or the end of the file). void skipUntil(MMToken::TokenKind K); - + + typedef llvm::SmallVector, 2> + ModuleId; + bool parseModuleId(ModuleId &Id); void parseModuleDecl(); void parseUmbrellaDecl(); void parseHeaderDecl(); @@ -567,15 +570,42 @@ void ModuleMapParser::skipUntil(MMToken::TokenKind K) { } while (true); } +/// \brief Parse a module-id. +/// +/// module-id: +/// identifier +/// identifier '.' module-id +/// +/// \returns true if an error occurred, false otherwise. +bool ModuleMapParser::parseModuleId(ModuleId &Id) { + Id.clear(); + do { + if (Tok.is(MMToken::Identifier)) { + Id.push_back(std::make_pair(Tok.getString(), Tok.getLocation())); + consumeToken(); + } else { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module_name); + return true; + } + + if (!Tok.is(MMToken::Period)) + break; + + consumeToken(); + } while (true); + + return false; +} + /// \brief Parse a module declaration. /// /// module-declaration: -/// 'framework'[opt] 'module' identifier { module-member* } +/// 'explicit'[opt] 'framework'[opt] 'module' module-id { module-member* } /// /// module-member: /// umbrella-declaration /// header-declaration -/// 'explicit'[opt] submodule-declaration +/// submodule-declaration /// export-declaration /// /// submodule-declaration: @@ -584,14 +614,14 @@ void ModuleMapParser::skipUntil(MMToken::TokenKind K) { void ModuleMapParser::parseModuleDecl() { assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword) || Tok.is(MMToken::FrameworkKeyword)); - // Parse 'explicit' or 'framework' keyword, if present. + SourceLocation ExplicitLoc; bool Explicit = false; bool Framework = false; // Parse 'explicit' keyword, if present. if (Tok.is(MMToken::ExplicitKeyword)) { - consumeToken(); + ExplicitLoc = consumeToken(); Explicit = true; } @@ -616,13 +646,52 @@ void ModuleMapParser::parseModuleDecl() { return parseInferredSubmoduleDecl(Explicit); // Parse the module name. - if (!Tok.is(MMToken::Identifier)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module_name); + ModuleId Id; + if (parseModuleId(Id)) { + HadError = true; + return; + } + + if (ActiveModule) { + if (Id.size() > 1) { + Diags.Report(Id.front().second, diag::err_mmap_nested_submodule_id) + << SourceRange(Id.front().second, Id.back().second); + + HadError = true; + return; + } + } else if (Id.size() == 1 && Explicit) { + // Top-level modules can't be explicit. + Diags.Report(ExplicitLoc, diag::err_mmap_explicit_top_level); + Explicit = false; + ExplicitLoc = SourceLocation(); HadError = true; - return; } - StringRef ModuleName = Tok.getString(); - SourceLocation ModuleNameLoc = consumeToken(); + + Module *PreviousActiveModule = ActiveModule; + if (Id.size() > 1) { + // This module map defines a submodule. Go find the module of which it + // is a submodule. + ActiveModule = 0; + for (unsigned I = 0, N = Id.size() - 1; I != N; ++I) { + if (Module *Next = Map.lookupModuleQualified(Id[I].first, ActiveModule)) { + ActiveModule = Next; + continue; + } + + if (ActiveModule) { + Diags.Report(Id[I].second, diag::err_mmap_missing_module_qualified) + << Id[I].first << ActiveModule->getTopLevelModule(); + } else { + Diags.Report(Id[I].second, diag::err_mmap_expected_module_name); + } + HadError = true; + return; + } + } + + StringRef ModuleName = Id.back().first; + SourceLocation ModuleNameLoc = Id.back().second; // Parse the opening brace. if (!Tok.is(MMToken::LBrace)) { @@ -699,8 +768,8 @@ void ModuleMapParser::parseModuleDecl() { HadError = true; } - // We're done parsing this module. Pop back to our parent scope. - ActiveModule = ActiveModule->Parent; + // We're done parsing this module. Pop back to the previous module. + ActiveModule = PreviousActiveModule; } /// \brief Append to \p Paths the set of paths needed to get to the @@ -738,7 +807,7 @@ void ModuleMapParser::parseUmbrellaDecl() { HadError = true; return; } - StringRef FileName = Tok.getString(); + std::string FileName = Tok.getString(); SourceLocation FileNameLoc = consumeToken(); // Check whether we already have an umbrella header. @@ -829,26 +898,45 @@ void ModuleMapParser::parseHeaderDecl() { HadError = true; return; } - StringRef FileName = Tok.getString(); + std::string FileName = Tok.getString(); SourceLocation FileNameLoc = consumeToken(); // Look for this file. + const FileEntry *File = 0; llvm::SmallString<128> PathName; - if (llvm::sys::path::is_relative(FileName)) { - // FIXME: Change this search to also look for private headers! + if (llvm::sys::path::is_absolute(FileName)) { + PathName = FileName; + File = SourceMgr.getFileManager().getFile(PathName); + } else { + // Search for the header file within the search directory. PathName += Directory->getName(); + unsigned PathLength = PathName.size(); if (ActiveModule->isPartOfFramework()) { appendSubframeworkPaths(ActiveModule, PathName); + + // Check whether this file is in the public headers. llvm::sys::path::append(PathName, "Headers"); + llvm::sys::path::append(PathName, FileName); + File = SourceMgr.getFileManager().getFile(PathName); + + if (!File) { + // Check whether this file is in the private headers. + PathName.resize(PathLength); + llvm::sys::path::append(PathName, "PrivateHeaders"); + llvm::sys::path::append(PathName, FileName); + File = SourceMgr.getFileManager().getFile(PathName); + } + } else { + // Lookup for normal headers. + llvm::sys::path::append(PathName, FileName); + File = SourceMgr.getFileManager().getFile(PathName); } } - llvm::sys::path::append(PathName, FileName); - // FIXME: We shouldn't be eagerly stat'ing every file named in a module map. // Come up with a lazy way to do this. - if (const FileEntry *File = SourceMgr.getFileManager().getFile(PathName)) { + if (File) { if (const Module *OwningModule = Map.Headers[File]) { Diags.Report(FileNameLoc, diag::err_mmap_header_conflict) << FileName << OwningModule->getFullModuleName(); @@ -1012,12 +1100,12 @@ bool ModuleMapParser::parseModuleMapFile() { case MMToken::EndOfFile: return HadError; + case MMToken::ExplicitKeyword: case MMToken::ModuleKeyword: case MMToken::FrameworkKeyword: parseModuleDecl(); break; - case MMToken::ExplicitKeyword: case MMToken::ExportKeyword: case MMToken::HeaderKeyword: case MMToken::Identifier: diff --git a/test/Modules/Inputs/DependsOnModule.framework/PrivateHeaders/DependsOnModulePrivate.h b/test/Modules/Inputs/DependsOnModule.framework/PrivateHeaders/DependsOnModulePrivate.h new file mode 100644 index 0000000000..4b3c30c7d3 --- /dev/null +++ b/test/Modules/Inputs/DependsOnModule.framework/PrivateHeaders/DependsOnModulePrivate.h @@ -0,0 +1,2 @@ +int depends_on_module_private; + diff --git a/test/Modules/Inputs/DependsOnModule.framework/module_private.map b/test/Modules/Inputs/DependsOnModule.framework/module_private.map new file mode 100644 index 0000000000..5ed0029085 --- /dev/null +++ b/test/Modules/Inputs/DependsOnModule.framework/module_private.map @@ -0,0 +1,6 @@ +explicit module DependsOnModule.Private { + explicit module DependsOnModule { + header "DependsOnModulePrivate.h" + } +} + diff --git a/test/Modules/auto-module-import.m b/test/Modules/auto-module-import.m index 52bbcec173..c1708e8eae 100644 --- a/test/Modules/auto-module-import.m +++ b/test/Modules/auto-module-import.m @@ -39,3 +39,6 @@ void testModuleSubFramework() { void testModuleSubFrameworkAgain() { char *msf = module_subframework; } + +// Test inclusion of private headers. +#include // expected-warning{{treating #include as an import of module 'DependsOnModule.Private.DependsOnModule'}}