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