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