diff options
author | Michael Spencer <bigcheesegs@gmail.com> | 2025-02-26 14:32:50 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-26 14:32:50 -0800 |
commit | 8fb88f568011fb916cda9d7927ac97c6751a8b89 (patch) | |
tree | 4fc0dcd235ecfc4b15dcae6d4f8ea752c7bdba74 /clang/lib/Lex/ModuleMap.cpp | |
parent | 39bab1de33333ee3c62b586c4e8d26f8c443bc60 (diff) | |
download | llvm-8fb88f568011fb916cda9d7927ac97c6751a8b89.zip llvm-8fb88f568011fb916cda9d7927ac97c6751a8b89.tar.gz llvm-8fb88f568011fb916cda9d7927ac97c6751a8b89.tar.bz2 |
[clang][modules] Separate parsing of modulemaps (#119740)
This separates out parsing of modulemaps from updating the
`clang::ModuleMap` information.
Currently this has no effect other than slightly changing diagnostics.
Upcoming changes will use this to allow searching for modules without
fully processing modulemaps.
This creates a new `modulemap` namespace because there are too many
things called ModuleMap* right now that mean different things. I'd like
to clean this up, but I'm not sure yet what I want to call everything.
This also drops the `SourceLocation` from `moduleMapFileRead`. This is
never used in tree, and in future patches I plan to make the modulemap
parser use a different `SourceManager` so that we can share modulemap
parsing between `CompilerInstance`s. This will make the `SourceLocation`
meaningless.
Diffstat (limited to 'clang/lib/Lex/ModuleMap.cpp')
-rw-r--r-- | clang/lib/Lex/ModuleMap.cpp | 1498 |
1 files changed, 237 insertions, 1261 deletions
diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index 998e2b9..e6985a4 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -24,9 +24,7 @@ #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 "clang/Lex/ModuleMapFile.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" @@ -34,7 +32,6 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/Allocator.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Path.h" @@ -1461,419 +1458,80 @@ bool ModuleMap::resolveConflicts(Module *Mod, bool Complain) { //----------------------------------------------------------------------------// namespace clang { +class ModuleMapParser { + modulemap::ModuleMapFile &MMF; + SourceManager &SourceMgr; - /// A token in a module map file. - struct MMToken { - enum TokenKind { - Comma, - ConfigMacros, - Conflict, - EndOfFile, - HeaderKeyword, - Identifier, - Exclaim, - ExcludeKeyword, - ExplicitKeyword, - ExportKeyword, - ExportAsKeyword, - ExternKeyword, - FrameworkKeyword, - LinkKeyword, - ModuleKeyword, - Period, - PrivateKeyword, - UmbrellaKeyword, - UseKeyword, - RequiresKeyword, - Star, - StringLiteral, - IntegerLiteral, - TextualKeyword, - LBrace, - RBrace, - LSquare, - RSquare - } Kind; - - SourceLocation::UIntTy Location; - unsigned StringLength; - union { - // If Kind != IntegerLiteral. - const char *StringData; - - // If Kind == IntegerLiteral. - uint64_t IntegerValue; - }; - - void clear() { - Kind = EndOfFile; - Location = 0; - StringLength = 0; - StringData = nullptr; - } + DiagnosticsEngine &Diags; + ModuleMap ⤅ - bool is(TokenKind K) const { return Kind == K; } + /// The current module map file. + FileID ModuleMapFID; - SourceLocation getLocation() const { - return SourceLocation::getFromRawEncoding(Location); - } + /// Source location of most recent parsed module declaration + SourceLocation CurrModuleDeclLoc; - uint64_t getInteger() const { - return Kind == IntegerLiteral ? IntegerValue : 0; - } + /// The directory that file names in this module map file should + /// be resolved relative to. + DirectoryEntryRef Directory; - StringRef getString() const { - return Kind == IntegerLiteral ? StringRef() - : StringRef(StringData, StringLength); - } - }; + /// Whether this module map is in a system header directory. + bool IsSystem; - class ModuleMapParser { - Lexer &L; - SourceManager &SourceMgr; - - /// Default target information, used only for string literal - /// parsing. - const TargetInfo *Target; - - DiagnosticsEngine &Diags; - ModuleMap ⤅ - - /// The current module map file. - FileID ModuleMapFID; - - /// Source location of most recent parsed module declaration - SourceLocation CurrModuleDeclLoc; - - /// The directory that file names in this module map file should - /// be resolved relative to. - DirectoryEntryRef Directory; - - /// Whether this module map is in a system header directory. - bool IsSystem; - - /// Whether an error occurred. - bool HadError = false; - - /// Stores string data for the various string literals referenced - /// during parsing. - llvm::BumpPtrAllocator StringData; - - /// The current token. - MMToken Tok; - - /// The active module. - Module *ActiveModule = nullptr; - - /// Whether a module uses the 'requires excluded' hack to mark its - /// contents as 'textual'. - /// - /// On older Darwin SDK versions, 'requires excluded' is used to mark the - /// contents of the Darwin.C.excluded (assert.h) and Tcl.Private modules as - /// non-modular headers. For backwards compatibility, we continue to - /// support this idiom for just these modules, and map the headers to - /// 'textual' to match the original intent. - llvm::SmallPtrSet<Module *, 2> UsesRequiresExcludedHack; - - /// Consume the current token and return its location. - SourceLocation consumeToken(); - - /// Skip tokens until we reach the a token with the given kind - /// (or the end of the file). - void skipUntil(MMToken::TokenKind K); - - bool parseModuleId(ModuleId &Id); - void parseModuleDecl(); - void parseExternModuleDecl(); - void parseRequiresDecl(); - 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); - - /// 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. - void diagnosePrivateModules(SourceLocation ExplicitLoc, - SourceLocation FrameworkLoc); - - using Attributes = ModuleMap::Attributes; - - bool parseOptionalAttributes(Attributes &Attrs); - - public: - ModuleMapParser(Lexer &L, SourceManager &SourceMgr, - const TargetInfo *Target, DiagnosticsEngine &Diags, - ModuleMap &Map, FileID ModuleMapFID, - DirectoryEntryRef Directory, bool IsSystem) - : L(L), SourceMgr(SourceMgr), Target(Target), Diags(Diags), Map(Map), - ModuleMapFID(ModuleMapFID), Directory(Directory), IsSystem(IsSystem) { - Tok.clear(); - consumeToken(); - } + /// Whether an error occurred. + bool HadError = false; - bool parseModuleMapFile(); - - bool terminatedByDirective() { return false; } - SourceLocation getLocation() { return Tok.getLocation(); } - }; + /// The active module. + Module *ActiveModule = nullptr; + + /// Whether a module uses the 'requires excluded' hack to mark its + /// contents as 'textual'. + /// + /// On older Darwin SDK versions, 'requires excluded' is used to mark the + /// contents of the Darwin.C.excluded (assert.h) and Tcl.Private modules as + /// non-modular headers. For backwards compatibility, we continue to + /// support this idiom for just these modules, and map the headers to + /// 'textual' to match the original intent. + llvm::SmallPtrSet<Module *, 2> UsesRequiresExcludedHack; + + void handleModuleDecl(const modulemap::ModuleDecl &MD); + void handleExternModuleDecl(const modulemap::ExternModuleDecl &EMD); + void handleRequiresDecl(const modulemap::RequiresDecl &RD); + void handleHeaderDecl(const modulemap::HeaderDecl &HD); + void handleUmbrellaDirDecl(const modulemap::UmbrellaDirDecl &UDD); + void handleExportDecl(const modulemap::ExportDecl &ED); + void handleExportAsDecl(const modulemap::ExportAsDecl &EAD); + void handleUseDecl(const modulemap::UseDecl &UD); + void handleLinkDecl(const modulemap::LinkDecl &LD); + void handleConfigMacros(const modulemap::ConfigMacrosDecl &CMD); + void handleConflict(const modulemap::ConflictDecl &CD); + void handleInferredModuleDecl(const modulemap::ModuleDecl &MD); + + /// 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. + void diagnosePrivateModules(SourceLocation StartLoc); + + using Attributes = ModuleMap::Attributes; + +public: + ModuleMapParser(modulemap::ModuleMapFile &MMF, SourceManager &SourceMgr, + DiagnosticsEngine &Diags, ModuleMap &Map, FileID ModuleMapFID, + DirectoryEntryRef Directory, bool IsSystem) + : MMF(MMF), SourceMgr(SourceMgr), Diags(Diags), Map(Map), + ModuleMapFID(ModuleMapFID), Directory(Directory), IsSystem(IsSystem) {} + + bool parseModuleMapFile(); +}; } // namespace clang -SourceLocation ModuleMapParser::consumeToken() { - SourceLocation Result = Tok.getLocation(); - -retry: - Tok.clear(); - Token LToken; - L.LexFromRawLexer(LToken); - Tok.Location = LToken.getLocation().getRawEncoding(); - switch (LToken.getKind()) { - case tok::raw_identifier: { - StringRef RI = LToken.getRawIdentifier(); - Tok.StringData = RI.data(); - Tok.StringLength = RI.size(); - Tok.Kind = llvm::StringSwitch<MMToken::TokenKind>(RI) - .Case("config_macros", MMToken::ConfigMacros) - .Case("conflict", MMToken::Conflict) - .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) - .Case("link", MMToken::LinkKeyword) - .Case("module", MMToken::ModuleKeyword) - .Case("private", MMToken::PrivateKeyword) - .Case("requires", MMToken::RequiresKeyword) - .Case("textual", MMToken::TextualKeyword) - .Case("umbrella", MMToken::UmbrellaKeyword) - .Case("use", MMToken::UseKeyword) - .Default(MMToken::Identifier); - break; - } - - case tok::comma: - Tok.Kind = MMToken::Comma; - break; - - case tok::eof: - Tok.Kind = MMToken::EndOfFile; - break; - - case tok::l_brace: - Tok.Kind = MMToken::LBrace; - break; - - case tok::l_square: - Tok.Kind = MMToken::LSquare; - break; - - case tok::period: - Tok.Kind = MMToken::Period; - break; - - case tok::r_brace: - Tok.Kind = MMToken::RBrace; - break; - - case tok::r_square: - Tok.Kind = MMToken::RSquare; - break; - - case tok::star: - Tok.Kind = MMToken::Star; - break; - - case tok::exclaim: - Tok.Kind = MMToken::Exclaim; - break; - - case tok::string_literal: { - if (LToken.hasUDSuffix()) { - Diags.Report(LToken.getLocation(), diag::err_invalid_string_udl); - HadError = true; - goto retry; - } - - // Parse the string literal. - LangOptions LangOpts; - StringLiteralParser StringLiteral(LToken, SourceMgr, LangOpts, *Target); - if (StringLiteral.hadError) - goto retry; - - // Copy the string literal into our string data allocator. - unsigned Length = StringLiteral.GetStringLength(); - char *Saved = StringData.Allocate<char>(Length + 1); - memcpy(Saved, StringLiteral.GetString().data(), Length); - Saved[Length] = 0; - - // Form the token. - Tok.Kind = MMToken::StringLiteral; - Tok.StringData = Saved; - Tok.StringLength = Length; - break; - } - - case tok::numeric_constant: { - // We don't support any suffixes or other complications. - SmallString<32> SpellingBuffer; - SpellingBuffer.resize(LToken.getLength() + 1); - const char *Start = SpellingBuffer.data(); - unsigned Length = - Lexer::getSpelling(LToken, Start, SourceMgr, Map.LangOpts); - uint64_t Value; - if (StringRef(Start, Length).getAsInteger(0, Value)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token); - HadError = true; - goto retry; - } - - Tok.Kind = MMToken::IntegerLiteral; - Tok.IntegerValue = Value; - break; - } - - case tok::comment: - goto retry; - - case tok::hash: - // A module map can be terminated prematurely by - // #pragma clang module contents - // When building the module, we'll treat the rest of the file as the - // contents of the module. - { - auto NextIsIdent = [&](StringRef Str) -> bool { - L.LexFromRawLexer(LToken); - return !LToken.isAtStartOfLine() && LToken.is(tok::raw_identifier) && - LToken.getRawIdentifier() == Str; - }; - if (NextIsIdent("pragma") && NextIsIdent("clang") && - NextIsIdent("module") && NextIsIdent("contents")) { - Tok.Kind = MMToken::EndOfFile; - break; - } - } - [[fallthrough]]; - - default: - Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token); - HadError = true; - goto retry; - } - - return Result; -} - -void ModuleMapParser::skipUntil(MMToken::TokenKind K) { - unsigned braceDepth = 0; - unsigned squareDepth = 0; - do { - switch (Tok.Kind) { - case MMToken::EndOfFile: - return; - - case MMToken::LBrace: - if (Tok.is(K) && braceDepth == 0 && squareDepth == 0) - return; - - ++braceDepth; - break; - - case MMToken::LSquare: - if (Tok.is(K) && braceDepth == 0 && squareDepth == 0) - return; - - ++squareDepth; - break; - - case MMToken::RBrace: - if (braceDepth > 0) - --braceDepth; - else if (Tok.is(K)) - return; - break; - - case MMToken::RSquare: - if (squareDepth > 0) - --squareDepth; - else if (Tok.is(K)) - return; - break; - - default: - if (braceDepth == 0 && squareDepth == 0 && Tok.is(K)) - return; - break; - } - - consumeToken(); - } while (true); -} - -/// 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) || Tok.is(MMToken::StringLiteral)) { - Id.push_back( - std::make_pair(std::string(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; -} - -namespace { - - /// Enumerates the known attributes. - enum AttributeKind { - /// An unknown attribute. - AT_unknown, - - /// The 'system' attribute. - AT_system, - - /// The 'extern_c' attribute. - AT_extern_c, - - /// The 'exhaustive' attribute. - AT_exhaustive, - - /// 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. -void ModuleMapParser::diagnosePrivateModules(SourceLocation ExplicitLoc, - SourceLocation FrameworkLoc) { +void ModuleMapParser::diagnosePrivateModules(SourceLocation StartLoc) { auto GenNoteAndFixIt = [&](StringRef BadName, StringRef Canonical, const Module *M, SourceRange ReplLoc) { auto D = Diags.Report(ActiveModule->DefinitionLoc, @@ -1902,12 +1560,10 @@ void ModuleMapParser::diagnosePrivateModules(SourceLocation ExplicitLoc, << FullName; SourceLocation FixItInitBegin = CurrModuleDeclLoc; - if (FrameworkLoc.isValid()) - FixItInitBegin = FrameworkLoc; - if (ExplicitLoc.isValid()) - FixItInitBegin = ExplicitLoc; + if (StartLoc.isValid()) + FixItInitBegin = StartLoc; - if (FrameworkLoc.isValid() || ActiveModule->Parent->IsFramework) + if (ActiveModule->Parent->IsFramework) FixedPrivModDecl.append("framework "); FixedPrivModDecl.append("module "); FixedPrivModDecl.append(Canonical); @@ -1929,103 +1585,29 @@ void ModuleMapParser::diagnosePrivateModules(SourceLocation ExplicitLoc, } } -/// Parse a module declaration. -/// -/// module-declaration: -/// 'extern' 'module' module-id string-literal -/// 'explicit'[opt] 'framework'[opt] 'module' module-id attributes[opt] -/// { module-member* } -/// -/// module-member: -/// requires-declaration -/// header-declaration -/// submodule-declaration -/// export-declaration -/// export-as-declaration -/// link-declaration -/// -/// submodule-declaration: -/// module-declaration -/// inferred-submodule-declaration -void ModuleMapParser::parseModuleDecl() { - assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword) || - Tok.is(MMToken::FrameworkKeyword) || Tok.is(MMToken::ExternKeyword)); - if (Tok.is(MMToken::ExternKeyword)) { - parseExternModuleDecl(); - return; - } - - // Parse 'explicit' or 'framework' keyword, if present. - SourceLocation ExplicitLoc; - SourceLocation FrameworkLoc; - bool Explicit = false; - bool Framework = false; - - // Parse 'explicit' keyword, if present. - if (Tok.is(MMToken::ExplicitKeyword)) { - ExplicitLoc = consumeToken(); - Explicit = true; - } - - // Parse 'framework' keyword, if present. - if (Tok.is(MMToken::FrameworkKeyword)) { - FrameworkLoc = consumeToken(); - Framework = true; - } +void ModuleMapParser::handleModuleDecl(const modulemap::ModuleDecl &MD) { + if (MD.Id.front().first == "*") + return handleInferredModuleDecl(MD); - // Parse 'module' keyword. - if (!Tok.is(MMToken::ModuleKeyword)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module); - consumeToken(); - HadError = true; - return; - } - CurrModuleDeclLoc = consumeToken(); // 'module' keyword - - // If we have a wildcard for the module name, this is an inferred submodule. - // Parse it. - if (Tok.is(MMToken::Star)) - return parseInferredModuleDecl(Framework, Explicit); - - // Parse the 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; - } + CurrModuleDeclLoc = MD.Location; Module *PreviousActiveModule = ActiveModule; - if (Id.size() > 1) { + if (MD.Id.size() > 1) { // This module map defines a submodule. Go find the module of which it // is a submodule. ActiveModule = nullptr; const Module *TopLevelModule = nullptr; - for (unsigned I = 0, N = Id.size() - 1; I != N; ++I) { - if (Module *Next = Map.lookupModuleQualified(Id[I].first, ActiveModule)) { + for (unsigned I = 0, N = MD.Id.size() - 1; I != N; ++I) { + if (Module *Next = + Map.lookupModuleQualified(MD.Id[I].first, ActiveModule)) { if (I == 0) TopLevelModule = Next; ActiveModule = Next; continue; } - Diags.Report(Id[I].second, diag::err_mmap_missing_parent_module) - << Id[I].first << (ActiveModule != nullptr) + Diags.Report(MD.Id[I].second, diag::err_mmap_missing_parent_module) + << MD.Id[I].first << (ActiveModule != nullptr) << (ActiveModule ? ActiveModule->getTopLevelModule()->getFullModuleName() : ""); @@ -2043,22 +1625,8 @@ void ModuleMapParser::parseModuleDecl() { } } - StringRef ModuleName = Id.back().first; - SourceLocation ModuleNameLoc = Id.back().second; - - // Parse the optional attribute list. - Attributes Attrs; - if (parseOptionalAttributes(Attrs)) - return; - - // Parse the opening brace. - if (!Tok.is(MMToken::LBrace)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace) - << ModuleName; - HadError = true; - return; - } - SourceLocation LBraceLoc = consumeToken(); + StringRef ModuleName = MD.Id.back().first; + SourceLocation ModuleNameLoc = MD.Id.back().second; // Determine whether this (sub)module has already been defined. Module *ShadowingModule = nullptr; @@ -2085,7 +1653,7 @@ void ModuleMapParser::parseModuleDecl() { // that \c Existing is part of a framework iff the redefinition of FW // we have just skipped had it too. Once we do that, stop checking // the local framework qualifier and only rely on \c Existing. - bool PartOfFramework = Framework || Existing->isPartOfFramework(); + bool PartOfFramework = MD.Framework || Existing->isPartOfFramework(); // - If we're building a (preprocessed) module and we've just loaded the // module map file from which it was created. bool ParsedAsMainInput = @@ -2096,14 +1664,6 @@ void ModuleMapParser::parseModuleDecl() { if (LoadedFromASTFile || Inferred || PartOfFramework || ParsedAsMainInput) { ActiveModule = PreviousActiveModule; // Skip the module definition. - skipUntil(MMToken::RBrace); - if (Tok.is(MMToken::RBrace)) - consumeToken(); - else { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace); - Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match); - HadError = true; - } return; } @@ -2114,12 +1674,6 @@ void ModuleMapParser::parseModuleDecl() { Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition) << ModuleName; Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition); - - // Skip the module definition. - skipUntil(MMToken::RBrace); - if (Tok.is(MMToken::RBrace)) - consumeToken(); - HadError = true; return; } @@ -2128,18 +1682,18 @@ void ModuleMapParser::parseModuleDecl() { // Start defining this module. if (ShadowingModule) { ActiveModule = - Map.createShadowedModule(ModuleName, Framework, ShadowingModule); + Map.createShadowedModule(ModuleName, MD.Framework, ShadowingModule); } else { ActiveModule = Map.findOrCreateModuleFirst(ModuleName, ActiveModule, - Framework, Explicit); + MD.Framework, MD.Explicit); } ActiveModule->DefinitionLoc = ModuleNameLoc; - if (Attrs.IsSystem || IsSystem) + if (MD.Attrs.IsSystem || IsSystem) ActiveModule->IsSystem = true; - if (Attrs.IsExternC) + if (MD.Attrs.IsExternC) ActiveModule->IsExternC = true; - if (Attrs.NoUndeclaredIncludes) + if (MD.Attrs.NoUndeclaredIncludes) ActiveModule->NoUndeclaredIncludes = true; ActiveModule->Directory = Directory; @@ -2161,89 +1715,34 @@ void ModuleMapParser::parseModuleDecl() { !Diags.isIgnored(diag::warn_mmap_mismatched_private_module_name, StartLoc) && ActiveModule->ModuleMapIsPrivate) - diagnosePrivateModules(ExplicitLoc, FrameworkLoc); - - bool Done = false; - do { - switch (Tok.Kind) { - case MMToken::EndOfFile: - case MMToken::RBrace: - Done = true; - break; - - case MMToken::ConfigMacros: - parseConfigMacros(); - break; - - case MMToken::Conflict: - parseConflict(); - break; - - case MMToken::ExplicitKeyword: - case MMToken::ExternKeyword: - case MMToken::FrameworkKeyword: - case MMToken::ModuleKeyword: - parseModuleDecl(); - break; - - case MMToken::ExportKeyword: - parseExportDecl(); - break; - - case MMToken::ExportAsKeyword: - parseExportAsDecl(); - break; - - case MMToken::UseKeyword: - parseUseDecl(); - break; - - case MMToken::RequiresKeyword: - parseRequiresDecl(); - break; - - case MMToken::TextualKeyword: - parseHeaderDecl(MMToken::TextualKeyword, consumeToken()); - break; - - case MMToken::UmbrellaKeyword: { - SourceLocation UmbrellaLoc = consumeToken(); - if (Tok.is(MMToken::HeaderKeyword)) - parseHeaderDecl(MMToken::UmbrellaKeyword, UmbrellaLoc); - else - parseUmbrellaDirDecl(UmbrellaLoc); - break; - } - - case MMToken::ExcludeKeyword: - parseHeaderDecl(MMToken::ExcludeKeyword, consumeToken()); - break; - - case MMToken::PrivateKeyword: - parseHeaderDecl(MMToken::PrivateKeyword, consumeToken()); - break; - - case MMToken::HeaderKeyword: - parseHeaderDecl(MMToken::HeaderKeyword, consumeToken()); - break; - - case MMToken::LinkKeyword: - parseLinkDecl(); - break; - - default: - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_member); - consumeToken(); - break; - } - } while (!Done); - - if (Tok.is(MMToken::RBrace)) - consumeToken(); - else { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace); - Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match); - HadError = true; + diagnosePrivateModules(MD.Location); + + for (const modulemap::Decl &Decl : MD.Decls) { + std::visit( + llvm::makeVisitor( + [&](const modulemap::RequiresDecl &RD) { handleRequiresDecl(RD); }, + [&](const modulemap::HeaderDecl &HD) { handleHeaderDecl(HD); }, + [&](const modulemap::UmbrellaDirDecl &UDD) { + handleUmbrellaDirDecl(UDD); + }, + [&](const modulemap::ModuleDecl &MD) { handleModuleDecl(MD); }, + [&](const modulemap::ExportDecl &ED) { handleExportDecl(ED); }, + [&](const modulemap::ExportAsDecl &EAD) { + handleExportAsDecl(EAD); + }, + [&](const modulemap::ExternModuleDecl &EMD) { + handleExternModuleDecl(EMD); + }, + [&](const modulemap::UseDecl &UD) { handleUseDecl(UD); }, + [&](const modulemap::LinkDecl &LD) { handleLinkDecl(LD); }, + [&](const modulemap::ConfigMacrosDecl &CMD) { + handleConfigMacros(CMD); + }, + [&](const modulemap::ConflictDecl &CD) { handleConflict(CD); }, + [&](const modulemap::ExcludeDecl &ED) { + Diags.Report(ED.Location, diag::err_mmap_expected_member); + }), + Decl); } // If the active module is a top-level framework, and there are no link @@ -2265,44 +1764,13 @@ void ModuleMapParser::parseModuleDecl() { ActiveModule = PreviousActiveModule; } -/// Parse an extern module declaration. -/// -/// extern module-declaration: -/// 'extern' 'module' module-id string-literal -void ModuleMapParser::parseExternModuleDecl() { - assert(Tok.is(MMToken::ExternKeyword)); - SourceLocation ExternLoc = consumeToken(); // 'extern' keyword - - // Parse 'module' keyword. - if (!Tok.is(MMToken::ModuleKeyword)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module); - consumeToken(); - HadError = true; - return; - } - consumeToken(); // 'module' keyword - - // Parse the module name. - ModuleId Id; - if (parseModuleId(Id)) { - HadError = true; - return; - } - - // Parse the referenced module map file name. - if (!Tok.is(MMToken::StringLiteral)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_mmap_file); - HadError = true; - return; - } - std::string FileName = std::string(Tok.getString()); - consumeToken(); // filename - - StringRef FileNameRef = FileName; +void ModuleMapParser::handleExternModuleDecl( + const modulemap::ExternModuleDecl &EMD) { + StringRef FileNameRef = EMD.Path; SmallString<128> ModuleMapFileName; if (llvm::sys::path::is_relative(FileNameRef)) { ModuleMapFileName += Directory.getName(); - llvm::sys::path::append(ModuleMapFileName, FileName); + llvm::sys::path::append(ModuleMapFileName, EMD.Path); FileNameRef = ModuleMapFileName; } if (auto File = SourceMgr.getFileManager().getOptionalFileRef(FileNameRef)) @@ -2311,7 +1779,7 @@ void ModuleMapParser::parseExternModuleDecl() { Map.HeaderInfo.getHeaderSearchOpts().ModuleMapFileHomeIsCwd ? Directory : File->getDir(), - FileID(), nullptr, ExternLoc); + FileID(), nullptr, EMD.Location); } /// Whether to add the requirement \p Feature to the module \p M. @@ -2343,88 +1811,35 @@ static bool shouldAddRequirement(Module *M, StringRef Feature, return true; } -/// Parse a requires declaration. -/// -/// requires-declaration: -/// 'requires' feature-list -/// -/// feature-list: -/// feature ',' feature-list -/// feature -/// -/// feature: -/// '!'[opt] identifier -void ModuleMapParser::parseRequiresDecl() { - assert(Tok.is(MMToken::RequiresKeyword)); - - // Parse 'requires' keyword. - consumeToken(); - - // Parse the feature-list. - do { - bool RequiredState = true; - if (Tok.is(MMToken::Exclaim)) { - RequiredState = false; - consumeToken(); - } - - if (!Tok.is(MMToken::Identifier)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_feature); - HadError = true; - return; - } - - // Consume the feature name. - std::string Feature = std::string(Tok.getString()); - consumeToken(); +void ModuleMapParser::handleRequiresDecl(const modulemap::RequiresDecl &RD) { + for (const modulemap::RequiresFeature &RF : RD.Features) { bool IsRequiresExcludedHack = false; bool ShouldAddRequirement = - shouldAddRequirement(ActiveModule, Feature, IsRequiresExcludedHack); + shouldAddRequirement(ActiveModule, RF.Feature, IsRequiresExcludedHack); if (IsRequiresExcludedHack) UsesRequiresExcludedHack.insert(ActiveModule); if (ShouldAddRequirement) { // Add this feature. - ActiveModule->addRequirement(Feature, RequiredState, Map.LangOpts, + ActiveModule->addRequirement(RF.Feature, RF.RequiredState, Map.LangOpts, *Map.Target); } - - if (!Tok.is(MMToken::Comma)) - break; - - // Consume the comma. - consumeToken(); - } while (true); + } } -/// Parse a header declaration. -/// -/// header-declaration: -/// 'textual'[opt] 'header' string-literal -/// 'private' 'textual'[opt] 'header' string-literal -/// 'exclude' 'header' string-literal -/// 'umbrella' 'header' string-literal -/// -/// FIXME: Support 'private textual header'. -void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken, - SourceLocation LeadingLoc) { +void ModuleMapParser::handleHeaderDecl(const modulemap::HeaderDecl &HD) { // We've already consumed the first token. ModuleMap::ModuleHeaderRole Role = ModuleMap::NormalHeader; - if (LeadingToken == MMToken::PrivateKeyword) { + if (HD.Private) { Role = ModuleMap::PrivateHeader; - // 'private' may optionally be followed by 'textual'. - if (Tok.is(MMToken::TextualKeyword)) { - LeadingToken = Tok.Kind; - consumeToken(); - } - } else if (LeadingToken == MMToken::ExcludeKeyword) { + } else if (HD.Excluded) { Role = ModuleMap::ExcludedHeader; } - if (LeadingToken == MMToken::TextualKeyword) + if (HD.Textual) Role = ModuleMap::ModuleHeaderRole(Role | ModuleMap::TextualHeader); if (UsesRequiresExcludedHack.count(ActiveModule)) { @@ -2433,28 +1848,10 @@ void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken, Role = ModuleMap::ModuleHeaderRole(Role | ModuleMap::TextualHeader); } - if (LeadingToken != MMToken::HeaderKeyword) { - if (!Tok.is(MMToken::HeaderKeyword)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) - << (LeadingToken == MMToken::PrivateKeyword ? "private" : - LeadingToken == MMToken::ExcludeKeyword ? "exclude" : - LeadingToken == MMToken::TextualKeyword ? "textual" : "umbrella"); - return; - } - consumeToken(); - } - - // Parse the header name. - if (!Tok.is(MMToken::StringLiteral)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) - << "header"; - HadError = true; - return; - } Module::UnresolvedHeaderDirective Header; - Header.FileName = std::string(Tok.getString()); - Header.FileNameLoc = consumeToken(); - Header.IsUmbrella = LeadingToken == MMToken::UmbrellaKeyword; + Header.FileName = HD.Path; + Header.FileNameLoc = HD.PathLoc; + Header.IsUmbrella = HD.Umbrella; Header.Kind = Map.headerRoleToKind(Role); // Check whether we already have an umbrella. @@ -2466,60 +1863,10 @@ void ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken, return; } - // If we were given stat information, parse it so we can skip looking for - // the file. - if (Tok.is(MMToken::LBrace)) { - SourceLocation LBraceLoc = consumeToken(); - - while (!Tok.is(MMToken::RBrace) && !Tok.is(MMToken::EndOfFile)) { - enum Attribute { Size, ModTime, Unknown }; - StringRef Str = Tok.getString(); - SourceLocation Loc = consumeToken(); - switch (llvm::StringSwitch<Attribute>(Str) - .Case("size", Size) - .Case("mtime", ModTime) - .Default(Unknown)) { - case Size: - if (Header.Size) - Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str; - if (!Tok.is(MMToken::IntegerLiteral)) { - Diags.Report(Tok.getLocation(), - diag::err_mmap_invalid_header_attribute_value) << Str; - skipUntil(MMToken::RBrace); - break; - } - Header.Size = Tok.getInteger(); - consumeToken(); - break; - - case ModTime: - if (Header.ModTime) - Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str; - if (!Tok.is(MMToken::IntegerLiteral)) { - Diags.Report(Tok.getLocation(), - diag::err_mmap_invalid_header_attribute_value) << Str; - skipUntil(MMToken::RBrace); - break; - } - Header.ModTime = Tok.getInteger(); - consumeToken(); - break; - - case Unknown: - Diags.Report(Loc, diag::err_mmap_expected_header_attribute); - skipUntil(MMToken::RBrace); - break; - } - } - - if (Tok.is(MMToken::RBrace)) - consumeToken(); - else { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace); - Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match); - HadError = true; - } - } + if (HD.Size) + Header.Size = HD.Size; + if (HD.MTime) + Header.ModTime = HD.MTime; bool NeedsFramework = false; // Don't add headers to the builtin modules if the builtin headers belong to @@ -2541,27 +1888,15 @@ static bool compareModuleHeaders(const Module::Header &A, return A.NameAsWritten < B.NameAsWritten; } -/// Parse an umbrella directory declaration. -/// -/// umbrella-dir-declaration: -/// umbrella string-literal -void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) { - // Parse the directory name. - if (!Tok.is(MMToken::StringLiteral)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) - << "umbrella"; - HadError = true; - return; - } - - std::string DirName = std::string(Tok.getString()); +void ModuleMapParser::handleUmbrellaDirDecl( + const modulemap::UmbrellaDirDecl &UDD) { + std::string DirName = std::string(UDD.Path); std::string DirNameAsWritten = DirName; - SourceLocation DirNameLoc = consumeToken(); // Check whether we already have an umbrella. if (!std::holds_alternative<std::monostate>(ActiveModule->Umbrella)) { - Diags.Report(DirNameLoc, diag::err_mmap_umbrella_clash) - << ActiveModule->getFullModuleName(); + Diags.Report(UDD.Location, diag::err_mmap_umbrella_clash) + << ActiveModule->getFullModuleName(); HadError = true; return; } @@ -2578,8 +1913,8 @@ void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) { } if (!Dir) { - Diags.Report(DirNameLoc, diag::warn_mmap_umbrella_dir_not_found) - << DirName; + Diags.Report(UDD.Location, diag::warn_mmap_umbrella_dir_not_found) + << DirName; return; } @@ -2609,8 +1944,8 @@ void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) { } if (Module *OwningModule = Map.UmbrellaDirs[*Dir]) { - Diags.Report(UmbrellaLoc, diag::err_mmap_umbrella_clash) - << OwningModule->getFullModuleName(); + Diags.Report(UDD.Location, diag::err_mmap_umbrella_clash) + << OwningModule->getFullModuleName(); HadError = true; return; } @@ -2619,521 +1954,169 @@ void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) { Map.setUmbrellaDirAsWritten(ActiveModule, *Dir, DirNameAsWritten, DirName); } -/// Parse a module export declaration. -/// -/// export-declaration: -/// 'export' wildcard-module-id -/// -/// wildcard-module-id: -/// identifier -/// '*' -/// identifier '.' wildcard-module-id -void ModuleMapParser::parseExportDecl() { - assert(Tok.is(MMToken::ExportKeyword)); - SourceLocation ExportLoc = consumeToken(); - - // Parse the module-id with an optional wildcard at the end. - ModuleId ParsedModuleId; - bool Wildcard = false; - do { - // FIXME: Support string-literal module names here. - if (Tok.is(MMToken::Identifier)) { - ParsedModuleId.push_back( - std::make_pair(std::string(Tok.getString()), Tok.getLocation())); - consumeToken(); - - if (Tok.is(MMToken::Period)) { - consumeToken(); - continue; - } - - break; - } - - if(Tok.is(MMToken::Star)) { - Wildcard = true; - consumeToken(); - break; - } - - Diags.Report(Tok.getLocation(), diag::err_mmap_module_id); - HadError = true; - return; - } while (true); - - Module::UnresolvedExportDecl Unresolved = { - ExportLoc, ParsedModuleId, Wildcard - }; +void ModuleMapParser::handleExportDecl(const modulemap::ExportDecl &ED) { + Module::UnresolvedExportDecl Unresolved = {ED.Location, ED.Id, ED.Wildcard}; ActiveModule->UnresolvedExports.push_back(Unresolved); } -/// 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; - } +void ModuleMapParser::handleExportAsDecl(const modulemap::ExportAsDecl &EAD) { + auto ModName = EAD.Id.front(); if (!ActiveModule->ExportAsModule.empty()) { - if (ActiveModule->ExportAsModule == Tok.getString()) { - Diags.Report(Tok.getLocation(), diag::warn_mmap_redundant_export_as) - << ActiveModule->Name << Tok.getString(); + if (ActiveModule->ExportAsModule == ModName.first) { + Diags.Report(ModName.second, diag::warn_mmap_redundant_export_as) + << ActiveModule->Name << ModName.first; } else { - Diags.Report(Tok.getLocation(), diag::err_mmap_conflicting_export_as) - << ActiveModule->Name << ActiveModule->ExportAsModule - << Tok.getString(); + Diags.Report(ModName.second, diag::err_mmap_conflicting_export_as) + << ActiveModule->Name << ActiveModule->ExportAsModule + << ModName.first; } } - ActiveModule->ExportAsModule = std::string(Tok.getString()); + ActiveModule->ExportAsModule = ModName.first; Map.addLinkAsDependency(ActiveModule); - - consumeToken(); } -/// Parse a module use declaration. -/// -/// use-declaration: -/// 'use' wildcard-module-id -void ModuleMapParser::parseUseDecl() { - assert(Tok.is(MMToken::UseKeyword)); - auto KWLoc = consumeToken(); - // Parse the module-id. - ModuleId ParsedModuleId; - parseModuleId(ParsedModuleId); - +void ModuleMapParser::handleUseDecl(const modulemap::UseDecl &UD) { if (ActiveModule->Parent) - Diags.Report(KWLoc, diag::err_mmap_use_decl_submodule); + Diags.Report(UD.Location, diag::err_mmap_use_decl_submodule); else - ActiveModule->UnresolvedDirectUses.push_back(ParsedModuleId); + ActiveModule->UnresolvedDirectUses.push_back(UD.Id); } -/// Parse a link declaration. -/// -/// module-declaration: -/// 'link' 'framework'[opt] string-literal -void ModuleMapParser::parseLinkDecl() { - assert(Tok.is(MMToken::LinkKeyword)); - SourceLocation LinkLoc = consumeToken(); - - // Parse the optional 'framework' keyword. - bool IsFramework = false; - if (Tok.is(MMToken::FrameworkKeyword)) { - consumeToken(); - IsFramework = true; - } - - // Parse the library name - if (!Tok.is(MMToken::StringLiteral)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_library_name) - << IsFramework << SourceRange(LinkLoc); - HadError = true; - return; - } - - std::string LibraryName = std::string(Tok.getString()); - consumeToken(); - ActiveModule->LinkLibraries.push_back(Module::LinkLibrary(LibraryName, - IsFramework)); +void ModuleMapParser::handleLinkDecl(const modulemap::LinkDecl &LD) { + ActiveModule->LinkLibraries.push_back( + Module::LinkLibrary(std::string{LD.Library}, LD.Framework)); } -/// Parse a configuration macro declaration. -/// -/// module-declaration: -/// 'config_macros' attributes[opt] config-macro-list? -/// -/// config-macro-list: -/// identifier (',' identifier)? -void ModuleMapParser::parseConfigMacros() { - assert(Tok.is(MMToken::ConfigMacros)); - SourceLocation ConfigMacrosLoc = consumeToken(); - - // Only top-level modules can have configuration macros. +void ModuleMapParser::handleConfigMacros( + const modulemap::ConfigMacrosDecl &CMD) { if (ActiveModule->Parent) { - Diags.Report(ConfigMacrosLoc, diag::err_mmap_config_macro_submodule); - } - - // Parse the optional attributes. - Attributes Attrs; - if (parseOptionalAttributes(Attrs)) - return; - - if (Attrs.IsExhaustive && !ActiveModule->Parent) { - ActiveModule->ConfigMacrosExhaustive = true; - } - - // If we don't have an identifier, we're done. - // FIXME: Support macros with the same name as a keyword here. - if (!Tok.is(MMToken::Identifier)) + Diags.Report(CMD.Location, diag::err_mmap_config_macro_submodule); return; - - // Consume the first identifier. - if (!ActiveModule->Parent) { - ActiveModule->ConfigMacros.push_back(Tok.getString().str()); } - consumeToken(); - - do { - // If there's a comma, consume it. - if (!Tok.is(MMToken::Comma)) - break; - consumeToken(); - // We expect to see a macro name here. - // FIXME: Support macros with the same name as a keyword here. - if (!Tok.is(MMToken::Identifier)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_config_macro); - break; - } - - // Consume the macro name. - if (!ActiveModule->Parent) { - ActiveModule->ConfigMacros.push_back(Tok.getString().str()); - } - consumeToken(); - } while (true); -} - -/// Format a module-id into a string. -static std::string formatModuleId(const ModuleId &Id) { - std::string result; - { - llvm::raw_string_ostream OS(result); - - for (unsigned I = 0, N = Id.size(); I != N; ++I) { - if (I) - OS << "."; - OS << Id[I].first; - } + // TODO: Is this really the behavior we want for multiple config_macros + // declarations? If any of them are exhaustive then all of them are. + if (CMD.Exhaustive) { + ActiveModule->ConfigMacrosExhaustive = true; } - - return result; + ActiveModule->ConfigMacros.insert(ActiveModule->ConfigMacros.end(), + CMD.Macros.begin(), CMD.Macros.end()); } -/// Parse a conflict declaration. -/// -/// module-declaration: -/// 'conflict' module-id ',' string-literal -void ModuleMapParser::parseConflict() { - assert(Tok.is(MMToken::Conflict)); - SourceLocation ConflictLoc = consumeToken(); +void ModuleMapParser::handleConflict(const modulemap::ConflictDecl &CD) { Module::UnresolvedConflict Conflict; - // Parse the module-id. - if (parseModuleId(Conflict.Id)) - return; - - // Parse the ','. - if (!Tok.is(MMToken::Comma)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_comma) - << SourceRange(ConflictLoc); - return; - } - consumeToken(); + Conflict.Id = CD.Id; + Conflict.Message = CD.Message; - // Parse the message. - if (!Tok.is(MMToken::StringLiteral)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_message) - << formatModuleId(Conflict.Id); - return; - } - Conflict.Message = Tok.getString().str(); - consumeToken(); - - // Add this unresolved conflict. ActiveModule->UnresolvedConflicts.push_back(Conflict); } -/// Parse an inferred module declaration (wildcard modules). -/// -/// module-declaration: -/// 'explicit'[opt] 'framework'[opt] 'module' * attributes[opt] -/// { inferred-module-member* } -/// -/// inferred-module-member: -/// 'export' '*' -/// 'exclude' identifier -void ModuleMapParser::parseInferredModuleDecl(bool Framework, bool Explicit) { - assert(Tok.is(MMToken::Star)); - SourceLocation StarLoc = consumeToken(); - bool Failed = false; +void ModuleMapParser::handleInferredModuleDecl( + const modulemap::ModuleDecl &MD) { + SourceLocation StarLoc = MD.Id.front().second; // Inferred modules must be submodules. - if (!ActiveModule && !Framework) { + if (!ActiveModule && !MD.Framework) { Diags.Report(StarLoc, diag::err_mmap_top_level_inferred_submodule); - Failed = true; + return; } if (ActiveModule) { // Inferred modules must have umbrella directories. - if (!Failed && ActiveModule->IsAvailable && - !ActiveModule->getEffectiveUmbrellaDir()) { + if (ActiveModule->IsAvailable && !ActiveModule->getEffectiveUmbrellaDir()) { Diags.Report(StarLoc, diag::err_mmap_inferred_no_umbrella); - Failed = true; + return; } // Check for redefinition of an inferred module. - if (!Failed && ActiveModule->InferSubmodules) { + if (ActiveModule->InferSubmodules) { Diags.Report(StarLoc, diag::err_mmap_inferred_redef); if (ActiveModule->InferredSubmoduleLoc.isValid()) Diags.Report(ActiveModule->InferredSubmoduleLoc, diag::note_mmap_prev_definition); - Failed = true; + return; } // Check for the 'framework' keyword, which is not permitted here. - if (Framework) { + if (MD.Framework) { Diags.Report(StarLoc, diag::err_mmap_inferred_framework_submodule); - Framework = false; + return; } - } else if (Explicit) { + } else if (MD.Explicit) { Diags.Report(StarLoc, diag::err_mmap_explicit_inferred_framework); - Explicit = false; - } - - // If there were any problems with this inferred submodule, skip its body. - if (Failed) { - if (Tok.is(MMToken::LBrace)) { - consumeToken(); - skipUntil(MMToken::RBrace); - if (Tok.is(MMToken::RBrace)) - consumeToken(); - } - HadError = true; return; } - // Parse optional attributes. - Attributes Attrs; - if (parseOptionalAttributes(Attrs)) - return; - if (ActiveModule) { // Note that we have an inferred submodule. ActiveModule->InferSubmodules = true; ActiveModule->InferredSubmoduleLoc = StarLoc; - ActiveModule->InferExplicitSubmodules = Explicit; + ActiveModule->InferExplicitSubmodules = MD.Explicit; } else { // We'll be inferring framework modules for this directory. auto &InfDir = Map.InferredDirectories[Directory]; InfDir.InferModules = true; - InfDir.Attrs = Attrs; + InfDir.Attrs = MD.Attrs; InfDir.ModuleMapFID = ModuleMapFID; // FIXME: Handle the 'framework' keyword. } - // Parse the opening brace. - if (!Tok.is(MMToken::LBrace)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace_wildcard); - HadError = true; - return; - } - SourceLocation LBraceLoc = consumeToken(); - - // Parse the body of the inferred submodule. - bool Done = false; - do { - switch (Tok.Kind) { - case MMToken::EndOfFile: - case MMToken::RBrace: - Done = true; - break; - - case MMToken::ExcludeKeyword: - if (ActiveModule) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_inferred_member) - << (ActiveModule != nullptr); - consumeToken(); - break; - } - - consumeToken(); - // FIXME: Support string-literal module names here. - if (!Tok.is(MMToken::Identifier)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_missing_exclude_name); - break; - } - - Map.InferredDirectories[Directory].ExcludedModules.push_back( - std::string(Tok.getString())); - consumeToken(); - break; - - case MMToken::ExportKeyword: - if (!ActiveModule) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_inferred_member) - << (ActiveModule != nullptr); - consumeToken(); - break; - } - - consumeToken(); - if (Tok.is(MMToken::Star)) - ActiveModule->InferExportWildcard = true; - else - Diags.Report(Tok.getLocation(), - diag::err_mmap_expected_export_wildcard); - consumeToken(); - break; - - case MMToken::ExplicitKeyword: - case MMToken::ModuleKeyword: - case MMToken::HeaderKeyword: - case MMToken::PrivateKeyword: - case MMToken::UmbrellaKeyword: - default: - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_inferred_member) - << (ActiveModule != nullptr); - consumeToken(); - break; - } - } while (!Done); - - if (Tok.is(MMToken::RBrace)) - consumeToken(); - else { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace); - Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match); - HadError = true; + for (const modulemap::Decl &Decl : MD.Decls) { + std::visit( + llvm::makeVisitor( + [&](const auto &Other) { + Diags.Report(Other.Location, + diag::err_mmap_expected_inferred_member) + << (ActiveModule != nullptr); + }, + [&](const modulemap::ExcludeDecl &ED) { + // Only inferred frameworks can have exclude decls + if (ActiveModule) { + Diags.Report(ED.Location, + diag::err_mmap_expected_inferred_member) + << (ActiveModule != nullptr); + HadError = true; + return; + } + Map.InferredDirectories[Directory].ExcludedModules.emplace_back( + ED.Module); + }, + [&](const modulemap::ExportDecl &ED) { + // Only inferred submodules can have export decls + if (!ActiveModule) { + Diags.Report(ED.Location, + diag::err_mmap_expected_inferred_member) + << (ActiveModule != nullptr); + HadError = true; + return; + } + + if (ED.Wildcard && ED.Id.size() == 0) + ActiveModule->InferExportWildcard = true; + else + Diags.Report(ED.Id.front().second, + diag::err_mmap_expected_export_wildcard); + }), + Decl); } } -/// Parse optional attributes. -/// -/// attributes: -/// attribute attributes -/// attribute -/// -/// attribute: -/// [ identifier ] -/// -/// \param Attrs Will be filled in with the parsed attributes. -/// -/// \returns true if an error occurred, false otherwise. -bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { - bool HadError = false; - - while (Tok.is(MMToken::LSquare)) { - // Consume the '['. - SourceLocation LSquareLoc = consumeToken(); - - // Check whether we have an attribute name here. - if (!Tok.is(MMToken::Identifier)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_attribute); - skipUntil(MMToken::RSquare); - if (Tok.is(MMToken::RSquare)) - consumeToken(); - HadError = true; - } - - // Decode the attribute name. - AttributeKind Attribute - = llvm::StringSwitch<AttributeKind>(Tok.getString()) - .Case("exhaustive", AT_exhaustive) - .Case("extern_c", AT_extern_c) - .Case("no_undeclared_includes", AT_no_undeclared_includes) - .Case("system", AT_system) - .Default(AT_unknown); - switch (Attribute) { - case AT_unknown: - Diags.Report(Tok.getLocation(), diag::warn_mmap_unknown_attribute) - << Tok.getString(); - break; - - case AT_system: - Attrs.IsSystem = true; - break; - - case AT_extern_c: - Attrs.IsExternC = true; - break; - - case AT_exhaustive: - Attrs.IsExhaustive = true; - break; - - case AT_no_undeclared_includes: - Attrs.NoUndeclaredIncludes = true; - break; - } - consumeToken(); - - // Consume the ']'. - if (!Tok.is(MMToken::RSquare)) { - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rsquare); - Diags.Report(LSquareLoc, diag::note_mmap_lsquare_match); - skipUntil(MMToken::RSquare); - HadError = true; - } - - if (Tok.is(MMToken::RSquare)) - consumeToken(); +bool ModuleMapParser::parseModuleMapFile() { + for (const auto &Decl : MMF.Decls) { + std::visit( + llvm::makeVisitor( + [&](const modulemap::ModuleDecl &MD) { handleModuleDecl(MD); }, + [&](const modulemap::ExternModuleDecl &EMD) { + handleExternModuleDecl(EMD); + }), + Decl); } - return HadError; } -/// Parse a module map file. -/// -/// module-map-file: -/// module-declaration* -bool ModuleMapParser::parseModuleMapFile() { - do { - switch (Tok.Kind) { - case MMToken::EndOfFile: - return HadError; - - case MMToken::ExplicitKeyword: - case MMToken::ExternKeyword: - case MMToken::ModuleKeyword: - case MMToken::FrameworkKeyword: - parseModuleDecl(); - break; - - case MMToken::Comma: - case MMToken::ConfigMacros: - case MMToken::Conflict: - case MMToken::Exclaim: - case MMToken::ExcludeKeyword: - case MMToken::ExportKeyword: - case MMToken::ExportAsKeyword: - case MMToken::HeaderKeyword: - case MMToken::Identifier: - case MMToken::LBrace: - case MMToken::LinkKeyword: - case MMToken::LSquare: - case MMToken::Period: - case MMToken::PrivateKeyword: - case MMToken::RBrace: - case MMToken::RSquare: - case MMToken::RequiresKeyword: - case MMToken::Star: - case MMToken::StringLiteral: - case MMToken::IntegerLiteral: - case MMToken::TextualKeyword: - case MMToken::UmbrellaKeyword: - case MMToken::UseKeyword: - Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module); - HadError = true; - consumeToken(); - break; - } - } while (true); -} - bool ModuleMap::parseModuleMapFile(FileEntryRef File, bool IsSystem, DirectoryEntryRef Dir, FileID ID, unsigned *Offset, @@ -3158,25 +2141,18 @@ bool ModuleMap::parseModuleMapFile(FileEntryRef File, bool IsSystem, assert((!Offset || *Offset <= Buffer->getBufferSize()) && "invalid buffer offset"); - // Parse this module map file. - Lexer L(SourceMgr.getLocForStartOfFile(ID), MMapLangOpts, - Buffer->getBufferStart(), - Buffer->getBufferStart() + (Offset ? *Offset : 0), - Buffer->getBufferEnd()); - SourceLocation Start = L.getSourceLocation(); - ModuleMapParser Parser(L, SourceMgr, Target, Diags, *this, ID, Dir, IsSystem); - bool Result = Parser.parseModuleMapFile(); - ParsedModuleMap[File] = Result; - - if (Offset) { - auto Loc = SourceMgr.getDecomposedLoc(Parser.getLocation()); - assert(Loc.first == ID && "stopped in a different file?"); - *Offset = Loc.second; + std::optional<modulemap::ModuleMapFile> MMF = + modulemap::parseModuleMap(ID, Dir, SourceMgr, Diags, IsSystem, Offset); + bool Result = false; + if (MMF) { + ModuleMapParser Parser(*MMF, SourceMgr, Diags, *this, ID, Dir, IsSystem); + Result = Parser.parseModuleMapFile(); } + ParsedModuleMap[File] = Result; // Notify callbacks that we parsed it. for (const auto &Cb : Callbacks) - Cb->moduleMapFileRead(Start, File, IsSystem); + Cb->moduleMapFileRead(MMF ? MMF->Start : SourceLocation(), File, IsSystem); return Result; } |