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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// 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 25 using namespace clang; 26 using namespace modulemap; 27 28 namespace { 29 struct 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 RSquare 59 } 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 94 struct ModuleMapFileParser { 95 // External context 96 Lexer &L; 97 DiagnosticsEngine &Diags; 98 99 /// Parsed representation of the module map file 100 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 131 std::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 namespace 146 147 std::optional<ModuleMapFile> 148 modulemap::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 177 bool 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-literal 233 /// 'explicit'[opt] 'framework'[opt] 'module' module-id attributes[opt] 234 /// { module-member* } 235 /// 236 /// module-member: 237 /// requires-declaration 238 /// header-declaration 239 /// submodule-declaration 240 /// export-declaration 241 /// export-as-declaration 242 /// link-declaration 243 /// 244 /// submodule-declaration: 245 /// module-declaration 246 /// inferred-submodule-declaration 247 std::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' keyword 281 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 } else 366 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 else 386 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 else 395 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 430 std::optional<ExternModuleDecl> ModuleMapFileParser::parseExternModuleDecl() { 431 assert(Tok.is(MMToken::ExternKeyword)); 432 ExternModuleDecl EMD; 433 EMD.Location = consumeToken(); // 'extern' keyword 434 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' keyword 443 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(); // filename 458 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)? 469 std::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-literal 514 std::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-id 546 /// 547 /// wildcard-module-id: 548 /// identifier 549 /// '*' 550 /// identifier '.' wildcard-module-id 551 std::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' identifier 591 std::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-id 613 std::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-list 626 /// 627 /// feature-list: 628 /// feature ',' feature-list 629 /// feature 630 /// 631 /// feature: 632 /// '!'[opt] identifier 633 std::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-literal 673 /// 'private' 'textual'[opt] 'header' string-literal 674 /// 'exclude' 'header' string-literal 675 /// 'umbrella' 'header' string-literal 676 std::optional<HeaderDecl> 677 ModuleMapFileParser::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 for 722 // 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' identifier 784 std::optional<ExcludeDecl> 785 ModuleMapFileParser::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-literal 804 std::optional<UmbrellaDirDecl> 805 ModuleMapFileParser::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-literal 825 std::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 name 838 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 850 SourceLocation ModuleMapFileParser::consumeToken() { 851 SourceLocation Result = Tok.getLocation(); 852 853 retry: 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 by 954 // #pragma clang module contents 955 // When building the module, we'll treat the rest of the file as the 956 // 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 980 void 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 /// identifier 1030 /// identifier '.' module-id 1031 /// 1032 /// \returns true if an error occurred, false otherwise. 1033 bool 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 attributes 1058 /// attribute 1059 /// 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. 1066 bool 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_includes 1098 }; 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 1150 static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out, int depth); 1151 1152 static 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.Path 1156 << "\"\n"; 1157 } 1158 1159 static 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 1225 static 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 1232 void 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