xref: /freebsd/contrib/llvm-project/clang/lib/Lex/ModuleMapFile.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
clear__anon5501bfd60111::MMToken71   void clear() {
72     Kind = EndOfFile;
73     Location = 0;
74     StringLength = 0;
75     StringData = nullptr;
76   }
77 
is__anon5501bfd60111::MMToken78   bool is(TokenKind K) const { return Kind == K; }
79 
getLocation__anon5501bfd60111::MMToken80   SourceLocation getLocation() const {
81     return SourceLocation::getFromRawEncoding(Location);
82   }
83 
getInteger__anon5501bfd60111::MMToken84   uint64_t getInteger() const {
85     return Kind == IntegerLiteral ? IntegerValue : 0;
86   }
87 
getString__anon5501bfd60111::MMToken88   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 
getLocation__anon5501bfd60111::ModuleMapFileParser128   SourceLocation getLocation() const { return Tok.getLocation(); };
129 };
130 
formatModuleId(const ModuleId & Id)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>
parseModuleMap(FileID ID,clang::DirectoryEntryRef Dir,SourceManager & SM,DiagnosticsEngine & Diags,bool IsSystem,unsigned * Offset)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 
parseTopLevelDecls()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
parseModuleDecl(bool TopLevel)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 
parseExternModuleDecl()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)?
parseConfigMacrosDecl()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
parseConflictDecl()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
parseExportDecl()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
parseExportAsDecl()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
parseUseDecl()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
parseRequiresDecl()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>
parseHeaderDecl(MMToken::TokenKind LeadingToken,clang::SourceLocation LeadingLoc)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>
parseExcludeDecl(clang::SourceLocation LeadingLoc)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>
parseUmbrellaDirDecl(clang::SourceLocation UmbrellaLoc)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
parseLinkDecl()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 
consumeToken()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 
skipUntil(MMToken::TokenKind K)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.
parseModuleId(ModuleId & Id)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.
parseOptionalAttributes(ModuleAttributes & Attrs)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 
dumpExternModule(const ExternModuleDecl & EMD,llvm::raw_ostream & out,int depth)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 
dumpDecls(ArrayRef<Decl> Decls,llvm::raw_ostream & out,int depth)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 
dumpModule(const ModuleDecl & MD,llvm::raw_ostream & out,int depth)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 
dump(llvm::raw_ostream & out) const1232 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