clang 22.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,
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
179 bool lexType(Token &Tok) {
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.
243 bool lexParHeading(Token &Tok) {
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.
284 bool lexWord(Token &Tok) {
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 const unsigned Length = WordText.size();
303 if (Length == 0) {
304 Pos = SavedPos;
305 return false;
306 }
307
308 char *TextPtr = Allocator.Allocate<char>(Length + 1);
309
310 memcpy(TextPtr, WordText.c_str(), Length + 1);
311 StringRef Text = StringRef(TextPtr, Length);
312
313 formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
314 return true;
315 }
316
317 bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
318 if (isEnd())
319 return false;
320
321 Position SavedPos = Pos;
322
323 consumeWhitespace();
324 SmallString<32> WordText;
325 const char *WordBegin = Pos.BufferPtr;
326 SourceLocation Loc = getSourceLocation();
327 bool Error = false;
328 if (!isEnd()) {
329 const char C = peek();
330 if (C == OpenDelim) {
331 WordText.push_back(C);
332 consumeChar();
333 } else
334 Error = true;
335 }
336 char C = '\0';
337 while (!Error && !isEnd()) {
338 C = peek();
339 WordText.push_back(C);
340 consumeChar();
341 if (C == CloseDelim)
342 break;
343 }
344 if (!Error && C != CloseDelim)
345 Error = true;
346
347 if (Error) {
348 Pos = SavedPos;
349 return false;
350 }
351
352 const unsigned Length = WordText.size();
353 char *TextPtr = Allocator.Allocate<char>(Length + 1);
354
355 memcpy(TextPtr, WordText.c_str(), Length + 1);
356 StringRef Text = StringRef(TextPtr, Length);
357
358 formTokenWithChars(Tok, Loc, WordBegin,
359 Pos.BufferPtr - WordBegin, Text);
360 return true;
361 }
362
363 /// Put back tokens that we didn't consume.
365 if (isEnd())
366 return;
367
368 bool HavePartialTok = false;
369 Token PartialTok;
370 if (Pos.BufferPtr != Pos.BufferStart) {
371 formTokenWithChars(PartialTok, getSourceLocation(),
372 Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
373 StringRef(Pos.BufferPtr,
374 Pos.BufferEnd - Pos.BufferPtr));
375 HavePartialTok = true;
376 Pos.CurToken++;
377 }
378
379 P.putBack(ArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
380 Pos.CurToken = Toks.size();
381
382 if (HavePartialTok)
383 P.putBack(PartialTok);
384 }
385};
386
387Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
388 const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
389 const CommandTraits &Traits):
390 L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
391 Traits(Traits) {
392 consumeToken();
393}
394
396 TextTokenRetokenizer &Retokenizer) {
397 Token Arg;
398 // Check if argument looks like direction specification: [dir]
399 // e.g., [in], [out], [in,out]
400 if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
402 Arg.getLocation(),
403 Arg.getEndLocation(),
404 Arg.getText());
405
406 if (Retokenizer.lexWord(Arg))
408 Arg.getLocation(),
409 Arg.getEndLocation(),
410 Arg.getText());
411}
412
414 TextTokenRetokenizer &Retokenizer) {
415 Token Arg;
416 if (Retokenizer.lexWord(Arg))
418 Arg.getLocation(),
419 Arg.getEndLocation(),
420 Arg.getText());
421}
422
424Parser::parseCommandArgs(TextTokenRetokenizer &Retokenizer, unsigned NumArgs) {
425 auto *Args = new (Allocator.Allocate<Comment::Argument>(NumArgs))
426 Comment::Argument[NumArgs];
427 unsigned ParsedArgs = 0;
428 Token Arg;
429 while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
430 Args[ParsedArgs] = Comment::Argument{
431 SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()};
432 ParsedArgs++;
433 }
434
435 return ArrayRef(Args, ParsedArgs);
436}
437
440 unsigned NumArgs) {
441 auto *Args = new (Allocator.Allocate<Comment::Argument>(NumArgs))
442 Comment::Argument[NumArgs];
443 unsigned ParsedArgs = 0;
444 Token Arg;
445
446 while (ParsedArgs < NumArgs && Retokenizer.lexType(Arg)) {
447 Args[ParsedArgs] = Comment::Argument{
448 SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()};
449 ParsedArgs++;
450 }
451
452 return ArrayRef(Args, ParsedArgs);
453}
454
457 unsigned NumArgs) {
458 assert(NumArgs > 0);
459 auto *Args = new (Allocator.Allocate<Comment::Argument>(NumArgs))
460 Comment::Argument[NumArgs];
461 unsigned ParsedArgs = 0;
462 Token Arg;
463
464 while (ParsedArgs < NumArgs && Retokenizer.lexParHeading(Arg)) {
465 Args[ParsedArgs] = Comment::Argument{
466 SourceRange(Arg.getLocation(), Arg.getEndLocation()), Arg.getText()};
467 ParsedArgs++;
468 }
469
470 return ArrayRef(Args, ParsedArgs);
471}
472
474 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
475
476 ParamCommandComment *PC = nullptr;
477 TParamCommandComment *TPC = nullptr;
478 BlockCommandComment *BC = nullptr;
479 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
480 CommandMarkerKind CommandMarker =
482 if (Info->IsParamCommand) {
484 Tok.getEndLocation(),
485 Tok.getCommandID(),
486 CommandMarker);
487 } else if (Info->IsTParamCommand) {
489 Tok.getEndLocation(),
490 Tok.getCommandID(),
491 CommandMarker);
492 } else {
494 Tok.getEndLocation(),
495 Tok.getCommandID(),
496 CommandMarker);
497 }
498 consumeToken();
499
500 if (isTokBlockCommand()) {
501 // Block command ahead. We can't nest block commands, so pretend that this
502 // command has an empty argument.
503 ParagraphComment *Paragraph = S.actOnParagraphComment({});
504 if (PC) {
505 S.actOnParamCommandFinish(PC, Paragraph);
506 return PC;
507 } else if (TPC) {
508 S.actOnTParamCommandFinish(TPC, Paragraph);
509 return TPC;
510 } else {
511 S.actOnBlockCommandFinish(BC, Paragraph);
512 return BC;
513 }
514 }
515
516 if (PC || TPC || Info->NumArgs > 0) {
517 // In order to parse command arguments we need to retokenize a few
518 // following text tokens.
519 TextTokenRetokenizer Retokenizer(Allocator, *this);
520
521 if (PC)
522 parseParamCommandArgs(PC, Retokenizer);
523 else if (TPC)
524 parseTParamCommandArgs(TPC, Retokenizer);
525 else if (Info->IsThrowsCommand)
527 BC, parseThrowCommandArgs(Retokenizer, Info->NumArgs));
528 else if (Info->IsParCommand)
530 parseParCommandArgs(Retokenizer, Info->NumArgs));
531 else
532 S.actOnBlockCommandArgs(BC, parseCommandArgs(Retokenizer, Info->NumArgs));
533
534 Retokenizer.putBackLeftoverTokens();
535 }
536
537 // If there's a block command ahead, we will attach an empty paragraph to
538 // this command.
539 bool EmptyParagraph = false;
540 if (isTokBlockCommand())
541 EmptyParagraph = true;
542 else if (Tok.is(tok::newline)) {
543 Token PrevTok = Tok;
544 consumeToken();
545 EmptyParagraph = isTokBlockCommand();
546 putBack(PrevTok);
547 }
548
549 ParagraphComment *Paragraph;
550 if (EmptyParagraph)
551 Paragraph = S.actOnParagraphComment({});
552 else {
554 // Since we have checked for a block command, we should have parsed a
555 // paragraph.
556 Paragraph = cast<ParagraphComment>(Block);
557 }
558
559 if (PC) {
560 S.actOnParamCommandFinish(PC, Paragraph);
561 return PC;
562 } else if (TPC) {
563 S.actOnTParamCommandFinish(TPC, Paragraph);
564 return TPC;
565 } else {
566 S.actOnBlockCommandFinish(BC, Paragraph);
567 return BC;
568 }
569}
570
572 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
575 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
576
577 const Token CommandTok = Tok;
578 consumeToken();
579
580 TextTokenRetokenizer Retokenizer(Allocator, *this);
582 parseCommandArgs(Retokenizer, Info->NumArgs);
583
585 CommandTok.getLocation(), CommandTok.getEndLocation(),
586 CommandTok.getCommandID(), CMK, Args);
587
588 if (Args.size() < Info->NumArgs) {
589 Diag(CommandTok.getEndLocation().getLocWithOffset(1),
590 diag::warn_doc_inline_command_not_enough_arguments)
591 << CommandTok.is(tok::at_command) << Info->Name << Args.size()
592 << Info->NumArgs
593 << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation());
594 }
595
596 Retokenizer.putBackLeftoverTokens();
597
598 return IC;
599}
600
602 assert(Tok.is(tok::html_start_tag));
605 Tok.getHTMLTagStartName());
606 consumeToken();
607
609 while (true) {
610 switch (Tok.getKind()) {
611 case tok::html_ident: {
612 Token Ident = Tok;
613 consumeToken();
614 if (Tok.isNot(tok::html_equals)) {
615 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
616 Ident.getHTMLIdent()));
617 continue;
618 }
619 Token Equals = Tok;
620 consumeToken();
622 Diag(Tok.getLocation(),
623 diag::warn_doc_html_start_tag_expected_quoted_string)
624 << SourceRange(Equals.getLocation());
625 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
626 Ident.getHTMLIdent()));
627 while (Tok.is(tok::html_equals) ||
629 consumeToken();
630 continue;
631 }
632 Attrs.push_back(HTMLStartTagComment::Attribute(
633 Ident.getLocation(),
634 Ident.getHTMLIdent(),
635 Equals.getLocation(),
637 Tok.getEndLocation()),
638 Tok.getHTMLQuotedString()));
639 consumeToken();
640 continue;
641 }
642
645 Tok.getLocation(),
646 /* IsSelfClosing = */ false);
647 consumeToken();
648 return HST;
649
652 Tok.getLocation(),
653 /* IsSelfClosing = */ true);
654 consumeToken();
655 return HST;
656
657 case tok::html_equals:
659 Diag(Tok.getLocation(),
660 diag::warn_doc_html_start_tag_expected_ident_or_greater);
661 while (Tok.is(tok::html_equals) ||
663 consumeToken();
664 if (Tok.is(tok::html_ident) ||
665 Tok.is(tok::html_greater) ||
667 continue;
668
671 /* IsSelfClosing = */ false);
672 return HST;
673
674 default:
675 // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
678 /* IsSelfClosing = */ false);
679 bool StartLineInvalid;
680 const unsigned StartLine = SourceMgr.getPresumedLineNumber(
681 HST->getLocation(),
682 &StartLineInvalid);
683 bool EndLineInvalid;
684 const unsigned EndLine = SourceMgr.getPresumedLineNumber(
685 Tok.getLocation(),
686 &EndLineInvalid);
687 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
688 Diag(Tok.getLocation(),
689 diag::warn_doc_html_start_tag_expected_ident_or_greater)
690 << HST->getSourceRange();
691 else {
692 Diag(Tok.getLocation(),
693 diag::warn_doc_html_start_tag_expected_ident_or_greater);
694 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
695 << HST->getSourceRange();
696 }
697 return HST;
698 }
699 }
700}
701
703 assert(Tok.is(tok::html_end_tag));
704 Token TokEndTag = Tok;
705 consumeToken();
707 if (Tok.is(tok::html_greater)) {
708 Loc = Tok.getLocation();
709 consumeToken();
710 }
711
712 return S.actOnHTMLEndTag(TokEndTag.getLocation(),
713 Loc,
714 TokEndTag.getHTMLTagEndName());
715}
716
719
720 while (true) {
721 switch (Tok.getKind()) {
724 case tok::eof:
725 break; // Block content or EOF ahead, finish this parapgaph.
726
728 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
729 Tok.getEndLocation(),
730 Tok.getUnknownCommandName()));
731 consumeToken();
732 continue;
733
735 case tok::at_command: {
736 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
737 if (Info->IsBlockCommand) {
738 if (Content.size() == 0)
739 return parseBlockCommand();
740 break; // Block command ahead, finish this parapgaph.
741 }
742 if (Info->IsVerbatimBlockEndCommand) {
743 Diag(Tok.getLocation(),
744 diag::warn_verbatim_block_end_without_start)
745 << Tok.is(tok::at_command)
746 << Info->Name
747 << SourceRange(Tok.getLocation(), Tok.getEndLocation());
748 consumeToken();
749 continue;
750 }
751 if (Info->IsUnknownCommand) {
752 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
753 Tok.getEndLocation(),
754 Info->getID()));
755 consumeToken();
756 continue;
757 }
758 assert(Info->IsInlineCommand);
759 Content.push_back(parseInlineCommand());
760 continue;
761 }
762
763 case tok::newline: {
764 consumeToken();
765 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
766 consumeToken();
767 break; // Two newlines -- end of paragraph.
768 }
769 // Also allow [tok::newline, tok::text, tok::newline] if the middle
770 // tok::text is just whitespace.
771 if (Tok.is(tok::text) && isWhitespace(Tok.getText())) {
772 Token WhitespaceTok = Tok;
773 consumeToken();
774 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
775 consumeToken();
776 break;
777 }
778 // We have [tok::newline, tok::text, non-newline]. Put back tok::text.
779 putBack(WhitespaceTok);
780 }
781 if (Content.size() > 0)
782 Content.back()->addTrailingNewline();
783 continue;
784 }
785
786 // Don't deal with HTML tag soup now.
788 Content.push_back(parseHTMLStartTag());
789 continue;
790
792 Content.push_back(parseHTMLEndTag());
793 continue;
794
795 case tok::text:
796 Content.push_back(S.actOnText(Tok.getLocation(),
797 Tok.getEndLocation(),
798 Tok.getText()));
799 consumeToken();
800 continue;
801
805 case tok::html_ident:
806 case tok::html_equals:
810 llvm_unreachable("should not see this token");
811 }
812 break;
813 }
814
815 return S.actOnParagraphComment(S.copyArray(ArrayRef(Content)));
816}
817
819 assert(Tok.is(tok::verbatim_block_begin));
820
823 Tok.getVerbatimBlockID());
824 consumeToken();
825
826 // Don't create an empty line if verbatim opening command is followed
827 // by a newline.
828 if (Tok.is(tok::newline))
829 consumeToken();
830
832 while (Tok.is(tok::verbatim_block_line) ||
833 Tok.is(tok::newline)) {
835 if (Tok.is(tok::verbatim_block_line)) {
838 consumeToken();
839 if (Tok.is(tok::newline)) {
840 consumeToken();
841 }
842 } else {
843 // Empty line, just a tok::newline.
845 consumeToken();
846 }
847 Lines.push_back(Line);
848 }
849
850 if (Tok.is(tok::verbatim_block_end)) {
851 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
852 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), Info->Name,
853 S.copyArray(ArrayRef(Lines)));
854 consumeToken();
855 } else {
856 // Unterminated \\verbatim block
858 S.copyArray(ArrayRef(Lines)));
859 }
860
861 return VB;
862}
863
865 assert(Tok.is(tok::verbatim_line_name));
866
867 Token NameTok = Tok;
868 consumeToken();
869
870 SourceLocation TextBegin;
871 StringRef Text;
872 // Next token might not be a tok::verbatim_line_text if verbatim line
873 // starting command comes just before a newline or comment end.
874 if (Tok.is(tok::verbatim_line_text)) {
875 TextBegin = Tok.getLocation();
877 } else {
878 TextBegin = NameTok.getEndLocation();
879 Text = "";
880 }
881
883 NameTok.getVerbatimLineID(),
884 TextBegin,
885 Text);
886 consumeToken();
887 return VL;
888}
889
891 switch (Tok.getKind()) {
892 case tok::text:
895 case tok::at_command:
899
901 return parseVerbatimBlock();
902
904 return parseVerbatimLine();
905
906 case tok::eof:
907 case tok::newline:
911 case tok::html_ident:
912 case tok::html_equals:
916 llvm_unreachable("should not see this token");
917 }
918 llvm_unreachable("bogus token kind");
919}
920
922 // Skip newlines at the beginning of the comment.
923 while (Tok.is(tok::newline))
924 consumeToken();
925
927 while (Tok.isNot(tok::eof)) {
928 Blocks.push_back(parseBlockContent());
929
930 // Skip extra newlines after paragraph end.
931 while (Tok.is(tok::newline))
932 consumeToken();
933 }
934 return S.actOnFullComment(S.copyArray(ArrayRef(Blocks)));
935}
936
937} // end namespace comments
938} // end namespace clang
StringRef P
Expr * E
SourceLocation Loc
Definition: SemaObjC.cpp:754
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:231
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.
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
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.
const CommandInfo * getCommandInfo(StringRef Name) const
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.
Definition: CommentParser.h:29
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)
void parseParamCommandArgs(ParamCommandComment *PC, TextTokenRetokenizer &Retokenizer)
Parse arguments for \param command.
FullComment * actOnFullComment(ArrayRef< BlockContentComment * > Blocks)
void actOnParamCommandDirectionArg(ParamCommandComment *Command, SourceLocation ArgLocBegin, SourceLocation ArgLocEnd, StringRef Arg)
InlineCommandComment * actOnInlineCommand(SourceLocation CommandLocBegin, SourceLocation CommandLocEnd, unsigned CommandID, CommandMarkerKind CommandMarker, ArrayRef< Comment::Argument > Args)
TParamCommandComment * actOnTParamCommandStart(SourceLocation LocBegin, SourceLocation LocEnd, unsigned CommandID, CommandMarkerKind CommandMarker)
void actOnTParamCommandFinish(TParamCommandComment *Command, ParagraphComment *Paragraph)
void actOnBlockCommandFinish(BlockCommandComment *Command, ParagraphComment *Paragraph)
Definition: CommentSema.cpp:67
TextComment * actOnText(SourceLocation LocBegin, SourceLocation LocEnd, StringRef Text)
void actOnBlockCommandArgs(BlockCommandComment *Command, ArrayRef< BlockCommandComment::Argument > Args)
Definition: CommentSema.cpp:62
BlockCommandComment * actOnBlockCommandStart(SourceLocation LocBegin, SourceLocation LocEnd, unsigned CommandID, CommandMarkerKind CommandMarker)
Definition: CommentSema.cpp:50
void actOnParamCommandParamNameArg(ParamCommandComment *Command, SourceLocation ArgLocBegin, SourceLocation ArgLocEnd, StringRef Arg)
HTMLEndTagComment * actOnHTMLEndTag(SourceLocation LocBegin, SourceLocation LocEnd, StringRef TagName)
VerbatimLineComment * actOnVerbatimLine(SourceLocation LocBegin, unsigned CommandID, SourceLocation TextBegin, StringRef Text)
void actOnVerbatimBlockFinish(VerbatimBlockComment *Block, SourceLocation CloseNameLocBegin, StringRef CloseName, ArrayRef< VerbatimBlockLineComment * > Lines)
VerbatimBlockLineComment * actOnVerbatimBlockLine(SourceLocation Loc, StringRef Text)
void actOnParamCommandFinish(ParamCommandComment *Command, ParagraphComment *Paragraph)
ArrayRef< T > copyArray(ArrayRef< T > Source)
Returns a copy of array, owned by Sema's allocator.
Definition: CommentSema.h:80
HTMLStartTagComment * actOnHTMLStartTagStart(SourceLocation LocBegin, StringRef TagName)
void actOnHTMLStartTagFinish(HTMLStartTagComment *Tag, ArrayRef< HTMLStartTagComment::Attribute > Attrs, SourceLocation GreaterLoc, bool IsSelfClosing)
ParamCommandComment * actOnParamCommandStart(SourceLocation LocBegin, SourceLocation LocEnd, unsigned CommandID, CommandMarkerKind CommandMarker)
Definition: CommentSema.cpp:80
VerbatimBlockComment * actOnVerbatimBlockStart(SourceLocation Loc, unsigned CommandID)
void actOnTParamCommandParamNameArg(TParamCommandComment *Command, SourceLocation ArgLocBegin, SourceLocation ArgLocEnd, StringRef Arg)
InlineContentComment * actOnUnknownCommand(SourceLocation LocBegin, SourceLocation LocEnd, StringRef CommandName)
ParagraphComment * actOnParagraphComment(ArrayRef< InlineContentComment * > Content)
Definition: CommentSema.cpp:45
Doxygen \tparam command, describes a template parameter.
Definition: Comment.h:814
Re-lexes a sequence of tok::text tokens.
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.
Comment token.
Definition: CommentLexer.h:55
StringRef getHTMLQuotedString() const LLVM_READONLY
Definition: CommentLexer.h:194
bool isNot(tok::TokenKind K) const LLVM_READONLY
Definition: CommentLexer.h:93
SourceLocation getEndLocation() const LLVM_READONLY
Definition: CommentLexer.h:83
unsigned getCommandID() const LLVM_READONLY
Definition: CommentLexer.h:120
StringRef getUnknownCommandName() const LLVM_READONLY
Definition: CommentLexer.h:109
StringRef getText() const LLVM_READONLY
Definition: CommentLexer.h:98
StringRef getHTMLIdent() const LLVM_READONLY
Definition: CommentLexer.h:183
StringRef getVerbatimBlockText() const LLVM_READONLY
Definition: CommentLexer.h:140
unsigned getVerbatimLineID() const LLVM_READONLY
Definition: CommentLexer.h:151
unsigned getVerbatimBlockID() const LLVM_READONLY
Definition: CommentLexer.h:130
bool is(tok::TokenKind K) const LLVM_READONLY
Definition: CommentLexer.h:92
SourceLocation getLocation() const LLVM_READONLY
Definition: CommentLexer.h:80
StringRef getVerbatimLineText() const LLVM_READONLY
Definition: CommentLexer.h:161
tok::TokenKind getKind() const LLVM_READONLY
Definition: CommentLexer.h:89
StringRef getHTMLTagStartName() const LLVM_READONLY
Definition: CommentLexer.h:172
StringRef getHTMLTagEndName() const LLVM_READONLY
Definition: CommentLexer.h:205
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.
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...
Definition: CharInfo.h:108
#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...