1 #include "clang/AST/CommentLexer.h" 2 #include "llvm/ADT/StringSwitch.h" 3 #include "llvm/Support/ErrorHandling.h" 4 5 namespace clang { 6 namespace comments { 7 8 void Token::dump(const Lexer &L, const SourceManager &SM) const { 9 llvm::errs() << "comments::Token Kind=" << Kind << " "; 10 Loc.dump(SM); 11 llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n"; 12 } 13 14 bool Lexer::isVerbatimBlockCommand(StringRef BeginName, 15 StringRef &EndName) const { 16 const char *Result = llvm::StringSwitch<const char *>(BeginName) 17 .Case("code", "endcode") 18 .Case("verbatim", "endverbatim") 19 .Case("htmlonly", "endhtmlonly") 20 .Case("latexonly", "endlatexonly") 21 .Case("xmlonly", "endxmlonly") 22 .Case("manonly", "endmanonly") 23 .Case("rtfonly", "endrtfonly") 24 25 .Case("dot", "enddot") 26 .Case("msc", "endmsc") 27 28 .Case("f$", "f$") // Inline LaTeX formula 29 .Case("f[", "f]") // Displayed LaTeX formula 30 .Case("f{", "f}") // LaTeX environment 31 32 .Default(NULL); 33 34 if (Result) { 35 EndName = Result; 36 return true; 37 } 38 39 for (VerbatimBlockCommandVector::const_iterator 40 I = VerbatimBlockCommands.begin(), 41 E = VerbatimBlockCommands.end(); 42 I != E; ++I) 43 if (I->BeginName == BeginName) { 44 EndName = I->EndName; 45 return true; 46 } 47 48 return false; 49 } 50 51 bool Lexer::isVerbatimLineCommand(StringRef Name) const { 52 bool Result = llvm::StringSwitch<bool>(Name) 53 .Case("fn", true) 54 .Case("var", true) 55 .Case("property", true) 56 .Case("typedef", true) 57 58 .Case("overload", true) 59 60 .Case("defgroup", true) 61 .Case("ingroup", true) 62 .Case("addtogroup", true) 63 .Case("weakgroup", true) 64 .Case("name", true) 65 66 .Case("section", true) 67 .Case("subsection", true) 68 .Case("subsubsection", true) 69 .Case("paragraph", true) 70 71 .Case("mainpage", true) 72 .Case("subpage", true) 73 .Case("ref", true) 74 75 .Default(false); 76 77 if (Result) 78 return true; 79 80 for (VerbatimLineCommandVector::const_iterator 81 I = VerbatimLineCommands.begin(), 82 E = VerbatimLineCommands.end(); 83 I != E; ++I) 84 if (I->Name == Name) 85 return true; 86 87 return false; 88 } 89 90 void Lexer::skipLineStartingDecorations() { 91 // This function should be called only for C comments 92 assert(CommentState == LCS_InsideCComment); 93 94 if (BufferPtr == CommentEnd) 95 return; 96 97 switch (*BufferPtr) { 98 case ' ': 99 case '\t': 100 case '\f': 101 case '\v': { 102 const char *NewBufferPtr = BufferPtr; 103 NewBufferPtr++; 104 if (NewBufferPtr == CommentEnd) 105 return; 106 107 char C = *NewBufferPtr; 108 while (C == ' ' || C == '\t' || C == '\f' || C == '\v') { 109 NewBufferPtr++; 110 if (NewBufferPtr == CommentEnd) 111 return; 112 C = *NewBufferPtr; 113 } 114 if (C == '*') 115 BufferPtr = NewBufferPtr + 1; 116 break; 117 } 118 case '*': 119 BufferPtr++; 120 break; 121 } 122 } 123 124 namespace { 125 const char *findNewline(const char *BufferPtr, const char *BufferEnd) { 126 for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 127 const char C = *BufferPtr; 128 if (C == '\n' || C == '\r') 129 return BufferPtr; 130 } 131 return BufferEnd; 132 } 133 134 const char *skipNewline(const char *BufferPtr, const char *BufferEnd) { 135 if (BufferPtr == BufferEnd) 136 return BufferPtr; 137 138 if (*BufferPtr == '\n') 139 BufferPtr++; 140 else { 141 assert(*BufferPtr == '\r'); 142 BufferPtr++; 143 if (BufferPtr != BufferEnd && *BufferPtr == '\n') 144 BufferPtr++; 145 } 146 return BufferPtr; 147 } 148 149 bool isHTMLIdentifierCharacter(char C) { 150 return (C >= 'a' && C <= 'z') || 151 (C >= 'A' && C <= 'Z') || 152 (C >= '0' && C <= '9'); 153 } 154 155 const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) { 156 for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 157 if (!isHTMLIdentifierCharacter(*BufferPtr)) 158 return BufferPtr; 159 } 160 return BufferEnd; 161 } 162 163 /// Skip HTML string quoted in single or double quotes. Escaping quotes inside 164 /// string allowed. 165 /// 166 /// Returns pointer to closing quote. 167 const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd) 168 { 169 const char Quote = *BufferPtr; 170 assert(Quote == '\"' || Quote == '\''); 171 172 BufferPtr++; 173 for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 174 const char C = *BufferPtr; 175 if (C == Quote && BufferPtr[-1] != '\\') 176 return BufferPtr; 177 } 178 return BufferEnd; 179 } 180 181 bool isHorizontalWhitespace(char C) { 182 return C == ' ' || C == '\t' || C == '\f' || C == '\v'; 183 } 184 185 bool isWhitespace(char C) { 186 return C == ' ' || C == '\n' || C == '\r' || 187 C == '\t' || C == '\f' || C == '\v'; 188 } 189 190 const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) { 191 for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 192 if (!isWhitespace(*BufferPtr)) 193 return BufferPtr; 194 } 195 return BufferEnd; 196 } 197 198 bool isCommandNameCharacter(char C) { 199 return (C >= 'a' && C <= 'z') || 200 (C >= 'A' && C <= 'Z') || 201 (C >= '0' && C <= '9'); 202 } 203 204 const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) { 205 for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 206 if (!isCommandNameCharacter(*BufferPtr)) 207 return BufferPtr; 208 } 209 return BufferEnd; 210 } 211 212 /// Return the one past end pointer for BCPL comments. 213 /// Handles newlines escaped with backslash or trigraph for backslahs. 214 const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) { 215 const char *CurPtr = BufferPtr; 216 while (CurPtr != BufferEnd) { 217 char C = *CurPtr; 218 while (C != '\n' && C != '\r') { 219 CurPtr++; 220 if (CurPtr == BufferEnd) 221 return BufferEnd; 222 C = *CurPtr; 223 } 224 // We found a newline, check if it is escaped. 225 const char *EscapePtr = CurPtr - 1; 226 while(isHorizontalWhitespace(*EscapePtr)) 227 EscapePtr--; 228 229 if (*EscapePtr == '\\' || 230 (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' && 231 EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) { 232 // We found an escaped newline. 233 CurPtr = skipNewline(CurPtr, BufferEnd); 234 } else 235 return CurPtr; // Not an escaped newline. 236 } 237 return BufferEnd; 238 } 239 240 /// Return the one past end pointer for C comments. 241 /// Very dumb, does not handle escaped newlines or trigraphs. 242 const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) { 243 for ( ; BufferPtr != BufferEnd; ++BufferPtr) { 244 if (*BufferPtr == '*') { 245 assert(BufferPtr + 1 != BufferEnd); 246 if (*(BufferPtr + 1) == '/') 247 return BufferPtr; 248 } 249 } 250 llvm_unreachable("buffer end hit before '*/' was seen"); 251 } 252 } // unnamed namespace 253 254 void Lexer::lexCommentText(Token &T) { 255 assert(CommentState == LCS_InsideBCPLComment || 256 CommentState == LCS_InsideCComment); 257 258 switch (State) { 259 case LS_Normal: 260 break; 261 case LS_VerbatimBlockFirstLine: 262 lexVerbatimBlockFirstLine(T); 263 return; 264 case LS_VerbatimBlockBody: 265 lexVerbatimBlockBody(T); 266 return; 267 case LS_VerbatimLineText: 268 lexVerbatimLineText(T); 269 return; 270 case LS_HTMLOpenTag: 271 lexHTMLOpenTag(T); 272 return; 273 } 274 275 assert(State == LS_Normal); 276 277 const char *TokenPtr = BufferPtr; 278 assert(TokenPtr < CommentEnd); 279 while (TokenPtr != CommentEnd) { 280 switch(*TokenPtr) { 281 case '\\': 282 case '@': { 283 TokenPtr++; 284 if (TokenPtr == CommentEnd) { 285 StringRef Text(BufferPtr, TokenPtr - BufferPtr); 286 formTokenWithChars(T, TokenPtr, tok::text); 287 T.setText(Text); 288 return; 289 } 290 char C = *TokenPtr; 291 switch (C) { 292 default: 293 break; 294 295 case '\\': case '@': case '&': case '$': 296 case '#': case '<': case '>': case '%': 297 case '\"': case '.': case ':': 298 // This is one of \\ \@ \& \$ etc escape sequences. 299 TokenPtr++; 300 if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') { 301 // This is the \:: escape sequence. 302 TokenPtr++; 303 } 304 StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1)); 305 formTokenWithChars(T, TokenPtr, tok::text); 306 T.setText(UnescapedText); 307 return; 308 } 309 310 // Don't make zero-length commands. 311 if (!isCommandNameCharacter(*TokenPtr)) { 312 StringRef Text(BufferPtr, TokenPtr - BufferPtr); 313 formTokenWithChars(T, TokenPtr, tok::text); 314 T.setText(Text); 315 return; 316 } 317 318 TokenPtr = skipCommandName(TokenPtr, CommentEnd); 319 unsigned Length = TokenPtr - (BufferPtr + 1); 320 321 // Hardcoded support for lexing LaTeX formula commands 322 // \f$ \f[ \f] \f{ \f} as a single command. 323 if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) { 324 C = *TokenPtr; 325 if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') { 326 TokenPtr++; 327 Length++; 328 } 329 } 330 331 const StringRef CommandName(BufferPtr + 1, Length); 332 StringRef EndName; 333 334 if (isVerbatimBlockCommand(CommandName, EndName)) { 335 setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, EndName); 336 return; 337 } 338 if (isVerbatimLineCommand(CommandName)) { 339 setupAndLexVerbatimLine(T, TokenPtr); 340 return; 341 } 342 formTokenWithChars(T, TokenPtr, tok::command); 343 T.setCommandName(CommandName); 344 return; 345 } 346 347 case '<': { 348 TokenPtr++; 349 if (TokenPtr == CommentEnd) { 350 StringRef Text(BufferPtr, TokenPtr - BufferPtr); 351 formTokenWithChars(T, TokenPtr, tok::text); 352 T.setText(Text); 353 return; 354 } 355 const char C = *TokenPtr; 356 if (isHTMLIdentifierCharacter(C)) 357 setupAndLexHTMLOpenTag(T); 358 else if (C == '/') 359 lexHTMLCloseTag(T); 360 else { 361 StringRef Text(BufferPtr, TokenPtr - BufferPtr); 362 formTokenWithChars(T, TokenPtr, tok::text); 363 T.setText(Text); 364 } 365 return; 366 } 367 368 case '\n': 369 case '\r': 370 TokenPtr = skipNewline(TokenPtr, CommentEnd); 371 formTokenWithChars(T, TokenPtr, tok::newline); 372 373 if (CommentState == LCS_InsideCComment) 374 skipLineStartingDecorations(); 375 return; 376 377 default: { 378 while (true) { 379 TokenPtr++; 380 if (TokenPtr == CommentEnd) 381 break; 382 char C = *TokenPtr; 383 if(C == '\n' || C == '\r' || 384 C == '\\' || C == '@' || C == '<') 385 break; 386 } 387 StringRef Text(BufferPtr, TokenPtr - BufferPtr); 388 formTokenWithChars(T, TokenPtr, tok::text); 389 T.setText(Text); 390 return; 391 } 392 } 393 } 394 } 395 396 void Lexer::setupAndLexVerbatimBlock(Token &T, 397 const char *TextBegin, 398 char Marker, StringRef EndName) { 399 VerbatimBlockEndCommandName.clear(); 400 VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@"); 401 VerbatimBlockEndCommandName.append(EndName); 402 403 StringRef Name(BufferPtr + 1, TextBegin - (BufferPtr + 1)); 404 formTokenWithChars(T, TextBegin, tok::verbatim_block_begin); 405 T.setVerbatimBlockName(Name); 406 407 State = LS_VerbatimBlockFirstLine; 408 } 409 410 void Lexer::lexVerbatimBlockFirstLine(Token &T) { 411 assert(BufferPtr < CommentEnd); 412 413 // FIXME: It would be better to scan the text once, finding either the block 414 // end command or newline. 415 // 416 // Extract current line. 417 const char *Newline = findNewline(BufferPtr, CommentEnd); 418 StringRef Line(BufferPtr, Newline - BufferPtr); 419 420 // Look for end command in current line. 421 size_t Pos = Line.find(VerbatimBlockEndCommandName); 422 const char *NextLine; 423 if (Pos == StringRef::npos) { 424 // Current line is completely verbatim. 425 NextLine = skipNewline(Newline, CommentEnd); 426 } else if (Pos == 0) { 427 // Current line contains just an end command. 428 const char *End = BufferPtr + VerbatimBlockEndCommandName.size(); 429 StringRef Name(BufferPtr + 1, End - (BufferPtr + 1)); 430 formTokenWithChars(T, End, tok::verbatim_block_end); 431 T.setVerbatimBlockName(Name); 432 State = LS_Normal; 433 return; 434 } else { 435 // There is some text, followed by end command. Extract text first. 436 NextLine = BufferPtr + Pos; 437 } 438 439 StringRef Text(BufferPtr, NextLine - BufferPtr); 440 formTokenWithChars(T, NextLine, tok::verbatim_block_line); 441 T.setVerbatimBlockText(Text); 442 443 State = LS_VerbatimBlockBody; 444 } 445 446 void Lexer::lexVerbatimBlockBody(Token &T) { 447 assert(State == LS_VerbatimBlockBody); 448 449 if (CommentState == LCS_InsideCComment) 450 skipLineStartingDecorations(); 451 452 lexVerbatimBlockFirstLine(T); 453 } 454 455 void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin) { 456 const StringRef Name(BufferPtr + 1, TextBegin - BufferPtr - 1); 457 formTokenWithChars(T, TextBegin, tok::verbatim_line_name); 458 T.setVerbatimLineName(Name); 459 460 State = LS_VerbatimLineText; 461 } 462 463 void Lexer::lexVerbatimLineText(Token &T) { 464 assert(State == LS_VerbatimLineText); 465 466 // Extract current line. 467 const char *Newline = findNewline(BufferPtr, CommentEnd); 468 const StringRef Text(BufferPtr, Newline - BufferPtr); 469 formTokenWithChars(T, Newline, tok::verbatim_line_text); 470 T.setVerbatimLineText(Text); 471 472 State = LS_Normal; 473 } 474 475 void Lexer::setupAndLexHTMLOpenTag(Token &T) { 476 assert(BufferPtr[0] == '<' && isHTMLIdentifierCharacter(BufferPtr[1])); 477 const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd); 478 479 StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1)); 480 formTokenWithChars(T, TagNameEnd, tok::html_tag_open); 481 T.setHTMLTagOpenName(Name); 482 483 BufferPtr = skipWhitespace(BufferPtr, CommentEnd); 484 485 if (BufferPtr != CommentEnd && *BufferPtr == '>') { 486 BufferPtr++; 487 return; 488 } 489 490 if (BufferPtr != CommentEnd && isHTMLIdentifierCharacter(*BufferPtr)) 491 State = LS_HTMLOpenTag; 492 } 493 494 void Lexer::lexHTMLOpenTag(Token &T) { 495 assert(State == LS_HTMLOpenTag); 496 497 const char *TokenPtr = BufferPtr; 498 char C = *TokenPtr; 499 if (isHTMLIdentifierCharacter(C)) { 500 TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd); 501 StringRef Ident(BufferPtr, TokenPtr - BufferPtr); 502 formTokenWithChars(T, TokenPtr, tok::html_ident); 503 T.setHTMLIdent(Ident); 504 } else { 505 switch (C) { 506 case '=': 507 TokenPtr++; 508 formTokenWithChars(T, TokenPtr, tok::html_equals); 509 break; 510 case '\"': 511 case '\'': { 512 const char *OpenQuote = TokenPtr; 513 TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd); 514 const char *ClosingQuote = TokenPtr; 515 if (TokenPtr != CommentEnd) // Skip closing quote. 516 TokenPtr++; 517 formTokenWithChars(T, TokenPtr, tok::html_quoted_string); 518 T.setHTMLQuotedString(StringRef(OpenQuote + 1, 519 ClosingQuote - (OpenQuote + 1))); 520 break; 521 } 522 case '>': 523 TokenPtr++; 524 formTokenWithChars(T, TokenPtr, tok::html_greater); 525 break; 526 } 527 } 528 529 // Now look ahead and return to normal state if we don't see any HTML tokens 530 // ahead. 531 BufferPtr = skipWhitespace(BufferPtr, CommentEnd); 532 if (BufferPtr == CommentEnd) { 533 State = LS_Normal; 534 return; 535 } 536 537 C = *BufferPtr; 538 if (!isHTMLIdentifierCharacter(C) && 539 C != '=' && C != '\"' && C != '\'' && C != '>') { 540 State = LS_Normal; 541 return; 542 } 543 } 544 545 void Lexer::lexHTMLCloseTag(Token &T) { 546 assert(BufferPtr[0] == '<' && BufferPtr[1] == '/'); 547 548 const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd); 549 const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd); 550 551 const char *End = skipWhitespace(TagNameEnd, CommentEnd); 552 if (End != CommentEnd && *End == '>') 553 End++; 554 555 formTokenWithChars(T, End, tok::html_tag_close); 556 T.setHTMLTagCloseName(StringRef(TagNameBegin, TagNameEnd - TagNameBegin)); 557 } 558 559 Lexer::Lexer(SourceLocation FileLoc, const CommentOptions &CommOpts, 560 const char *BufferStart, const char *BufferEnd): 561 BufferStart(BufferStart), BufferEnd(BufferEnd), 562 FileLoc(FileLoc), CommOpts(CommOpts), BufferPtr(BufferStart), 563 CommentState(LCS_BeforeComment), State(LS_Normal) { 564 } 565 566 void Lexer::lex(Token &T) { 567 again: 568 switch (CommentState) { 569 case LCS_BeforeComment: 570 if (BufferPtr == BufferEnd) { 571 formTokenWithChars(T, BufferPtr, tok::eof); 572 return; 573 } 574 575 assert(*BufferPtr == '/'); 576 BufferPtr++; // Skip first slash. 577 switch(*BufferPtr) { 578 case '/': { // BCPL comment. 579 BufferPtr++; // Skip second slash. 580 581 if (BufferPtr != BufferEnd) { 582 // Skip Doxygen magic marker, if it is present. 583 // It might be missing because of a typo //< or /*<, or because we 584 // merged this non-Doxygen comment into a bunch of Doxygen comments 585 // around it: /** ... */ /* ... */ /** ... */ 586 const char C = *BufferPtr; 587 if (C == '/' || C == '!') 588 BufferPtr++; 589 } 590 591 // Skip less-than symbol that marks trailing comments. 592 // Skip it even if the comment is not a Doxygen one, because //< and /*< 593 // are frequent typos. 594 if (BufferPtr != BufferEnd && *BufferPtr == '<') 595 BufferPtr++; 596 597 CommentState = LCS_InsideBCPLComment; 598 State = LS_Normal; 599 CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd); 600 goto again; 601 } 602 case '*': { // C comment. 603 BufferPtr++; // Skip star. 604 605 // Skip Doxygen magic marker. 606 const char C = *BufferPtr; 607 if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!') 608 BufferPtr++; 609 610 // Skip less-than symbol that marks trailing comments. 611 if (BufferPtr != BufferEnd && *BufferPtr == '<') 612 BufferPtr++; 613 614 CommentState = LCS_InsideCComment; 615 State = LS_Normal; 616 CommentEnd = findCCommentEnd(BufferPtr, BufferEnd); 617 goto again; 618 } 619 default: 620 llvm_unreachable("second character of comment should be '/' or '*'"); 621 } 622 623 case LCS_BetweenComments: { 624 // Consecutive comments are extracted only if there is only whitespace 625 // between them. So we can search for the start of the next comment. 626 const char *EndWhitespace = BufferPtr; 627 while(EndWhitespace != BufferEnd && *EndWhitespace != '/') 628 EndWhitespace++; 629 630 // Turn any whitespace between comments (and there is only whitespace 631 // between them) into a newline. We have two newlines between comments 632 // in total (first one was synthesized after a comment). 633 formTokenWithChars(T, EndWhitespace, tok::newline); 634 635 CommentState = LCS_BeforeComment; 636 break; 637 } 638 639 case LCS_InsideBCPLComment: 640 case LCS_InsideCComment: 641 if (BufferPtr != CommentEnd) { 642 lexCommentText(T); 643 break; 644 } else { 645 // Skip C comment closing sequence. 646 if (CommentState == LCS_InsideCComment) { 647 assert(BufferPtr[0] == '*' && BufferPtr[1] == '/'); 648 BufferPtr += 2; 649 assert(BufferPtr <= BufferEnd); 650 651 // Synthenize newline just after the C comment, regardless if there is 652 // actually a newline. 653 formTokenWithChars(T, BufferPtr, tok::newline); 654 655 CommentState = LCS_BetweenComments; 656 break; 657 } else { 658 // Don't synthesized a newline after BCPL comment. 659 CommentState = LCS_BetweenComments; 660 goto again; 661 } 662 } 663 } 664 } 665 666 StringRef Lexer::getSpelling(const Token &Tok, 667 const SourceManager &SourceMgr, 668 bool *Invalid) const { 669 SourceLocation Loc = Tok.getLocation(); 670 std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc); 671 672 bool InvalidTemp = false; 673 StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp); 674 if (InvalidTemp) { 675 *Invalid = true; 676 return StringRef(); 677 } 678 679 const char *Begin = File.data() + LocInfo.second; 680 return StringRef(Begin, Tok.getLength()); 681 } 682 683 void Lexer::addVerbatimBlockCommand(StringRef BeginName, StringRef EndName) { 684 VerbatimBlockCommand VBC; 685 VBC.BeginName = BeginName; 686 VBC.EndName = EndName; 687 VerbatimBlockCommands.push_back(VBC); 688 } 689 690 void Lexer::addVerbatimLineCommand(StringRef Name) { 691 VerbatimLineCommand VLC; 692 VLC.Name = Name; 693 VerbatimLineCommands.push_back(VLC); 694 } 695 696 } // end namespace comments 697 } // end namespace clang 698 699