Path: blob/main/contrib/llvm-project/clang/lib/Lex/ModuleMapFile.cpp
213764 views
//===- ModuleMapFile.cpp - ------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file handles parsing of modulemap files into a simple AST.10///11//===----------------------------------------------------------------------===//1213#include "clang/Lex/ModuleMapFile.h"14#include "clang/Basic/Diagnostic.h"15#include "clang/Basic/LangOptions.h"16#include "clang/Basic/Module.h"17#include "clang/Basic/SourceManager.h"18#include "clang/Lex/LexDiagnostic.h"19#include "clang/Lex/Lexer.h"20#include "clang/Lex/ModuleMap.h"21#include "llvm/ADT/STLExtras.h"22#include <optional>2324using namespace clang;25using namespace modulemap;2627namespace {28struct MMToken {29enum TokenKind {30Comma,31ConfigMacros,32Conflict,33EndOfFile,34HeaderKeyword,35Identifier,36Exclaim,37ExcludeKeyword,38ExplicitKeyword,39ExportKeyword,40ExportAsKeyword,41ExternKeyword,42FrameworkKeyword,43LinkKeyword,44ModuleKeyword,45Period,46PrivateKeyword,47UmbrellaKeyword,48UseKeyword,49RequiresKeyword,50Star,51StringLiteral,52IntegerLiteral,53TextualKeyword,54LBrace,55RBrace,56LSquare,57RSquare58} Kind;5960SourceLocation::UIntTy Location;61unsigned StringLength;62union {63// If Kind != IntegerLiteral.64const char *StringData;6566// If Kind == IntegerLiteral.67uint64_t IntegerValue;68};6970void clear() {71Kind = EndOfFile;72Location = 0;73StringLength = 0;74StringData = nullptr;75}7677bool is(TokenKind K) const { return Kind == K; }7879SourceLocation getLocation() const {80return SourceLocation::getFromRawEncoding(Location);81}8283uint64_t getInteger() const {84return Kind == IntegerLiteral ? IntegerValue : 0;85}8687StringRef getString() const {88return Kind == IntegerLiteral ? StringRef()89: StringRef(StringData, StringLength);90}91};9293struct ModuleMapFileParser {94// External context95Lexer &L;96DiagnosticsEngine &Diags;9798/// Parsed representation of the module map file99ModuleMapFile MMF{};100101bool HadError = false;102103/// The current token.104MMToken Tok{};105106bool parseTopLevelDecls();107std::optional<ModuleDecl> parseModuleDecl(bool TopLevel);108std::optional<ExternModuleDecl> parseExternModuleDecl();109std::optional<ConfigMacrosDecl> parseConfigMacrosDecl();110std::optional<ConflictDecl> parseConflictDecl();111std::optional<ExportDecl> parseExportDecl();112std::optional<ExportAsDecl> parseExportAsDecl();113std::optional<UseDecl> parseUseDecl();114std::optional<RequiresDecl> parseRequiresDecl();115std::optional<HeaderDecl> parseHeaderDecl(MMToken::TokenKind LeadingToken,116SourceLocation LeadingLoc);117std::optional<ExcludeDecl> parseExcludeDecl(clang::SourceLocation LeadingLoc);118std::optional<UmbrellaDirDecl>119parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);120std::optional<LinkDecl> parseLinkDecl();121122SourceLocation consumeToken();123void skipUntil(MMToken::TokenKind K);124bool parseModuleId(ModuleId &Id);125bool parseOptionalAttributes(ModuleAttributes &Attrs);126127SourceLocation getLocation() const { return Tok.getLocation(); };128};129130std::string formatModuleId(const ModuleId &Id) {131std::string result;132{133llvm::raw_string_ostream OS(result);134135for (unsigned I = 0, N = Id.size(); I != N; ++I) {136if (I)137OS << ".";138OS << Id[I].first;139}140}141142return result;143}144} // end anonymous namespace145146std::optional<ModuleMapFile>147modulemap::parseModuleMap(FileID ID, clang::DirectoryEntryRef Dir,148SourceManager &SM, DiagnosticsEngine &Diags,149bool IsSystem, unsigned *Offset) {150std::optional<llvm::MemoryBufferRef> Buffer = SM.getBufferOrNone(ID);151LangOptions LOpts;152LOpts.LangStd = clang::LangStandard::lang_c99;153Lexer L(SM.getLocForStartOfFile(ID), LOpts, Buffer->getBufferStart(),154Buffer->getBufferStart() + (Offset ? *Offset : 0),155Buffer->getBufferEnd());156SourceLocation Start = L.getSourceLocation();157158ModuleMapFileParser Parser{L, Diags};159bool Failed = Parser.parseTopLevelDecls();160161if (Offset) {162auto Loc = SM.getDecomposedLoc(Parser.getLocation());163assert(Loc.first == ID && "stopped in a different file?");164*Offset = Loc.second;165}166167if (Failed)168return std::nullopt;169Parser.MMF.ID = ID;170Parser.MMF.Dir = Dir;171Parser.MMF.Start = Start;172Parser.MMF.IsSystem = IsSystem;173return std::move(Parser.MMF);174}175176bool ModuleMapFileParser::parseTopLevelDecls() {177Tok.clear();178consumeToken();179do {180switch (Tok.Kind) {181case MMToken::EndOfFile:182return HadError;183case MMToken::ExternKeyword: {184std::optional<ExternModuleDecl> EMD = parseExternModuleDecl();185if (EMD)186MMF.Decls.push_back(std::move(*EMD));187break;188}189case MMToken::ExplicitKeyword:190case MMToken::ModuleKeyword:191case MMToken::FrameworkKeyword: {192std::optional<ModuleDecl> MD = parseModuleDecl(true);193if (MD)194MMF.Decls.push_back(std::move(*MD));195break;196}197case MMToken::Comma:198case MMToken::ConfigMacros:199case MMToken::Conflict:200case MMToken::Exclaim:201case MMToken::ExcludeKeyword:202case MMToken::ExportKeyword:203case MMToken::ExportAsKeyword:204case MMToken::HeaderKeyword:205case MMToken::Identifier:206case MMToken::LBrace:207case MMToken::LinkKeyword:208case MMToken::LSquare:209case MMToken::Period:210case MMToken::PrivateKeyword:211case MMToken::RBrace:212case MMToken::RSquare:213case MMToken::RequiresKeyword:214case MMToken::Star:215case MMToken::StringLiteral:216case MMToken::IntegerLiteral:217case MMToken::TextualKeyword:218case MMToken::UmbrellaKeyword:219case MMToken::UseKeyword:220Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);221HadError = true;222consumeToken();223break;224}225} while (true);226}227228/// Parse a module declaration.229///230/// module-declaration:231/// 'extern' 'module' module-id string-literal232/// 'explicit'[opt] 'framework'[opt] 'module' module-id attributes[opt]233/// { module-member* }234///235/// module-member:236/// requires-declaration237/// header-declaration238/// submodule-declaration239/// export-declaration240/// export-as-declaration241/// link-declaration242///243/// submodule-declaration:244/// module-declaration245/// inferred-submodule-declaration246std::optional<ModuleDecl> ModuleMapFileParser::parseModuleDecl(bool TopLevel) {247assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword) ||248Tok.is(MMToken::FrameworkKeyword));249250ModuleDecl MDecl;251252SourceLocation ExplicitLoc;253MDecl.Explicit = false;254MDecl.Framework = false;255256// Parse 'explicit' keyword, if present.257if (Tok.is(MMToken::ExplicitKeyword)) {258MDecl.Location = ExplicitLoc = consumeToken();259MDecl.Explicit = true;260}261262// Parse 'framework' keyword, if present.263if (Tok.is(MMToken::FrameworkKeyword)) {264SourceLocation FrameworkLoc = consumeToken();265if (!MDecl.Location.isValid())266MDecl.Location = FrameworkLoc;267MDecl.Framework = true;268}269270// Parse 'module' keyword.271if (!Tok.is(MMToken::ModuleKeyword)) {272Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);273consumeToken();274HadError = true;275return std::nullopt;276}277SourceLocation ModuleLoc = consumeToken();278if (!MDecl.Location.isValid())279MDecl.Location = ModuleLoc; // 'module' keyword280281// If we have a wildcard for the module name, this is an inferred submodule.282// We treat it as a normal module at this point.283if (Tok.is(MMToken::Star)) {284SourceLocation StarLoc = consumeToken();285MDecl.Id.push_back({"*", StarLoc});286if (TopLevel && !MDecl.Framework) {287Diags.Report(StarLoc, diag::err_mmap_top_level_inferred_submodule);288HadError = true;289return std::nullopt;290}291} else {292// Parse the module name.293if (parseModuleId(MDecl.Id)) {294HadError = true;295return std::nullopt;296}297if (!TopLevel) {298if (MDecl.Id.size() > 1) {299Diags.Report(MDecl.Id.front().second,300diag::err_mmap_nested_submodule_id)301<< SourceRange(MDecl.Id.front().second, MDecl.Id.back().second);302303HadError = true;304}305} else if (MDecl.Id.size() == 1 && MDecl.Explicit) {306// Top-level modules can't be explicit.307Diags.Report(ExplicitLoc, diag::err_mmap_explicit_top_level);308MDecl.Explicit = false;309HadError = true;310}311}312313// Parse the optional attribute list.314if (parseOptionalAttributes(MDecl.Attrs))315return std::nullopt;316317// Parse the opening brace.318if (!Tok.is(MMToken::LBrace)) {319Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace)320<< MDecl.Id.back().first;321HadError = true;322return std::nullopt;323}324SourceLocation LBraceLoc = consumeToken();325326bool Done = false;327do {328std::optional<Decl> SubDecl;329switch (Tok.Kind) {330case MMToken::EndOfFile:331case MMToken::RBrace:332Done = true;333break;334335case MMToken::ConfigMacros:336// Only top-level modules can have configuration macros.337if (!TopLevel)338Diags.Report(Tok.getLocation(), diag::err_mmap_config_macro_submodule);339SubDecl = parseConfigMacrosDecl();340break;341342case MMToken::Conflict:343SubDecl = parseConflictDecl();344break;345346case MMToken::ExternKeyword:347SubDecl = parseExternModuleDecl();348break;349350case MMToken::ExplicitKeyword:351case MMToken::FrameworkKeyword:352case MMToken::ModuleKeyword:353SubDecl = parseModuleDecl(false);354break;355356case MMToken::ExportKeyword:357SubDecl = parseExportDecl();358break;359360case MMToken::ExportAsKeyword:361if (!TopLevel) {362Diags.Report(Tok.getLocation(), diag::err_mmap_submodule_export_as);363parseExportAsDecl();364} else365SubDecl = parseExportAsDecl();366break;367368case MMToken::UseKeyword:369SubDecl = parseUseDecl();370break;371372case MMToken::RequiresKeyword:373SubDecl = parseRequiresDecl();374break;375376case MMToken::TextualKeyword:377SubDecl = parseHeaderDecl(MMToken::TextualKeyword, consumeToken());378break;379380case MMToken::UmbrellaKeyword: {381SourceLocation UmbrellaLoc = consumeToken();382if (Tok.is(MMToken::HeaderKeyword))383SubDecl = parseHeaderDecl(MMToken::UmbrellaKeyword, UmbrellaLoc);384else385SubDecl = parseUmbrellaDirDecl(UmbrellaLoc);386break;387}388389case MMToken::ExcludeKeyword: {390SourceLocation ExcludeLoc = consumeToken();391if (Tok.is(MMToken::HeaderKeyword))392SubDecl = parseHeaderDecl(MMToken::ExcludeKeyword, ExcludeLoc);393else394SubDecl = parseExcludeDecl(ExcludeLoc);395break;396}397398case MMToken::PrivateKeyword:399SubDecl = parseHeaderDecl(MMToken::PrivateKeyword, consumeToken());400break;401402case MMToken::HeaderKeyword:403SubDecl = parseHeaderDecl(MMToken::HeaderKeyword, consumeToken());404break;405406case MMToken::LinkKeyword:407SubDecl = parseLinkDecl();408break;409410default:411Diags.Report(Tok.getLocation(), diag::err_mmap_expected_member);412consumeToken();413break;414}415if (SubDecl)416MDecl.Decls.push_back(std::move(*SubDecl));417} while (!Done);418419if (Tok.is(MMToken::RBrace))420consumeToken();421else {422Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace);423Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match);424HadError = true;425}426return std::move(MDecl);427}428429std::optional<ExternModuleDecl> ModuleMapFileParser::parseExternModuleDecl() {430assert(Tok.is(MMToken::ExternKeyword));431ExternModuleDecl EMD;432EMD.Location = consumeToken(); // 'extern' keyword433434// Parse 'module' keyword.435if (!Tok.is(MMToken::ModuleKeyword)) {436Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);437consumeToken();438HadError = true;439return std::nullopt;440}441consumeToken(); // 'module' keyword442443// Parse the module name.444if (parseModuleId(EMD.Id)) {445HadError = true;446return std::nullopt;447}448449// Parse the referenced module map file name.450if (!Tok.is(MMToken::StringLiteral)) {451Diags.Report(Tok.getLocation(), diag::err_mmap_expected_mmap_file);452HadError = true;453return std::nullopt;454}455EMD.Path = Tok.getString();456consumeToken(); // filename457458return std::move(EMD);459}460461/// Parse a configuration macro declaration.462///463/// module-declaration:464/// 'config_macros' attributes[opt] config-macro-list?465///466/// config-macro-list:467/// identifier (',' identifier)?468std::optional<ConfigMacrosDecl> ModuleMapFileParser::parseConfigMacrosDecl() {469assert(Tok.is(MMToken::ConfigMacros));470ConfigMacrosDecl CMDecl;471CMDecl.Location = consumeToken();472473// Parse the optional attributes.474ModuleAttributes Attrs;475if (parseOptionalAttributes(Attrs))476return std::nullopt;477478CMDecl.Exhaustive = Attrs.IsExhaustive;479480// If we don't have an identifier, we're done.481// FIXME: Support macros with the same name as a keyword here.482if (!Tok.is(MMToken::Identifier))483return std::nullopt;484485// Consume the first identifier.486CMDecl.Macros.push_back(Tok.getString());487consumeToken();488489do {490// If there's a comma, consume it.491if (!Tok.is(MMToken::Comma))492break;493consumeToken();494495// We expect to see a macro name here.496// FIXME: Support macros with the same name as a keyword here.497if (!Tok.is(MMToken::Identifier)) {498Diags.Report(Tok.getLocation(), diag::err_mmap_expected_config_macro);499return std::nullopt;500}501502// Consume the macro name.503CMDecl.Macros.push_back(Tok.getString());504consumeToken();505} while (true);506return std::move(CMDecl);507}508509/// Parse a conflict declaration.510///511/// module-declaration:512/// 'conflict' module-id ',' string-literal513std::optional<ConflictDecl> ModuleMapFileParser::parseConflictDecl() {514assert(Tok.is(MMToken::Conflict));515ConflictDecl CD;516CD.Location = consumeToken();517518// Parse the module-id.519if (parseModuleId(CD.Id))520return std::nullopt;521522// Parse the ','.523if (!Tok.is(MMToken::Comma)) {524Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_comma)525<< SourceRange(CD.Location);526return std::nullopt;527}528consumeToken();529530// Parse the message.531if (!Tok.is(MMToken::StringLiteral)) {532Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_message)533<< formatModuleId(CD.Id);534return std::nullopt;535}536CD.Message = Tok.getString();537consumeToken();538return std::move(CD);539}540541/// Parse a module export declaration.542///543/// export-declaration:544/// 'export' wildcard-module-id545///546/// wildcard-module-id:547/// identifier548/// '*'549/// identifier '.' wildcard-module-id550std::optional<ExportDecl> ModuleMapFileParser::parseExportDecl() {551assert(Tok.is(MMToken::ExportKeyword));552ExportDecl ED;553ED.Location = consumeToken();554555// Parse the module-id with an optional wildcard at the end.556ED.Wildcard = false;557do {558// FIXME: Support string-literal module names here.559if (Tok.is(MMToken::Identifier)) {560ED.Id.push_back(561std::make_pair(std::string(Tok.getString()), Tok.getLocation()));562consumeToken();563564if (Tok.is(MMToken::Period)) {565consumeToken();566continue;567}568569break;570}571572if (Tok.is(MMToken::Star)) {573ED.Wildcard = true;574consumeToken();575break;576}577578Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);579HadError = true;580return std::nullopt;581} while (true);582583return std::move(ED);584}585586/// Parse a module export_as declaration.587///588/// export-as-declaration:589/// 'export_as' identifier590std::optional<ExportAsDecl> ModuleMapFileParser::parseExportAsDecl() {591assert(Tok.is(MMToken::ExportAsKeyword));592ExportAsDecl EAD;593EAD.Location = consumeToken();594595if (!Tok.is(MMToken::Identifier)) {596Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);597HadError = true;598return std::nullopt;599}600601if (parseModuleId(EAD.Id))602return std::nullopt;603if (EAD.Id.size() > 1)604Diags.Report(EAD.Id[1].second, diag::err_mmap_qualified_export_as);605return std::move(EAD);606}607608/// Parse a module use declaration.609///610/// use-declaration:611/// 'use' wildcard-module-id612std::optional<UseDecl> ModuleMapFileParser::parseUseDecl() {613assert(Tok.is(MMToken::UseKeyword));614UseDecl UD;615UD.Location = consumeToken();616if (parseModuleId(UD.Id))617return std::nullopt;618return std::move(UD);619}620621/// Parse a requires declaration.622///623/// requires-declaration:624/// 'requires' feature-list625///626/// feature-list:627/// feature ',' feature-list628/// feature629///630/// feature:631/// '!'[opt] identifier632std::optional<RequiresDecl> ModuleMapFileParser::parseRequiresDecl() {633assert(Tok.is(MMToken::RequiresKeyword));634RequiresDecl RD;635RD.Location = consumeToken();636637// Parse the feature-list.638do {639bool RequiredState = true;640if (Tok.is(MMToken::Exclaim)) {641RequiredState = false;642consumeToken();643}644645if (!Tok.is(MMToken::Identifier)) {646Diags.Report(Tok.getLocation(), diag::err_mmap_expected_feature);647HadError = true;648return std::nullopt;649}650651// Consume the feature name.652RequiresFeature RF;653RF.Feature = Tok.getString();654RF.Location = consumeToken();655RF.RequiredState = RequiredState;656657RD.Features.push_back(std::move(RF));658659if (!Tok.is(MMToken::Comma))660break;661662// Consume the comma.663consumeToken();664} while (true);665return std::move(RD);666}667668/// Parse a header declaration.669///670/// header-declaration:671/// 'textual'[opt] 'header' string-literal672/// 'private' 'textual'[opt] 'header' string-literal673/// 'exclude' 'header' string-literal674/// 'umbrella' 'header' string-literal675std::optional<HeaderDecl>676ModuleMapFileParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,677clang::SourceLocation LeadingLoc) {678HeaderDecl HD;679HD.Private = false;680HD.Excluded = false;681HD.Textual = false;682// We've already consumed the first token.683HD.Location = LeadingLoc;684685if (LeadingToken == MMToken::PrivateKeyword) {686HD.Private = true;687// 'private' may optionally be followed by 'textual'.688if (Tok.is(MMToken::TextualKeyword)) {689HD.Textual = true;690LeadingToken = Tok.Kind;691consumeToken();692}693} else if (LeadingToken == MMToken::ExcludeKeyword)694HD.Excluded = true;695else if (LeadingToken == MMToken::TextualKeyword)696HD.Textual = true;697698if (LeadingToken != MMToken::HeaderKeyword) {699if (!Tok.is(MMToken::HeaderKeyword)) {700Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)701<< (LeadingToken == MMToken::PrivateKeyword ? "private"702: LeadingToken == MMToken::ExcludeKeyword ? "exclude"703: LeadingToken == MMToken::TextualKeyword ? "textual"704: "umbrella");705return std::nullopt;706}707consumeToken();708}709710// Parse the header name.711if (!Tok.is(MMToken::StringLiteral)) {712Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) << "header";713HadError = true;714return std::nullopt;715}716HD.Path = Tok.getString();717HD.PathLoc = consumeToken();718HD.Umbrella = LeadingToken == MMToken::UmbrellaKeyword;719720// If we were given stat information, parse it so we can skip looking for721// the file.722if (Tok.is(MMToken::LBrace)) {723SourceLocation LBraceLoc = consumeToken();724725while (!Tok.is(MMToken::RBrace) && !Tok.is(MMToken::EndOfFile)) {726enum Attribute { Size, ModTime, Unknown };727StringRef Str = Tok.getString();728SourceLocation Loc = consumeToken();729switch (llvm::StringSwitch<Attribute>(Str)730.Case("size", Size)731.Case("mtime", ModTime)732.Default(Unknown)) {733case Size:734if (HD.Size)735Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;736if (!Tok.is(MMToken::IntegerLiteral)) {737Diags.Report(Tok.getLocation(),738diag::err_mmap_invalid_header_attribute_value)739<< Str;740skipUntil(MMToken::RBrace);741break;742}743HD.Size = Tok.getInteger();744consumeToken();745break;746747case ModTime:748if (HD.MTime)749Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;750if (!Tok.is(MMToken::IntegerLiteral)) {751Diags.Report(Tok.getLocation(),752diag::err_mmap_invalid_header_attribute_value)753<< Str;754skipUntil(MMToken::RBrace);755break;756}757HD.MTime = Tok.getInteger();758consumeToken();759break;760761case Unknown:762Diags.Report(Loc, diag::err_mmap_expected_header_attribute);763skipUntil(MMToken::RBrace);764break;765}766}767768if (Tok.is(MMToken::RBrace))769consumeToken();770else {771Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace);772Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match);773HadError = true;774}775}776return std::move(HD);777}778779/// Parse an exclude declaration.780///781/// exclude-declaration:782/// 'exclude' identifier783std::optional<ExcludeDecl>784ModuleMapFileParser::parseExcludeDecl(clang::SourceLocation LeadingLoc) {785// FIXME: Support string-literal module names here.786if (!Tok.is(MMToken::Identifier)) {787Diags.Report(Tok.getLocation(), diag::err_mmap_missing_exclude_name);788HadError = true;789return std::nullopt;790}791792ExcludeDecl ED;793ED.Location = LeadingLoc;794ED.Module = Tok.getString();795consumeToken();796return std::move(ED);797}798799/// Parse an umbrella directory declaration.800///801/// umbrella-dir-declaration:802/// umbrella string-literal803std::optional<UmbrellaDirDecl>804ModuleMapFileParser::parseUmbrellaDirDecl(clang::SourceLocation UmbrellaLoc) {805UmbrellaDirDecl UDD;806UDD.Location = UmbrellaLoc;807// Parse the directory name.808if (!Tok.is(MMToken::StringLiteral)) {809Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)810<< "umbrella";811HadError = true;812return std::nullopt;813}814815UDD.Path = Tok.getString();816consumeToken();817return std::move(UDD);818}819820/// Parse a link declaration.821///822/// module-declaration:823/// 'link' 'framework'[opt] string-literal824std::optional<LinkDecl> ModuleMapFileParser::parseLinkDecl() {825assert(Tok.is(MMToken::LinkKeyword));826LinkDecl LD;827LD.Location = consumeToken();828829// Parse the optional 'framework' keyword.830LD.Framework = false;831if (Tok.is(MMToken::FrameworkKeyword)) {832consumeToken();833LD.Framework = true;834}835836// Parse the library name837if (!Tok.is(MMToken::StringLiteral)) {838Diags.Report(Tok.getLocation(), diag::err_mmap_expected_library_name)839<< LD.Framework << SourceRange(LD.Location);840HadError = true;841return std::nullopt;842}843844LD.Library = Tok.getString();845consumeToken();846return std::move(LD);847}848849SourceLocation ModuleMapFileParser::consumeToken() {850SourceLocation Result = Tok.getLocation();851852retry:853Tok.clear();854Token LToken;855L.LexFromRawLexer(LToken);856Tok.Location = LToken.getLocation().getRawEncoding();857switch (LToken.getKind()) {858case tok::raw_identifier: {859StringRef RI = LToken.getRawIdentifier();860Tok.StringData = RI.data();861Tok.StringLength = RI.size();862Tok.Kind = llvm::StringSwitch<MMToken::TokenKind>(RI)863.Case("config_macros", MMToken::ConfigMacros)864.Case("conflict", MMToken::Conflict)865.Case("exclude", MMToken::ExcludeKeyword)866.Case("explicit", MMToken::ExplicitKeyword)867.Case("export", MMToken::ExportKeyword)868.Case("export_as", MMToken::ExportAsKeyword)869.Case("extern", MMToken::ExternKeyword)870.Case("framework", MMToken::FrameworkKeyword)871.Case("header", MMToken::HeaderKeyword)872.Case("link", MMToken::LinkKeyword)873.Case("module", MMToken::ModuleKeyword)874.Case("private", MMToken::PrivateKeyword)875.Case("requires", MMToken::RequiresKeyword)876.Case("textual", MMToken::TextualKeyword)877.Case("umbrella", MMToken::UmbrellaKeyword)878.Case("use", MMToken::UseKeyword)879.Default(MMToken::Identifier);880break;881}882883case tok::comma:884Tok.Kind = MMToken::Comma;885break;886887case tok::eof:888Tok.Kind = MMToken::EndOfFile;889break;890891case tok::l_brace:892Tok.Kind = MMToken::LBrace;893break;894895case tok::l_square:896Tok.Kind = MMToken::LSquare;897break;898899case tok::period:900Tok.Kind = MMToken::Period;901break;902903case tok::r_brace:904Tok.Kind = MMToken::RBrace;905break;906907case tok::r_square:908Tok.Kind = MMToken::RSquare;909break;910911case tok::star:912Tok.Kind = MMToken::Star;913break;914915case tok::exclaim:916Tok.Kind = MMToken::Exclaim;917break;918919case tok::string_literal: {920if (LToken.hasUDSuffix()) {921Diags.Report(LToken.getLocation(), diag::err_invalid_string_udl);922HadError = true;923goto retry;924}925926// Form the token.927Tok.Kind = MMToken::StringLiteral;928Tok.StringData = LToken.getLiteralData() + 1;929Tok.StringLength = LToken.getLength() - 2;930break;931}932933case tok::numeric_constant: {934// We don't support any suffixes or other complications.935uint64_t Value;936if (StringRef(LToken.getLiteralData(), LToken.getLength())937.getAsInteger(0, Value)) {938Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token);939HadError = true;940goto retry;941}942943Tok.Kind = MMToken::IntegerLiteral;944Tok.IntegerValue = Value;945break;946}947948case tok::comment:949goto retry;950951case tok::hash:952// A module map can be terminated prematurely by953// #pragma clang module contents954// When building the module, we'll treat the rest of the file as the955// contents of the module.956{957auto NextIsIdent = [&](StringRef Str) -> bool {958L.LexFromRawLexer(LToken);959return !LToken.isAtStartOfLine() && LToken.is(tok::raw_identifier) &&960LToken.getRawIdentifier() == Str;961};962if (NextIsIdent("pragma") && NextIsIdent("clang") &&963NextIsIdent("module") && NextIsIdent("contents")) {964Tok.Kind = MMToken::EndOfFile;965break;966}967}968[[fallthrough]];969970default:971Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token);972HadError = true;973goto retry;974}975976return Result;977}978979void ModuleMapFileParser::skipUntil(MMToken::TokenKind K) {980unsigned braceDepth = 0;981unsigned squareDepth = 0;982do {983switch (Tok.Kind) {984case MMToken::EndOfFile:985return;986987case MMToken::LBrace:988if (Tok.is(K) && braceDepth == 0 && squareDepth == 0)989return;990991++braceDepth;992break;993994case MMToken::LSquare:995if (Tok.is(K) && braceDepth == 0 && squareDepth == 0)996return;997998++squareDepth;999break;10001001case MMToken::RBrace:1002if (braceDepth > 0)1003--braceDepth;1004else if (Tok.is(K))1005return;1006break;10071008case MMToken::RSquare:1009if (squareDepth > 0)1010--squareDepth;1011else if (Tok.is(K))1012return;1013break;10141015default:1016if (braceDepth == 0 && squareDepth == 0 && Tok.is(K))1017return;1018break;1019}10201021consumeToken();1022} while (true);1023}10241025/// Parse a module-id.1026///1027/// module-id:1028/// identifier1029/// identifier '.' module-id1030///1031/// \returns true if an error occurred, false otherwise.1032bool ModuleMapFileParser::parseModuleId(ModuleId &Id) {1033Id.clear();1034do {1035if (Tok.is(MMToken::Identifier) || Tok.is(MMToken::StringLiteral)) {1036Id.push_back(1037std::make_pair(std::string(Tok.getString()), Tok.getLocation()));1038consumeToken();1039} else {1040Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module_name);1041return true;1042}10431044if (!Tok.is(MMToken::Period))1045break;10461047consumeToken();1048} while (true);10491050return false;1051}10521053/// Parse optional attributes.1054///1055/// attributes:1056/// attribute attributes1057/// attribute1058///1059/// attribute:1060/// [ identifier ]1061///1062/// \param Attrs Will be filled in with the parsed attributes.1063///1064/// \returns true if an error occurred, false otherwise.1065bool ModuleMapFileParser::parseOptionalAttributes(ModuleAttributes &Attrs) {1066bool Error = false;10671068while (Tok.is(MMToken::LSquare)) {1069// Consume the '['.1070SourceLocation LSquareLoc = consumeToken();10711072// Check whether we have an attribute name here.1073if (!Tok.is(MMToken::Identifier)) {1074Diags.Report(Tok.getLocation(), diag::err_mmap_expected_attribute);1075skipUntil(MMToken::RSquare);1076if (Tok.is(MMToken::RSquare))1077consumeToken();1078Error = true;1079}10801081/// Enumerates the known attributes.1082enum AttributeKind {1083/// An unknown attribute.1084AT_unknown,10851086/// The 'system' attribute.1087AT_system,10881089/// The 'extern_c' attribute.1090AT_extern_c,10911092/// The 'exhaustive' attribute.1093AT_exhaustive,10941095/// The 'no_undeclared_includes' attribute.1096AT_no_undeclared_includes1097};10981099// Decode the attribute name.1100AttributeKind Attribute =1101llvm::StringSwitch<AttributeKind>(Tok.getString())1102.Case("exhaustive", AT_exhaustive)1103.Case("extern_c", AT_extern_c)1104.Case("no_undeclared_includes", AT_no_undeclared_includes)1105.Case("system", AT_system)1106.Default(AT_unknown);1107switch (Attribute) {1108case AT_unknown:1109Diags.Report(Tok.getLocation(), diag::warn_mmap_unknown_attribute)1110<< Tok.getString();1111break;11121113case AT_system:1114Attrs.IsSystem = true;1115break;11161117case AT_extern_c:1118Attrs.IsExternC = true;1119break;11201121case AT_exhaustive:1122Attrs.IsExhaustive = true;1123break;11241125case AT_no_undeclared_includes:1126Attrs.NoUndeclaredIncludes = true;1127break;1128}1129consumeToken();11301131// Consume the ']'.1132if (!Tok.is(MMToken::RSquare)) {1133Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rsquare);1134Diags.Report(LSquareLoc, diag::note_mmap_lsquare_match);1135skipUntil(MMToken::RSquare);1136Error = true;1137}11381139if (Tok.is(MMToken::RSquare))1140consumeToken();1141}11421143if (Error)1144HadError = true;11451146return Error;1147}11481149static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out, int depth);11501151static void dumpExternModule(const ExternModuleDecl &EMD,1152llvm::raw_ostream &out, int depth) {1153out.indent(depth * 2);1154out << "extern module " << formatModuleId(EMD.Id) << " \"" << EMD.Path1155<< "\"\n";1156}11571158static void dumpDecls(ArrayRef<Decl> Decls, llvm::raw_ostream &out, int depth) {1159for (const auto &Decl : Decls) {1160std::visit(llvm::makeVisitor(1161[&](const RequiresDecl &RD) {1162out.indent(depth * 2);1163out << "requires\n";1164},1165[&](const HeaderDecl &HD) {1166out.indent(depth * 2);1167if (HD.Private)1168out << "private ";1169if (HD.Textual)1170out << "textual ";1171if (HD.Excluded)1172out << "excluded ";1173if (HD.Umbrella)1174out << "umbrella ";1175out << "header \"" << HD.Path << "\"\n";1176},1177[&](const UmbrellaDirDecl &UDD) {1178out.indent(depth * 2);1179out << "umbrella\n";1180},1181[&](const ModuleDecl &MD) { dumpModule(MD, out, depth); },1182[&](const ExcludeDecl &ED) {1183out.indent(depth * 2);1184out << "exclude " << ED.Module << "\n";1185},1186[&](const ExportDecl &ED) {1187out.indent(depth * 2);1188out << "export "1189<< (ED.Wildcard ? "*" : formatModuleId(ED.Id)) << "\n";1190},1191[&](const ExportAsDecl &EAD) {1192out.indent(depth * 2);1193out << "export as\n";1194},1195[&](const ExternModuleDecl &EMD) {1196dumpExternModule(EMD, out, depth);1197},1198[&](const UseDecl &UD) {1199out.indent(depth * 2);1200out << "use\n";1201},1202[&](const LinkDecl &LD) {1203out.indent(depth * 2);1204out << "link\n";1205},1206[&](const ConfigMacrosDecl &CMD) {1207out.indent(depth * 2);1208out << "config_macros ";1209if (CMD.Exhaustive)1210out << "[exhaustive] ";1211for (auto Macro : CMD.Macros) {1212out << Macro << " ";1213}1214out << "\n";1215},1216[&](const ConflictDecl &CD) {1217out.indent(depth * 2);1218out << "conflicts\n";1219}),1220Decl);1221}1222}12231224static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out,1225int depth) {1226out.indent(depth * 2);1227out << "module " << formatModuleId(MD.Id) << "\n";1228dumpDecls(MD.Decls, out, depth + 1);1229}12301231void ModuleMapFile::dump(llvm::raw_ostream &out) const {1232for (const auto &Decl : Decls) {1233std::visit(1234llvm::makeVisitor([&](const ModuleDecl &MD) { dumpModule(MD, out, 0); },1235[&](const ExternModuleDecl &EMD) {1236dumpExternModule(EMD, out, 0);1237}),1238Decl);1239}1240}124112421243