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