diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp')
| -rw-r--r-- | contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp | 244 |
1 files changed, 178 insertions, 66 deletions
diff --git a/contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp b/contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp index 40f78ce25ceb..b3ac10c5c5ae 100644 --- a/contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp +++ b/contrib/llvm/tools/clang/lib/Lex/ModuleMap.cpp @@ -1,4 +1,4 @@ -//===--- ModuleMap.cpp - Describe the layout of modules ---------*- C++ -*-===// +//===- ModuleMap.cpp - Describe the layout of modules ---------------------===// // // The LLVM Compiler Infrastructure // @@ -11,29 +11,47 @@ // of a module as it relates to headers. // //===----------------------------------------------------------------------===// + #include "clang/Lex/ModuleMap.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Diagnostic.h" -#include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/Module.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" -#include "clang/Basic/TargetOptions.h" +#include "clang/Basic/VirtualFileSystem.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/LexDiagnostic.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/LiteralSupport.h" +#include "clang/Lex/Token.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Allocator.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Host.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" -#include <stdlib.h> -#if defined(LLVM_ON_UNIX) -#include <limits.h> -#endif +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <string> +#include <system_error> +#include <utility> + using namespace clang; Module::HeaderKind ModuleMap::headerRoleToKind(ModuleHeaderRole Role) { @@ -80,7 +98,7 @@ ModuleMap::resolveExport(Module *Mod, // Resolve the module-id. Module *Context = resolveModuleId(Unresolved.Id, Mod, Complain); if (!Context) - return Module::ExportDecl(); + return {}; return Module::ExportDecl(Context, Unresolved.Wildcard); } @@ -256,8 +274,7 @@ ModuleMap::ModuleMap(SourceManager &SourceMgr, DiagnosticsEngine &Diags, const LangOptions &LangOpts, const TargetInfo *Target, HeaderSearch &HeaderInfo) : SourceMgr(SourceMgr), Diags(Diags), LangOpts(LangOpts), Target(Target), - HeaderInfo(HeaderInfo), BuiltinIncludeDir(nullptr), - SourceModule(nullptr), NumCreatedModules(0) { + HeaderInfo(HeaderInfo) { MMapLangOpts.LineComment = true; } @@ -345,7 +362,7 @@ ModuleMap::KnownHeader ModuleMap::findHeaderInUmbrellaDirs(const FileEntry *File, SmallVectorImpl<const DirectoryEntry *> &IntermediateDirs) { if (UmbrellaDirs.empty()) - return KnownHeader(); + return {}; const DirectoryEntry *Dir = File->getDir(); assert(Dir && "file in no directory"); @@ -373,7 +390,7 @@ ModuleMap::findHeaderInUmbrellaDirs(const FileEntry *File, // Resolve the parent path to a directory entry. Dir = SourceMgr.getFileManager().getDirectory(DirName); } while (Dir); - return KnownHeader(); + return {}; } static bool violatesPrivateInclude(Module *RequestingModule, @@ -503,7 +520,7 @@ ModuleMap::KnownHeader ModuleMap::findModuleForHeader(const FileEntry *File, bool AllowTextual) { auto MakeResult = [&](ModuleMap::KnownHeader R) -> ModuleMap::KnownHeader { if (!AllowTextual && R.getRole() & ModuleMap::TextualHeader) - return ModuleMap::KnownHeader(); + return {}; return R; }; @@ -593,7 +610,7 @@ ModuleMap::findOrCreateModuleForHeaderInUmbrellaDir(const FileEntry *File) { return Header; } - return KnownHeader(); + return {}; } ArrayRef<ModuleMap::KnownHeader> @@ -746,8 +763,18 @@ std::pair<Module *, bool> ModuleMap::findOrCreateModule(StringRef Name, return std::make_pair(Result, true); } +Module *ModuleMap::createGlobalModuleForInterfaceUnit(SourceLocation Loc) { + assert(!PendingGlobalModule && "created multiple global modules"); + PendingGlobalModule.reset( + new Module("<global>", Loc, nullptr, /*IsFramework*/ false, + /*IsExplicit*/ true, NumCreatedModules++)); + PendingGlobalModule->Kind = Module::GlobalModuleFragment; + return PendingGlobalModule.get(); +} + Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc, - StringRef Name) { + StringRef Name, + Module *GlobalModule) { assert(LangOpts.CurrentModule == Name && "module name mismatch"); assert(!Modules[Name] && "redefining existing module"); @@ -757,6 +784,12 @@ Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc, Result->Kind = Module::ModuleInterfaceUnit; Modules[Name] = SourceModule = Result; + // Reparent the current global module fragment as a submodule of this module. + assert(GlobalModule == PendingGlobalModule.get() && + "unexpected global module"); + GlobalModule->setParent(Result); + PendingGlobalModule.release(); // now owned by parent + // Mark the main source file as being within the newly-created module so that // declarations and macros are properly visibility-restricted to it. auto *MainFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); @@ -1173,6 +1206,7 @@ bool ModuleMap::resolveConflicts(Module *Mod, bool Complain) { //----------------------------------------------------------------------------// namespace clang { + /// \brief A token in a module map file. struct MMToken { enum TokenKind { @@ -1186,6 +1220,7 @@ namespace clang { ExcludeKeyword, ExplicitKeyword, ExportKeyword, + ExportAsKeyword, ExternKeyword, FrameworkKeyword, LinkKeyword, @@ -1210,6 +1245,7 @@ namespace clang { union { // If Kind != IntegerLiteral. const char *StringData; + // If Kind == IntegerLiteral. uint64_t IntegerValue; }; @@ -1259,7 +1295,7 @@ namespace clang { bool IsSystem; /// \brief Whether an error occurred. - bool HadError; + bool HadError = false; /// \brief Stores string data for the various string literals referenced /// during parsing. @@ -1269,7 +1305,7 @@ namespace clang { MMToken Tok; /// \brief The active module. - Module *ActiveModule; + Module *ActiveModule = nullptr; /// \brief Whether a module uses the 'requires excluded' hack to mark its /// contents as 'textual'. @@ -1288,22 +1324,24 @@ namespace clang { /// (or the end of the file). void skipUntil(MMToken::TokenKind K); - typedef SmallVector<std::pair<std::string, SourceLocation>, 2> ModuleId; + using ModuleId = SmallVector<std::pair<std::string, SourceLocation>, 2>; + bool parseModuleId(ModuleId &Id); void parseModuleDecl(); void parseExternModuleDecl(); void parseRequiresDecl(); - void parseHeaderDecl(clang::MMToken::TokenKind, - SourceLocation LeadingLoc); + void parseHeaderDecl(MMToken::TokenKind, SourceLocation LeadingLoc); void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc); void parseExportDecl(); + void parseExportAsDecl(); void parseUseDecl(); void parseLinkDecl(); void parseConfigMacros(); void parseConflict(); void parseInferredModuleDecl(bool Framework, bool Explicit); - typedef ModuleMap::Attributes Attributes; + using Attributes = ModuleMap::Attributes; + bool parseOptionalAttributes(Attributes &Attrs); public: @@ -1314,10 +1352,9 @@ namespace clang { const FileEntry *ModuleMapFile, const DirectoryEntry *Directory, bool IsSystem) - : L(L), SourceMgr(SourceMgr), Target(Target), Diags(Diags), Map(Map), - ModuleMapFile(ModuleMapFile), Directory(Directory), - IsSystem(IsSystem), HadError(false), ActiveModule(nullptr) - { + : L(L), SourceMgr(SourceMgr), Target(Target), Diags(Diags), Map(Map), + ModuleMapFile(ModuleMapFile), Directory(Directory), + IsSystem(IsSystem) { Tok.clear(); consumeToken(); } @@ -1327,7 +1364,8 @@ namespace clang { bool terminatedByDirective() { return false; } SourceLocation getLocation() { return Tok.getLocation(); } }; -} + +} // namespace clang SourceLocation ModuleMapParser::consumeToken() { SourceLocation Result = Tok.getLocation(); @@ -1348,6 +1386,7 @@ retry: .Case("exclude", MMToken::ExcludeKeyword) .Case("explicit", MMToken::ExplicitKeyword) .Case("export", MMToken::ExportKeyword) + .Case("export_as", MMToken::ExportAsKeyword) .Case("extern", MMToken::ExternKeyword) .Case("framework", MMToken::FrameworkKeyword) .Case("header", MMToken::HeaderKeyword) @@ -1548,19 +1587,73 @@ bool ModuleMapParser::parseModuleId(ModuleId &Id) { } namespace { + /// \brief Enumerates the known attributes. enum AttributeKind { /// \brief An unknown attribute. AT_unknown, + /// \brief The 'system' attribute. AT_system, + /// \brief The 'extern_c' attribute. AT_extern_c, + /// \brief The 'exhaustive' attribute. AT_exhaustive, + /// \brief The 'no_undeclared_includes' attribute. AT_no_undeclared_includes }; + +} // 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. @@ -1575,6 +1668,7 @@ namespace { /// header-declaration /// submodule-declaration /// export-declaration +/// export-as-declaration /// link-declaration /// /// submodule-declaration: @@ -1683,7 +1777,6 @@ void ModuleMapParser::parseModuleDecl() { if (parseOptionalAttributes(Attrs)) return; - // Parse the opening brace. if (!Tok.is(MMToken::LBrace)) { Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace) @@ -1746,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 { @@ -1809,6 +1882,10 @@ void ModuleMapParser::parseModuleDecl() { parseExportDecl(); break; + case MMToken::ExportAsKeyword: + parseExportAsDecl(); + break; + case MMToken::UseKeyword: parseUseDecl(); break; @@ -2269,6 +2346,41 @@ void ModuleMapParser::parseExportDecl() { ActiveModule->UnresolvedExports.push_back(Unresolved); } +/// \brief Parse a module export_as declaration. +/// +/// export-as-declaration: +/// 'export_as' identifier +void ModuleMapParser::parseExportAsDecl() { + assert(Tok.is(MMToken::ExportAsKeyword)); + consumeToken(); + + if (!Tok.is(MMToken::Identifier)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_module_id); + HadError = true; + return; + } + + if (ActiveModule->Parent) { + Diags.Report(Tok.getLocation(), diag::err_mmap_submodule_export_as); + consumeToken(); + return; + } + + if (!ActiveModule->ExportAsModule.empty()) { + if (ActiveModule->ExportAsModule == Tok.getString()) { + Diags.Report(Tok.getLocation(), diag::warn_mmap_redundant_export_as) + << ActiveModule->Name << Tok.getString(); + } else { + Diags.Report(Tok.getLocation(), diag::err_mmap_conflicting_export_as) + << ActiveModule->Name << ActiveModule->ExportAsModule + << Tok.getString(); + } + } + + ActiveModule->ExportAsModule = Tok.getString(); + consumeToken(); +} + /// \brief Parse a module use declaration. /// /// use-declaration: @@ -2516,7 +2628,7 @@ void ModuleMapParser::parseInferredModuleDecl(bool Framework, bool Explicit) { Done = true; break; - case MMToken::ExcludeKeyword: { + case MMToken::ExcludeKeyword: if (ActiveModule) { Diags.Report(Tok.getLocation(), diag::err_mmap_expected_inferred_member) << (ActiveModule != nullptr); @@ -2535,7 +2647,6 @@ void ModuleMapParser::parseInferredModuleDecl(bool Framework, bool Explicit) { .push_back(Tok.getString()); consumeToken(); break; - } case MMToken::ExportKeyword: if (!ActiveModule) { @@ -2674,6 +2785,7 @@ bool ModuleMapParser::parseModuleMapFile() { case MMToken::Exclaim: case MMToken::ExcludeKeyword: case MMToken::ExportKeyword: + case MMToken::ExportAsKeyword: case MMToken::HeaderKeyword: case MMToken::Identifier: case MMToken::LBrace: |
