1 //===--- CommentParser.cpp - Doxygen comment parser -----------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "clang/AST/CommentParser.h"
11 #include "clang/AST/CommentCommandTraits.h"
12 #include "clang/AST/CommentDiagnostic.h"
13 #include "clang/AST/CommentSema.h"
14 #include "clang/Basic/CharInfo.h"
15 #include "clang/Basic/SourceManager.h"
16 #include "llvm/Support/ErrorHandling.h"
17
18 namespace clang {
19 namespace comments {
20
21 /// Re-lexes a sequence of tok::text tokens.
22 class TextTokenRetokenizer {
23 llvm::BumpPtrAllocator &Allocator;
24 Parser &P;
25
26 /// This flag is set when there are no more tokens we can fetch from lexer.
27 bool NoMoreInterestingTokens;
28
29 /// Token buffer: tokens we have processed and lookahead.
30 SmallVector<Token, 16> Toks;
31
32 /// A position in \c Toks.
33 struct Position {
34 unsigned CurToken;
35 const char *BufferStart;
36 const char *BufferEnd;
37 const char *BufferPtr;
38 SourceLocation BufferStartLoc;
39 };
40
41 /// Current position in Toks.
42 Position Pos;
43
isEnd() const44 bool isEnd() const {
45 return Pos.CurToken >= Toks.size();
46 }
47
48 /// Sets up the buffer pointers to point to current token.
setupBuffer()49 void setupBuffer() {
50 assert(!isEnd());
51 const Token &Tok = Toks[Pos.CurToken];
52
53 Pos.BufferStart = Tok.getText().begin();
54 Pos.BufferEnd = Tok.getText().end();
55 Pos.BufferPtr = Pos.BufferStart;
56 Pos.BufferStartLoc = Tok.getLocation();
57 }
58
getSourceLocation() const59 SourceLocation getSourceLocation() const {
60 const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart;
61 return Pos.BufferStartLoc.getLocWithOffset(CharNo);
62 }
63
peek() const64 char peek() const {
65 assert(!isEnd());
66 assert(Pos.BufferPtr != Pos.BufferEnd);
67 return *Pos.BufferPtr;
68 }
69
consumeChar()70 void consumeChar() {
71 assert(!isEnd());
72 assert(Pos.BufferPtr != Pos.BufferEnd);
73 Pos.BufferPtr++;
74 if (Pos.BufferPtr == Pos.BufferEnd) {
75 Pos.CurToken++;
76 if (isEnd() && !addToken())
77 return;
78
79 assert(!isEnd());
80 setupBuffer();
81 }
82 }
83
84 /// Add a token.
85 /// Returns true on success, false if there are no interesting tokens to
86 /// fetch from lexer.
addToken()87 bool addToken() {
88 if (NoMoreInterestingTokens)
89 return false;
90
91 if (P.Tok.is(tok::newline)) {
92 // If we see a single newline token between text tokens, skip it.
93 Token Newline = P.Tok;
94 P.consumeToken();
95 if (P.Tok.isNot(tok::text)) {
96 P.putBack(Newline);
97 NoMoreInterestingTokens = true;
98 return false;
99 }
100 }
101 if (P.Tok.isNot(tok::text)) {
102 NoMoreInterestingTokens = true;
103 return false;
104 }
105
106 Toks.push_back(P.Tok);
107 P.consumeToken();
108 if (Toks.size() == 1)
109 setupBuffer();
110 return true;
111 }
112
consumeWhitespace()113 void consumeWhitespace() {
114 while (!isEnd()) {
115 if (isWhitespace(peek()))
116 consumeChar();
117 else
118 break;
119 }
120 }
121
formTokenWithChars(Token & Result,SourceLocation Loc,const char * TokBegin,unsigned TokLength,StringRef Text)122 void formTokenWithChars(Token &Result,
123 SourceLocation Loc,
124 const char *TokBegin,
125 unsigned TokLength,
126 StringRef Text) {
127 Result.setLocation(Loc);
128 Result.setKind(tok::text);
129 Result.setLength(TokLength);
130 #ifndef NDEBUG
131 Result.TextPtr = "<UNSET>";
132 Result.IntVal = 7;
133 #endif
134 Result.setText(Text);
135 }
136
137 public:
TextTokenRetokenizer(llvm::BumpPtrAllocator & Allocator,Parser & P)138 TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P):
139 Allocator(Allocator), P(P), NoMoreInterestingTokens(false) {
140 Pos.CurToken = 0;
141 addToken();
142 }
143
144 /// Extract a word -- sequence of non-whitespace characters.
lexWord(Token & Tok)145 bool lexWord(Token &Tok) {
146 if (isEnd())
147 return false;
148
149 Position SavedPos = Pos;
150
151 consumeWhitespace();
152 SmallString<32> WordText;
153 const char *WordBegin = Pos.BufferPtr;
154 SourceLocation Loc = getSourceLocation();
155 while (!isEnd()) {
156 const char C = peek();
157 if (!isWhitespace(C)) {
158 WordText.push_back(C);
159 consumeChar();
160 } else
161 break;
162 }
163 const unsigned Length = WordText.size();
164 if (Length == 0) {
165 Pos = SavedPos;
166 return false;
167 }
168
169 char *TextPtr = Allocator.Allocate<char>(Length + 1);
170
171 memcpy(TextPtr, WordText.c_str(), Length + 1);
172 StringRef Text = StringRef(TextPtr, Length);
173
174 formTokenWithChars(Tok, Loc, WordBegin, Length, Text);
175 return true;
176 }
177
lexDelimitedSeq(Token & Tok,char OpenDelim,char CloseDelim)178 bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) {
179 if (isEnd())
180 return false;
181
182 Position SavedPos = Pos;
183
184 consumeWhitespace();
185 SmallString<32> WordText;
186 const char *WordBegin = Pos.BufferPtr;
187 SourceLocation Loc = getSourceLocation();
188 bool Error = false;
189 if (!isEnd()) {
190 const char C = peek();
191 if (C == OpenDelim) {
192 WordText.push_back(C);
193 consumeChar();
194 } else
195 Error = true;
196 }
197 char C = '\0';
198 while (!Error && !isEnd()) {
199 C = peek();
200 WordText.push_back(C);
201 consumeChar();
202 if (C == CloseDelim)
203 break;
204 }
205 if (!Error && C != CloseDelim)
206 Error = true;
207
208 if (Error) {
209 Pos = SavedPos;
210 return false;
211 }
212
213 const unsigned Length = WordText.size();
214 char *TextPtr = Allocator.Allocate<char>(Length + 1);
215
216 memcpy(TextPtr, WordText.c_str(), Length + 1);
217 StringRef Text = StringRef(TextPtr, Length);
218
219 formTokenWithChars(Tok, Loc, WordBegin,
220 Pos.BufferPtr - WordBegin, Text);
221 return true;
222 }
223
224 /// Put back tokens that we didn't consume.
putBackLeftoverTokens()225 void putBackLeftoverTokens() {
226 if (isEnd())
227 return;
228
229 bool HavePartialTok = false;
230 Token PartialTok;
231 if (Pos.BufferPtr != Pos.BufferStart) {
232 formTokenWithChars(PartialTok, getSourceLocation(),
233 Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr,
234 StringRef(Pos.BufferPtr,
235 Pos.BufferEnd - Pos.BufferPtr));
236 HavePartialTok = true;
237 Pos.CurToken++;
238 }
239
240 P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end()));
241 Pos.CurToken = Toks.size();
242
243 if (HavePartialTok)
244 P.putBack(PartialTok);
245 }
246 };
247
Parser(Lexer & L,Sema & S,llvm::BumpPtrAllocator & Allocator,const SourceManager & SourceMgr,DiagnosticsEngine & Diags,const CommandTraits & Traits)248 Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
249 const SourceManager &SourceMgr, DiagnosticsEngine &Diags,
250 const CommandTraits &Traits):
251 L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
252 Traits(Traits) {
253 consumeToken();
254 }
255
parseParamCommandArgs(ParamCommandComment * PC,TextTokenRetokenizer & Retokenizer)256 void Parser::parseParamCommandArgs(ParamCommandComment *PC,
257 TextTokenRetokenizer &Retokenizer) {
258 Token Arg;
259 // Check if argument looks like direction specification: [dir]
260 // e.g., [in], [out], [in,out]
261 if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
262 S.actOnParamCommandDirectionArg(PC,
263 Arg.getLocation(),
264 Arg.getEndLocation(),
265 Arg.getText());
266
267 if (Retokenizer.lexWord(Arg))
268 S.actOnParamCommandParamNameArg(PC,
269 Arg.getLocation(),
270 Arg.getEndLocation(),
271 Arg.getText());
272 }
273
parseTParamCommandArgs(TParamCommandComment * TPC,TextTokenRetokenizer & Retokenizer)274 void Parser::parseTParamCommandArgs(TParamCommandComment *TPC,
275 TextTokenRetokenizer &Retokenizer) {
276 Token Arg;
277 if (Retokenizer.lexWord(Arg))
278 S.actOnTParamCommandParamNameArg(TPC,
279 Arg.getLocation(),
280 Arg.getEndLocation(),
281 Arg.getText());
282 }
283
parseBlockCommandArgs(BlockCommandComment * BC,TextTokenRetokenizer & Retokenizer,unsigned NumArgs)284 void Parser::parseBlockCommandArgs(BlockCommandComment *BC,
285 TextTokenRetokenizer &Retokenizer,
286 unsigned NumArgs) {
287 typedef BlockCommandComment::Argument Argument;
288 Argument *Args =
289 new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs];
290 unsigned ParsedArgs = 0;
291 Token Arg;
292 while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) {
293 Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(),
294 Arg.getEndLocation()),
295 Arg.getText());
296 ParsedArgs++;
297 }
298
299 S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs));
300 }
301
parseBlockCommand()302 BlockCommandComment *Parser::parseBlockCommand() {
303 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
304
305 ParamCommandComment *PC;
306 TParamCommandComment *TPC;
307 BlockCommandComment *BC;
308 bool IsParam = false;
309 bool IsTParam = false;
310 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
311 CommandMarkerKind CommandMarker =
312 Tok.is(tok::backslash_command) ? CMK_Backslash : CMK_At;
313 if (Info->IsParamCommand) {
314 IsParam = true;
315 PC = S.actOnParamCommandStart(Tok.getLocation(),
316 Tok.getEndLocation(),
317 Tok.getCommandID(),
318 CommandMarker);
319 } else if (Info->IsTParamCommand) {
320 IsTParam = true;
321 TPC = S.actOnTParamCommandStart(Tok.getLocation(),
322 Tok.getEndLocation(),
323 Tok.getCommandID(),
324 CommandMarker);
325 } else {
326 BC = S.actOnBlockCommandStart(Tok.getLocation(),
327 Tok.getEndLocation(),
328 Tok.getCommandID(),
329 CommandMarker);
330 }
331 consumeToken();
332
333 if (isTokBlockCommand()) {
334 // Block command ahead. We can't nest block commands, so pretend that this
335 // command has an empty argument.
336 ParagraphComment *Paragraph = S.actOnParagraphComment(
337 ArrayRef<InlineContentComment *>());
338 if (IsParam) {
339 S.actOnParamCommandFinish(PC, Paragraph);
340 return PC;
341 } else if (IsTParam) {
342 S.actOnTParamCommandFinish(TPC, Paragraph);
343 return TPC;
344 } else {
345 S.actOnBlockCommandFinish(BC, Paragraph);
346 return BC;
347 }
348 }
349
350 if (IsParam || IsTParam || Info->NumArgs > 0) {
351 // In order to parse command arguments we need to retokenize a few
352 // following text tokens.
353 TextTokenRetokenizer Retokenizer(Allocator, *this);
354
355 if (IsParam)
356 parseParamCommandArgs(PC, Retokenizer);
357 else if (IsTParam)
358 parseTParamCommandArgs(TPC, Retokenizer);
359 else
360 parseBlockCommandArgs(BC, Retokenizer, Info->NumArgs);
361
362 Retokenizer.putBackLeftoverTokens();
363 }
364
365 // If there's a block command ahead, we will attach an empty paragraph to
366 // this command.
367 bool EmptyParagraph = false;
368 if (isTokBlockCommand())
369 EmptyParagraph = true;
370 else if (Tok.is(tok::newline)) {
371 Token PrevTok = Tok;
372 consumeToken();
373 EmptyParagraph = isTokBlockCommand();
374 putBack(PrevTok);
375 }
376
377 ParagraphComment *Paragraph;
378 if (EmptyParagraph)
379 Paragraph = S.actOnParagraphComment(ArrayRef<InlineContentComment *>());
380 else {
381 BlockContentComment *Block = parseParagraphOrBlockCommand();
382 // Since we have checked for a block command, we should have parsed a
383 // paragraph.
384 Paragraph = cast<ParagraphComment>(Block);
385 }
386
387 if (IsParam) {
388 S.actOnParamCommandFinish(PC, Paragraph);
389 return PC;
390 } else if (IsTParam) {
391 S.actOnTParamCommandFinish(TPC, Paragraph);
392 return TPC;
393 } else {
394 S.actOnBlockCommandFinish(BC, Paragraph);
395 return BC;
396 }
397 }
398
parseInlineCommand()399 InlineCommandComment *Parser::parseInlineCommand() {
400 assert(Tok.is(tok::backslash_command) || Tok.is(tok::at_command));
401
402 const Token CommandTok = Tok;
403 consumeToken();
404
405 TextTokenRetokenizer Retokenizer(Allocator, *this);
406
407 Token ArgTok;
408 bool ArgTokValid = Retokenizer.lexWord(ArgTok);
409
410 InlineCommandComment *IC;
411 if (ArgTokValid) {
412 IC = S.actOnInlineCommand(CommandTok.getLocation(),
413 CommandTok.getEndLocation(),
414 CommandTok.getCommandID(),
415 ArgTok.getLocation(),
416 ArgTok.getEndLocation(),
417 ArgTok.getText());
418 } else {
419 IC = S.actOnInlineCommand(CommandTok.getLocation(),
420 CommandTok.getEndLocation(),
421 CommandTok.getCommandID());
422 }
423
424 Retokenizer.putBackLeftoverTokens();
425
426 return IC;
427 }
428
parseHTMLStartTag()429 HTMLStartTagComment *Parser::parseHTMLStartTag() {
430 assert(Tok.is(tok::html_start_tag));
431 HTMLStartTagComment *HST =
432 S.actOnHTMLStartTagStart(Tok.getLocation(),
433 Tok.getHTMLTagStartName());
434 consumeToken();
435
436 SmallVector<HTMLStartTagComment::Attribute, 2> Attrs;
437 while (true) {
438 switch (Tok.getKind()) {
439 case tok::html_ident: {
440 Token Ident = Tok;
441 consumeToken();
442 if (Tok.isNot(tok::html_equals)) {
443 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
444 Ident.getHTMLIdent()));
445 continue;
446 }
447 Token Equals = Tok;
448 consumeToken();
449 if (Tok.isNot(tok::html_quoted_string)) {
450 Diag(Tok.getLocation(),
451 diag::warn_doc_html_start_tag_expected_quoted_string)
452 << SourceRange(Equals.getLocation());
453 Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(),
454 Ident.getHTMLIdent()));
455 while (Tok.is(tok::html_equals) ||
456 Tok.is(tok::html_quoted_string))
457 consumeToken();
458 continue;
459 }
460 Attrs.push_back(HTMLStartTagComment::Attribute(
461 Ident.getLocation(),
462 Ident.getHTMLIdent(),
463 Equals.getLocation(),
464 SourceRange(Tok.getLocation(),
465 Tok.getEndLocation()),
466 Tok.getHTMLQuotedString()));
467 consumeToken();
468 continue;
469 }
470
471 case tok::html_greater:
472 S.actOnHTMLStartTagFinish(HST,
473 S.copyArray(llvm::makeArrayRef(Attrs)),
474 Tok.getLocation(),
475 /* IsSelfClosing = */ false);
476 consumeToken();
477 return HST;
478
479 case tok::html_slash_greater:
480 S.actOnHTMLStartTagFinish(HST,
481 S.copyArray(llvm::makeArrayRef(Attrs)),
482 Tok.getLocation(),
483 /* IsSelfClosing = */ true);
484 consumeToken();
485 return HST;
486
487 case tok::html_equals:
488 case tok::html_quoted_string:
489 Diag(Tok.getLocation(),
490 diag::warn_doc_html_start_tag_expected_ident_or_greater);
491 while (Tok.is(tok::html_equals) ||
492 Tok.is(tok::html_quoted_string))
493 consumeToken();
494 if (Tok.is(tok::html_ident) ||
495 Tok.is(tok::html_greater) ||
496 Tok.is(tok::html_slash_greater))
497 continue;
498
499 S.actOnHTMLStartTagFinish(HST,
500 S.copyArray(llvm::makeArrayRef(Attrs)),
501 SourceLocation(),
502 /* IsSelfClosing = */ false);
503 return HST;
504
505 default:
506 // Not a token from an HTML start tag. Thus HTML tag prematurely ended.
507 S.actOnHTMLStartTagFinish(HST,
508 S.copyArray(llvm::makeArrayRef(Attrs)),
509 SourceLocation(),
510 /* IsSelfClosing = */ false);
511 bool StartLineInvalid;
512 const unsigned StartLine = SourceMgr.getPresumedLineNumber(
513 HST->getLocation(),
514 &StartLineInvalid);
515 bool EndLineInvalid;
516 const unsigned EndLine = SourceMgr.getPresumedLineNumber(
517 Tok.getLocation(),
518 &EndLineInvalid);
519 if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
520 Diag(Tok.getLocation(),
521 diag::warn_doc_html_start_tag_expected_ident_or_greater)
522 << HST->getSourceRange();
523 else {
524 Diag(Tok.getLocation(),
525 diag::warn_doc_html_start_tag_expected_ident_or_greater);
526 Diag(HST->getLocation(), diag::note_doc_html_tag_started_here)
527 << HST->getSourceRange();
528 }
529 return HST;
530 }
531 }
532 }
533
parseHTMLEndTag()534 HTMLEndTagComment *Parser::parseHTMLEndTag() {
535 assert(Tok.is(tok::html_end_tag));
536 Token TokEndTag = Tok;
537 consumeToken();
538 SourceLocation Loc;
539 if (Tok.is(tok::html_greater)) {
540 Loc = Tok.getLocation();
541 consumeToken();
542 }
543
544 return S.actOnHTMLEndTag(TokEndTag.getLocation(),
545 Loc,
546 TokEndTag.getHTMLTagEndName());
547 }
548
parseParagraphOrBlockCommand()549 BlockContentComment *Parser::parseParagraphOrBlockCommand() {
550 SmallVector<InlineContentComment *, 8> Content;
551
552 while (true) {
553 switch (Tok.getKind()) {
554 case tok::verbatim_block_begin:
555 case tok::verbatim_line_name:
556 case tok::eof:
557 assert(Content.size() != 0);
558 break; // Block content or EOF ahead, finish this parapgaph.
559
560 case tok::unknown_command:
561 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
562 Tok.getEndLocation(),
563 Tok.getUnknownCommandName()));
564 consumeToken();
565 continue;
566
567 case tok::backslash_command:
568 case tok::at_command: {
569 const CommandInfo *Info = Traits.getCommandInfo(Tok.getCommandID());
570 if (Info->IsBlockCommand) {
571 if (Content.size() == 0)
572 return parseBlockCommand();
573 break; // Block command ahead, finish this parapgaph.
574 }
575 if (Info->IsVerbatimBlockEndCommand) {
576 Diag(Tok.getLocation(),
577 diag::warn_verbatim_block_end_without_start)
578 << Tok.is(tok::at_command)
579 << Info->Name
580 << SourceRange(Tok.getLocation(), Tok.getEndLocation());
581 consumeToken();
582 continue;
583 }
584 if (Info->IsUnknownCommand) {
585 Content.push_back(S.actOnUnknownCommand(Tok.getLocation(),
586 Tok.getEndLocation(),
587 Info->getID()));
588 consumeToken();
589 continue;
590 }
591 assert(Info->IsInlineCommand);
592 Content.push_back(parseInlineCommand());
593 continue;
594 }
595
596 case tok::newline: {
597 consumeToken();
598 if (Tok.is(tok::newline) || Tok.is(tok::eof)) {
599 consumeToken();
600 break; // Two newlines -- end of paragraph.
601 }
602 if (Content.size() > 0)
603 Content.back()->addTrailingNewline();
604 continue;
605 }
606
607 // Don't deal with HTML tag soup now.
608 case tok::html_start_tag:
609 Content.push_back(parseHTMLStartTag());
610 continue;
611
612 case tok::html_end_tag:
613 Content.push_back(parseHTMLEndTag());
614 continue;
615
616 case tok::text:
617 Content.push_back(S.actOnText(Tok.getLocation(),
618 Tok.getEndLocation(),
619 Tok.getText()));
620 consumeToken();
621 continue;
622
623 case tok::verbatim_block_line:
624 case tok::verbatim_block_end:
625 case tok::verbatim_line_text:
626 case tok::html_ident:
627 case tok::html_equals:
628 case tok::html_quoted_string:
629 case tok::html_greater:
630 case tok::html_slash_greater:
631 llvm_unreachable("should not see this token");
632 }
633 break;
634 }
635
636 return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
637 }
638
parseVerbatimBlock()639 VerbatimBlockComment *Parser::parseVerbatimBlock() {
640 assert(Tok.is(tok::verbatim_block_begin));
641
642 VerbatimBlockComment *VB =
643 S.actOnVerbatimBlockStart(Tok.getLocation(),
644 Tok.getVerbatimBlockID());
645 consumeToken();
646
647 // Don't create an empty line if verbatim opening command is followed
648 // by a newline.
649 if (Tok.is(tok::newline))
650 consumeToken();
651
652 SmallVector<VerbatimBlockLineComment *, 8> Lines;
653 while (Tok.is(tok::verbatim_block_line) ||
654 Tok.is(tok::newline)) {
655 VerbatimBlockLineComment *Line;
656 if (Tok.is(tok::verbatim_block_line)) {
657 Line = S.actOnVerbatimBlockLine(Tok.getLocation(),
658 Tok.getVerbatimBlockText());
659 consumeToken();
660 if (Tok.is(tok::newline)) {
661 consumeToken();
662 }
663 } else {
664 // Empty line, just a tok::newline.
665 Line = S.actOnVerbatimBlockLine(Tok.getLocation(), "");
666 consumeToken();
667 }
668 Lines.push_back(Line);
669 }
670
671 if (Tok.is(tok::verbatim_block_end)) {
672 const CommandInfo *Info = Traits.getCommandInfo(Tok.getVerbatimBlockID());
673 S.actOnVerbatimBlockFinish(VB, Tok.getLocation(),
674 Info->Name,
675 S.copyArray(llvm::makeArrayRef(Lines)));
676 consumeToken();
677 } else {
678 // Unterminated \\verbatim block
679 S.actOnVerbatimBlockFinish(VB, SourceLocation(), "",
680 S.copyArray(llvm::makeArrayRef(Lines)));
681 }
682
683 return VB;
684 }
685
parseVerbatimLine()686 VerbatimLineComment *Parser::parseVerbatimLine() {
687 assert(Tok.is(tok::verbatim_line_name));
688
689 Token NameTok = Tok;
690 consumeToken();
691
692 SourceLocation TextBegin;
693 StringRef Text;
694 // Next token might not be a tok::verbatim_line_text if verbatim line
695 // starting command comes just before a newline or comment end.
696 if (Tok.is(tok::verbatim_line_text)) {
697 TextBegin = Tok.getLocation();
698 Text = Tok.getVerbatimLineText();
699 } else {
700 TextBegin = NameTok.getEndLocation();
701 Text = "";
702 }
703
704 VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(),
705 NameTok.getVerbatimLineID(),
706 TextBegin,
707 Text);
708 consumeToken();
709 return VL;
710 }
711
parseBlockContent()712 BlockContentComment *Parser::parseBlockContent() {
713 switch (Tok.getKind()) {
714 case tok::text:
715 case tok::unknown_command:
716 case tok::backslash_command:
717 case tok::at_command:
718 case tok::html_start_tag:
719 case tok::html_end_tag:
720 return parseParagraphOrBlockCommand();
721
722 case tok::verbatim_block_begin:
723 return parseVerbatimBlock();
724
725 case tok::verbatim_line_name:
726 return parseVerbatimLine();
727
728 case tok::eof:
729 case tok::newline:
730 case tok::verbatim_block_line:
731 case tok::verbatim_block_end:
732 case tok::verbatim_line_text:
733 case tok::html_ident:
734 case tok::html_equals:
735 case tok::html_quoted_string:
736 case tok::html_greater:
737 case tok::html_slash_greater:
738 llvm_unreachable("should not see this token");
739 }
740 llvm_unreachable("bogus token kind");
741 }
742
parseFullComment()743 FullComment *Parser::parseFullComment() {
744 // Skip newlines at the beginning of the comment.
745 while (Tok.is(tok::newline))
746 consumeToken();
747
748 SmallVector<BlockContentComment *, 8> Blocks;
749 while (Tok.isNot(tok::eof)) {
750 Blocks.push_back(parseBlockContent());
751
752 // Skip extra newlines after paragraph end.
753 while (Tok.is(tok::newline))
754 consumeToken();
755 }
756 return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks)));
757 }
758
759 } // end namespace comments
760 } // end namespace clang
761