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