clang 23.0.0git
CommentParser.cpp
Go to the documentation of this file.
1//===--- CommentParser.cpp - Doxygen comment parser -----------------------===//
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
10#include "clang/AST/Comment.h"
16#include "llvm/Support/ErrorHandling.h"
17
18namespace clang {
19
20static inline bool isWhitespace(llvm::StringRef S) {
21 for (StringRef::const_iterator I = S.begin(), E = S.end(); I != E; ++I) {
22 if (!isWhitespace(*I))
23 return false;
24 }
25 return true;
26}
27
28namespace comments {
29
30/// Re-lexes a sequence of tok::text tokens.
32 llvm::BumpPtrAllocator &Allocator;
33 Parser &P;
34
35 /// This flag is set when there are no more tokens we can fetch from lexer.
36 bool NoMoreInterestingTokens;
37
38 /// Token buffer: tokens we have processed and lookahead.
40
41 /// A position in \c Toks.
42 struct Position {
43 const char *BufferStart;
44 const char *BufferEnd;
45 const char *BufferPtr;
46 SourceLocation BufferStartLoc;
47 unsigned CurToken;
48 };
49
50 /// Current position in Toks.
51 Position Pos;
52
53 bool isEnd() const {
54 return Pos.CurToken >= Toks.size();
55 }
56
57 /// Sets up the buffer pointers to point to current token.
58 void setupBuffer() {
59 assert(!isEnd());
60 const Token &Tok = Toks[Pos.CurToken];
61
62 Pos.BufferStart = Tok.getText().begin();
63 Pos.BufferEnd = Tok.getText().end();
64 Pos.BufferPtr = Pos.BufferStart;
65 Pos.BufferStartLoc = Tok.getLocation();
66 }
67
68 SourceLocation getSourceLocation() const {
69 const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
70 return Pos.BufferStartLoc.getLocWithOffset(CharNo);
71 }
72
73 char peek() const {
74 assert(!isEnd());
75 assert(Pos.BufferPtr != Pos.BufferEnd);
76 return *Pos.BufferPtr;
77 }
78
79 void consumeChar() {
80 assert(!isEnd());
81 assert(Pos.BufferPtr != Pos.BufferEnd);
82 Pos.BufferPtr++;
83 if (Pos.BufferPtr == Pos.BufferEnd) {
84 Pos.CurToken++;
85 if (isEnd() && !addToken())
86 return;
87
88 assert(!isEnd());
89 setupBuffer();
90 }
91 }
92
93 /// Extract a template type
94 bool lexTemplate(SmallString<32> &WordText) {
95 unsigned BracketCount = 0;
96 while (!isEnd()) {
97 const char C = peek();
98 WordText.push_back(C);
99 consumeChar();
100 switch (C) {
101 case '<': {
102 BracketCount++;
103 break;
104 }
105 case '>': {
106 BracketCount--;
107 if (!BracketCount)
108 return true;
109 break;
110 }
111 default:
112 break;
113 }
114 }
115 return false;
116 }
117
118 /// Add a token.
119 /// Returns true on success, false if there are no interesting tokens to
120 /// fetch from lexer.
121 bool addToken() {
122 if (NoMoreInterestingTokens)
123 return false;
124
125 if (P.Tok.is(tok::newline)) {
126 // If we see a single newline token between text tokens, skip it.
127 Token Newline = P.Tok;
128 P.consumeToken();
129 if (P.Tok.isNot(tok::text)) {
130 P.putBack(Newline);
131 NoMoreInterestingTokens = true;
132 return false;
133 }
134 }
135 if (P.Tok.isNot(tok::text)) {
136 NoMoreInterestingTokens = true;
137 return false;
138 }
139
140 Toks.push_back(P.Tok);
141 P.consumeToken();
142 if (Toks.size() == 1)
143 setupBuffer();
144 return true;
145 }
146
147 void consumeWhitespace() {
148 while (!isEnd()) {
149 if (isWhitespace(peek()))
150 consumeChar();
151 else
152 break;
153 }
154 }
155
156 void formTokenWithChars(Token &Result,
157 SourceLocation Loc,
158 const char *TokBegin,
159 unsigned TokLength,
160 StringRef Text) {
161 Result.setLocation(Loc);
162 Result.setKind(tok::text);
163 Result.setLength(TokLength);
164#ifndef NDEBUG
165 Result.TextPtr = "<UNSET>";
166 Result.IntVal = 7;
167#endif
168 Result.setText(Text);
169 }
170
171public:
172 TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
173 Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
174 Pos.CurToken = 0;
175 addToken();
176 }
177
178 /// Extract a type argument
180 if (isEnd())
181 return false;
182
183 // Save current position in case we need to rollback because the type is
184 // empty.
185 Position SavedPos = Pos;
186
187 // Consume any leading whitespace.
188 consumeWhitespace();
189 SmallString<32> WordText;
190 const char *WordBegin = Pos.BufferPtr;
191 SourceLocation Loc = getSourceLocation();
192
193 while (!isEnd()) {
194 const char C = peek();
195 // For non-whitespace characters we check if it's a template or otherwise
196 // continue reading the text into a word.
197 if (!isWhitespace(C)) {
198 if (C == '<') {
199 if (!lexTemplate(WordText))
200 return false;
201 } else {
202 WordText.push_back(C);
203 consumeChar();
204 }
205 } else {
206 consumeChar();
207 break;
208 }
209 }
210
211 const unsigned Length = WordText.size();
212 if (Length == 0) {
213 Pos = SavedPos;
214 return false;
215 }
216
217 char *TextPtr = Allocator.Allocate<char>(Length + 1);
218
219 memcpy(TextPtr, WordText.c_str(), Length + 1);
220 StringRef Text = StringRef(TextPtr, Length);
221
222 formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
223 return true;
224 }
225
226 // Check if this line starts with @par or \par
228 unsigned Offset = 1;
229
230 // Skip all whitespace characters at the beginning.
231 // This needs to backtrack because Pos has already advanced past the
232 // actual \par or @par command by the time this function is called.
233 while (isWhitespace(*(Pos.BufferPtr - Offset)))
234 Offset++;
235
236 // Once we've reached the whitespace, backtrack and check if the previous
237 // four characters are \par or @par.
238 llvm::StringRef LineStart(Pos.BufferPtr - Offset - 3, 4);
239 return LineStart.starts_with("\\par") || LineStart.starts_with("@par");
240 }
241
242 /// Extract a par command argument-header.
244 if (isEnd())
245 return false;
246
247 Position SavedPos = Pos;
248
249 consumeWhitespace();
250 SmallString<32> WordText;
251 const char *WordBegin = Pos.BufferPtr;
252 SourceLocation Loc = getSourceLocation();
253
255 return false;
256
257 // Read until the end of this token, which is effectively the end of the
258 // line. This gets us the content of the par header, if there is one.
259 while (!isEnd()) {
260 WordText.push_back(peek());
261 if (Pos.BufferPtr + 1 == Pos.BufferEnd) {
262 consumeChar();
263 break;
264 }
265 consumeChar();
266 }
267
268 unsigned Length = WordText.size();
269 if (Length == 0) {
270 Pos = SavedPos;
271 return false;
272 }
273
274 char *TextPtr = Allocator.Allocate<char>(Length + 1);
275
276 memcpy(TextPtr, WordText.c_str(), Length + 1);
277 StringRef Text = StringRef(TextPtr, Length);
278
279 formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
280 return true;
281 }
282
283 /// Extract a word -- sequence of non-whitespace characters.
285 if (isEnd())
286 return false;
287
288 Position SavedPos = Pos;
289
290 consumeWhitespace();
291 SmallString<32> WordText;
292 const char *WordBegin = Pos.BufferPtr;
293 SourceLocation Loc = getSourceLocation();
294 while (!isEnd()) {
295 const char C = peek();
296 if (!isWhitespace(C)) {
297 WordText.push_back(C);
298 consumeChar();
299 } else
300 break;
301 }
302 if (WordText.ends_with(':'))
303 WordText.pop_back();
304 const unsigned Length = WordText.size();
305 if (Length == 0) {
306 Pos = SavedPos;
307 return false;
308 }
309
310 char *TextPtr = Allocator.Allocate<char>(Length + 1);
311
312 memcpy(TextPtr, WordText.c_str(), Length + 1);
313 StringRef Text = StringRef(TextPtr, Length);
314
315 formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
316 return true;
317 }
318
319 bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
320 if (isEnd())
321 return false;
322
323 Position SavedPos = Pos;
324
325 consumeWhitespace();
326 SmallString<32> WordText;
327 const char *WordBegin = Pos.BufferPtr;
328 SourceLocation Loc = getSourceLocation();
329 bool Error = false;
330 if (!isEnd()) {
331 const char C = peek();
332 if (C == OpenDelim) {
333 WordText.push_back(C);
334 consumeChar();
335 } else
336 Error = true;
337 }
338 char C = '\0';
339 while (!Error && !isEnd()) {
340 C = peek();
341 WordText.push_back(C);
342 consumeChar();
343 if (C == CloseDelim)
344 break;
345 }
346 if (!Error && C != CloseDelim)
347 Error = true;
348
349 if (Error) {
350 Pos = SavedPos;
351 return false;
352 }
353
354 const unsigned Length = WordText.size();
355 char *TextPtr = Allocator.Allocate<char>(Length + 1);
356
357 memcpy(TextPtr, WordText.c_str(), Length + 1);
358 StringRef Text = StringRef(TextPtr, Length);
359
360 formTokenWithChars(Tok, Loc, WordBegin,
361 Pos.BufferPtr - WordBegin, Text);
362 return true;
363 }
364
365 /// Put back tokens that we didn't consume.
367 if (isEnd())
368 return;
369
370 bool HavePartialTok = false;
371 Token PartialTok;
372 if (Pos.BufferPtr != Pos.BufferStart) {
373 formTokenWithChars(PartialTok, getSourceLocation(),
374 Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
375 StringRef(Pos.BufferPtr,
376 Pos.BufferEnd - Pos.BufferPtr));
377 HavePartialTok = true;
378 Pos.CurToken++;
379 }
380
381 P.putBack(ArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
382 Pos.CurToken = Toks.size();
383
384 if (HavePartialTok)
385 P.putBack(PartialTok);
386 }
387};
388
389Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
390 const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
391 const CommandTraits &Traits):
392 L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
393 Traits(Traits) {
394 consumeToken();
395}
396
398 TextTokenRetokenizer &Retokenizer) {
399 Token Arg;
400 // Check if argument looks like direction specification: [dir]
401 // e.g., [in], [out], [in,out]
402 if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
403 S.actOnParamCommandDirectionArg(PC,
404 Arg.getLocation(),
405 Arg.getEndLocation(),
406 Arg.getText());
407
408 if (Retokenizer.lexWord(Arg))
409 S.actOnParamCommandParamNameArg(PC,
410 Arg.getLocation(),
411 Arg.getEndLocation(),
412 Arg.getText());
413}
414
416 TextTokenRetokenizer &Retokenizer) {
417 Token Arg;
418 if (Retokenizer.lexWord(Arg))
419 S.actOnTParamCommandParamNameArg(TPC,
420 Arg.getLocation(),
421 Arg.getEndLocation(),
422 Arg.getText());
423}
424
426Parser::parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs) {
427 auto *Args = new (Allocator.Allocate<Comment::Argument>(NumArgs))
428 Comment::Argument[NumArgs];
429 unsigned ParsedArgs = 0;
430 Token Arg;
431 while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
432 Args[ParsedArgs] = Comment::Argument{
433 SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()};
434 ParsedArgs++;
435 }
436
437 return ArrayRef(Args, ParsedArgs);
438}
439
442 unsigned NumArgs) {
443 auto *Args = new (Allocator.Allocate<Comment::Argument>(NumArgs))
444 Comment::Argument[NumArgs];
445 unsigned ParsedArgs = 0;
446 Token Arg;
447
448 while (ParsedArgs < NumArgs && Retokenizer.lexType(Arg)) {
449 Args[ParsedArgs] = Comment::Argument{
450 SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()};
451 ParsedArgs++;
452 }
453
454 return ArrayRef(Args, ParsedArgs);
455}
456
459 unsigned NumArgs) {
460 assert(NumArgs > 0);
461 auto *Args = new (Allocator.Allocate<Comment::Argument>(NumArgs))
462 Comment::Argument[NumArgs];
463 unsigned ParsedArgs = 0;
464 Token Arg;
465
466 while (ParsedArgs < NumArgs && Retokenizer.lexParHeading(Arg)) {
467 Args[ParsedArgs] = Comment::Argument{
468 SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()};
469 ParsedArgs++;
470 }
471
472 return ArrayRef(Args, ParsedArgs);
473}
474
476 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
477
478 ParamCommandComment *PC = nullptr;
479 TParamCommandComment *TPC = nullptr;
480 BlockCommandComment *BC = nullptr;
481 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
482 CommandMarkerKind CommandMarker =
484 if (Info->IsParamCommand) {
485 PC = S.actOnParamCommandStart(Tok.getLocation(),
486 Tok.getEndLocation(),
487 Tok.getCommandID(),
488 CommandMarker);
489 } else if (Info->IsTParamCommand) {
490 TPC = S.actOnTParamCommandStart(Tok.getLocation(),
491 Tok.getEndLocation(),
492 Tok.getCommandID(),
493 CommandMarker);
494 } else {
495 BC = S.actOnBlockCommandStart(Tok.getLocation(),
496 Tok.getEndLocation(),
497 Tok.getCommandID(),
498 CommandMarker);
499 }
500 consumeToken();
501
502 if (isTokBlockCommand()) {
503 // Block command ahead. We can't nest block commands, so pretend that this
504 // command has an empty argument.
505 ParagraphComment *Paragraph = S.actOnParagraphComment({});
506 if (PC) {
507 S.actOnParamCommandFinish(PC, Paragraph);
508 return PC;
509 } else if (TPC) {
510 S.actOnTParamCommandFinish(TPC, Paragraph);
511 return TPC;
512 } else {
513 S.actOnBlockCommandFinish(BC, Paragraph);
514 return BC;
515 }
516 }
517
518 if (PC || TPC || Info->NumArgs > 0) {
519 // In order to parse command arguments we need to retokenize a few
520 // following text tokens.
521 TextTokenRetokenizer Retokenizer(Allocator, *this);
522
523 if (PC)
524 parseParamCommandArgs(PC, Retokenizer);
525 else if (TPC)
526 parseTParamCommandArgs(TPC, Retokenizer);
527 else if (Info->IsThrowsCommand)
528 S.actOnBlockCommandArgs(
529 BC, parseThrowCommandArgs(Retokenizer, Info->NumArgs));
530 else if (Info->IsParCommand)
531 S.actOnBlockCommandArgs(BC,
532 parseParCommandArgs(Retokenizer, Info->NumArgs));
533 else
534 S.actOnBlockCommandArgs(BC, parseCommandArgs(Retokenizer, Info->NumArgs));
535
536 Retokenizer.putBackLeftoverTokens();
537 }
538
539 // If there's a block command ahead, we will attach an empty paragraph to
540 // this command.
541 bool EmptyParagraph = false;
542 if (isTokBlockCommand())
543 EmptyParagraph = true;
544 else if (Tok.is(tok::newline)) {
545 Token PrevTok = Tok;
546 consumeToken();
547 EmptyParagraph = isTokBlockCommand();
548 putBack(PrevTok);
549 }
550
551 ParagraphComment *Paragraph;
552 if (EmptyParagraph)
553 Paragraph = S.actOnParagraphComment({});
554 else {
556 // Since we have checked for a block command, we should have parsed a
557 // paragraph.
558 Paragraph = cast<ParagraphComment>(Block);
559 }
560
561 if (PC) {
562 S.actOnParamCommandFinish(PC, Paragraph);
563 return PC;
564 } else if (TPC) {
565 S.actOnTParamCommandFinish(TPC, Paragraph);
566 return TPC;
567 } else {
568 S.actOnBlockCommandFinish(BC, Paragraph);
569 return BC;
570 }
571}
572
574 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
577 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
578
579 const Token CommandTok = Tok;
580 consumeToken();
581
582 TextTokenRetokenizer Retokenizer(Allocator, *this);
584 parseCommandArgs(Retokenizer, Info->NumArgs);
585
586 InlineCommandComment *IC = S.actOnInlineCommand(
587 CommandTok.getLocation(), CommandTok.getEndLocation(),
588 CommandTok.getCommandID(), CMK, Args);
589
590 if (Args.size() < Info->NumArgs) {
591 Diag(CommandTok.getEndLocation().getLocWithOffset(1),
592 diag::warn_doc_inline_command_not_enough_arguments)
593 << CommandTok.is(tok::at_command) << Info->Name << Args.size()
594 << Info->NumArgs
595 << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation());
596 }
597
598 Retokenizer.putBackLeftoverTokens();
599
600 return IC;
601}
602
604 assert(Tok.is(tok::html_start_tag));
606 S.actOnHTMLStartTagStart(Tok.getLocation(),
607 Tok.getHTMLTagStartName());
608 consumeToken();
609
611 while (true) {
612 switch (Tok.getKind()) {
613 case tok::html_ident: {
614 Token Ident = Tok;
615 consumeToken();
616 if (Tok.isNot(tok::html_equals)) {
617 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
618 Ident.getHTMLIdent()));
619 continue;
620 }
621 Token Equals = Tok;
622 consumeToken();
623 if (Tok.isNot(tok::html_quoted_string)) {
624 Diag(Tok.getLocation(),
625 diag::warn_doc_html_start_tag_expected_quoted_string)
626 << SourceRange(Equals.getLocation());
627 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
628 Ident.getHTMLIdent()));
629 while (Tok.is(tok::html_equals) ||
631 consumeToken();
632 continue;
633 }
634 Attrs.push_back(HTMLStartTagComment::Attribute(
635 Ident.getLocation(),
636 Ident.getHTMLIdent(),
637 Equals.getLocation(),
638 SourceRange(Tok.getLocation(),
639 Tok.getEndLocation()),
640 Tok.getHTMLQuotedString()));
641 consumeToken();
642 continue;
643 }
644
646 S.actOnHTMLStartTagFinish(HST, S.copyArray(ArrayRef(Attrs)),
647 Tok.getLocation(),
648 /* IsSelfClosing = */ false);
649 consumeToken();
650 return HST;
651
653 S.actOnHTMLStartTagFinish(HST, S.copyArray(ArrayRef(Attrs)),
654 Tok.getLocation(),
655 /* IsSelfClosing = */ true);
656 consumeToken();
657 return HST;
658
659 case tok::html_equals:
661 Diag(Tok.getLocation(),
662 diag::warn_doc_html_start_tag_expected_ident_or_greater);
663 while (Tok.is(tok::html_equals) ||
665 consumeToken();
666 if (Tok.is(tok::html_ident) ||
667 Tok.is(tok::html_greater) ||
669 continue;
670
671 S.actOnHTMLStartTagFinish(HST, S.copyArray(ArrayRef(Attrs)),
673 /* IsSelfClosing = */ false);
674 return HST;
675
676 default:
677 // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
678 S.actOnHTMLStartTagFinish(HST, S.copyArray(ArrayRef(Attrs)),
680 /* IsSelfClosing = */ false);
681 bool StartLineInvalid;
682 const unsigned StartLine = SourceMgr.getPresumedLineNumber(
683 HST->getLocation(),
684 &StartLineInvalid);
685 bool EndLineInvalid;
686 const unsigned EndLine = SourceMgr.getPresumedLineNumber(
687 Tok.getLocation(),
688 &EndLineInvalid);
689 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
690 Diag(Tok.getLocation(),
691 diag::warn_doc_html_start_tag_expected_ident_or_greater)
692 << HST->getSourceRange();
693 else {
694 Diag(Tok.getLocation(),
695 diag::warn_doc_html_start_tag_expected_ident_or_greater);
696 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
697 << HST->getSourceRange();
698 }
699 return HST;
700 }
701 }
702}
703
705 assert(Tok.is(tok::html_end_tag));
706 Token TokEndTag = Tok;
707 consumeToken();
708 SourceLocation Loc;
709 if (Tok.is(tok::html_greater)) {
710 Loc = Tok.getLocation();
711 consumeToken();
712 }
713
714 return S.actOnHTMLEndTag(TokEndTag.getLocation(),
715 Loc,
716 TokEndTag.getHTMLTagEndName());
717}
718
721
722 while (true) {
723 switch (Tok.getKind()) {
726 case tok::eof:
727 break; // Block content or EOF ahead, finish this parapgaph.
728
730 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
731 Tok.getEndLocation(),
732 Tok.getUnknownCommandName()));
733 consumeToken();
734 continue;
735
737 case tok::at_command: {
738 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
739 if (Info->IsBlockCommand) {
740 if (Content.size() == 0)
741 return parseBlockCommand();
742 break; // Block command ahead, finish this parapgaph.
743 }
744 if (Info->IsVerbatimBlockEndCommand) {
745 Diag(Tok.getLocation(),
746 diag::warn_verbatim_block_end_without_start)
747 << Tok.is(tok::at_command)
748 << Info->Name
749 << SourceRange(Tok.getLocation(), Tok.getEndLocation());
750 consumeToken();
751 continue;
752 }
753 if (Info->IsUnknownCommand) {
754 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
755 Tok.getEndLocation(),
756 Info->getID()));
757 consumeToken();
758 continue;
759 }
760 assert(Info->IsInlineCommand);
761 Content.push_back(parseInlineCommand());
762 continue;
763 }
764
765 case tok::newline: {
766 consumeToken();
767 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
768 consumeToken();
769 break; // Two newlines -- end of paragraph.
770 }
771 // Also allow [tok::newline, tok::text, tok::newline] if the middle
772 // tok::text is just whitespace.
773 if (Tok.is(tok::text) && isWhitespace(Tok.getText())) {
774 Token WhitespaceTok = Tok;
775 consumeToken();
776 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
777 consumeToken();
778 break;
779 }
780 // We have [tok::newline, tok::text, non-newline]. Put back tok::text.
781 putBack(WhitespaceTok);
782 }
783 if (Content.size() > 0)
784 Content.back()->addTrailingNewline();
785 continue;
786 }
787
788 // Don't deal with HTML tag soup now.
790 Content.push_back(parseHTMLStartTag());
791 continue;
792
794 Content.push_back(parseHTMLEndTag());
795 continue;
796
797 case tok::text:
798 Content.push_back(S.actOnText(Tok.getLocation(),
799 Tok.getEndLocation(),
800 Tok.getText()));
801 consumeToken();
802 continue;
803
807 case tok::html_ident:
808 case tok::html_equals:
812 llvm_unreachable("should not see this token");
813 }
814 break;
815 }
816
817 return S.actOnParagraphComment(S.copyArray(ArrayRef(Content)));
818}
819
821 assert(Tok.is(tok::verbatim_block_begin));
822
824 S.actOnVerbatimBlockStart(Tok.getLocation(),
825 Tok.getVerbatimBlockID());
826 consumeToken();
827
828 // Don't create an empty line if verbatim opening command is followed
829 // by a newline.
830 if (Tok.is(tok::newline))
831 consumeToken();
832
834 while (Tok.is(tok::verbatim_block_line) ||
835 Tok.is(tok::newline)) {
837 if (Tok.is(tok::verbatim_block_line)) {
838 Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
839 Tok.getVerbatimBlockText());
840 consumeToken();
841 if (Tok.is(tok::newline)) {
842 consumeToken();
843 }
844 } else {
845 // Empty line, just a tok::newline.
846 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
847 consumeToken();
848 }
849 Lines.push_back(Line);
850 }
851
852 if (Tok.is(tok::verbatim_block_end)) {
853 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
854 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), Info->Name,
855 S.copyArray(ArrayRef(Lines)));
856 consumeToken();
857 } else {
858 // Unterminated \\verbatim block
859 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
860 S.copyArray(ArrayRef(Lines)));
861 }
862
863 return VB;
864}
865
867 assert(Tok.is(tok::verbatim_line_name));
868
869 Token NameTok = Tok;
870 consumeToken();
871
872 SourceLocation TextBegin;
873 StringRef Text;
874 // Next token might not be a tok::verbatim_line_text if verbatim line
875 // starting command comes just before a newline or comment end.
876 if (Tok.is(tok::verbatim_line_text)) {
877 TextBegin = Tok.getLocation();
878 Text = Tok.getVerbatimLineText();
879 } else {
880 TextBegin = NameTok.getEndLocation();
881 Text = "";
882 }
883
884 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
885 NameTok.getVerbatimLineID(),
886 TextBegin,
887 Text);
888 consumeToken();
889 return VL;
890}
891
893 switch (Tok.getKind()) {
894 case tok::text:
897 case tok::at_command:
901
903 return parseVerbatimBlock();
904
906 return parseVerbatimLine();
907
908 case tok::eof:
909 case tok::newline:
913 case tok::html_ident:
914 case tok::html_equals:
918 llvm_unreachable("should not see this token");
919 }
920 llvm_unreachable("bogus token kind");
921}
922
924 // Skip newlines at the beginning of the comment.
925 while (Tok.is(tok::newline))
926 consumeToken();
927
929 while (Tok.isNot(tok::eof)) {
930 Blocks.push_back(parseBlockContent());
931
932 // Skip extra newlines after paragraph end.
933 while (Tok.is(tok::newline))
934 consumeToken();
935 }
936 return S.actOnFullComment(S.copyArray(ArrayRef(Blocks)));
937}
938
939} // end namespace comments
940} // end namespace clang
Token Tok
The Token.
Defines the SourceManager interface.
__DEVICE__ void * memcpy(void *__a, const void *__b, size_t __c)
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:233
Encodes a location in the source.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
A command that has zero or more word-like arguments (number of word-like arguments depends on command...
Definition Comment.h:625
Block content (contains inline content).
Definition Comment.h:559
This class provides information about commands that can be used in comments.
SourceLocation getLocation() const LLVM_READONLY
Definition Comment.h:255
SourceRange getSourceRange() const LLVM_READONLY
Definition Comment.h:249
A full comment attached to a declaration, contains block content.
Definition Comment.h:1104
An opening HTML tag with attributes.
Definition Comment.h:454
A command with word-like arguments that is considered inline content.
Definition Comment.h:341
A single paragraph that contains inline content.
Definition Comment.h:576
Doxygen \param command.
Definition Comment.h:732
Doxygen comment parser.
VerbatimLineComment * parseVerbatimLine()
InlineCommandComment * parseInlineCommand()
ArrayRef< Comment::Argument > parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs)
ArrayRef< Comment::Argument > parseThrowCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs)
Parse arguments for.
void parseTParamCommandArgs(TParamCommandComment *TPC, TextTokenRetokenizer &Retokenizer)
Parse arguments for \tparam command.
BlockContentComment * parseParagraphOrBlockCommand()
HTMLEndTagComment * parseHTMLEndTag()
VerbatimBlockComment * parseVerbatimBlock()
BlockCommandComment * parseBlockCommand()
BlockContentComment * parseBlockContent()
HTMLStartTagComment * parseHTMLStartTag()
FullComment * parseFullComment()
ArrayRef< Comment::Argument > parseParCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs)
friend class TextTokenRetokenizer
void parseParamCommandArgs(ParamCommandComment *PC, TextTokenRetokenizer &Retokenizer)
Parse arguments for \param command.
Doxygen \tparam command, describes a template parameter.
Definition Comment.h:814
bool lexParHeading(Token &Tok)
Extract a par command argument-header.
void putBackLeftoverTokens()
Put back tokens that we didn't consume.
bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim)
TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P)
bool lexType(Token &Tok)
Extract a type argument.
bool lexWord(Token &Tok)
Extract a word – sequence of non-whitespace characters.
SourceLocation getEndLocation() const LLVM_READONLY
unsigned getCommandID() const LLVM_READONLY
StringRef getText() const LLVM_READONLY
StringRef getHTMLIdent() const LLVM_READONLY
unsigned getVerbatimLineID() const LLVM_READONLY
bool is(tok::TokenKind K) const LLVM_READONLY
SourceLocation getLocation() const LLVM_READONLY
StringRef getHTMLTagEndName() const LLVM_READONLY
A verbatim block command (e.
Definition Comment.h:900
A line of text contained in a verbatim block.
Definition Comment.h:875
A verbatim line command.
Definition Comment.h:951
CommandMarkerKind
Describes the syntax that was used in a documentation command.
Definition Comment.h:39
@ CMK_Backslash
Command started with a backslash character:
Definition Comment.h:44
@ CMK_At
Command started with an 'at' character:
Definition Comment.h:50
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
Definition TypeBase.h:905
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...
Definition CharInfo.h:108
U cast(CodeGen::Address addr)
Definition Address.h:327
#define false
Definition stdbool.h:26
Information about a single command.
unsigned IsBlockCommand
True if this command is a block command (of any kind).
unsigned IsTParamCommand
True if this command is introducing documentation for a template parameter (\tparam or an alias).
unsigned IsParCommand
True if this is a \par command.
unsigned IsVerbatimBlockEndCommand
True if this command is an end command for a verbatim-like block.
unsigned IsParamCommand
True if this command is introducing documentation for a function parameter (\param or an alias).
unsigned IsThrowsCommand
True if this command is \throws or an alias.
unsigned IsInlineCommand
True if this command is a inline command (of any kind).
unsigned IsUnknownCommand
True if this command is unknown.
unsigned NumArgs
Number of word-like arguments for a given block command, except for \param and \tparam commands – the...