| 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 | , |
| 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(Encoding: 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(FID: ID); |
| 152 | LangOptions LOpts; |
| 153 | LOpts.LangStd = clang::LangStandard::lang_c99; |
| 154 | Lexer L(SM.getLocForStartOfFile(FID: ID), LOpts, Buffer->getBufferStart(), |
| 155 | Buffer->getBufferStart() + (Offset ? *Offset : 0), |
| 156 | Buffer->getBufferEnd()); |
| 157 | SourceLocation Start = L.getSourceLocation(); |
| 158 | |
| 159 | ModuleMapFileParser Parser{.L: L, .Diags: Diags}; |
| 160 | bool Failed = Parser.parseTopLevelDecls(); |
| 161 | |
| 162 | if (Offset) { |
| 163 | auto Loc = SM.getDecomposedLoc(Loc: 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(x: std::move(*EMD)); |
| 188 | break; |
| 189 | } |
| 190 | case MMToken::ExplicitKeyword: |
| 191 | case MMToken::ModuleKeyword: |
| 192 | case MMToken::FrameworkKeyword: { |
| 193 | std::optional<ModuleDecl> MD = parseModuleDecl(TopLevel: true); |
| 194 | if (MD) |
| 195 | MMF.Decls.push_back(x: 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(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::ExplicitKeyword)) { |
| 259 | MDecl.Location = ExplicitLoc = consumeToken(); |
| 260 | MDecl.Explicit = true; |
| 261 | } |
| 262 | |
| 263 | // Parse 'framework' keyword, if present. |
| 264 | if (Tok.is(K: 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(K: MMToken::ModuleKeyword)) { |
| 273 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::Star)) { |
| 285 | SourceLocation StarLoc = consumeToken(); |
| 286 | MDecl.Id.push_back(Elt: {"*" , StarLoc}); |
| 287 | if (TopLevel && !MDecl.Framework) { |
| 288 | Diags.Report(Loc: StarLoc, DiagID: 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(Id&: MDecl.Id)) { |
| 295 | HadError = true; |
| 296 | return std::nullopt; |
| 297 | } |
| 298 | if (!TopLevel) { |
| 299 | if (MDecl.Id.size() > 1) { |
| 300 | Diags.Report(Loc: MDecl.Id.front().second, |
| 301 | DiagID: 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(Loc: ExplicitLoc, DiagID: 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(Attrs&: MDecl.Attrs)) |
| 316 | return std::nullopt; |
| 317 | |
| 318 | // Parse the opening brace. |
| 319 | if (!Tok.is(K: MMToken::LBrace)) { |
| 320 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(Loc: Tok.getLocation(), DiagID: 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(TopLevel: false); |
| 355 | break; |
| 356 | |
| 357 | case MMToken::ExportKeyword: |
| 358 | SubDecl = parseExportDecl(); |
| 359 | break; |
| 360 | |
| 361 | case MMToken::ExportAsKeyword: |
| 362 | if (!TopLevel) { |
| 363 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(LeadingToken: MMToken::TextualKeyword, LeadingLoc: consumeToken()); |
| 379 | break; |
| 380 | |
| 381 | case MMToken::UmbrellaKeyword: { |
| 382 | SourceLocation UmbrellaLoc = consumeToken(); |
| 383 | if (Tok.is(K: MMToken::HeaderKeyword)) |
| 384 | SubDecl = parseHeaderDecl(LeadingToken: MMToken::UmbrellaKeyword, LeadingLoc: UmbrellaLoc); |
| 385 | else |
| 386 | SubDecl = parseUmbrellaDirDecl(UmbrellaLoc); |
| 387 | break; |
| 388 | } |
| 389 | |
| 390 | case MMToken::ExcludeKeyword: { |
| 391 | SourceLocation ExcludeLoc = consumeToken(); |
| 392 | if (Tok.is(K: MMToken::HeaderKeyword)) |
| 393 | SubDecl = parseHeaderDecl(LeadingToken: MMToken::ExcludeKeyword, LeadingLoc: ExcludeLoc); |
| 394 | else |
| 395 | SubDecl = parseExcludeDecl(LeadingLoc: ExcludeLoc); |
| 396 | break; |
| 397 | } |
| 398 | |
| 399 | case MMToken::PrivateKeyword: |
| 400 | SubDecl = parseHeaderDecl(LeadingToken: MMToken::PrivateKeyword, LeadingLoc: consumeToken()); |
| 401 | break; |
| 402 | |
| 403 | case MMToken::HeaderKeyword: |
| 404 | SubDecl = parseHeaderDecl(LeadingToken: MMToken::HeaderKeyword, LeadingLoc: consumeToken()); |
| 405 | break; |
| 406 | |
| 407 | case MMToken::LinkKeyword: |
| 408 | SubDecl = parseLinkDecl(); |
| 409 | break; |
| 410 | |
| 411 | default: |
| 412 | Diags.Report(Loc: Tok.getLocation(), DiagID: diag::err_mmap_expected_member); |
| 413 | consumeToken(); |
| 414 | break; |
| 415 | } |
| 416 | if (SubDecl) |
| 417 | MDecl.Decls.push_back(x: std::move(*SubDecl)); |
| 418 | } while (!Done); |
| 419 | |
| 420 | if (Tok.is(K: MMToken::RBrace)) |
| 421 | consumeToken(); |
| 422 | else { |
| 423 | Diags.Report(Loc: Tok.getLocation(), DiagID: diag::err_mmap_expected_rbrace); |
| 424 | Diags.Report(Loc: LBraceLoc, DiagID: 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(K: MMToken::ModuleKeyword)) { |
| 437 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(Id&: EMD.Id)) { |
| 446 | HadError = true; |
| 447 | return std::nullopt; |
| 448 | } |
| 449 | |
| 450 | // Parse the referenced module map file name. |
| 451 | if (!Tok.is(K: MMToken::StringLiteral)) { |
| 452 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::Identifier)) |
| 484 | return std::nullopt; |
| 485 | |
| 486 | // Consume the first identifier. |
| 487 | CMDecl.Macros.push_back(x: Tok.getString()); |
| 488 | consumeToken(); |
| 489 | |
| 490 | do { |
| 491 | // If there's a comma, consume it. |
| 492 | if (!Tok.is(K: 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(K: MMToken::Identifier)) { |
| 499 | Diags.Report(Loc: Tok.getLocation(), DiagID: diag::err_mmap_expected_config_macro); |
| 500 | return std::nullopt; |
| 501 | } |
| 502 | |
| 503 | // Consume the macro name. |
| 504 | CMDecl.Macros.push_back(x: 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(Id&: CD.Id)) |
| 521 | return std::nullopt; |
| 522 | |
| 523 | // Parse the ','. |
| 524 | if (!Tok.is(K: MMToken::Comma)) { |
| 525 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::StringLiteral)) { |
| 533 | Diags.Report(Loc: Tok.getLocation(), DiagID: diag::err_mmap_expected_conflicts_message) |
| 534 | << formatModuleId(Id: 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(K: MMToken::Identifier)) { |
| 561 | ED.Id.push_back( |
| 562 | Elt: std::make_pair(x: std::string(Tok.getString()), y: Tok.getLocation())); |
| 563 | consumeToken(); |
| 564 | |
| 565 | if (Tok.is(K: MMToken::Period)) { |
| 566 | consumeToken(); |
| 567 | continue; |
| 568 | } |
| 569 | |
| 570 | break; |
| 571 | } |
| 572 | |
| 573 | if (Tok.is(K: MMToken::Star)) { |
| 574 | ED.Wildcard = true; |
| 575 | consumeToken(); |
| 576 | break; |
| 577 | } |
| 578 | |
| 579 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::Identifier)) { |
| 597 | Diags.Report(Loc: Tok.getLocation(), DiagID: diag::err_mmap_module_id); |
| 598 | HadError = true; |
| 599 | return std::nullopt; |
| 600 | } |
| 601 | |
| 602 | if (parseModuleId(Id&: EAD.Id)) |
| 603 | return std::nullopt; |
| 604 | if (EAD.Id.size() > 1) |
| 605 | Diags.Report(Loc: EAD.Id[1].second, DiagID: 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(Id&: 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(K: MMToken::Exclaim)) { |
| 642 | RequiredState = false; |
| 643 | consumeToken(); |
| 644 | } |
| 645 | |
| 646 | if (!Tok.is(K: MMToken::Identifier)) { |
| 647 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(x: std::move(RF)); |
| 659 | |
| 660 | if (!Tok.is(K: 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::(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(K: 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(K: MMToken::HeaderKeyword)) { |
| 701 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::StringLiteral)) { |
| 713 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::LBrace)) { |
| 724 | SourceLocation LBraceLoc = consumeToken(); |
| 725 | |
| 726 | while (!Tok.is(K: MMToken::RBrace) && !Tok.is(K: 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(S: "size" , Value: Size) |
| 732 | .Case(S: "mtime" , Value: ModTime) |
| 733 | .Default(Value: Unknown)) { |
| 734 | case Size: |
| 735 | if (HD.Size) |
| 736 | Diags.Report(Loc, DiagID: diag::err_mmap_duplicate_header_attribute) << Str; |
| 737 | if (!Tok.is(K: MMToken::IntegerLiteral)) { |
| 738 | Diags.Report(Loc: Tok.getLocation(), |
| 739 | DiagID: diag::err_mmap_invalid_header_attribute_value) |
| 740 | << Str; |
| 741 | skipUntil(K: 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, DiagID: diag::err_mmap_duplicate_header_attribute) << Str; |
| 751 | if (!Tok.is(K: MMToken::IntegerLiteral)) { |
| 752 | Diags.Report(Loc: Tok.getLocation(), |
| 753 | DiagID: diag::err_mmap_invalid_header_attribute_value) |
| 754 | << Str; |
| 755 | skipUntil(K: MMToken::RBrace); |
| 756 | break; |
| 757 | } |
| 758 | HD.MTime = Tok.getInteger(); |
| 759 | consumeToken(); |
| 760 | break; |
| 761 | |
| 762 | case Unknown: |
| 763 | Diags.Report(Loc, DiagID: diag::err_mmap_expected_header_attribute); |
| 764 | skipUntil(K: MMToken::RBrace); |
| 765 | break; |
| 766 | } |
| 767 | } |
| 768 | |
| 769 | if (Tok.is(K: MMToken::RBrace)) |
| 770 | consumeToken(); |
| 771 | else { |
| 772 | Diags.Report(Loc: Tok.getLocation(), DiagID: diag::err_mmap_expected_rbrace); |
| 773 | Diags.Report(Loc: LBraceLoc, DiagID: 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(K: MMToken::Identifier)) { |
| 788 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::StringLiteral)) { |
| 810 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::FrameworkKeyword)) { |
| 833 | consumeToken(); |
| 834 | LD.Framework = true; |
| 835 | } |
| 836 | |
| 837 | // Parse the library name |
| 838 | if (!Tok.is(K: MMToken::StringLiteral)) { |
| 839 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(Result&: 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(S: "config_macros" , Value: MMToken::ConfigMacros) |
| 865 | .Case(S: "conflict" , Value: MMToken::Conflict) |
| 866 | .Case(S: "exclude" , Value: MMToken::ExcludeKeyword) |
| 867 | .Case(S: "explicit" , Value: MMToken::ExplicitKeyword) |
| 868 | .Case(S: "export" , Value: MMToken::ExportKeyword) |
| 869 | .Case(S: "export_as" , Value: MMToken::ExportAsKeyword) |
| 870 | .Case(S: "extern" , Value: MMToken::ExternKeyword) |
| 871 | .Case(S: "framework" , Value: MMToken::FrameworkKeyword) |
| 872 | .Case(S: "header" , Value: MMToken::HeaderKeyword) |
| 873 | .Case(S: "link" , Value: MMToken::LinkKeyword) |
| 874 | .Case(S: "module" , Value: MMToken::ModuleKeyword) |
| 875 | .Case(S: "private" , Value: MMToken::PrivateKeyword) |
| 876 | .Case(S: "requires" , Value: MMToken::RequiresKeyword) |
| 877 | .Case(S: "textual" , Value: MMToken::TextualKeyword) |
| 878 | .Case(S: "umbrella" , Value: MMToken::UmbrellaKeyword) |
| 879 | .Case(S: "use" , Value: MMToken::UseKeyword) |
| 880 | .Default(Value: 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(Loc: LToken.getLocation(), DiagID: 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(Radix: 0, Result&: Value)) { |
| 939 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(Result&: LToken); |
| 960 | return !LToken.isAtStartOfLine() && LToken.is(K: 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(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::Identifier) || Tok.is(K: MMToken::StringLiteral)) { |
| 1037 | Id.push_back( |
| 1038 | Elt: std::make_pair(x: std::string(Tok.getString()), y: Tok.getLocation())); |
| 1039 | consumeToken(); |
| 1040 | } else { |
| 1041 | Diags.Report(Loc: Tok.getLocation(), DiagID: diag::err_mmap_expected_module_name); |
| 1042 | return true; |
| 1043 | } |
| 1044 | |
| 1045 | if (!Tok.is(K: 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(K: MMToken::LSquare)) { |
| 1070 | // Consume the '['. |
| 1071 | SourceLocation LSquareLoc = consumeToken(); |
| 1072 | |
| 1073 | // Check whether we have an attribute name here. |
| 1074 | if (!Tok.is(K: MMToken::Identifier)) { |
| 1075 | Diags.Report(Loc: Tok.getLocation(), DiagID: diag::err_mmap_expected_attribute); |
| 1076 | skipUntil(K: MMToken::RSquare); |
| 1077 | if (Tok.is(K: 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(S: "exhaustive" , Value: AT_exhaustive) |
| 1104 | .Case(S: "extern_c" , Value: AT_extern_c) |
| 1105 | .Case(S: "no_undeclared_includes" , Value: AT_no_undeclared_includes) |
| 1106 | .Case(S: "system" , Value: AT_system) |
| 1107 | .Default(Value: AT_unknown); |
| 1108 | switch (Attribute) { |
| 1109 | case AT_unknown: |
| 1110 | Diags.Report(Loc: Tok.getLocation(), DiagID: 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(K: MMToken::RSquare)) { |
| 1134 | Diags.Report(Loc: Tok.getLocation(), DiagID: diag::err_mmap_expected_rsquare); |
| 1135 | Diags.Report(Loc: LSquareLoc, DiagID: diag::note_mmap_lsquare_match); |
| 1136 | skipUntil(K: MMToken::RSquare); |
| 1137 | Error = true; |
| 1138 | } |
| 1139 | |
| 1140 | if (Tok.is(K: 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(NumSpaces: depth * 2); |
| 1155 | out << "extern module " << formatModuleId(Id: EMD.Id) << " \"" << EMD.Path |
| 1156 | << "\"\n" ; |
| 1157 | } |
| 1158 | |
| 1159 | static void (ArrayRef<Decl> Decls, llvm::raw_ostream &out, int depth) { |
| 1160 | for (const auto &Decl : Decls) { |
| 1161 | std::visit(visitor: llvm::makeVisitor( |
| 1162 | Callables: [&](const RequiresDecl &RD) { |
| 1163 | out.indent(NumSpaces: depth * 2); |
| 1164 | out << "requires\n" ; |
| 1165 | }, |
| 1166 | Callables: [&](const HeaderDecl &HD) { |
| 1167 | out.indent(NumSpaces: 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 | Callables: [&](const UmbrellaDirDecl &UDD) { |
| 1179 | out.indent(NumSpaces: depth * 2); |
| 1180 | out << "umbrella\n" ; |
| 1181 | }, |
| 1182 | Callables: [&](const ModuleDecl &MD) { dumpModule(MD, out, depth); }, |
| 1183 | Callables: [&](const ExcludeDecl &ED) { |
| 1184 | out.indent(NumSpaces: depth * 2); |
| 1185 | out << "exclude " << ED.Module << "\n" ; |
| 1186 | }, |
| 1187 | Callables: [&](const ExportDecl &ED) { |
| 1188 | out.indent(NumSpaces: depth * 2); |
| 1189 | out << "export " |
| 1190 | << (ED.Wildcard ? "*" : formatModuleId(Id: ED.Id)) << "\n" ; |
| 1191 | }, |
| 1192 | Callables: [&](const ExportAsDecl &EAD) { |
| 1193 | out.indent(NumSpaces: depth * 2); |
| 1194 | out << "export as\n" ; |
| 1195 | }, |
| 1196 | Callables: [&](const ExternModuleDecl &EMD) { |
| 1197 | dumpExternModule(EMD, out, depth); |
| 1198 | }, |
| 1199 | Callables: [&](const UseDecl &UD) { |
| 1200 | out.indent(NumSpaces: depth * 2); |
| 1201 | out << "use\n" ; |
| 1202 | }, |
| 1203 | Callables: [&](const LinkDecl &LD) { |
| 1204 | out.indent(NumSpaces: depth * 2); |
| 1205 | out << "link\n" ; |
| 1206 | }, |
| 1207 | Callables: [&](const ConfigMacrosDecl &CMD) { |
| 1208 | out.indent(NumSpaces: 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 | Callables: [&](const ConflictDecl &CD) { |
| 1218 | out.indent(NumSpaces: depth * 2); |
| 1219 | out << "conflicts\n" ; |
| 1220 | }), |
| 1221 | variants: Decl); |
| 1222 | } |
| 1223 | } |
| 1224 | |
| 1225 | static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out, |
| 1226 | int depth) { |
| 1227 | out.indent(NumSpaces: depth * 2); |
| 1228 | out << "module " << formatModuleId(Id: MD.Id) << "\n" ; |
| 1229 | dumpDecls(Decls: MD.Decls, out, depth: depth + 1); |
| 1230 | } |
| 1231 | |
| 1232 | void ModuleMapFile::dump(llvm::raw_ostream &out) const { |
| 1233 | for (const auto &Decl : Decls) { |
| 1234 | std::visit( |
| 1235 | visitor: llvm::makeVisitor(Callables: [&](const ModuleDecl &MD) { dumpModule(MD, out, depth: 0); }, |
| 1236 | Callables: [&](const ExternModuleDecl &EMD) { |
| 1237 | dumpExternModule(EMD, out, depth: 0); |
| 1238 | }), |
| 1239 | variants: Decl); |
| 1240 | } |
| 1241 | } |
| 1242 | |