brintos

brintos / llvm-project-archived public Read only

0
0
Text · 33.7 KiB · 183e919 Raw
1242 lines · cpp
1//===- ModuleMapFile.cpp - ------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file handles parsing of modulemap files into a simple AST.11///12//===----------------------------------------------------------------------===//13 14#include "clang/Lex/ModuleMapFile.h"15#include "clang/Basic/Diagnostic.h"16#include "clang/Basic/LangOptions.h"17#include "clang/Basic/Module.h"18#include "clang/Basic/SourceManager.h"19#include "clang/Lex/LexDiagnostic.h"20#include "clang/Lex/Lexer.h"21#include "clang/Lex/ModuleMap.h"22#include "llvm/ADT/STLExtras.h"23#include <optional>24 25using namespace clang;26using namespace modulemap;27 28namespace {29struct MMToken {30  enum TokenKind {31    Comma,32    ConfigMacros,33    Conflict,34    EndOfFile,35    HeaderKeyword,36    Identifier,37    Exclaim,38    ExcludeKeyword,39    ExplicitKeyword,40    ExportKeyword,41    ExportAsKeyword,42    ExternKeyword,43    FrameworkKeyword,44    LinkKeyword,45    ModuleKeyword,46    Period,47    PrivateKeyword,48    UmbrellaKeyword,49    UseKeyword,50    RequiresKeyword,51    Star,52    StringLiteral,53    IntegerLiteral,54    TextualKeyword,55    LBrace,56    RBrace,57    LSquare,58    RSquare59  } Kind;60 61  SourceLocation::UIntTy Location;62  unsigned StringLength;63  union {64    // If Kind != IntegerLiteral.65    const char *StringData;66 67    // If Kind == IntegerLiteral.68    uint64_t IntegerValue;69  };70 71  void clear() {72    Kind = EndOfFile;73    Location = 0;74    StringLength = 0;75    StringData = nullptr;76  }77 78  bool is(TokenKind K) const { return Kind == K; }79 80  SourceLocation getLocation() const {81    return SourceLocation::getFromRawEncoding(Location);82  }83 84  uint64_t getInteger() const {85    return Kind == IntegerLiteral ? IntegerValue : 0;86  }87 88  StringRef getString() const {89    return Kind == IntegerLiteral ? StringRef()90                                  : StringRef(StringData, StringLength);91  }92};93 94struct ModuleMapFileParser {95  // External context96  Lexer &L;97  DiagnosticsEngine &Diags;98 99  /// Parsed representation of the module map file100  ModuleMapFile MMF{};101 102  bool HadError = false;103 104  /// The current token.105  MMToken Tok{};106 107  bool parseTopLevelDecls();108  std::optional<ModuleDecl> parseModuleDecl(bool TopLevel);109  std::optional<ExternModuleDecl> parseExternModuleDecl();110  std::optional<ConfigMacrosDecl> parseConfigMacrosDecl();111  std::optional<ConflictDecl> parseConflictDecl();112  std::optional<ExportDecl> parseExportDecl();113  std::optional<ExportAsDecl> parseExportAsDecl();114  std::optional<UseDecl> parseUseDecl();115  std::optional<RequiresDecl> parseRequiresDecl();116  std::optional<HeaderDecl> parseHeaderDecl(MMToken::TokenKind LeadingToken,117                                            SourceLocation LeadingLoc);118  std::optional<ExcludeDecl> parseExcludeDecl(clang::SourceLocation LeadingLoc);119  std::optional<UmbrellaDirDecl>120  parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);121  std::optional<LinkDecl> parseLinkDecl();122 123  SourceLocation consumeToken();124  void skipUntil(MMToken::TokenKind K);125  bool parseModuleId(ModuleId &Id);126  bool parseOptionalAttributes(ModuleAttributes &Attrs);127 128  SourceLocation getLocation() const { return Tok.getLocation(); };129};130 131std::string formatModuleId(const ModuleId &Id) {132  std::string result;133  {134    llvm::raw_string_ostream OS(result);135 136    for (unsigned I = 0, N = Id.size(); I != N; ++I) {137      if (I)138        OS << ".";139      OS << Id[I].first;140    }141  }142 143  return result;144}145} // end anonymous namespace146 147std::optional<ModuleMapFile>148modulemap::parseModuleMap(FileID ID, clang::DirectoryEntryRef Dir,149                          SourceManager &SM, DiagnosticsEngine &Diags,150                          bool IsSystem, unsigned *Offset) {151  std::optional<llvm::MemoryBufferRef> Buffer = SM.getBufferOrNone(ID);152  LangOptions LOpts;153  LOpts.LangStd = clang::LangStandard::lang_c99;154  Lexer L(SM.getLocForStartOfFile(ID), LOpts, Buffer->getBufferStart(),155          Buffer->getBufferStart() + (Offset ? *Offset : 0),156          Buffer->getBufferEnd());157  SourceLocation Start = L.getSourceLocation();158 159  ModuleMapFileParser Parser{L, Diags};160  bool Failed = Parser.parseTopLevelDecls();161 162  if (Offset) {163    auto Loc = SM.getDecomposedLoc(Parser.getLocation());164    assert(Loc.first == ID && "stopped in a different file?");165    *Offset = Loc.second;166  }167 168  if (Failed)169    return std::nullopt;170  Parser.MMF.ID = ID;171  Parser.MMF.Dir = Dir;172  Parser.MMF.Start = Start;173  Parser.MMF.IsSystem = IsSystem;174  return std::move(Parser.MMF);175}176 177bool ModuleMapFileParser::parseTopLevelDecls() {178  Tok.clear();179  consumeToken();180  do {181    switch (Tok.Kind) {182    case MMToken::EndOfFile:183      return HadError;184    case MMToken::ExternKeyword: {185      std::optional<ExternModuleDecl> EMD = parseExternModuleDecl();186      if (EMD)187        MMF.Decls.push_back(std::move(*EMD));188      break;189    }190    case MMToken::ExplicitKeyword:191    case MMToken::ModuleKeyword:192    case MMToken::FrameworkKeyword: {193      std::optional<ModuleDecl> MD = parseModuleDecl(true);194      if (MD)195        MMF.Decls.push_back(std::move(*MD));196      break;197    }198    case MMToken::Comma:199    case MMToken::ConfigMacros:200    case MMToken::Conflict:201    case MMToken::Exclaim:202    case MMToken::ExcludeKeyword:203    case MMToken::ExportKeyword:204    case MMToken::ExportAsKeyword:205    case MMToken::HeaderKeyword:206    case MMToken::Identifier:207    case MMToken::LBrace:208    case MMToken::LinkKeyword:209    case MMToken::LSquare:210    case MMToken::Period:211    case MMToken::PrivateKeyword:212    case MMToken::RBrace:213    case MMToken::RSquare:214    case MMToken::RequiresKeyword:215    case MMToken::Star:216    case MMToken::StringLiteral:217    case MMToken::IntegerLiteral:218    case MMToken::TextualKeyword:219    case MMToken::UmbrellaKeyword:220    case MMToken::UseKeyword:221      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);222      HadError = true;223      consumeToken();224      break;225    }226  } while (true);227}228 229/// Parse a module declaration.230///231///   module-declaration:232///     'extern' 'module' module-id string-literal233///     'explicit'[opt] 'framework'[opt] 'module' module-id attributes[opt]234///       { module-member* }235///236///   module-member:237///     requires-declaration238///     header-declaration239///     submodule-declaration240///     export-declaration241///     export-as-declaration242///     link-declaration243///244///   submodule-declaration:245///     module-declaration246///     inferred-submodule-declaration247std::optional<ModuleDecl> ModuleMapFileParser::parseModuleDecl(bool TopLevel) {248  assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword) ||249         Tok.is(MMToken::FrameworkKeyword));250 251  ModuleDecl MDecl;252 253  SourceLocation ExplicitLoc;254  MDecl.Explicit = false;255  MDecl.Framework = false;256 257  // Parse 'explicit' keyword, if present.258  if (Tok.is(MMToken::ExplicitKeyword)) {259    MDecl.Location = ExplicitLoc = consumeToken();260    MDecl.Explicit = true;261  }262 263  // Parse 'framework' keyword, if present.264  if (Tok.is(MMToken::FrameworkKeyword)) {265    SourceLocation FrameworkLoc = consumeToken();266    if (!MDecl.Location.isValid())267      MDecl.Location = FrameworkLoc;268    MDecl.Framework = true;269  }270 271  // Parse 'module' keyword.272  if (!Tok.is(MMToken::ModuleKeyword)) {273    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);274    consumeToken();275    HadError = true;276    return std::nullopt;277  }278  SourceLocation ModuleLoc = consumeToken();279  if (!MDecl.Location.isValid())280    MDecl.Location = ModuleLoc; // 'module' keyword281 282  // If we have a wildcard for the module name, this is an inferred submodule.283  // We treat it as a normal module at this point.284  if (Tok.is(MMToken::Star)) {285    SourceLocation StarLoc = consumeToken();286    MDecl.Id.push_back({"*", StarLoc});287    if (TopLevel && !MDecl.Framework) {288      Diags.Report(StarLoc, diag::err_mmap_top_level_inferred_submodule);289      HadError = true;290      return std::nullopt;291    }292  } else {293    // Parse the module name.294    if (parseModuleId(MDecl.Id)) {295      HadError = true;296      return std::nullopt;297    }298    if (!TopLevel) {299      if (MDecl.Id.size() > 1) {300        Diags.Report(MDecl.Id.front().second,301                     diag::err_mmap_nested_submodule_id)302            << SourceRange(MDecl.Id.front().second, MDecl.Id.back().second);303 304        HadError = true;305      }306    } else if (MDecl.Id.size() == 1 && MDecl.Explicit) {307      // Top-level modules can't be explicit.308      Diags.Report(ExplicitLoc, diag::err_mmap_explicit_top_level);309      MDecl.Explicit = false;310      HadError = true;311    }312  }313 314  // Parse the optional attribute list.315  if (parseOptionalAttributes(MDecl.Attrs))316    return std::nullopt;317 318  // Parse the opening brace.319  if (!Tok.is(MMToken::LBrace)) {320    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_lbrace)321        << MDecl.Id.back().first;322    HadError = true;323    return std::nullopt;324  }325  SourceLocation LBraceLoc = consumeToken();326 327  bool Done = false;328  do {329    std::optional<Decl> SubDecl;330    switch (Tok.Kind) {331    case MMToken::EndOfFile:332    case MMToken::RBrace:333      Done = true;334      break;335 336    case MMToken::ConfigMacros:337      // Only top-level modules can have configuration macros.338      if (!TopLevel)339        Diags.Report(Tok.getLocation(), diag::err_mmap_config_macro_submodule);340      SubDecl = parseConfigMacrosDecl();341      break;342 343    case MMToken::Conflict:344      SubDecl = parseConflictDecl();345      break;346 347    case MMToken::ExternKeyword:348      SubDecl = parseExternModuleDecl();349      break;350 351    case MMToken::ExplicitKeyword:352    case MMToken::FrameworkKeyword:353    case MMToken::ModuleKeyword:354      SubDecl = parseModuleDecl(false);355      break;356 357    case MMToken::ExportKeyword:358      SubDecl = parseExportDecl();359      break;360 361    case MMToken::ExportAsKeyword:362      if (!TopLevel) {363        Diags.Report(Tok.getLocation(), diag::err_mmap_submodule_export_as);364        parseExportAsDecl();365      } else366        SubDecl = parseExportAsDecl();367      break;368 369    case MMToken::UseKeyword:370      SubDecl = parseUseDecl();371      break;372 373    case MMToken::RequiresKeyword:374      SubDecl = parseRequiresDecl();375      break;376 377    case MMToken::TextualKeyword:378      SubDecl = parseHeaderDecl(MMToken::TextualKeyword, consumeToken());379      break;380 381    case MMToken::UmbrellaKeyword: {382      SourceLocation UmbrellaLoc = consumeToken();383      if (Tok.is(MMToken::HeaderKeyword))384        SubDecl = parseHeaderDecl(MMToken::UmbrellaKeyword, UmbrellaLoc);385      else386        SubDecl = parseUmbrellaDirDecl(UmbrellaLoc);387      break;388    }389 390    case MMToken::ExcludeKeyword: {391      SourceLocation ExcludeLoc = consumeToken();392      if (Tok.is(MMToken::HeaderKeyword))393        SubDecl = parseHeaderDecl(MMToken::ExcludeKeyword, ExcludeLoc);394      else395        SubDecl = parseExcludeDecl(ExcludeLoc);396      break;397    }398 399    case MMToken::PrivateKeyword:400      SubDecl = parseHeaderDecl(MMToken::PrivateKeyword, consumeToken());401      break;402 403    case MMToken::HeaderKeyword:404      SubDecl = parseHeaderDecl(MMToken::HeaderKeyword, consumeToken());405      break;406 407    case MMToken::LinkKeyword:408      SubDecl = parseLinkDecl();409      break;410 411    default:412      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_member);413      consumeToken();414      break;415    }416    if (SubDecl)417      MDecl.Decls.push_back(std::move(*SubDecl));418  } while (!Done);419 420  if (Tok.is(MMToken::RBrace))421    consumeToken();422  else {423    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace);424    Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match);425    HadError = true;426  }427  return std::move(MDecl);428}429 430std::optional<ExternModuleDecl> ModuleMapFileParser::parseExternModuleDecl() {431  assert(Tok.is(MMToken::ExternKeyword));432  ExternModuleDecl EMD;433  EMD.Location = consumeToken(); // 'extern' keyword434 435  // Parse 'module' keyword.436  if (!Tok.is(MMToken::ModuleKeyword)) {437    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module);438    consumeToken();439    HadError = true;440    return std::nullopt;441  }442  consumeToken(); // 'module' keyword443 444  // Parse the module name.445  if (parseModuleId(EMD.Id)) {446    HadError = true;447    return std::nullopt;448  }449 450  // Parse the referenced module map file name.451  if (!Tok.is(MMToken::StringLiteral)) {452    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_mmap_file);453    HadError = true;454    return std::nullopt;455  }456  EMD.Path = Tok.getString();457  consumeToken(); // filename458 459  return std::move(EMD);460}461 462/// Parse a configuration macro declaration.463///464///   module-declaration:465///     'config_macros' attributes[opt] config-macro-list?466///467///   config-macro-list:468///     identifier (',' identifier)?469std::optional<ConfigMacrosDecl> ModuleMapFileParser::parseConfigMacrosDecl() {470  assert(Tok.is(MMToken::ConfigMacros));471  ConfigMacrosDecl CMDecl;472  CMDecl.Location = consumeToken();473 474  // Parse the optional attributes.475  ModuleAttributes Attrs;476  if (parseOptionalAttributes(Attrs))477    return std::nullopt;478 479  CMDecl.Exhaustive = Attrs.IsExhaustive;480 481  // If we don't have an identifier, we're done.482  // FIXME: Support macros with the same name as a keyword here.483  if (!Tok.is(MMToken::Identifier))484    return std::nullopt;485 486  // Consume the first identifier.487  CMDecl.Macros.push_back(Tok.getString());488  consumeToken();489 490  do {491    // If there's a comma, consume it.492    if (!Tok.is(MMToken::Comma))493      break;494    consumeToken();495 496    // We expect to see a macro name here.497    // FIXME: Support macros with the same name as a keyword here.498    if (!Tok.is(MMToken::Identifier)) {499      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_config_macro);500      return std::nullopt;501    }502 503    // Consume the macro name.504    CMDecl.Macros.push_back(Tok.getString());505    consumeToken();506  } while (true);507  return std::move(CMDecl);508}509 510/// Parse a conflict declaration.511///512///   module-declaration:513///     'conflict' module-id ',' string-literal514std::optional<ConflictDecl> ModuleMapFileParser::parseConflictDecl() {515  assert(Tok.is(MMToken::Conflict));516  ConflictDecl CD;517  CD.Location = consumeToken();518 519  // Parse the module-id.520  if (parseModuleId(CD.Id))521    return std::nullopt;522 523  // Parse the ','.524  if (!Tok.is(MMToken::Comma)) {525    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_comma)526        << SourceRange(CD.Location);527    return std::nullopt;528  }529  consumeToken();530 531  // Parse the message.532  if (!Tok.is(MMToken::StringLiteral)) {533    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_message)534        << formatModuleId(CD.Id);535    return std::nullopt;536  }537  CD.Message = Tok.getString();538  consumeToken();539  return std::move(CD);540}541 542/// Parse a module export declaration.543///544///   export-declaration:545///     'export' wildcard-module-id546///547///   wildcard-module-id:548///     identifier549///     '*'550///     identifier '.' wildcard-module-id551std::optional<ExportDecl> ModuleMapFileParser::parseExportDecl() {552  assert(Tok.is(MMToken::ExportKeyword));553  ExportDecl ED;554  ED.Location = consumeToken();555 556  // Parse the module-id with an optional wildcard at the end.557  ED.Wildcard = false;558  do {559    // FIXME: Support string-literal module names here.560    if (Tok.is(MMToken::Identifier)) {561      ED.Id.push_back(562          std::make_pair(std::string(Tok.getString()), Tok.getLocation()));563      consumeToken();564 565      if (Tok.is(MMToken::Period)) {566        consumeToken();567        continue;568      }569 570      break;571    }572 573    if (Tok.is(MMToken::Star)) {574      ED.Wildcard = true;575      consumeToken();576      break;577    }578 579    Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);580    HadError = true;581    return std::nullopt;582  } while (true);583 584  return std::move(ED);585}586 587/// Parse a module export_as declaration.588///589///   export-as-declaration:590///     'export_as' identifier591std::optional<ExportAsDecl> ModuleMapFileParser::parseExportAsDecl() {592  assert(Tok.is(MMToken::ExportAsKeyword));593  ExportAsDecl EAD;594  EAD.Location = consumeToken();595 596  if (!Tok.is(MMToken::Identifier)) {597    Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);598    HadError = true;599    return std::nullopt;600  }601 602  if (parseModuleId(EAD.Id))603    return std::nullopt;604  if (EAD.Id.size() > 1)605    Diags.Report(EAD.Id[1].second, diag::err_mmap_qualified_export_as);606  return std::move(EAD);607}608 609/// Parse a module use declaration.610///611///   use-declaration:612///     'use' wildcard-module-id613std::optional<UseDecl> ModuleMapFileParser::parseUseDecl() {614  assert(Tok.is(MMToken::UseKeyword));615  UseDecl UD;616  UD.Location = consumeToken();617  if (parseModuleId(UD.Id))618    return std::nullopt;619  return std::move(UD);620}621 622/// Parse a requires declaration.623///624///   requires-declaration:625///     'requires' feature-list626///627///   feature-list:628///     feature ',' feature-list629///     feature630///631///   feature:632///     '!'[opt] identifier633std::optional<RequiresDecl> ModuleMapFileParser::parseRequiresDecl() {634  assert(Tok.is(MMToken::RequiresKeyword));635  RequiresDecl RD;636  RD.Location = consumeToken();637 638  // Parse the feature-list.639  do {640    bool RequiredState = true;641    if (Tok.is(MMToken::Exclaim)) {642      RequiredState = false;643      consumeToken();644    }645 646    if (!Tok.is(MMToken::Identifier)) {647      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_feature);648      HadError = true;649      return std::nullopt;650    }651 652    // Consume the feature name.653    RequiresFeature RF;654    RF.Feature = Tok.getString();655    RF.Location = consumeToken();656    RF.RequiredState = RequiredState;657 658    RD.Features.push_back(std::move(RF));659 660    if (!Tok.is(MMToken::Comma))661      break;662 663    // Consume the comma.664    consumeToken();665  } while (true);666  return std::move(RD);667}668 669/// Parse a header declaration.670///671///   header-declaration:672///     'textual'[opt] 'header' string-literal673///     'private' 'textual'[opt] 'header' string-literal674///     'exclude' 'header' string-literal675///     'umbrella' 'header' string-literal676std::optional<HeaderDecl>677ModuleMapFileParser::parseHeaderDecl(MMToken::TokenKind LeadingToken,678                                     clang::SourceLocation LeadingLoc) {679  HeaderDecl HD;680  HD.Private = false;681  HD.Excluded = false;682  HD.Textual = false;683  // We've already consumed the first token.684  HD.Location = LeadingLoc;685 686  if (LeadingToken == MMToken::PrivateKeyword) {687    HD.Private = true;688    // 'private' may optionally be followed by 'textual'.689    if (Tok.is(MMToken::TextualKeyword)) {690      HD.Textual = true;691      LeadingToken = Tok.Kind;692      consumeToken();693    }694  } else if (LeadingToken == MMToken::ExcludeKeyword)695    HD.Excluded = true;696  else if (LeadingToken == MMToken::TextualKeyword)697    HD.Textual = true;698 699  if (LeadingToken != MMToken::HeaderKeyword) {700    if (!Tok.is(MMToken::HeaderKeyword)) {701      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)702          << (LeadingToken == MMToken::PrivateKeyword   ? "private"703              : LeadingToken == MMToken::ExcludeKeyword ? "exclude"704              : LeadingToken == MMToken::TextualKeyword ? "textual"705                                                        : "umbrella");706      return std::nullopt;707    }708    consumeToken();709  }710 711  // Parse the header name.712  if (!Tok.is(MMToken::StringLiteral)) {713    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header) << "header";714    HadError = true;715    return std::nullopt;716  }717  HD.Path = Tok.getString();718  HD.PathLoc = consumeToken();719  HD.Umbrella = LeadingToken == MMToken::UmbrellaKeyword;720 721  // If we were given stat information, parse it so we can skip looking for722  // the file.723  if (Tok.is(MMToken::LBrace)) {724    SourceLocation LBraceLoc = consumeToken();725 726    while (!Tok.is(MMToken::RBrace) && !Tok.is(MMToken::EndOfFile)) {727      enum Attribute { Size, ModTime, Unknown };728      StringRef Str = Tok.getString();729      SourceLocation Loc = consumeToken();730      switch (llvm::StringSwitch<Attribute>(Str)731                  .Case("size", Size)732                  .Case("mtime", ModTime)733                  .Default(Unknown)) {734      case Size:735        if (HD.Size)736          Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;737        if (!Tok.is(MMToken::IntegerLiteral)) {738          Diags.Report(Tok.getLocation(),739                       diag::err_mmap_invalid_header_attribute_value)740              << Str;741          skipUntil(MMToken::RBrace);742          break;743        }744        HD.Size = Tok.getInteger();745        consumeToken();746        break;747 748      case ModTime:749        if (HD.MTime)750          Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;751        if (!Tok.is(MMToken::IntegerLiteral)) {752          Diags.Report(Tok.getLocation(),753                       diag::err_mmap_invalid_header_attribute_value)754              << Str;755          skipUntil(MMToken::RBrace);756          break;757        }758        HD.MTime = Tok.getInteger();759        consumeToken();760        break;761 762      case Unknown:763        Diags.Report(Loc, diag::err_mmap_expected_header_attribute);764        skipUntil(MMToken::RBrace);765        break;766      }767    }768 769    if (Tok.is(MMToken::RBrace))770      consumeToken();771    else {772      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rbrace);773      Diags.Report(LBraceLoc, diag::note_mmap_lbrace_match);774      HadError = true;775    }776  }777  return std::move(HD);778}779 780/// Parse an exclude declaration.781///782/// exclude-declaration:783///   'exclude' identifier784std::optional<ExcludeDecl>785ModuleMapFileParser::parseExcludeDecl(clang::SourceLocation LeadingLoc) {786  // FIXME: Support string-literal module names here.787  if (!Tok.is(MMToken::Identifier)) {788    Diags.Report(Tok.getLocation(), diag::err_mmap_missing_exclude_name);789    HadError = true;790    return std::nullopt;791  }792 793  ExcludeDecl ED;794  ED.Location = LeadingLoc;795  ED.Module = Tok.getString();796  consumeToken();797  return std::move(ED);798}799 800/// Parse an umbrella directory declaration.801///802///   umbrella-dir-declaration:803///     umbrella string-literal804std::optional<UmbrellaDirDecl>805ModuleMapFileParser::parseUmbrellaDirDecl(clang::SourceLocation UmbrellaLoc) {806  UmbrellaDirDecl UDD;807  UDD.Location = UmbrellaLoc;808  // Parse the directory name.809  if (!Tok.is(MMToken::StringLiteral)) {810    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_header)811        << "umbrella";812    HadError = true;813    return std::nullopt;814  }815 816  UDD.Path = Tok.getString();817  consumeToken();818  return std::move(UDD);819}820 821/// Parse a link declaration.822///823///   module-declaration:824///     'link' 'framework'[opt] string-literal825std::optional<LinkDecl> ModuleMapFileParser::parseLinkDecl() {826  assert(Tok.is(MMToken::LinkKeyword));827  LinkDecl LD;828  LD.Location = consumeToken();829 830  // Parse the optional 'framework' keyword.831  LD.Framework = false;832  if (Tok.is(MMToken::FrameworkKeyword)) {833    consumeToken();834    LD.Framework = true;835  }836 837  // Parse the library name838  if (!Tok.is(MMToken::StringLiteral)) {839    Diags.Report(Tok.getLocation(), diag::err_mmap_expected_library_name)840        << LD.Framework << SourceRange(LD.Location);841    HadError = true;842    return std::nullopt;843  }844 845  LD.Library = Tok.getString();846  consumeToken();847  return std::move(LD);848}849 850SourceLocation ModuleMapFileParser::consumeToken() {851  SourceLocation Result = Tok.getLocation();852 853retry:854  Tok.clear();855  Token LToken;856  L.LexFromRawLexer(LToken);857  Tok.Location = LToken.getLocation().getRawEncoding();858  switch (LToken.getKind()) {859  case tok::raw_identifier: {860    StringRef RI = LToken.getRawIdentifier();861    Tok.StringData = RI.data();862    Tok.StringLength = RI.size();863    Tok.Kind = llvm::StringSwitch<MMToken::TokenKind>(RI)864                   .Case("config_macros", MMToken::ConfigMacros)865                   .Case("conflict", MMToken::Conflict)866                   .Case("exclude", MMToken::ExcludeKeyword)867                   .Case("explicit", MMToken::ExplicitKeyword)868                   .Case("export", MMToken::ExportKeyword)869                   .Case("export_as", MMToken::ExportAsKeyword)870                   .Case("extern", MMToken::ExternKeyword)871                   .Case("framework", MMToken::FrameworkKeyword)872                   .Case("header", MMToken::HeaderKeyword)873                   .Case("link", MMToken::LinkKeyword)874                   .Case("module", MMToken::ModuleKeyword)875                   .Case("private", MMToken::PrivateKeyword)876                   .Case("requires", MMToken::RequiresKeyword)877                   .Case("textual", MMToken::TextualKeyword)878                   .Case("umbrella", MMToken::UmbrellaKeyword)879                   .Case("use", MMToken::UseKeyword)880                   .Default(MMToken::Identifier);881    break;882  }883 884  case tok::comma:885    Tok.Kind = MMToken::Comma;886    break;887 888  case tok::eof:889    Tok.Kind = MMToken::EndOfFile;890    break;891 892  case tok::l_brace:893    Tok.Kind = MMToken::LBrace;894    break;895 896  case tok::l_square:897    Tok.Kind = MMToken::LSquare;898    break;899 900  case tok::period:901    Tok.Kind = MMToken::Period;902    break;903 904  case tok::r_brace:905    Tok.Kind = MMToken::RBrace;906    break;907 908  case tok::r_square:909    Tok.Kind = MMToken::RSquare;910    break;911 912  case tok::star:913    Tok.Kind = MMToken::Star;914    break;915 916  case tok::exclaim:917    Tok.Kind = MMToken::Exclaim;918    break;919 920  case tok::string_literal: {921    if (LToken.hasUDSuffix()) {922      Diags.Report(LToken.getLocation(), diag::err_invalid_string_udl);923      HadError = true;924      goto retry;925    }926 927    // Form the token.928    Tok.Kind = MMToken::StringLiteral;929    Tok.StringData = LToken.getLiteralData() + 1;930    Tok.StringLength = LToken.getLength() - 2;931    break;932  }933 934  case tok::numeric_constant: {935    // We don't support any suffixes or other complications.936    uint64_t Value;937    if (StringRef(LToken.getLiteralData(), LToken.getLength())938            .getAsInteger(0, Value)) {939      Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token);940      HadError = true;941      goto retry;942    }943 944    Tok.Kind = MMToken::IntegerLiteral;945    Tok.IntegerValue = Value;946    break;947  }948 949  case tok::comment:950    goto retry;951 952  case tok::hash:953    // A module map can be terminated prematurely by954    //   #pragma clang module contents955    // When building the module, we'll treat the rest of the file as the956    // contents of the module.957    {958      auto NextIsIdent = [&](StringRef Str) -> bool {959        L.LexFromRawLexer(LToken);960        return !LToken.isAtStartOfLine() && LToken.is(tok::raw_identifier) &&961               LToken.getRawIdentifier() == Str;962      };963      if (NextIsIdent("pragma") && NextIsIdent("clang") &&964          NextIsIdent("module") && NextIsIdent("contents")) {965        Tok.Kind = MMToken::EndOfFile;966        break;967      }968    }969    [[fallthrough]];970 971  default:972    Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token);973    HadError = true;974    goto retry;975  }976 977  return Result;978}979 980void ModuleMapFileParser::skipUntil(MMToken::TokenKind K) {981  unsigned braceDepth = 0;982  unsigned squareDepth = 0;983  do {984    switch (Tok.Kind) {985    case MMToken::EndOfFile:986      return;987 988    case MMToken::LBrace:989      if (Tok.is(K) && braceDepth == 0 && squareDepth == 0)990        return;991 992      ++braceDepth;993      break;994 995    case MMToken::LSquare:996      if (Tok.is(K) && braceDepth == 0 && squareDepth == 0)997        return;998 999      ++squareDepth;1000      break;1001 1002    case MMToken::RBrace:1003      if (braceDepth > 0)1004        --braceDepth;1005      else if (Tok.is(K))1006        return;1007      break;1008 1009    case MMToken::RSquare:1010      if (squareDepth > 0)1011        --squareDepth;1012      else if (Tok.is(K))1013        return;1014      break;1015 1016    default:1017      if (braceDepth == 0 && squareDepth == 0 && Tok.is(K))1018        return;1019      break;1020    }1021 1022    consumeToken();1023  } while (true);1024}1025 1026/// Parse a module-id.1027///1028///   module-id:1029///     identifier1030///     identifier '.' module-id1031///1032/// \returns true if an error occurred, false otherwise.1033bool ModuleMapFileParser::parseModuleId(ModuleId &Id) {1034  Id.clear();1035  do {1036    if (Tok.is(MMToken::Identifier) || Tok.is(MMToken::StringLiteral)) {1037      Id.push_back(1038          std::make_pair(std::string(Tok.getString()), Tok.getLocation()));1039      consumeToken();1040    } else {1041      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module_name);1042      return true;1043    }1044 1045    if (!Tok.is(MMToken::Period))1046      break;1047 1048    consumeToken();1049  } while (true);1050 1051  return false;1052}1053 1054/// Parse optional attributes.1055///1056///   attributes:1057///     attribute attributes1058///     attribute1059///1060///   attribute:1061///     [ identifier ]1062///1063/// \param Attrs Will be filled in with the parsed attributes.1064///1065/// \returns true if an error occurred, false otherwise.1066bool ModuleMapFileParser::parseOptionalAttributes(ModuleAttributes &Attrs) {1067  bool Error = false;1068 1069  while (Tok.is(MMToken::LSquare)) {1070    // Consume the '['.1071    SourceLocation LSquareLoc = consumeToken();1072 1073    // Check whether we have an attribute name here.1074    if (!Tok.is(MMToken::Identifier)) {1075      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_attribute);1076      skipUntil(MMToken::RSquare);1077      if (Tok.is(MMToken::RSquare))1078        consumeToken();1079      Error = true;1080    }1081 1082    /// Enumerates the known attributes.1083    enum AttributeKind {1084      /// An unknown attribute.1085      AT_unknown,1086 1087      /// The 'system' attribute.1088      AT_system,1089 1090      /// The 'extern_c' attribute.1091      AT_extern_c,1092 1093      /// The 'exhaustive' attribute.1094      AT_exhaustive,1095 1096      /// The 'no_undeclared_includes' attribute.1097      AT_no_undeclared_includes1098    };1099 1100    // Decode the attribute name.1101    AttributeKind Attribute =1102        llvm::StringSwitch<AttributeKind>(Tok.getString())1103            .Case("exhaustive", AT_exhaustive)1104            .Case("extern_c", AT_extern_c)1105            .Case("no_undeclared_includes", AT_no_undeclared_includes)1106            .Case("system", AT_system)1107            .Default(AT_unknown);1108    switch (Attribute) {1109    case AT_unknown:1110      Diags.Report(Tok.getLocation(), diag::warn_mmap_unknown_attribute)1111          << Tok.getString();1112      break;1113 1114    case AT_system:1115      Attrs.IsSystem = true;1116      break;1117 1118    case AT_extern_c:1119      Attrs.IsExternC = true;1120      break;1121 1122    case AT_exhaustive:1123      Attrs.IsExhaustive = true;1124      break;1125 1126    case AT_no_undeclared_includes:1127      Attrs.NoUndeclaredIncludes = true;1128      break;1129    }1130    consumeToken();1131 1132    // Consume the ']'.1133    if (!Tok.is(MMToken::RSquare)) {1134      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rsquare);1135      Diags.Report(LSquareLoc, diag::note_mmap_lsquare_match);1136      skipUntil(MMToken::RSquare);1137      Error = true;1138    }1139 1140    if (Tok.is(MMToken::RSquare))1141      consumeToken();1142  }1143 1144  if (Error)1145    HadError = true;1146 1147  return Error;1148}1149 1150static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out, int depth);1151 1152static void dumpExternModule(const ExternModuleDecl &EMD,1153                             llvm::raw_ostream &out, int depth) {1154  out.indent(depth * 2);1155  out << "extern module " << formatModuleId(EMD.Id) << " \"" << EMD.Path1156      << "\"\n";1157}1158 1159static void dumpDecls(ArrayRef<Decl> Decls, llvm::raw_ostream &out, int depth) {1160  for (const auto &Decl : Decls) {1161    std::visit(llvm::makeVisitor(1162                   [&](const RequiresDecl &RD) {1163                     out.indent(depth * 2);1164                     out << "requires\n";1165                   },1166                   [&](const HeaderDecl &HD) {1167                     out.indent(depth * 2);1168                     if (HD.Private)1169                       out << "private ";1170                     if (HD.Textual)1171                       out << "textual ";1172                     if (HD.Excluded)1173                       out << "excluded ";1174                     if (HD.Umbrella)1175                       out << "umbrella ";1176                     out << "header \"" << HD.Path << "\"\n";1177                   },1178                   [&](const UmbrellaDirDecl &UDD) {1179                     out.indent(depth * 2);1180                     out << "umbrella\n";1181                   },1182                   [&](const ModuleDecl &MD) { dumpModule(MD, out, depth); },1183                   [&](const ExcludeDecl &ED) {1184                     out.indent(depth * 2);1185                     out << "exclude " << ED.Module << "\n";1186                   },1187                   [&](const ExportDecl &ED) {1188                     out.indent(depth * 2);1189                     out << "export "1190                         << (ED.Wildcard ? "*" : formatModuleId(ED.Id)) << "\n";1191                   },1192                   [&](const ExportAsDecl &EAD) {1193                     out.indent(depth * 2);1194                     out << "export as\n";1195                   },1196                   [&](const ExternModuleDecl &EMD) {1197                     dumpExternModule(EMD, out, depth);1198                   },1199                   [&](const UseDecl &UD) {1200                     out.indent(depth * 2);1201                     out << "use\n";1202                   },1203                   [&](const LinkDecl &LD) {1204                     out.indent(depth * 2);1205                     out << "link\n";1206                   },1207                   [&](const ConfigMacrosDecl &CMD) {1208                     out.indent(depth * 2);1209                     out << "config_macros ";1210                     if (CMD.Exhaustive)1211                       out << "[exhaustive] ";1212                     for (auto Macro : CMD.Macros) {1213                       out << Macro << " ";1214                     }1215                     out << "\n";1216                   },1217                   [&](const ConflictDecl &CD) {1218                     out.indent(depth * 2);1219                     out << "conflicts\n";1220                   }),1221               Decl);1222  }1223}1224 1225static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out,1226                       int depth) {1227  out.indent(depth * 2);1228  out << "module " << formatModuleId(MD.Id) << "\n";1229  dumpDecls(MD.Decls, out, depth + 1);1230}1231 1232void ModuleMapFile::dump(llvm::raw_ostream &out) const {1233  for (const auto &Decl : Decls) {1234    std::visit(1235        llvm::makeVisitor([&](const ModuleDecl &MD) { dumpModule(MD, out, 0); },1236                          [&](const ExternModuleDecl &EMD) {1237                            dumpExternModule(EMD, out, 0);1238                          }),1239        Decl);1240  }1241}1242