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
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(Encoding: Location);
82 }
83
84 uint64_t getInteger() const {
85 return Kind == IntegerLiteral ? IntegerValue : 0;
86 }
87
88 StringRef getString() const {
89 return Kind == IntegerLiteral ? StringRef()
90 : StringRef(StringData, StringLength);
91 }
92};
93
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>
148modulemap::parseModuleMap(FileID ID, clang::DirectoryEntryRef Dir,
149 SourceManager &SM, DiagnosticsEngine &Diags,
150 bool IsSystem, unsigned *Offset) {
151 std::optional<llvm::MemoryBufferRef> Buffer = SM.getBufferOrNone(FID: ID);
152 LangOptions LOpts;
153 LOpts.LangStd = clang::LangStandard::lang_c99;
154 Lexer L(SM.getLocForStartOfFile(FID: ID), LOpts, Buffer->getBufferStart(),
155 Buffer->getBufferStart() + (Offset ? *Offset : 0),
156 Buffer->getBufferEnd());
157 SourceLocation Start = L.getSourceLocation();
158
159 ModuleMapFileParser Parser{.L: L, .Diags: Diags};
160 bool Failed = Parser.parseTopLevelDecls();
161
162 if (Offset) {
163 auto Loc = SM.getDecomposedLoc(Loc: Parser.getLocation());
164 assert(Loc.first == ID && "stopped in a different file?");
165 *Offset = Loc.second;
166 }
167
168 if (Failed)
169 return std::nullopt;
170 Parser.MMF.ID = ID;
171 Parser.MMF.Dir = Dir;
172 Parser.MMF.Start = Start;
173 Parser.MMF.IsSystem = IsSystem;
174 return std::move(Parser.MMF);
175}
176
177bool ModuleMapFileParser::parseTopLevelDecls() {
178 Tok.clear();
179 consumeToken();
180 do {
181 switch (Tok.Kind) {
182 case MMToken::EndOfFile:
183 return HadError;
184 case MMToken::ExternKeyword: {
185 std::optional<ExternModuleDecl> EMD = parseExternModuleDecl();
186 if (EMD)
187 MMF.Decls.push_back(x: std::move(*EMD));
188 break;
189 }
190 case MMToken::ExplicitKeyword:
191 case MMToken::ModuleKeyword:
192 case MMToken::FrameworkKeyword: {
193 std::optional<ModuleDecl> MD = parseModuleDecl(TopLevel: true);
194 if (MD)
195 MMF.Decls.push_back(x: std::move(*MD));
196 break;
197 }
198 case MMToken::Comma:
199 case MMToken::ConfigMacros:
200 case MMToken::Conflict:
201 case MMToken::Exclaim:
202 case MMToken::ExcludeKeyword:
203 case MMToken::ExportKeyword:
204 case MMToken::ExportAsKeyword:
205 case MMToken::HeaderKeyword:
206 case MMToken::Identifier:
207 case MMToken::LBrace:
208 case MMToken::LinkKeyword:
209 case MMToken::LSquare:
210 case MMToken::Period:
211 case MMToken::PrivateKeyword:
212 case MMToken::RBrace:
213 case MMToken::RSquare:
214 case MMToken::RequiresKeyword:
215 case MMToken::Star:
216 case MMToken::StringLiteral:
217 case MMToken::IntegerLiteral:
218 case MMToken::TextualKeyword:
219 case MMToken::UmbrellaKeyword:
220 case MMToken::UseKeyword:
221 Diags.Report(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
247std::optional<ModuleDecl> ModuleMapFileParser::parseModuleDecl(bool TopLevel) {
248 assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword) ||
249 Tok.is(MMToken::FrameworkKeyword));
250
251 ModuleDecl MDecl;
252
253 SourceLocation ExplicitLoc;
254 MDecl.Explicit = false;
255 MDecl.Framework = false;
256
257 // Parse 'explicit' keyword, if present.
258 if (Tok.is(K: MMToken::ExplicitKeyword)) {
259 MDecl.Location = ExplicitLoc = consumeToken();
260 MDecl.Explicit = true;
261 }
262
263 // Parse 'framework' keyword, if present.
264 if (Tok.is(K: MMToken::FrameworkKeyword)) {
265 SourceLocation FrameworkLoc = consumeToken();
266 if (!MDecl.Location.isValid())
267 MDecl.Location = FrameworkLoc;
268 MDecl.Framework = true;
269 }
270
271 // Parse 'module' keyword.
272 if (!Tok.is(K: MMToken::ModuleKeyword)) {
273 Diags.Report(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(K: MMToken::Star)) {
285 SourceLocation StarLoc = consumeToken();
286 MDecl.Id.push_back(Elt: {"*", 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(Id&: 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(Attrs&: MDecl.Attrs))
316 return std::nullopt;
317
318 // Parse the opening brace.
319 if (!Tok.is(K: 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(TopLevel: false);
355 break;
356
357 case MMToken::ExportKeyword:
358 SubDecl = parseExportDecl();
359 break;
360
361 case MMToken::ExportAsKeyword:
362 if (!TopLevel) {
363 Diags.Report(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(LeadingToken: MMToken::TextualKeyword, LeadingLoc: consumeToken());
379 break;
380
381 case MMToken::UmbrellaKeyword: {
382 SourceLocation UmbrellaLoc = consumeToken();
383 if (Tok.is(K: MMToken::HeaderKeyword))
384 SubDecl = parseHeaderDecl(LeadingToken: MMToken::UmbrellaKeyword, LeadingLoc: UmbrellaLoc);
385 else
386 SubDecl = parseUmbrellaDirDecl(UmbrellaLoc);
387 break;
388 }
389
390 case MMToken::ExcludeKeyword: {
391 SourceLocation ExcludeLoc = consumeToken();
392 if (Tok.is(K: MMToken::HeaderKeyword))
393 SubDecl = parseHeaderDecl(LeadingToken: MMToken::ExcludeKeyword, LeadingLoc: ExcludeLoc);
394 else
395 SubDecl = parseExcludeDecl(LeadingLoc: ExcludeLoc);
396 break;
397 }
398
399 case MMToken::PrivateKeyword:
400 SubDecl = parseHeaderDecl(LeadingToken: MMToken::PrivateKeyword, LeadingLoc: consumeToken());
401 break;
402
403 case MMToken::HeaderKeyword:
404 SubDecl = parseHeaderDecl(LeadingToken: MMToken::HeaderKeyword, LeadingLoc: consumeToken());
405 break;
406
407 case MMToken::LinkKeyword:
408 SubDecl = parseLinkDecl();
409 break;
410
411 default:
412 Diags.Report(Tok.getLocation(), diag::err_mmap_expected_member);
413 consumeToken();
414 break;
415 }
416 if (SubDecl)
417 MDecl.Decls.push_back(x: std::move(*SubDecl));
418 } while (!Done);
419
420 if (Tok.is(K: MMToken::RBrace))
421 consumeToken();
422 else {
423 Diags.Report(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
430std::optional<ExternModuleDecl> ModuleMapFileParser::parseExternModuleDecl() {
431 assert(Tok.is(MMToken::ExternKeyword));
432 ExternModuleDecl EMD;
433 EMD.Location = consumeToken(); // 'extern' keyword
434
435 // Parse 'module' keyword.
436 if (!Tok.is(K: MMToken::ModuleKeyword)) {
437 Diags.Report(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(Id&: EMD.Id)) {
446 HadError = true;
447 return std::nullopt;
448 }
449
450 // Parse the referenced module map file name.
451 if (!Tok.is(K: MMToken::StringLiteral)) {
452 Diags.Report(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)?
469std::optional<ConfigMacrosDecl> ModuleMapFileParser::parseConfigMacrosDecl() {
470 assert(Tok.is(MMToken::ConfigMacros));
471 ConfigMacrosDecl CMDecl;
472 CMDecl.Location = consumeToken();
473
474 // Parse the optional attributes.
475 ModuleAttributes Attrs;
476 if (parseOptionalAttributes(Attrs))
477 return std::nullopt;
478
479 CMDecl.Exhaustive = Attrs.IsExhaustive;
480
481 // If we don't have an identifier, we're done.
482 // FIXME: Support macros with the same name as a keyword here.
483 if (!Tok.is(K: MMToken::Identifier))
484 return std::nullopt;
485
486 // Consume the first identifier.
487 CMDecl.Macros.push_back(x: Tok.getString());
488 consumeToken();
489
490 do {
491 // If there's a comma, consume it.
492 if (!Tok.is(K: MMToken::Comma))
493 break;
494 consumeToken();
495
496 // We expect to see a macro name here.
497 // FIXME: Support macros with the same name as a keyword here.
498 if (!Tok.is(K: MMToken::Identifier)) {
499 Diags.Report(Tok.getLocation(), diag::err_mmap_expected_config_macro);
500 return std::nullopt;
501 }
502
503 // Consume the macro name.
504 CMDecl.Macros.push_back(x: Tok.getString());
505 consumeToken();
506 } while (true);
507 return std::move(CMDecl);
508}
509
510/// Parse a conflict declaration.
511///
512/// module-declaration:
513/// 'conflict' module-id ',' string-literal
514std::optional<ConflictDecl> ModuleMapFileParser::parseConflictDecl() {
515 assert(Tok.is(MMToken::Conflict));
516 ConflictDecl CD;
517 CD.Location = consumeToken();
518
519 // Parse the module-id.
520 if (parseModuleId(Id&: CD.Id))
521 return std::nullopt;
522
523 // Parse the ','.
524 if (!Tok.is(K: MMToken::Comma)) {
525 Diags.Report(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(K: MMToken::StringLiteral)) {
533 Diags.Report(Tok.getLocation(), diag::err_mmap_expected_conflicts_message)
534 << formatModuleId(Id: CD.Id);
535 return std::nullopt;
536 }
537 CD.Message = Tok.getString();
538 consumeToken();
539 return std::move(CD);
540}
541
542/// Parse a module export declaration.
543///
544/// export-declaration:
545/// 'export' wildcard-module-id
546///
547/// wildcard-module-id:
548/// identifier
549/// '*'
550/// identifier '.' wildcard-module-id
551std::optional<ExportDecl> ModuleMapFileParser::parseExportDecl() {
552 assert(Tok.is(MMToken::ExportKeyword));
553 ExportDecl ED;
554 ED.Location = consumeToken();
555
556 // Parse the module-id with an optional wildcard at the end.
557 ED.Wildcard = false;
558 do {
559 // FIXME: Support string-literal module names here.
560 if (Tok.is(K: MMToken::Identifier)) {
561 ED.Id.push_back(
562 Elt: std::make_pair(x: std::string(Tok.getString()), y: Tok.getLocation()));
563 consumeToken();
564
565 if (Tok.is(K: MMToken::Period)) {
566 consumeToken();
567 continue;
568 }
569
570 break;
571 }
572
573 if (Tok.is(K: MMToken::Star)) {
574 ED.Wildcard = true;
575 consumeToken();
576 break;
577 }
578
579 Diags.Report(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
591std::optional<ExportAsDecl> ModuleMapFileParser::parseExportAsDecl() {
592 assert(Tok.is(MMToken::ExportAsKeyword));
593 ExportAsDecl EAD;
594 EAD.Location = consumeToken();
595
596 if (!Tok.is(K: MMToken::Identifier)) {
597 Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);
598 HadError = true;
599 return std::nullopt;
600 }
601
602 if (parseModuleId(Id&: EAD.Id))
603 return std::nullopt;
604 if (EAD.Id.size() > 1)
605 Diags.Report(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
613std::optional<UseDecl> ModuleMapFileParser::parseUseDecl() {
614 assert(Tok.is(MMToken::UseKeyword));
615 UseDecl UD;
616 UD.Location = consumeToken();
617 if (parseModuleId(Id&: UD.Id))
618 return std::nullopt;
619 return std::move(UD);
620}
621
622/// Parse a requires declaration.
623///
624/// requires-declaration:
625/// 'requires' feature-list
626///
627/// feature-list:
628/// feature ',' feature-list
629/// feature
630///
631/// feature:
632/// '!'[opt] identifier
633std::optional<RequiresDecl> ModuleMapFileParser::parseRequiresDecl() {
634 assert(Tok.is(MMToken::RequiresKeyword));
635 RequiresDecl RD;
636 RD.Location = consumeToken();
637
638 // Parse the feature-list.
639 do {
640 bool RequiredState = true;
641 if (Tok.is(K: MMToken::Exclaim)) {
642 RequiredState = false;
643 consumeToken();
644 }
645
646 if (!Tok.is(K: MMToken::Identifier)) {
647 Diags.Report(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(x: std::move(RF));
659
660 if (!Tok.is(K: MMToken::Comma))
661 break;
662
663 // Consume the comma.
664 consumeToken();
665 } while (true);
666 return std::move(RD);
667}
668
669/// Parse a header declaration.
670///
671/// header-declaration:
672/// 'textual'[opt] 'header' string-literal
673/// 'private' 'textual'[opt] 'header' string-literal
674/// 'exclude' 'header' string-literal
675/// 'umbrella' 'header' string-literal
676std::optional<HeaderDecl>
677ModuleMapFileParser::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(K: MMToken::TextualKeyword)) {
690 HD.Textual = true;
691 LeadingToken = Tok.Kind;
692 consumeToken();
693 }
694 } else if (LeadingToken == MMToken::ExcludeKeyword)
695 HD.Excluded = true;
696 else if (LeadingToken == MMToken::TextualKeyword)
697 HD.Textual = true;
698
699 if (LeadingToken != MMToken::HeaderKeyword) {
700 if (!Tok.is(K: MMToken::HeaderKeyword)) {
701 Diags.Report(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(K: 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(K: MMToken::LBrace)) {
724 SourceLocation LBraceLoc = consumeToken();
725
726 while (!Tok.is(K: MMToken::RBrace) && !Tok.is(K: MMToken::EndOfFile)) {
727 enum Attribute { Size, ModTime, Unknown };
728 StringRef Str = Tok.getString();
729 SourceLocation Loc = consumeToken();
730 switch (llvm::StringSwitch<Attribute>(Str)
731 .Case(S: "size", Value: Size)
732 .Case(S: "mtime", Value: ModTime)
733 .Default(Value: Unknown)) {
734 case Size:
735 if (HD.Size)
736 Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;
737 if (!Tok.is(K: MMToken::IntegerLiteral)) {
738 Diags.Report(Tok.getLocation(),
739 diag::err_mmap_invalid_header_attribute_value)
740 << Str;
741 skipUntil(K: MMToken::RBrace);
742 break;
743 }
744 HD.Size = Tok.getInteger();
745 consumeToken();
746 break;
747
748 case ModTime:
749 if (HD.MTime)
750 Diags.Report(Loc, diag::err_mmap_duplicate_header_attribute) << Str;
751 if (!Tok.is(K: MMToken::IntegerLiteral)) {
752 Diags.Report(Tok.getLocation(),
753 diag::err_mmap_invalid_header_attribute_value)
754 << Str;
755 skipUntil(K: MMToken::RBrace);
756 break;
757 }
758 HD.MTime = Tok.getInteger();
759 consumeToken();
760 break;
761
762 case Unknown:
763 Diags.Report(Loc, diag::err_mmap_expected_header_attribute);
764 skipUntil(K: MMToken::RBrace);
765 break;
766 }
767 }
768
769 if (Tok.is(K: MMToken::RBrace))
770 consumeToken();
771 else {
772 Diags.Report(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
784std::optional<ExcludeDecl>
785ModuleMapFileParser::parseExcludeDecl(clang::SourceLocation LeadingLoc) {
786 // FIXME: Support string-literal module names here.
787 if (!Tok.is(K: 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
804std::optional<UmbrellaDirDecl>
805ModuleMapFileParser::parseUmbrellaDirDecl(clang::SourceLocation UmbrellaLoc) {
806 UmbrellaDirDecl UDD;
807 UDD.Location = UmbrellaLoc;
808 // Parse the directory name.
809 if (!Tok.is(K: MMToken::StringLiteral)) {
810 Diags.Report(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
825std::optional<LinkDecl> ModuleMapFileParser::parseLinkDecl() {
826 assert(Tok.is(MMToken::LinkKeyword));
827 LinkDecl LD;
828 LD.Location = consumeToken();
829
830 // Parse the optional 'framework' keyword.
831 LD.Framework = false;
832 if (Tok.is(K: MMToken::FrameworkKeyword)) {
833 consumeToken();
834 LD.Framework = true;
835 }
836
837 // Parse the library name
838 if (!Tok.is(K: MMToken::StringLiteral)) {
839 Diags.Report(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
850SourceLocation ModuleMapFileParser::consumeToken() {
851 SourceLocation Result = Tok.getLocation();
852
853retry:
854 Tok.clear();
855 Token LToken;
856 L.LexFromRawLexer(Result&: LToken);
857 Tok.Location = LToken.getLocation().getRawEncoding();
858 switch (LToken.getKind()) {
859 case tok::raw_identifier: {
860 StringRef RI = LToken.getRawIdentifier();
861 Tok.StringData = RI.data();
862 Tok.StringLength = RI.size();
863 Tok.Kind = llvm::StringSwitch<MMToken::TokenKind>(RI)
864 .Case(S: "config_macros", Value: MMToken::ConfigMacros)
865 .Case(S: "conflict", Value: MMToken::Conflict)
866 .Case(S: "exclude", Value: MMToken::ExcludeKeyword)
867 .Case(S: "explicit", Value: MMToken::ExplicitKeyword)
868 .Case(S: "export", Value: MMToken::ExportKeyword)
869 .Case(S: "export_as", Value: MMToken::ExportAsKeyword)
870 .Case(S: "extern", Value: MMToken::ExternKeyword)
871 .Case(S: "framework", Value: MMToken::FrameworkKeyword)
872 .Case(S: "header", Value: MMToken::HeaderKeyword)
873 .Case(S: "link", Value: MMToken::LinkKeyword)
874 .Case(S: "module", Value: MMToken::ModuleKeyword)
875 .Case(S: "private", Value: MMToken::PrivateKeyword)
876 .Case(S: "requires", Value: MMToken::RequiresKeyword)
877 .Case(S: "textual", Value: MMToken::TextualKeyword)
878 .Case(S: "umbrella", Value: MMToken::UmbrellaKeyword)
879 .Case(S: "use", Value: MMToken::UseKeyword)
880 .Default(Value: MMToken::Identifier);
881 break;
882 }
883
884 case tok::comma:
885 Tok.Kind = MMToken::Comma;
886 break;
887
888 case tok::eof:
889 Tok.Kind = MMToken::EndOfFile;
890 break;
891
892 case tok::l_brace:
893 Tok.Kind = MMToken::LBrace;
894 break;
895
896 case tok::l_square:
897 Tok.Kind = MMToken::LSquare;
898 break;
899
900 case tok::period:
901 Tok.Kind = MMToken::Period;
902 break;
903
904 case tok::r_brace:
905 Tok.Kind = MMToken::RBrace;
906 break;
907
908 case tok::r_square:
909 Tok.Kind = MMToken::RSquare;
910 break;
911
912 case tok::star:
913 Tok.Kind = MMToken::Star;
914 break;
915
916 case tok::exclaim:
917 Tok.Kind = MMToken::Exclaim;
918 break;
919
920 case tok::string_literal: {
921 if (LToken.hasUDSuffix()) {
922 Diags.Report(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(Radix: 0, Result&: 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(Result&: LToken);
960 return !LToken.isAtStartOfLine() && LToken.is(K: tok::raw_identifier) &&
961 LToken.getRawIdentifier() == Str;
962 };
963 if (NextIsIdent("pragma") && NextIsIdent("clang") &&
964 NextIsIdent("module") && NextIsIdent("contents")) {
965 Tok.Kind = MMToken::EndOfFile;
966 break;
967 }
968 }
969 [[fallthrough]];
970
971 default:
972 Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token);
973 HadError = true;
974 goto retry;
975 }
976
977 return Result;
978}
979
980void 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.
1033bool ModuleMapFileParser::parseModuleId(ModuleId &Id) {
1034 Id.clear();
1035 do {
1036 if (Tok.is(K: MMToken::Identifier) || Tok.is(K: MMToken::StringLiteral)) {
1037 Id.push_back(
1038 Elt: std::make_pair(x: std::string(Tok.getString()), y: Tok.getLocation()));
1039 consumeToken();
1040 } else {
1041 Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module_name);
1042 return true;
1043 }
1044
1045 if (!Tok.is(K: MMToken::Period))
1046 break;
1047
1048 consumeToken();
1049 } while (true);
1050
1051 return false;
1052}
1053
1054/// Parse optional attributes.
1055///
1056/// attributes:
1057/// attribute attributes
1058/// attribute
1059///
1060/// attribute:
1061/// [ identifier ]
1062///
1063/// \param Attrs Will be filled in with the parsed attributes.
1064///
1065/// \returns true if an error occurred, false otherwise.
1066bool ModuleMapFileParser::parseOptionalAttributes(ModuleAttributes &Attrs) {
1067 bool Error = false;
1068
1069 while (Tok.is(K: MMToken::LSquare)) {
1070 // Consume the '['.
1071 SourceLocation LSquareLoc = consumeToken();
1072
1073 // Check whether we have an attribute name here.
1074 if (!Tok.is(K: MMToken::Identifier)) {
1075 Diags.Report(Tok.getLocation(), diag::err_mmap_expected_attribute);
1076 skipUntil(K: MMToken::RSquare);
1077 if (Tok.is(K: MMToken::RSquare))
1078 consumeToken();
1079 Error = true;
1080 }
1081
1082 /// Enumerates the known attributes.
1083 enum AttributeKind {
1084 /// An unknown attribute.
1085 AT_unknown,
1086
1087 /// The 'system' attribute.
1088 AT_system,
1089
1090 /// The 'extern_c' attribute.
1091 AT_extern_c,
1092
1093 /// The 'exhaustive' attribute.
1094 AT_exhaustive,
1095
1096 /// The 'no_undeclared_includes' attribute.
1097 AT_no_undeclared_includes
1098 };
1099
1100 // Decode the attribute name.
1101 AttributeKind Attribute =
1102 llvm::StringSwitch<AttributeKind>(Tok.getString())
1103 .Case(S: "exhaustive", Value: AT_exhaustive)
1104 .Case(S: "extern_c", Value: AT_extern_c)
1105 .Case(S: "no_undeclared_includes", Value: AT_no_undeclared_includes)
1106 .Case(S: "system", Value: AT_system)
1107 .Default(Value: AT_unknown);
1108 switch (Attribute) {
1109 case AT_unknown:
1110 Diags.Report(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(K: MMToken::RSquare)) {
1134 Diags.Report(Tok.getLocation(), diag::err_mmap_expected_rsquare);
1135 Diags.Report(LSquareLoc, diag::note_mmap_lsquare_match);
1136 skipUntil(K: MMToken::RSquare);
1137 Error = true;
1138 }
1139
1140 if (Tok.is(K: MMToken::RSquare))
1141 consumeToken();
1142 }
1143
1144 if (Error)
1145 HadError = true;
1146
1147 return Error;
1148}
1149
1150static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out, int depth);
1151
1152static void dumpExternModule(const ExternModuleDecl &EMD,
1153 llvm::raw_ostream &out, int depth) {
1154 out.indent(NumSpaces: depth * 2);
1155 out << "extern module " << formatModuleId(Id: EMD.Id) << " \"" << EMD.Path
1156 << "\"\n";
1157}
1158
1159static void dumpDecls(ArrayRef<Decl> Decls, llvm::raw_ostream &out, int depth) {
1160 for (const auto &Decl : Decls) {
1161 std::visit(visitor: llvm::makeVisitor(
1162 Callables: [&](const RequiresDecl &RD) {
1163 out.indent(NumSpaces: depth * 2);
1164 out << "requires\n";
1165 },
1166 Callables: [&](const HeaderDecl &HD) {
1167 out.indent(NumSpaces: depth * 2);
1168 if (HD.Private)
1169 out << "private ";
1170 if (HD.Textual)
1171 out << "textual ";
1172 if (HD.Excluded)
1173 out << "excluded ";
1174 if (HD.Umbrella)
1175 out << "umbrella ";
1176 out << "header \"" << HD.Path << "\"\n";
1177 },
1178 Callables: [&](const UmbrellaDirDecl &UDD) {
1179 out.indent(NumSpaces: depth * 2);
1180 out << "umbrella\n";
1181 },
1182 Callables: [&](const ModuleDecl &MD) { dumpModule(MD, out, depth); },
1183 Callables: [&](const ExcludeDecl &ED) {
1184 out.indent(NumSpaces: depth * 2);
1185 out << "exclude " << ED.Module << "\n";
1186 },
1187 Callables: [&](const ExportDecl &ED) {
1188 out.indent(NumSpaces: depth * 2);
1189 out << "export "
1190 << (ED.Wildcard ? "*" : formatModuleId(Id: ED.Id)) << "\n";
1191 },
1192 Callables: [&](const ExportAsDecl &EAD) {
1193 out.indent(NumSpaces: depth * 2);
1194 out << "export as\n";
1195 },
1196 Callables: [&](const ExternModuleDecl &EMD) {
1197 dumpExternModule(EMD, out, depth);
1198 },
1199 Callables: [&](const UseDecl &UD) {
1200 out.indent(NumSpaces: depth * 2);
1201 out << "use\n";
1202 },
1203 Callables: [&](const LinkDecl &LD) {
1204 out.indent(NumSpaces: depth * 2);
1205 out << "link\n";
1206 },
1207 Callables: [&](const ConfigMacrosDecl &CMD) {
1208 out.indent(NumSpaces: depth * 2);
1209 out << "config_macros ";
1210 if (CMD.Exhaustive)
1211 out << "[exhaustive] ";
1212 for (auto Macro : CMD.Macros) {
1213 out << Macro << " ";
1214 }
1215 out << "\n";
1216 },
1217 Callables: [&](const ConflictDecl &CD) {
1218 out.indent(NumSpaces: depth * 2);
1219 out << "conflicts\n";
1220 }),
1221 variants: Decl);
1222 }
1223}
1224
1225static void dumpModule(const ModuleDecl &MD, llvm::raw_ostream &out,
1226 int depth) {
1227 out.indent(NumSpaces: depth * 2);
1228 out << "module " << formatModuleId(Id: MD.Id) << "\n";
1229 dumpDecls(Decls: MD.Decls, out, depth: depth + 1);
1230}
1231
1232void ModuleMapFile::dump(llvm::raw_ostream &out) const {
1233 for (const auto &Decl : Decls) {
1234 std::visit(
1235 visitor: llvm::makeVisitor(Callables: [&](const ModuleDecl &MD) { dumpModule(MD, out, depth: 0); },
1236 Callables: [&](const ExternModuleDecl &EMD) {
1237 dumpExternModule(EMD, out, depth: 0);
1238 }),
1239 variants: Decl);
1240 }
1241}
1242

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang/lib/Lex/ModuleMapFile.cpp