[clang] [llvm] [clang][modules] Separate parsing of modulemaps (PR #119740)
Michael Spencer via llvm-commits
llvm-commits at lists.llvm.org
Thu Dec 12 10:36:18 PST 2024
https://github.com/Bigcheese created https://github.com/llvm/llvm-project/pull/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.
>From 12f19b16945b66e75a32cade1f7cb7aac8424b12 Mon Sep 17 00:00:00 2001
From: Michael Spencer <bigcheesegs at gmail.com>
Date: Thu, 5 Dec 2024 14:53:50 -0800
Subject: [PATCH] [clang][modules] Separate parsing of modulemaps
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.
---
.../include/clang/Basic/DiagnosticLexKinds.td | 2 +
clang/include/clang/Basic/Module.h | 24 +
clang/include/clang/Lex/ModuleMap.h | 24 +-
clang/include/clang/Lex/ModuleMapFile.h | 142 ++
clang/lib/Lex/CMakeLists.txt | 1 +
clang/lib/Lex/ModuleMap.cpp | 1400 +++--------------
clang/lib/Lex/ModuleMapFile.cpp | 1248 +++++++++++++++
.../Modules/Inputs/export_as_test.modulemap | 4 +
clang/test/Modules/diagnostics.modulemap | 21 +-
clang/test/Modules/export_as_test.c | 5 +-
llvm/include/llvm/ADT/STLExtras.h | 9 +
11 files changed, 1634 insertions(+), 1246 deletions(-)
create mode 100644 clang/include/clang/Lex/ModuleMapFile.h
create mode 100644 clang/lib/Lex/ModuleMapFile.cpp
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 959376b0847216..e5869619e69d35 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -914,6 +914,8 @@ def warn_mmap_redundant_export_as : Warning<
InGroup<PrivateModule>;
def err_mmap_submodule_export_as : Error<
"only top-level modules can be re-exported as public">;
+def err_mmap_qualified_export_as : Error<
+ "a module can only be re-exported as another top-level module">;
def warn_quoted_include_in_framework_header : Warning<
"double-quoted include \"%0\" in framework header, "
diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h
index dd384c1d76c5fd..ef6e2ac95d0819 100644
--- a/clang/include/clang/Basic/Module.h
+++ b/clang/include/clang/Basic/Module.h
@@ -100,6 +100,30 @@ struct ASTFileSignature : std::array<uint8_t, 20> {
}
};
+/// The set of attributes that can be attached to a module.
+struct ModuleAttributes {
+ /// Whether this is a system module.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned IsSystem : 1;
+
+ /// Whether this is an extern "C" module.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned IsExternC : 1;
+
+ /// Whether this is an exhaustive set of configuration macros.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned IsExhaustive : 1;
+
+ /// Whether files in this module can only include non-modular headers
+ /// and headers from used modules.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned NoUndeclaredIncludes : 1;
+
+ ModuleAttributes()
+ : IsSystem(false), IsExternC(false), IsExhaustive(false),
+ NoUndeclaredIncludes(false) {}
+};
+
/// Required to construct a Module.
///
/// This tag type is only constructible by ModuleMap, guaranteeing it ownership
diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h
index 53e9e0ec83ddb1..9de1b3b546c115 100644
--- a/clang/include/clang/Lex/ModuleMap.h
+++ b/clang/include/clang/Lex/ModuleMap.h
@@ -232,29 +232,7 @@ class ModuleMap {
llvm::DenseMap<Module *, unsigned> ModuleScopeIDs;
- /// The set of attributes that can be attached to a module.
- struct Attributes {
- /// Whether this is a system module.
- LLVM_PREFERRED_TYPE(bool)
- unsigned IsSystem : 1;
-
- /// Whether this is an extern "C" module.
- LLVM_PREFERRED_TYPE(bool)
- unsigned IsExternC : 1;
-
- /// Whether this is an exhaustive set of configuration macros.
- LLVM_PREFERRED_TYPE(bool)
- unsigned IsExhaustive : 1;
-
- /// Whether files in this module can only include non-modular headers
- /// and headers from used modules.
- LLVM_PREFERRED_TYPE(bool)
- unsigned NoUndeclaredIncludes : 1;
-
- Attributes()
- : IsSystem(false), IsExternC(false), IsExhaustive(false),
- NoUndeclaredIncludes(false) {}
- };
+ using Attributes = ModuleAttributes;
/// A directory for which framework modules can be inferred.
struct InferredDirectory {
diff --git a/clang/include/clang/Lex/ModuleMapFile.h b/clang/include/clang/Lex/ModuleMapFile.h
new file mode 100644
index 00000000000000..8b79897876ad61
--- /dev/null
+++ b/clang/include/clang/Lex/ModuleMapFile.h
@@ -0,0 +1,142 @@
+//===- ModuleMapFile.h - Parsing and representation -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LEX_MODULEMAPFILE_H
+#define LLVM_CLANG_LEX_MODULEMAPFILE_H
+
+#include "clang/Basic/LLVM.h"
+// TODO: Consider moving ModuleId to another header, parsing a modulemap file is
+// intended to not depend on anything about the clang::Module class.
+#include "clang/Basic/Module.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <optional>
+#include <variant>
+
+namespace clang {
+
+class DiagnosticsEngine;
+class SourceManager;
+
+namespace modulemap {
+
+using Decl =
+ std::variant<struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl,
+ struct ModuleDecl, struct ExcludeDecl, struct ExportDecl,
+ struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl,
+ struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl>;
+
+struct RequiresFeature {
+ SourceLocation Location;
+ StringRef Feature;
+ bool RequiredState = true;
+};
+
+struct RequiresDecl {
+ SourceLocation Location;
+ std::vector<RequiresFeature> Features;
+};
+
+struct HeaderDecl {
+ SourceLocation Location;
+ StringRef Path;
+ SourceLocation PathLoc;
+ std::optional<int64_t> Size;
+ std::optional<int64_t> MTime;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Private : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Textual : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Umbrella : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Excluded : 1;
+};
+
+struct UmbrellaDirDecl {
+ SourceLocation Location;
+ StringRef Path;
+};
+
+struct ModuleDecl {
+ SourceLocation Location; /// Points to the first keyword in the decl.
+ ModuleId Id;
+ ModuleAttributes Attrs;
+ std::vector<Decl> Decls;
+
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Explicit : 1;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Framework : 1;
+};
+
+struct ExcludeDecl {
+ SourceLocation Location;
+ StringRef Module;
+};
+
+struct ExportDecl {
+ SourceLocation Location;
+ ModuleId Id;
+ bool Wildcard;
+};
+
+struct ExportAsDecl {
+ SourceLocation Location;
+ ModuleId Id;
+};
+
+struct ExternModuleDecl {
+ SourceLocation Location;
+ ModuleId Id;
+ StringRef Path;
+};
+
+struct UseDecl {
+ SourceLocation Location;
+ ModuleId Id;
+};
+
+struct LinkDecl {
+ SourceLocation Location;
+ StringRef Library;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Framework : 1;
+};
+
+struct ConfigMacrosDecl {
+ SourceLocation Location;
+ std::vector<StringRef> Macros;
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Exhaustive : 1;
+};
+
+struct ConflictDecl {
+ SourceLocation Location;
+ ModuleId Id;
+ StringRef Message;
+};
+
+using TopLevelDecl =
+ std::variant<ModuleDecl, ExternModuleDecl>;
+
+struct ModuleMapFile {
+ std::vector<TopLevelDecl> Decls;
+};
+
+std::optional<ModuleMapFile> parseModuleMap(FileEntryRef File,
+ SourceManager &SM,
+ DiagnosticsEngine &Diags,
+ bool IsSystem, unsigned *Offset);
+void dumpModuleMapFile(ModuleMapFile &MMF, llvm::raw_ostream &out);
+
+} // namespace modulemap
+} // namespace clang
+
+#endif
diff --git a/clang/lib/Lex/CMakeLists.txt b/clang/lib/Lex/CMakeLists.txt
index 766336b89a2382..5a049a1d84fd0d 100644
--- a/clang/lib/Lex/CMakeLists.txt
+++ b/clang/lib/Lex/CMakeLists.txt
@@ -15,6 +15,7 @@ add_clang_library(clangLex
MacroArgs.cpp
MacroInfo.cpp
ModuleMap.cpp
+ ModuleMapFile.cpp
PPCaching.cpp
PPCallbacks.cpp
PPConditionalDirectiveRecord.cpp
diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp
index ccf94f6345ff28..11b38db9eb6526 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,81 +1458,10 @@ bool ModuleMap::resolveConflicts(Module *Mod, bool Complain) {
//----------------------------------------------------------------------------//
namespace clang {
-
- /// 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;
- }
-
- bool is(TokenKind K) const { return Kind == K; }
-
- SourceLocation getLocation() const {
- return SourceLocation::getFromRawEncoding(Location);
- }
-
- uint64_t getInteger() const {
- return Kind == IntegerLiteral ? IntegerValue : 0;
- }
-
- StringRef getString() const {
- return Kind == IntegerLiteral ? StringRef()
- : StringRef(StringData, StringLength);
- }
- };
-
class ModuleMapParser {
- Lexer &L;
+ modulemap::ModuleMapFile &MMF;
SourceManager &SourceMgr;
- /// Default target information, used only for string literal
- /// parsing.
- const TargetInfo *Target;
-
DiagnosticsEngine &Diags;
ModuleMap ⤅
@@ -1555,13 +1481,6 @@ namespace clang {
/// 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;
@@ -1575,305 +1494,45 @@ namespace clang {
/// '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);
+ 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 ExplicitLoc,
- SourceLocation FrameworkLoc);
+ void diagnosePrivateModules(SourceLocation StartLoc);
using Attributes = ModuleMap::Attributes;
- bool parseOptionalAttributes(Attributes &Attrs);
-
public:
- ModuleMapParser(Lexer &L, SourceManager &SourceMgr,
- const TargetInfo *Target, DiagnosticsEngine &Diags,
+ ModuleMapParser(modulemap::ModuleMapFile &MMF,
+ SourceManager &SourceMgr, 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();
- }
+ : MMF(MMF), SourceMgr(SourceMgr), Diags(Diags), Map(Map),
+ ModuleMapFID(ModuleMapFID), Directory(Directory), IsSystem(IsSystem) {}
bool parseModuleMapFile();
-
- bool terminatedByDirective() { return false; }
- SourceLocation getLocation() { return Tok.getLocation(); }
};
} // 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 +1561,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 +1586,28 @@ 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;
- }
-
- // 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);
+void ModuleMapParser::handleModuleDecl(const modulemap::ModuleDecl &MD) {
+ if (MD.Id.front().first == "*")
+ return handleInferredModuleDecl(MD);
- // 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::overloaded{
+ [&](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,36 @@ 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 +1849,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 +1864,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,26 +1889,13 @@ 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)
+ Diags.Report(UDD.Location, diag::err_mmap_umbrella_clash)
<< ActiveModule->getFullModuleName();
HadError = true;
return;
@@ -2578,7 +1913,7 @@ void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) {
}
if (!Dir) {
- Diags.Report(DirNameLoc, diag::warn_mmap_umbrella_dir_not_found)
+ Diags.Report(UDD.Location, diag::warn_mmap_umbrella_dir_not_found)
<< DirName;
return;
}
@@ -2609,7 +1944,7 @@ void ModuleMapParser::parseUmbrellaDirDecl(SourceLocation UmbrellaLoc) {
}
if (Module *OwningModule = Map.UmbrellaDirs[*Dir]) {
- Diags.Report(UmbrellaLoc, diag::err_mmap_umbrella_clash)
+ Diags.Report(UDD.Location, diag::err_mmap_umbrella_clash)
<< OwningModule->getFullModuleName();
HadError = true;
return;
@@ -2619,520 +1954,170 @@ 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);
-
+void ModuleMapParser::handleExportDecl(const modulemap::ExportDecl &ED) {
Module::UnresolvedExportDecl Unresolved = {
- ExportLoc, ParsedModuleId, Wildcard
+ 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)
+ Diags.Report(ModName.second, diag::err_mmap_conflicting_export_as)
<< ActiveModule->Name << ActiveModule->ExportAsModule
- << Tok.getString();
+ << 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();
-
- // 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();
+ Conflict.Id = CD.Id;
+ Conflict.Message = CD.Message;
- // 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 &&
+ 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.
Map.InferredDirectories[Directory].InferModules = true;
- Map.InferredDirectories[Directory].Attrs = Attrs;
+ Map.InferredDirectories[Directory].Attrs = MD.Attrs;
Map.InferredDirectories[Directory].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::overloaded{
+ [&](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::overloaded{[&](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,
@@ -3157,25 +2142,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(File, 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(SourceLocation(), File, IsSystem);
return Result;
}
diff --git a/clang/lib/Lex/ModuleMapFile.cpp b/clang/lib/Lex/ModuleMapFile.cpp
new file mode 100644
index 00000000000000..23e6dd80e28973
--- /dev/null
+++ b/clang/lib/Lex/ModuleMapFile.cpp
@@ -0,0 +1,1248 @@
+//===- ModuleMapFile.cpp - ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file handles parsing of modulemap files into a simple AST.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Lex/ModuleMapFile.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/Module.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/LexDiagnostic.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/ModuleMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Format.h"
+#include <optional>
+
+using namespace clang;
+using namespace modulemap;
+
+namespace {
+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;
+ }
+
+ bool is(TokenKind K) const { return Kind == K; }
+
+ SourceLocation getLocation() const {
+ return SourceLocation::getFromRawEncoding(Location);
+ }
+
+ uint64_t getInteger() const {
+ return Kind == IntegerLiteral ? IntegerValue : 0;
+ }
+
+ StringRef getString() const {
+ return Kind == IntegerLiteral ? StringRef()
+ : StringRef(StringData, StringLength);
+ }
+};
+
+struct ModuleMapParser {
+ // External context
+ Lexer &L;
+ DiagnosticsEngine &Diags;
+
+ /// The current module map file.
+ FileEntryRef MMFile;
+
+ /// Parsed representation of the module map file
+ ModuleMapFile MMF{};
+
+ /// Temporarily store string data during parsing
+ llvm::BumpPtrAllocator StringData{};
+
+ bool HadError = false;
+
+ /// The current token.
+ MMToken Tok{};
+
+ bool parseTopLevelDecls();
+ std::optional<ModuleDecl> parseModuleDecl(bool TopLevel);
+ std::optional<ExternModuleDecl> parseExternModuleDecl();
+ std::optional<ConfigMacrosDecl> parseConfigMacrosDecl();
+ std::optional<ConflictDecl> parseConflictDecl();
+ std::optional<ExportDecl> parseExportDecl();
+ std::optional<ExportAsDecl> parseExportAsDecl();
+ std::optional<UseDecl> parseUseDecl();
+ std::optional<RequiresDecl> parseRequiresDecl();
+ std::optional<HeaderDecl> parseHeaderDecl(MMToken::TokenKind LeadingToken,
+ SourceLocation LeadingLoc);
+ std::optional<ExcludeDecl> parseExcludeDecl(clang::SourceLocation LeadingLoc);
+ std::optional<UmbrellaDirDecl>
+ parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);
+ std::optional<LinkDecl> parseLinkDecl();
+
+ SourceLocation consumeToken();
+ void skipUntil(MMToken::TokenKind K);
+ bool parseModuleId(ModuleId &Id);
+ bool parseOptionalAttributes(ModuleAttributes &Attrs);
+
+ SourceLocation getLocation() const { return Tok.getLocation(); };
+};
+
+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;
+ }
+ }
+
+ return result;
+}
+} // end anonymous namespace
+
+std::optional<ModuleMapFile> modulemap::parseModuleMap(FileEntryRef File,
+ SourceManager &SM,
+ DiagnosticsEngine &Diags,
+ bool IsSystem,
+ unsigned *Offset) {
+ auto FileCharacter =
+ IsSystem ? SrcMgr::C_System_ModuleMap : SrcMgr::C_User_ModuleMap;
+ FileID ID = SM.getOrCreateFileID(File, FileCharacter);
+ std::optional<llvm::MemoryBufferRef> Buffer = SM.getBufferOrNone(ID);
+ LangOptions LOpts;
+ LOpts.LangStd = clang::LangStandard::lang_c99;
+ Lexer L(SM.getLocForStartOfFile(ID), LOpts, Buffer->getBufferStart(),
+ Buffer->getBufferStart() + (Offset ? *Offset : 0),
+ Buffer->getBufferEnd());
+
+ ModuleMapParser Parser{L, Diags, File};
+ bool Failed = Parser.parseTopLevelDecls();
+
+ if (Offset) {
+ auto Loc = SM.getDecomposedLoc(Parser.getLocation());
+ assert(Loc.first == ID && "stopped in a different file?");
+ *Offset = Loc.second;
+ }
+
+ if (Failed)
+ return std::nullopt;
+ return std::move(Parser.MMF);
+}
+
+bool ModuleMapParser::parseTopLevelDecls() {
+ Tok.clear();
+ consumeToken();
+ do {
+ switch (Tok.Kind) {
+ case MMToken::EndOfFile:
+ return HadError;
+ case MMToken::ExternKeyword: {
+ std::optional<ExternModuleDecl> EMD = parseExternModuleDecl();
+ if (EMD)
+ MMF.Decls.push_back(std::move(*EMD));
+ break;
+ }
+ case MMToken::ExplicitKeyword:
+ case MMToken::ModuleKeyword:
+ case MMToken::FrameworkKeyword: {
+ std::optional<ModuleDecl> MD = parseModuleDecl(true);
+ if (MD)
+ MMF.Decls.push_back(std::move(*MD));
+ 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);
+}
+
+/// 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
+std::optional<ModuleDecl> ModuleMapParser::parseModuleDecl(bool TopLevel) {
+ assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword) ||
+ Tok.is(MMToken::FrameworkKeyword));
+
+ ModuleDecl MDecl;
+
+ SourceLocation ExplicitLoc;
+ MDecl.Explicit = false;
+ MDecl.Framework = false;
+
+ // Parse 'explicit' keyword, if present.
+ if (Tok.is(MMToken::ExplicitKeyword)) {
+ MDecl.Location = ExplicitLoc = consumeToken();
+ MDecl.Explicit = true;
+ }
+
+ // Parse 'framework' keyword, if present.
+ if (Tok.is(MMToken::FrameworkKeyword)) {
+ SourceLocation FrameworkLoc = consumeToken();
+ if (!MDecl.Location.isValid())
+ MDecl.Location = FrameworkLoc;
+ MDecl.Framework = true;
+ }
+
+ // Parse 'module' keyword.
+ if (!Tok.is(MMToken::ModuleKeyword)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);
+ consumeToken();
+ HadError = true;
+ return std::nullopt;
+ }
+ SourceLocation ModuleLoc = consumeToken();
+ if (!MDecl.Location.isValid())
+ MDecl.Location = ModuleLoc; // 'module' keyword
+
+ // If we have a wildcard for the module name, this is an inferred submodule.
+ // We treat it as a normal module at this point.
+ if (Tok.is(MMToken::Star)) {
+ SourceLocation StarLoc = consumeToken();
+ MDecl.Id.push_back({"*", StarLoc});
+ if (TopLevel && !MDecl.Framework) {
+ Diags.Report(StarLoc, diag::err_mmap_top_level_inferred_submodule);
+ HadError = true;
+ return std::nullopt;
+ }
+ } else {
+ // Parse the module name.
+ if (parseModuleId(MDecl.Id)) {
+ HadError = true;
+ return std::nullopt;
+ }
+ if (!TopLevel) {
+ if (MDecl.Id.size() > 1) {
+ Diags.Report(MDecl.Id.front().second,
+ diag::err_mmap_nested_submodule_id)
+ << SourceRange(MDecl.Id.front().second, MDecl.Id.back().second);
+
+ HadError = true;
+ }
+ } else if (MDecl.Id.size() == 1 && MDecl.Explicit) {
+ // Top-level modules can't be explicit.
+ Diags.Report(ExplicitLoc, diag::err_mmap_explicit_top_level);
+ MDecl.Explicit = false;
+ HadError = true;
+ }
+ }
+
+ // Parse the optional attribute list.
+ if (parseOptionalAttributes(MDecl.Attrs))
+ return std::nullopt;
+
+ // Parse the opening brace.
+ if (!Tok.is(MMToken::LBrace)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace)
+ << MDecl.Id.back().first;
+ HadError = true;
+ return std::nullopt;
+ }
+ SourceLocation LBraceLoc = consumeToken();
+
+ bool Done = false;
+ do {
+ std::optional<Decl> SubDecl;
+ switch (Tok.Kind) {
+ case MMToken::EndOfFile:
+ case MMToken::RBrace:
+ Done = true;
+ break;
+
+ case MMToken::ConfigMacros:
+ // Only top-level modules can have configuration macros.
+ if (!TopLevel)
+ Diags.Report(Tok.getLocation(), diag::err_mmap_config_macro_submodule);
+ SubDecl = parseConfigMacrosDecl();
+ break;
+
+ case MMToken::Conflict:
+ SubDecl = parseConflictDecl();
+ break;
+
+ case MMToken::ExternKeyword:
+ SubDecl = parseExternModuleDecl();
+ break;
+
+ case MMToken::ExplicitKeyword:
+ case MMToken::FrameworkKeyword:
+ case MMToken::ModuleKeyword:
+ SubDecl = parseModuleDecl(false);
+ break;
+
+ case MMToken::ExportKeyword:
+ SubDecl = parseExportDecl();
+ break;
+
+ case MMToken::ExportAsKeyword:
+ if (!TopLevel) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_submodule_export_as);
+ parseExportAsDecl();
+ } else
+ SubDecl = parseExportAsDecl();
+ break;
+
+ case MMToken::UseKeyword:
+ SubDecl = parseUseDecl();
+ break;
+
+ case MMToken::RequiresKeyword:
+ SubDecl = parseRequiresDecl();
+ break;
+
+ case MMToken::TextualKeyword:
+ SubDecl = parseHeaderDecl(MMToken::TextualKeyword, consumeToken());
+ break;
+
+ case MMToken::UmbrellaKeyword: {
+ SourceLocation UmbrellaLoc = consumeToken();
+ if (Tok.is(MMToken::HeaderKeyword))
+ SubDecl = parseHeaderDecl(MMToken::UmbrellaKeyword, UmbrellaLoc);
+ else
+ SubDecl = parseUmbrellaDirDecl(UmbrellaLoc);
+ break;
+ }
+
+ case MMToken::ExcludeKeyword: {
+ SourceLocation ExcludeLoc = consumeToken();
+ if (Tok.is(MMToken::HeaderKeyword))
+ SubDecl = parseHeaderDecl(MMToken::ExcludeKeyword, ExcludeLoc);
+ else
+ SubDecl = parseExcludeDecl(ExcludeLoc);
+ break;
+ }
+
+ case MMToken::PrivateKeyword:
+ SubDecl = parseHeaderDecl(MMToken::PrivateKeyword, consumeToken());
+ break;
+
+ case MMToken::HeaderKeyword:
+ SubDecl = parseHeaderDecl(MMToken::HeaderKeyword, consumeToken());
+ break;
+
+ case MMToken::LinkKeyword:
+ SubDecl = parseLinkDecl();
+ break;
+
+ default:
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_member);
+ consumeToken();
+ break;
+ }
+ if (SubDecl)
+ MDecl.Decls.push_back(std::move(*SubDecl));
+ } 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;
+ }
+ return std::move(MDecl);
+}
+
+std::optional<ExternModuleDecl> ModuleMapParser::parseExternModuleDecl() {
+ assert(Tok.is(MMToken::ExternKeyword));
+ ExternModuleDecl EMD;
+ EMD.Location = consumeToken(); // 'extern' keyword
+
+ // Parse 'module' keyword.
+ if (!Tok.is(MMToken::ModuleKeyword)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);
+ consumeToken();
+ HadError = true;
+ return std::nullopt;
+ }
+ consumeToken(); // 'module' keyword
+
+ // Parse the module name.
+ if (parseModuleId(EMD.Id)) {
+ HadError = true;
+ return std::nullopt;
+ }
+
+ // 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::nullopt;
+ }
+ EMD.Path = Tok.getString();
+ consumeToken(); // filename
+
+ return std::move(EMD);
+}
+
+/// Parse a configuration macro declaration.
+///
+/// module-declaration:
+/// 'config_macros' attributes[opt] config-macro-list?
+///
+/// config-macro-list:
+/// identifier (',' identifier)?
+std::optional<ConfigMacrosDecl> ModuleMapParser::parseConfigMacrosDecl() {
+ assert(Tok.is(MMToken::ConfigMacros));
+ ConfigMacrosDecl CMDecl;
+ CMDecl.Location = consumeToken();
+
+ // Parse the optional attributes.
+ ModuleAttributes Attrs;
+ if (parseOptionalAttributes(Attrs))
+ return std::nullopt;
+
+ CMDecl.Exhaustive = Attrs.IsExhaustive;
+
+ // 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))
+ return std::nullopt;
+
+ // Consume the first identifier.
+ CMDecl.Macros.push_back(Tok.getString());
+ 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);
+ return std::nullopt;
+ }
+
+ // Consume the macro name.
+ CMDecl.Macros.push_back(Tok.getString());
+ consumeToken();
+ } while (true);
+ return std::move(CMDecl);
+}
+
+/// Parse a conflict declaration.
+///
+/// module-declaration:
+/// 'conflict' module-id ',' string-literal
+std::optional<ConflictDecl> ModuleMapParser::parseConflictDecl() {
+ assert(Tok.is(MMToken::Conflict));
+ ConflictDecl CD;
+ CD.Location = consumeToken();
+
+ // Parse the module-id.
+ if (parseModuleId(CD.Id))
+ return std::nullopt;
+
+ // Parse the ','.
+ if (!Tok.is(MMToken::Comma)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_comma)
+ << SourceRange(CD.Location);
+ return std::nullopt;
+ }
+ consumeToken();
+
+ // Parse the message.
+ if (!Tok.is(MMToken::StringLiteral)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_message)
+ << formatModuleId(CD.Id);
+ return std::nullopt;
+ }
+ CD.Message = Tok.getString();
+ consumeToken();
+ return std::move(CD);
+}
+
+/// Parse a module export declaration.
+///
+/// export-declaration:
+/// 'export' wildcard-module-id
+///
+/// wildcard-module-id:
+/// identifier
+/// '*'
+/// identifier '.' wildcard-module-id
+std::optional<ExportDecl> ModuleMapParser::parseExportDecl() {
+ assert(Tok.is(MMToken::ExportKeyword));
+ ExportDecl ED;
+ ED.Location = consumeToken();
+
+ // Parse the module-id with an optional wildcard at the end.
+ ED.Wildcard = false;
+ do {
+ // FIXME: Support string-literal module names here.
+ if (Tok.is(MMToken::Identifier)) {
+ ED.Id.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)) {
+ ED.Wildcard = true;
+ consumeToken();
+ break;
+ }
+
+ Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);
+ HadError = true;
+ return std::nullopt;
+ } while (true);
+
+ return std::move(ED);
+}
+
+/// Parse a module export_as declaration.
+///
+/// export-as-declaration:
+/// 'export_as' identifier
+std::optional<ExportAsDecl> ModuleMapParser::parseExportAsDecl() {
+ assert(Tok.is(MMToken::ExportAsKeyword));
+ ExportAsDecl EAD;
+ EAD.Location = consumeToken();
+
+ if (!Tok.is(MMToken::Identifier)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);
+ HadError = true;
+ return std::nullopt;
+ }
+
+ if (parseModuleId(EAD.Id))
+ return std::nullopt;
+ if (EAD.Id.size() > 1)
+ Diags.Report(EAD.Id[1].second, diag::err_mmap_qualified_export_as);
+ return std::move(EAD);
+}
+
+/// Parse a module use declaration.
+///
+/// use-declaration:
+/// 'use' wildcard-module-id
+std::optional<UseDecl> ModuleMapParser::parseUseDecl() {
+ assert(Tok.is(MMToken::UseKeyword));
+ UseDecl UD;
+ UD.Location = consumeToken();
+ if (parseModuleId(UD.Id))
+ return std::nullopt;
+ return std::move(UD);
+}
+
+/// Parse a requires declaration.
+///
+/// requires-declaration:
+/// 'requires' feature-list
+///
+/// feature-list:
+/// feature ',' feature-list
+/// feature
+///
+/// feature:
+/// '!'[opt] identifier
+std::optional<RequiresDecl> ModuleMapParser::parseRequiresDecl() {
+ assert(Tok.is(MMToken::RequiresKeyword));
+ RequiresDecl RD;
+ RD.Location = 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 std::nullopt;
+ }
+
+ // Consume the feature name.
+ RequiresFeature RF;
+ RF.Feature = Tok.getString();
+ RF.Location = consumeToken();
+ RF.RequiredState = RequiredState;
+
+ RD.Features.push_back(std::move(RF));
+
+ if (!Tok.is(MMToken::Comma))
+ break;
+
+ // Consume the comma.
+ consumeToken();
+ } while (true);
+ return std::move(RD);
+}
+
+/// 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
+std::optional<HeaderDecl>
+ModuleMapParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,
+ clang::SourceLocation LeadingLoc) {
+ HeaderDecl HD;
+ HD.Private = false;
+ HD.Excluded = false;
+ HD.Textual = false;
+ // We've already consumed the first token.
+ HD.Location = LeadingLoc;
+
+ if (LeadingToken == MMToken::PrivateKeyword) {
+ HD.Private = true;
+ // 'private' may optionally be followed by 'textual'.
+ if (Tok.is(MMToken::TextualKeyword)) {
+ HD.Textual = true;
+ LeadingToken = Tok.Kind;
+ consumeToken();
+ }
+ } else if (LeadingToken == MMToken::ExcludeKeyword)
+ HD.Excluded = true;
+ else if (LeadingToken == MMToken::TextualKeyword)
+ HD.Textual = true;
+
+ 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 std::nullopt;
+ }
+ consumeToken();
+ }
+
+ // Parse the header name.
+ if (!Tok.is(MMToken::StringLiteral)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) << "header";
+ HadError = true;
+ return std::nullopt;
+ }
+ HD.Path = Tok.getString();
+ HD.PathLoc = consumeToken();
+ HD.Umbrella = LeadingToken == MMToken::UmbrellaKeyword;
+
+ // 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 (HD.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;
+ }
+ HD.Size = Tok.getInteger();
+ consumeToken();
+ break;
+
+ case ModTime:
+ if (HD.MTime)
+ 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;
+ }
+ HD.MTime = 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;
+ }
+ }
+ return std::move(HD);
+}
+
+/// Parse an exclude declaration.
+///
+/// exclude-declaration:
+/// 'exclude' identifier
+std::optional<ExcludeDecl>
+ModuleMapParser::parseExcludeDecl(clang::SourceLocation LeadingLoc) {
+ // FIXME: Support string-literal module names here.
+ if (!Tok.is(MMToken::Identifier)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_missing_exclude_name);
+ HadError = true;
+ return std::nullopt;
+ }
+
+ ExcludeDecl ED;
+ ED.Location = LeadingLoc;
+ ED.Module = Tok.getString();
+ consumeToken();
+ return std::move(ED);
+}
+
+/// Parse an umbrella directory declaration.
+///
+/// umbrella-dir-declaration:
+/// umbrella string-literal
+std::optional<UmbrellaDirDecl>
+ModuleMapParser::parseUmbrellaDirDecl(clang::SourceLocation UmbrellaLoc) {
+ UmbrellaDirDecl UDD;
+ UDD.Location = UmbrellaLoc;
+ // Parse the directory name.
+ if (!Tok.is(MMToken::StringLiteral)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)
+ << "umbrella";
+ HadError = true;
+ return std::nullopt;
+ }
+
+ UDD.Path = Tok.getString();
+ consumeToken();
+ return std::move(UDD);
+}
+
+/// Parse a link declaration.
+///
+/// module-declaration:
+/// 'link' 'framework'[opt] string-literal
+std::optional<LinkDecl> ModuleMapParser::parseLinkDecl() {
+ assert(Tok.is(MMToken::LinkKeyword));
+ LinkDecl LD;
+ LD.Location = consumeToken();
+
+ // Parse the optional 'framework' keyword.
+ LD.Framework = false;
+ if (Tok.is(MMToken::FrameworkKeyword)) {
+ consumeToken();
+ LD.Framework = true;
+ }
+
+ // Parse the library name
+ if (!Tok.is(MMToken::StringLiteral)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_expected_library_name)
+ << LD.Framework << SourceRange(LD.Location);
+ HadError = true;
+ return std::nullopt;
+ }
+
+ LD.Library = Tok.getString();
+ consumeToken();
+ return std::move(LD);
+}
+
+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;
+ }
+
+ // Form the token.
+ Tok.Kind = MMToken::StringLiteral;
+ Tok.StringData = LToken.getLiteralData() + 1;
+ Tok.StringLength = LToken.getLength() - 2;
+ break;
+ }
+
+ case tok::numeric_constant: {
+ // We don't support any suffixes or other complications.
+ uint64_t Value;
+ if (StringRef(LToken.getLiteralData(), LToken.getLength())
+ .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;
+}
+
+/// 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(ModuleAttributes &Attrs) {
+ bool Error = 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();
+ Error = true;
+ }
+
+ /// 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
+ };
+
+ // 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);
+ Error = true;
+ }
+
+ if (Tok.is(MMToken::RSquare))
+ consumeToken();
+ }
+
+ if (Error)
+ HadError = true;
+
+ return Error;
+}
+
+static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out, int depth);
+
+static void dumpExternModule(const ExternModuleDecl &EMD,
+ llvm::raw_ostream &out, int depth) {
+ out.indent(depth * 2);
+ out << "extern module " << formatModuleId(EMD.Id) << " \"" << EMD.Path
+ << "\"\n";
+}
+
+static void dumpDecls(ArrayRef<Decl> Decls, llvm::raw_ostream &out, int depth) {
+ for (const auto &Decl : Decls) {
+ std::visit(llvm::overloaded{
+ [&](const RequiresDecl &RD) {
+ out.indent(depth * 2);
+ out << "requires\n";
+ },
+ [&](const HeaderDecl &HD) {
+ out.indent(depth * 2);
+ if (HD.Private)
+ out << "private ";
+ if (HD.Textual)
+ out << "textual ";
+ if (HD.Excluded)
+ out << "excluded ";
+ if (HD.Umbrella)
+ out << "umbrella ";
+ out << "header \"" << HD.Path << "\"\n";
+ },
+ [&](const UmbrellaDirDecl &UDD) {
+ out.indent(depth * 2);
+ out << "umbrella\n";
+ },
+ [&](const ModuleDecl &MD) { dumpModule(MD, out, depth); },
+ [&](const ExcludeDecl &ED) {
+ out.indent(depth * 2);
+ out << "exclude " << ED.Module << "\n";
+ },
+ [&](const ExportDecl &ED) {
+ out.indent(depth * 2);
+ out << "export "
+ << (ED.Wildcard ? "*" : formatModuleId(ED.Id)) << "\n";
+ },
+ [&](const ExportAsDecl &EAD) {
+ out.indent(depth * 2);
+ out << "export as\n";
+ },
+ [&](const ExternModuleDecl &EMD) {
+ dumpExternModule(EMD, out, depth);
+ },
+ [&](const UseDecl &UD) {
+ out.indent(depth * 2);
+ out << "use\n";
+ },
+ [&](const LinkDecl &LD) {
+ out.indent(depth * 2);
+ out << "link\n";
+ },
+ [&](const ConfigMacrosDecl &CMD) {
+ out.indent(depth * 2);
+ out << "config_macros ";
+ if (CMD.Exhaustive)
+ out << "[exhaustive] ";
+ for (auto Macro : CMD.Macros) {
+ out << Macro << " ";
+ }
+ out << "\n";
+ },
+ [&](const ConflictDecl &CD) {
+ out.indent(depth * 2);
+ out << "conflicts\n";
+ }},
+ Decl);
+ }
+}
+
+static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out,
+ int depth) {
+ out.indent(depth * 2);
+ out << "module " << formatModuleId(MD.Id) << "\n";
+ dumpDecls(MD.Decls, out, depth + 1);
+}
+
+void modulemap::dumpModuleMapFile(ModuleMapFile &MMF, llvm::raw_ostream &out) {
+ for (const auto &Decl : MMF.Decls) {
+ std::visit(
+ llvm::overloaded{[&](const ModuleDecl &MD) { dumpModule(MD, out, 0); },
+ [&](const ExternModuleDecl &EMD) {
+ dumpExternModule(EMD, out, 0);
+ }},
+ Decl);
+ }
+}
diff --git a/clang/test/Modules/Inputs/export_as_test.modulemap b/clang/test/Modules/Inputs/export_as_test.modulemap
index 4aaec4194ef5af..bbe60924046e98 100644
--- a/clang/test/Modules/Inputs/export_as_test.modulemap
+++ b/clang/test/Modules/Inputs/export_as_test.modulemap
@@ -7,3 +7,7 @@ module PrivateFoo {
export_as Wibble
}
}
+
+module B {
+ export_as C.B
+}
diff --git a/clang/test/Modules/diagnostics.modulemap b/clang/test/Modules/diagnostics.modulemap
index c12fef50c38ed7..4f893ac897034e 100644
--- a/clang/test/Modules/diagnostics.modulemap
+++ b/clang/test/Modules/diagnostics.modulemap
@@ -1,36 +1,39 @@
// RUN: rm -rf %t
-// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/diagnostics-aux.modulemap -fmodule-map-file=%s -fsyntax-only -x c++ /dev/null 2>&1 | FileCheck %s --implicit-check-not error:
+// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/diagnostics-aux.modulemap -fmodule-map-file=%s -fsyntax-only -x c++ /dev/null 2>&1 | FileCheck %s
// CHECK: In file included from {{.*}}diagnostics-aux.modulemap:3:
// CHECK: diagnostics-aux-2.modulemap:2:3: error: expected
// PR22299: Ensure we can produce diagnostics for duplicate modules from -fmodule-map-file=.
//
-// CHECK: diagnostics.modulemap:[[@LINE+2]]:8: error: redefinition of module 'foo'
-// CHECK: diagnostics-aux.modulemap:1:8: note: previously defined here
+// CHECK-DAG: diagnostics.modulemap:[[@LINE+2]]:8: error: redefinition of module 'foo'
+// CHECK-DAG: diagnostics-aux.modulemap:1:8: note: previously defined here
module foo {}
//* Check that we accept BCPL comments properly, not just as an extension. */
module bad_use {
- // CHECK: diagnostics.modulemap:[[@LINE+1]]:22: error: use declarations are only allowed in top-level modules
+ // CHECK-DAG: diagnostics.modulemap:[[@LINE+1]]:22: error: use declarations are only allowed in top-level modules
module submodule { use foo }
}
module header_attr {
- // CHECK: diagnostics.modulemap:[[@LINE+1]]:20: error: expected a header attribute name
+ // CHECK-DAG: diagnostics.modulemap:[[@LINE+1]]:20: error: expected a header attribute name
header "foo.h" { x }
- // CHECK: diagnostics.modulemap:[[@LINE+1]]:27: error: header attribute 'size' specified multiple times
+ // CHECK-DAG: diagnostics.modulemap:[[@LINE+1]]:27: error: header attribute 'size' specified multiple times
header "bar.h" { size 1 size 2 }
- // CHECK: diagnostics.modulemap:[[@LINE+1]]:25: error: expected integer literal as value for header attribute 'size'
+ // CHECK-DAG: diagnostics.modulemap:[[@LINE+1]]:25: error: expected integer literal as value for header attribute 'size'
header "baz.h" { size "30 kilobytes" }
header "quux.h" { size 1 mtime 2 }
header "no_attrs.h" {}
}
-// CHECK: diagnostics.modulemap:[[@LINE+1]]:8: error: no module named 'unknown' found, parent module must be defined before the submodule
+// CHECK-DAG: diagnostics.modulemap:[[@LINE+1]]:8: error: no module named 'unknown' found, parent module must be defined before the submodule
module unknown.submodule {}
module known_top_level {}
-// CHECK: diagnostics.modulemap:[[@LINE+1]]:24: error: no module named 'unknown' in 'known_top_level', parent module must be defined before the submodule
+// CHECK-DAG: diagnostics.modulemap:[[@LINE+1]]:24: error: no module named 'unknown' in 'known_top_level', parent module must be defined before the submodule
module known_top_level.unknown.submodule {}
+
+// Check that there were no other errors emitted.
+// CHECK: 8 errors generated
diff --git a/clang/test/Modules/export_as_test.c b/clang/test/Modules/export_as_test.c
index a73d6bfc460d6e..a98dcafd4e6692 100644
--- a/clang/test/Modules/export_as_test.c
+++ b/clang/test/Modules/export_as_test.c
@@ -2,8 +2,7 @@
// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/export_as_test.modulemap %s 2> %t.err
// RUN: FileCheck %s < %t.err
+// CHECK: export_as_test.modulemap:7:5: error: only top-level modules can be re-exported as public
+// CHECK: export_as_test.modulemap:12:15: error: a module can only be re-exported as another top-level module
// CHECK: export_as_test.modulemap:3:13: error: conflicting re-export of module 'PrivateFoo' as 'Foo' or 'Bar'
// CHECK: export_as_test.modulemap:4:13: warning: module 'PrivateFoo' already re-exported as 'Bar'
-// CHECK: export_as_test.modulemap:7:15: error: only top-level modules can be re-exported as public
-
-
diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h
index ace5f60b572d75..1e9765058fea7d 100644
--- a/llvm/include/llvm/ADT/STLExtras.h
+++ b/llvm/include/llvm/ADT/STLExtras.h
@@ -2596,6 +2596,15 @@ template <typename T> using has_sizeof = decltype(sizeof(T));
template <typename T>
constexpr bool is_incomplete_v = !is_detected<detail::has_sizeof, T>::value;
+//===----------------------------------------------------------------------===//
+// Extra additions to <variant>
+//===----------------------------------------------------------------------===//
+
+template <class... Ts> struct overloaded : Ts... {
+ using Ts::operator()...;
+};
+template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
+
} // end namespace llvm
namespace std {
More information about the llvm-commits
mailing list