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(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 | |
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(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 | |
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(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 |
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(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 | |
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(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)? |
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(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 |
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(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 |
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(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 |
591 | std::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 |
613 | std::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 |
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(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 |
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(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 |
784 | std::optional<ExcludeDecl> |
785 | ModuleMapFileParser::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 |
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(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 |
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(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 | |
850 | SourceLocation ModuleMapFileParser::consumeToken() { |
851 | SourceLocation Result = Tok.getLocation(); |
852 | |
853 | retry: |
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 | |
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(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. |
1066 | bool 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 | |
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(NumSpaces: depth * 2); |
1155 | out << "extern module "<< formatModuleId(Id: 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(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 | |
1225 | static 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 | |
1232 | void 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 |
Definitions
- MMToken
- TokenKind
- clear
- is
- getLocation
- getInteger
- getString
- ModuleMapFileParser
- getLocation
- formatModuleId
- parseModuleMap
- parseTopLevelDecls
- parseModuleDecl
- parseExternModuleDecl
- parseConfigMacrosDecl
- parseConflictDecl
- parseExportDecl
- parseExportAsDecl
- parseUseDecl
- parseRequiresDecl
- parseHeaderDecl
- parseExcludeDecl
- parseUmbrellaDirDecl
- parseLinkDecl
- consumeToken
- skipUntil
- parseModuleId
- parseOptionalAttributes
- dumpExternModule
- dumpDecls
- dumpModule
Improve your Profiling and Debugging skills
Find out more