1 //===--- Comment.h - Comment AST nodes --------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines comment AST nodes. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_CLANG_AST_COMMENT_H 14 #define LLVM_CLANG_AST_COMMENT_H 15 16 #include "clang/AST/CommentCommandTraits.h" 17 #include "clang/AST/DeclObjC.h" 18 #include "clang/AST/Type.h" 19 #include "clang/Basic/SourceLocation.h" 20 #include "llvm/ADT/ArrayRef.h" 21 #include "llvm/ADT/StringRef.h" 22 23 namespace clang { 24 class Decl; 25 class ParmVarDecl; 26 class TemplateParameterList; 27 28 namespace comments { 29 class FullComment; 30 enum class InlineCommandRenderKind; 31 enum class ParamCommandPassDirection; 32 33 /// Describes the syntax that was used in a documentation command. 34 /// 35 /// Exact values of this enumeration are important because they used to select 36 /// parts of diagnostic messages. Audit diagnostics before changing or adding 37 /// a new value. 38 enum CommandMarkerKind { 39 /// Command started with a backslash character: 40 /// \code 41 /// \foo 42 /// \endcode 43 CMK_Backslash = 0, 44 45 /// Command started with an 'at' character: 46 /// \code 47 /// @foo 48 /// \endcode 49 CMK_At = 1 50 }; 51 52 enum class CommentKind { 53 None = 0, 54 #define COMMENT(CLASS, PARENT) CLASS, 55 #define COMMENT_RANGE(BASE, FIRST, LAST) \ 56 First##BASE##Constant = FIRST, Last##BASE##Constant = LAST, 57 #define LAST_COMMENT_RANGE(BASE, FIRST, LAST) \ 58 First##BASE##Constant = FIRST, Last##BASE##Constant = LAST 59 #define ABSTRACT_COMMENT(COMMENT) 60 #include "clang/AST/CommentNodes.inc" 61 }; 62 63 /// Any part of the comment. 64 /// Abstract class. 65 class Comment { 66 protected: 67 /// Preferred location to show caret. 68 SourceLocation Loc; 69 70 /// Source range of this AST node. 71 SourceRange Range; 72 73 class CommentBitfields { 74 friend class Comment; 75 76 /// Type of this AST node. 77 LLVM_PREFERRED_TYPE(CommentKind) 78 unsigned Kind : 8; 79 }; 80 enum { NumCommentBits = 8 }; 81 82 class InlineContentCommentBitfields { 83 friend class InlineContentComment; 84 85 LLVM_PREFERRED_TYPE(CommentBitfields) 86 unsigned : NumCommentBits; 87 88 /// True if there is a newline after this inline content node. 89 /// (There is no separate AST node for a newline.) 90 LLVM_PREFERRED_TYPE(bool) 91 unsigned HasTrailingNewline : 1; 92 }; 93 enum { NumInlineContentCommentBits = NumCommentBits + 1 }; 94 95 class TextCommentBitfields { 96 friend class TextComment; 97 98 LLVM_PREFERRED_TYPE(InlineContentCommentBitfields) 99 unsigned : NumInlineContentCommentBits; 100 101 /// True if \c IsWhitespace field contains a valid value. 102 LLVM_PREFERRED_TYPE(bool) 103 mutable unsigned IsWhitespaceValid : 1; 104 105 /// True if this comment AST node contains only whitespace. 106 LLVM_PREFERRED_TYPE(bool) 107 mutable unsigned IsWhitespace : 1; 108 }; 109 enum { NumTextCommentBits = NumInlineContentCommentBits + 2 }; 110 111 class InlineCommandCommentBitfields { 112 friend class InlineCommandComment; 113 114 LLVM_PREFERRED_TYPE(InlineContentCommentBitfields) 115 unsigned : NumInlineContentCommentBits; 116 117 LLVM_PREFERRED_TYPE(InlineCommandRenderKind) 118 unsigned RenderKind : 3; 119 120 LLVM_PREFERRED_TYPE(CommandTraits::KnownCommandIDs) 121 unsigned CommandID : CommandInfo::NumCommandIDBits; 122 }; 123 enum { NumInlineCommandCommentBits = NumInlineContentCommentBits + 3 + 124 CommandInfo::NumCommandIDBits }; 125 126 class HTMLTagCommentBitfields { 127 friend class HTMLTagComment; 128 129 LLVM_PREFERRED_TYPE(InlineContentCommentBitfields) 130 unsigned : NumInlineContentCommentBits; 131 132 /// True if we found that this tag is malformed in some way. 133 LLVM_PREFERRED_TYPE(bool) 134 unsigned IsMalformed : 1; 135 }; 136 enum { NumHTMLTagCommentBits = NumInlineContentCommentBits + 1 }; 137 138 class HTMLStartTagCommentBitfields { 139 friend class HTMLStartTagComment; 140 141 LLVM_PREFERRED_TYPE(HTMLTagCommentBitfields) 142 unsigned : NumHTMLTagCommentBits; 143 144 /// True if this tag is self-closing (e. g., <br />). This is based on tag 145 /// spelling in comment (plain <br> would not set this flag). 146 LLVM_PREFERRED_TYPE(bool) 147 unsigned IsSelfClosing : 1; 148 }; 149 enum { NumHTMLStartTagCommentBits = NumHTMLTagCommentBits + 1 }; 150 151 class ParagraphCommentBitfields { 152 friend class ParagraphComment; 153 154 LLVM_PREFERRED_TYPE(CommentBitfields) 155 unsigned : NumCommentBits; 156 157 /// True if \c IsWhitespace field contains a valid value. 158 LLVM_PREFERRED_TYPE(bool) 159 mutable unsigned IsWhitespaceValid : 1; 160 161 /// True if this comment AST node contains only whitespace. 162 LLVM_PREFERRED_TYPE(bool) 163 mutable unsigned IsWhitespace : 1; 164 }; 165 enum { NumParagraphCommentBits = NumCommentBits + 2 }; 166 167 class BlockCommandCommentBitfields { 168 friend class BlockCommandComment; 169 170 LLVM_PREFERRED_TYPE(CommentBitfields) 171 unsigned : NumCommentBits; 172 173 LLVM_PREFERRED_TYPE(CommandTraits::KnownCommandIDs) 174 unsigned CommandID : CommandInfo::NumCommandIDBits; 175 176 /// Describes the syntax that was used in a documentation command. 177 /// Contains values from CommandMarkerKind enum. 178 LLVM_PREFERRED_TYPE(CommandMarkerKind) 179 unsigned CommandMarker : 1; 180 }; 181 enum { NumBlockCommandCommentBits = NumCommentBits + 182 CommandInfo::NumCommandIDBits + 1 }; 183 184 class ParamCommandCommentBitfields { 185 friend class ParamCommandComment; 186 187 LLVM_PREFERRED_TYPE(BlockCommandCommentBitfields) 188 unsigned : NumBlockCommandCommentBits; 189 190 /// Parameter passing direction. 191 LLVM_PREFERRED_TYPE(ParamCommandPassDirection) 192 unsigned Direction : 2; 193 194 /// True if direction was specified explicitly in the comment. 195 LLVM_PREFERRED_TYPE(bool) 196 unsigned IsDirectionExplicit : 1; 197 }; 198 enum { NumParamCommandCommentBits = NumBlockCommandCommentBits + 3 }; 199 200 union { 201 CommentBitfields CommentBits; 202 InlineContentCommentBitfields InlineContentCommentBits; 203 TextCommentBitfields TextCommentBits; 204 InlineCommandCommentBitfields InlineCommandCommentBits; 205 HTMLTagCommentBitfields HTMLTagCommentBits; 206 HTMLStartTagCommentBitfields HTMLStartTagCommentBits; 207 ParagraphCommentBitfields ParagraphCommentBits; 208 BlockCommandCommentBitfields BlockCommandCommentBits; 209 ParamCommandCommentBitfields ParamCommandCommentBits; 210 }; 211 212 void setSourceRange(SourceRange SR) { 213 Range = SR; 214 } 215 216 void setLocation(SourceLocation L) { 217 Loc = L; 218 } 219 220 public: 221 struct Argument { 222 SourceRange Range; 223 StringRef Text; 224 }; 225 226 Comment(CommentKind K, 227 SourceLocation LocBegin, 228 SourceLocation LocEnd) : 229 Loc(LocBegin), Range(SourceRange(LocBegin, LocEnd)) { 230 CommentBits.Kind = llvm::to_underlying(K); 231 } 232 233 CommentKind getCommentKind() const { 234 return static_cast<CommentKind>(CommentBits.Kind); 235 } 236 237 const char *getCommentKindName() const; 238 239 void dump() const; 240 void dumpColor() const; 241 void dump(raw_ostream &OS, const ASTContext &Context) const; 242 243 SourceRange getSourceRange() const LLVM_READONLY { return Range; } 244 245 SourceLocation getBeginLoc() const LLVM_READONLY { return Range.getBegin(); } 246 247 SourceLocation getEndLoc() const LLVM_READONLY { return Range.getEnd(); } 248 249 SourceLocation getLocation() const LLVM_READONLY { return Loc; } 250 251 typedef Comment * const *child_iterator; 252 253 child_iterator child_begin() const; 254 child_iterator child_end() const; 255 256 // TODO: const child iterator 257 258 unsigned child_count() const { 259 return child_end() - child_begin(); 260 } 261 }; 262 263 /// Inline content (contained within a block). 264 /// Abstract class. 265 class InlineContentComment : public Comment { 266 protected: 267 InlineContentComment(CommentKind K, 268 SourceLocation LocBegin, 269 SourceLocation LocEnd) : 270 Comment(K, LocBegin, LocEnd) { 271 InlineContentCommentBits.HasTrailingNewline = 0; 272 } 273 274 public: 275 static bool classof(const Comment *C) { 276 return C->getCommentKind() >= 277 CommentKind::FirstInlineContentCommentConstant && 278 C->getCommentKind() <= CommentKind::LastInlineContentCommentConstant; 279 } 280 281 void addTrailingNewline() { 282 InlineContentCommentBits.HasTrailingNewline = 1; 283 } 284 285 bool hasTrailingNewline() const { 286 return InlineContentCommentBits.HasTrailingNewline; 287 } 288 }; 289 290 /// Plain text. 291 class TextComment : public InlineContentComment { 292 StringRef Text; 293 294 public: 295 TextComment(SourceLocation LocBegin, SourceLocation LocEnd, StringRef Text) 296 : InlineContentComment(CommentKind::TextComment, LocBegin, LocEnd), 297 Text(Text) { 298 TextCommentBits.IsWhitespaceValid = false; 299 } 300 301 static bool classof(const Comment *C) { 302 return C->getCommentKind() == CommentKind::TextComment; 303 } 304 305 child_iterator child_begin() const { return nullptr; } 306 307 child_iterator child_end() const { return nullptr; } 308 309 StringRef getText() const LLVM_READONLY { return Text; } 310 311 bool isWhitespace() const { 312 if (TextCommentBits.IsWhitespaceValid) 313 return TextCommentBits.IsWhitespace; 314 315 TextCommentBits.IsWhitespace = isWhitespaceNoCache(); 316 TextCommentBits.IsWhitespaceValid = true; 317 return TextCommentBits.IsWhitespace; 318 } 319 320 private: 321 bool isWhitespaceNoCache() const; 322 }; 323 324 /// The most appropriate rendering mode for this command, chosen on command 325 /// semantics in Doxygen. 326 enum class InlineCommandRenderKind { 327 Normal, 328 Bold, 329 Monospaced, 330 Emphasized, 331 Anchor 332 }; 333 334 /// A command with word-like arguments that is considered inline content. 335 class InlineCommandComment : public InlineContentComment { 336 protected: 337 /// Command arguments. 338 ArrayRef<Argument> Args; 339 340 public: 341 InlineCommandComment(SourceLocation LocBegin, SourceLocation LocEnd, 342 unsigned CommandID, InlineCommandRenderKind RK, 343 ArrayRef<Argument> Args) 344 : InlineContentComment(CommentKind::InlineCommandComment, LocBegin, 345 LocEnd), 346 Args(Args) { 347 InlineCommandCommentBits.RenderKind = llvm::to_underlying(RK); 348 InlineCommandCommentBits.CommandID = CommandID; 349 } 350 351 static bool classof(const Comment *C) { 352 return C->getCommentKind() == CommentKind::InlineCommandComment; 353 } 354 355 child_iterator child_begin() const { return nullptr; } 356 357 child_iterator child_end() const { return nullptr; } 358 359 unsigned getCommandID() const { 360 return InlineCommandCommentBits.CommandID; 361 } 362 363 StringRef getCommandName(const CommandTraits &Traits) const { 364 return Traits.getCommandInfo(getCommandID())->Name; 365 } 366 367 SourceRange getCommandNameRange() const { 368 return SourceRange(getBeginLoc().getLocWithOffset(-1), getEndLoc()); 369 } 370 371 InlineCommandRenderKind getRenderKind() const { 372 return static_cast<InlineCommandRenderKind>( 373 InlineCommandCommentBits.RenderKind); 374 } 375 376 unsigned getNumArgs() const { 377 return Args.size(); 378 } 379 380 StringRef getArgText(unsigned Idx) const { 381 return Args[Idx].Text; 382 } 383 384 SourceRange getArgRange(unsigned Idx) const { 385 return Args[Idx].Range; 386 } 387 }; 388 389 /// Abstract class for opening and closing HTML tags. HTML tags are always 390 /// treated as inline content (regardless HTML semantics). 391 class HTMLTagComment : public InlineContentComment { 392 protected: 393 StringRef TagName; 394 SourceRange TagNameRange; 395 396 HTMLTagComment(CommentKind K, 397 SourceLocation LocBegin, 398 SourceLocation LocEnd, 399 StringRef TagName, 400 SourceLocation TagNameBegin, 401 SourceLocation TagNameEnd) : 402 InlineContentComment(K, LocBegin, LocEnd), 403 TagName(TagName), 404 TagNameRange(TagNameBegin, TagNameEnd) { 405 setLocation(TagNameBegin); 406 HTMLTagCommentBits.IsMalformed = 0; 407 } 408 409 public: 410 static bool classof(const Comment *C) { 411 return C->getCommentKind() >= CommentKind::FirstHTMLTagCommentConstant && 412 C->getCommentKind() <= CommentKind::LastHTMLTagCommentConstant; 413 } 414 415 StringRef getTagName() const LLVM_READONLY { return TagName; } 416 417 SourceRange getTagNameSourceRange() const LLVM_READONLY { 418 SourceLocation L = getLocation(); 419 return SourceRange(L.getLocWithOffset(1), 420 L.getLocWithOffset(1 + TagName.size())); 421 } 422 423 bool isMalformed() const { 424 return HTMLTagCommentBits.IsMalformed; 425 } 426 427 void setIsMalformed() { 428 HTMLTagCommentBits.IsMalformed = 1; 429 } 430 }; 431 432 /// An opening HTML tag with attributes. 433 class HTMLStartTagComment : public HTMLTagComment { 434 public: 435 class Attribute { 436 public: 437 SourceLocation NameLocBegin; 438 StringRef Name; 439 440 SourceLocation EqualsLoc; 441 442 SourceRange ValueRange; 443 StringRef Value; 444 445 Attribute() { } 446 447 Attribute(SourceLocation NameLocBegin, StringRef Name) 448 : NameLocBegin(NameLocBegin), Name(Name), EqualsLoc(SourceLocation()) {} 449 450 Attribute(SourceLocation NameLocBegin, StringRef Name, 451 SourceLocation EqualsLoc, SourceRange ValueRange, StringRef Value) 452 : NameLocBegin(NameLocBegin), Name(Name), EqualsLoc(EqualsLoc), 453 ValueRange(ValueRange), Value(Value) {} 454 455 SourceLocation getNameLocEnd() const { 456 return NameLocBegin.getLocWithOffset(Name.size()); 457 } 458 459 SourceRange getNameRange() const { 460 return SourceRange(NameLocBegin, getNameLocEnd()); 461 } 462 }; 463 464 private: 465 ArrayRef<Attribute> Attributes; 466 467 public: 468 HTMLStartTagComment(SourceLocation LocBegin, StringRef TagName) 469 : HTMLTagComment(CommentKind::HTMLStartTagComment, LocBegin, 470 LocBegin.getLocWithOffset(1 + TagName.size()), TagName, 471 LocBegin.getLocWithOffset(1), 472 LocBegin.getLocWithOffset(1 + TagName.size())) { 473 HTMLStartTagCommentBits.IsSelfClosing = false; 474 } 475 476 static bool classof(const Comment *C) { 477 return C->getCommentKind() == CommentKind::HTMLStartTagComment; 478 } 479 480 child_iterator child_begin() const { return nullptr; } 481 482 child_iterator child_end() const { return nullptr; } 483 484 unsigned getNumAttrs() const { 485 return Attributes.size(); 486 } 487 488 const Attribute &getAttr(unsigned Idx) const { 489 return Attributes[Idx]; 490 } 491 492 void setAttrs(ArrayRef<Attribute> Attrs) { 493 Attributes = Attrs; 494 if (!Attrs.empty()) { 495 const Attribute &Attr = Attrs.back(); 496 SourceLocation L = Attr.ValueRange.getEnd(); 497 if (L.isValid()) 498 Range.setEnd(L); 499 else { 500 Range.setEnd(Attr.getNameLocEnd()); 501 } 502 } 503 } 504 505 void setGreaterLoc(SourceLocation GreaterLoc) { 506 Range.setEnd(GreaterLoc); 507 } 508 509 bool isSelfClosing() const { 510 return HTMLStartTagCommentBits.IsSelfClosing; 511 } 512 513 void setSelfClosing() { 514 HTMLStartTagCommentBits.IsSelfClosing = true; 515 } 516 }; 517 518 /// A closing HTML tag. 519 class HTMLEndTagComment : public HTMLTagComment { 520 public: 521 HTMLEndTagComment(SourceLocation LocBegin, SourceLocation LocEnd, 522 StringRef TagName) 523 : HTMLTagComment(CommentKind::HTMLEndTagComment, LocBegin, LocEnd, 524 TagName, LocBegin.getLocWithOffset(2), 525 LocBegin.getLocWithOffset(2 + TagName.size())) {} 526 527 static bool classof(const Comment *C) { 528 return C->getCommentKind() == CommentKind::HTMLEndTagComment; 529 } 530 531 child_iterator child_begin() const { return nullptr; } 532 533 child_iterator child_end() const { return nullptr; } 534 }; 535 536 /// Block content (contains inline content). 537 /// Abstract class. 538 class BlockContentComment : public Comment { 539 protected: 540 BlockContentComment(CommentKind K, 541 SourceLocation LocBegin, 542 SourceLocation LocEnd) : 543 Comment(K, LocBegin, LocEnd) 544 { } 545 546 public: 547 static bool classof(const Comment *C) { 548 return C->getCommentKind() >= 549 CommentKind::FirstBlockContentCommentConstant && 550 C->getCommentKind() <= CommentKind::LastBlockContentCommentConstant; 551 } 552 }; 553 554 /// A single paragraph that contains inline content. 555 class ParagraphComment : public BlockContentComment { 556 ArrayRef<InlineContentComment *> Content; 557 558 public: 559 ParagraphComment(ArrayRef<InlineContentComment *> Content) 560 : BlockContentComment(CommentKind::ParagraphComment, SourceLocation(), 561 SourceLocation()), 562 Content(Content) { 563 if (Content.empty()) { 564 ParagraphCommentBits.IsWhitespace = true; 565 ParagraphCommentBits.IsWhitespaceValid = true; 566 return; 567 } 568 569 ParagraphCommentBits.IsWhitespaceValid = false; 570 571 setSourceRange(SourceRange(Content.front()->getBeginLoc(), 572 Content.back()->getEndLoc())); 573 setLocation(Content.front()->getBeginLoc()); 574 } 575 576 static bool classof(const Comment *C) { 577 return C->getCommentKind() == CommentKind::ParagraphComment; 578 } 579 580 child_iterator child_begin() const { 581 return reinterpret_cast<child_iterator>(Content.begin()); 582 } 583 584 child_iterator child_end() const { 585 return reinterpret_cast<child_iterator>(Content.end()); 586 } 587 588 bool isWhitespace() const { 589 if (ParagraphCommentBits.IsWhitespaceValid) 590 return ParagraphCommentBits.IsWhitespace; 591 592 ParagraphCommentBits.IsWhitespace = isWhitespaceNoCache(); 593 ParagraphCommentBits.IsWhitespaceValid = true; 594 return ParagraphCommentBits.IsWhitespace; 595 } 596 597 private: 598 bool isWhitespaceNoCache() const; 599 }; 600 601 /// A command that has zero or more word-like arguments (number of word-like 602 /// arguments depends on command name) and a paragraph as an argument 603 /// (e. g., \\brief). 604 class BlockCommandComment : public BlockContentComment { 605 protected: 606 /// Word-like arguments. 607 ArrayRef<Argument> Args; 608 609 /// Paragraph argument. 610 ParagraphComment *Paragraph; 611 612 BlockCommandComment(CommentKind K, 613 SourceLocation LocBegin, 614 SourceLocation LocEnd, 615 unsigned CommandID, 616 CommandMarkerKind CommandMarker) : 617 BlockContentComment(K, LocBegin, LocEnd), 618 Paragraph(nullptr) { 619 setLocation(getCommandNameBeginLoc()); 620 BlockCommandCommentBits.CommandID = CommandID; 621 BlockCommandCommentBits.CommandMarker = CommandMarker; 622 } 623 624 public: 625 BlockCommandComment(SourceLocation LocBegin, SourceLocation LocEnd, 626 unsigned CommandID, CommandMarkerKind CommandMarker) 627 : BlockContentComment(CommentKind::BlockCommandComment, LocBegin, LocEnd), 628 Paragraph(nullptr) { 629 setLocation(getCommandNameBeginLoc()); 630 BlockCommandCommentBits.CommandID = CommandID; 631 BlockCommandCommentBits.CommandMarker = CommandMarker; 632 } 633 634 static bool classof(const Comment *C) { 635 return C->getCommentKind() >= 636 CommentKind::FirstBlockCommandCommentConstant && 637 C->getCommentKind() <= CommentKind::LastBlockCommandCommentConstant; 638 } 639 640 child_iterator child_begin() const { 641 return reinterpret_cast<child_iterator>(&Paragraph); 642 } 643 644 child_iterator child_end() const { 645 return reinterpret_cast<child_iterator>(&Paragraph + 1); 646 } 647 648 unsigned getCommandID() const { 649 return BlockCommandCommentBits.CommandID; 650 } 651 652 StringRef getCommandName(const CommandTraits &Traits) const { 653 return Traits.getCommandInfo(getCommandID())->Name; 654 } 655 656 SourceLocation getCommandNameBeginLoc() const { 657 return getBeginLoc().getLocWithOffset(1); 658 } 659 660 SourceRange getCommandNameRange(const CommandTraits &Traits) const { 661 StringRef Name = getCommandName(Traits); 662 return SourceRange(getCommandNameBeginLoc(), 663 getBeginLoc().getLocWithOffset(1 + Name.size())); 664 } 665 666 unsigned getNumArgs() const { 667 return Args.size(); 668 } 669 670 StringRef getArgText(unsigned Idx) const { 671 return Args[Idx].Text; 672 } 673 674 SourceRange getArgRange(unsigned Idx) const { 675 return Args[Idx].Range; 676 } 677 678 void setArgs(ArrayRef<Argument> A) { 679 Args = A; 680 if (Args.size() > 0) { 681 SourceLocation NewLocEnd = Args.back().Range.getEnd(); 682 if (NewLocEnd.isValid()) 683 setSourceRange(SourceRange(getBeginLoc(), NewLocEnd)); 684 } 685 } 686 687 ParagraphComment *getParagraph() const LLVM_READONLY { 688 return Paragraph; 689 } 690 691 bool hasNonWhitespaceParagraph() const { 692 return Paragraph && !Paragraph->isWhitespace(); 693 } 694 695 void setParagraph(ParagraphComment *PC) { 696 Paragraph = PC; 697 SourceLocation NewLocEnd = PC->getEndLoc(); 698 if (NewLocEnd.isValid()) 699 setSourceRange(SourceRange(getBeginLoc(), NewLocEnd)); 700 } 701 702 CommandMarkerKind getCommandMarker() const LLVM_READONLY { 703 return static_cast<CommandMarkerKind>( 704 BlockCommandCommentBits.CommandMarker); 705 } 706 }; 707 708 enum class ParamCommandPassDirection { In, Out, InOut }; 709 710 /// Doxygen \\param command. 711 class ParamCommandComment : public BlockCommandComment { 712 private: 713 /// Parameter index in the function declaration. 714 unsigned ParamIndex; 715 716 public: 717 enum : unsigned { 718 InvalidParamIndex = ~0U, 719 VarArgParamIndex = ~0U/*InvalidParamIndex*/ - 1U 720 }; 721 722 ParamCommandComment(SourceLocation LocBegin, SourceLocation LocEnd, 723 unsigned CommandID, CommandMarkerKind CommandMarker) 724 : BlockCommandComment(CommentKind::ParamCommandComment, LocBegin, LocEnd, 725 CommandID, CommandMarker), 726 ParamIndex(InvalidParamIndex) { 727 ParamCommandCommentBits.Direction = 728 llvm::to_underlying(ParamCommandPassDirection::In); 729 ParamCommandCommentBits.IsDirectionExplicit = false; 730 } 731 732 static bool classof(const Comment *C) { 733 return C->getCommentKind() == CommentKind::ParamCommandComment; 734 } 735 736 static const char *getDirectionAsString(ParamCommandPassDirection D); 737 738 ParamCommandPassDirection getDirection() const LLVM_READONLY { 739 return static_cast<ParamCommandPassDirection>( 740 ParamCommandCommentBits.Direction); 741 } 742 743 bool isDirectionExplicit() const LLVM_READONLY { 744 return ParamCommandCommentBits.IsDirectionExplicit; 745 } 746 747 void setDirection(ParamCommandPassDirection Direction, bool Explicit) { 748 ParamCommandCommentBits.Direction = llvm::to_underlying(Direction); 749 ParamCommandCommentBits.IsDirectionExplicit = Explicit; 750 } 751 752 bool hasParamName() const { 753 return getNumArgs() > 0; 754 } 755 756 StringRef getParamName(const FullComment *FC) const; 757 758 StringRef getParamNameAsWritten() const { 759 return Args[0].Text; 760 } 761 762 SourceRange getParamNameRange() const { 763 return Args[0].Range; 764 } 765 766 bool isParamIndexValid() const LLVM_READONLY { 767 return ParamIndex != InvalidParamIndex; 768 } 769 770 bool isVarArgParam() const LLVM_READONLY { 771 return ParamIndex == VarArgParamIndex; 772 } 773 774 void setIsVarArgParam() { 775 ParamIndex = VarArgParamIndex; 776 assert(isParamIndexValid()); 777 } 778 779 unsigned getParamIndex() const LLVM_READONLY { 780 assert(isParamIndexValid()); 781 assert(!isVarArgParam()); 782 return ParamIndex; 783 } 784 785 void setParamIndex(unsigned Index) { 786 ParamIndex = Index; 787 assert(isParamIndexValid()); 788 assert(!isVarArgParam()); 789 } 790 }; 791 792 /// Doxygen \\tparam command, describes a template parameter. 793 class TParamCommandComment : public BlockCommandComment { 794 private: 795 /// If this template parameter name was resolved (found in template parameter 796 /// list), then this stores a list of position indexes in all template 797 /// parameter lists. 798 /// 799 /// For example: 800 /// \verbatim 801 /// template<typename C, template<typename T> class TT> 802 /// void test(TT<int> aaa); 803 /// \endverbatim 804 /// For C: Position = { 0 } 805 /// For TT: Position = { 1 } 806 /// For T: Position = { 1, 0 } 807 ArrayRef<unsigned> Position; 808 809 public: 810 TParamCommandComment(SourceLocation LocBegin, SourceLocation LocEnd, 811 unsigned CommandID, CommandMarkerKind CommandMarker) 812 : BlockCommandComment(CommentKind::TParamCommandComment, LocBegin, LocEnd, 813 CommandID, CommandMarker) {} 814 815 static bool classof(const Comment *C) { 816 return C->getCommentKind() == CommentKind::TParamCommandComment; 817 } 818 819 bool hasParamName() const { 820 return getNumArgs() > 0; 821 } 822 823 StringRef getParamName(const FullComment *FC) const; 824 825 StringRef getParamNameAsWritten() const { 826 return Args[0].Text; 827 } 828 829 SourceRange getParamNameRange() const { 830 return Args[0].Range; 831 } 832 833 bool isPositionValid() const LLVM_READONLY { 834 return !Position.empty(); 835 } 836 837 unsigned getDepth() const { 838 assert(isPositionValid()); 839 return Position.size(); 840 } 841 842 unsigned getIndex(unsigned Depth) const { 843 assert(isPositionValid()); 844 return Position[Depth]; 845 } 846 847 void setPosition(ArrayRef<unsigned> NewPosition) { 848 Position = NewPosition; 849 assert(isPositionValid()); 850 } 851 }; 852 853 /// A line of text contained in a verbatim block. 854 class VerbatimBlockLineComment : public Comment { 855 StringRef Text; 856 857 public: 858 VerbatimBlockLineComment(SourceLocation LocBegin, StringRef Text) 859 : Comment(CommentKind::VerbatimBlockLineComment, LocBegin, 860 LocBegin.getLocWithOffset(Text.size())), 861 Text(Text) {} 862 863 static bool classof(const Comment *C) { 864 return C->getCommentKind() == CommentKind::VerbatimBlockLineComment; 865 } 866 867 child_iterator child_begin() const { return nullptr; } 868 869 child_iterator child_end() const { return nullptr; } 870 871 StringRef getText() const LLVM_READONLY { 872 return Text; 873 } 874 }; 875 876 /// A verbatim block command (e. g., preformatted code). Verbatim block has an 877 /// opening and a closing command and contains multiple lines of text 878 /// (VerbatimBlockLineComment nodes). 879 class VerbatimBlockComment : public BlockCommandComment { 880 protected: 881 StringRef CloseName; 882 SourceLocation CloseNameLocBegin; 883 ArrayRef<VerbatimBlockLineComment *> Lines; 884 885 public: 886 VerbatimBlockComment(SourceLocation LocBegin, SourceLocation LocEnd, 887 unsigned CommandID) 888 : BlockCommandComment(CommentKind::VerbatimBlockComment, LocBegin, LocEnd, 889 CommandID, 890 CMK_At) // FIXME: improve source fidelity. 891 {} 892 893 static bool classof(const Comment *C) { 894 return C->getCommentKind() == CommentKind::VerbatimBlockComment; 895 } 896 897 child_iterator child_begin() const { 898 return reinterpret_cast<child_iterator>(Lines.begin()); 899 } 900 901 child_iterator child_end() const { 902 return reinterpret_cast<child_iterator>(Lines.end()); 903 } 904 905 void setCloseName(StringRef Name, SourceLocation LocBegin) { 906 CloseName = Name; 907 CloseNameLocBegin = LocBegin; 908 } 909 910 void setLines(ArrayRef<VerbatimBlockLineComment *> L) { 911 Lines = L; 912 } 913 914 StringRef getCloseName() const { 915 return CloseName; 916 } 917 918 unsigned getNumLines() const { 919 return Lines.size(); 920 } 921 922 StringRef getText(unsigned LineIdx) const { 923 return Lines[LineIdx]->getText(); 924 } 925 }; 926 927 /// A verbatim line command. Verbatim line has an opening command, a single 928 /// line of text (up to the newline after the opening command) and has no 929 /// closing command. 930 class VerbatimLineComment : public BlockCommandComment { 931 protected: 932 StringRef Text; 933 SourceLocation TextBegin; 934 935 public: 936 VerbatimLineComment(SourceLocation LocBegin, SourceLocation LocEnd, 937 unsigned CommandID, SourceLocation TextBegin, 938 StringRef Text) 939 : BlockCommandComment(CommentKind::VerbatimLineComment, LocBegin, LocEnd, 940 CommandID, 941 CMK_At), // FIXME: improve source fidelity. 942 Text(Text), TextBegin(TextBegin) {} 943 944 static bool classof(const Comment *C) { 945 return C->getCommentKind() == CommentKind::VerbatimLineComment; 946 } 947 948 child_iterator child_begin() const { return nullptr; } 949 950 child_iterator child_end() const { return nullptr; } 951 952 StringRef getText() const { 953 return Text; 954 } 955 956 SourceRange getTextRange() const { 957 return SourceRange(TextBegin, getEndLoc()); 958 } 959 }; 960 961 /// Information about the declaration, useful to clients of FullComment. 962 struct DeclInfo { 963 /// Declaration the comment is actually attached to (in the source). 964 /// Should not be NULL. 965 const Decl *CommentDecl; 966 967 /// CurrentDecl is the declaration with which the FullComment is associated. 968 /// 969 /// It can be different from \c CommentDecl. It happens when we decide 970 /// that the comment originally attached to \c CommentDecl is fine for 971 /// \c CurrentDecl too (for example, for a redeclaration or an overrider of 972 /// \c CommentDecl). 973 /// 974 /// The information in the DeclInfo corresponds to CurrentDecl. 975 const Decl *CurrentDecl; 976 977 /// Parameters that can be referenced by \\param if \c CommentDecl is something 978 /// that we consider a "function". 979 ArrayRef<const ParmVarDecl *> ParamVars; 980 981 /// Function return type if \c CommentDecl is something that we consider 982 /// a "function". 983 QualType ReturnType; 984 985 /// Template parameters that can be referenced by \\tparam if \c CommentDecl is 986 /// a template (\c IsTemplateDecl or \c IsTemplatePartialSpecialization is 987 /// true). 988 const TemplateParameterList *TemplateParameters; 989 990 /// A simplified description of \c CommentDecl kind that should be good enough 991 /// for documentation rendering purposes. 992 enum DeclKind { 993 /// Everything else not explicitly mentioned below. 994 OtherKind, 995 996 /// Something that we consider a "function": 997 /// \li function, 998 /// \li function template, 999 /// \li function template specialization, 1000 /// \li member function, 1001 /// \li member function template, 1002 /// \li member function template specialization, 1003 /// \li ObjC method, 1004 FunctionKind, 1005 1006 /// Something that we consider a "class": 1007 /// \li class/struct, 1008 /// \li class template, 1009 /// \li class template (partial) specialization. 1010 ClassKind, 1011 1012 /// Something that we consider a "variable": 1013 /// \li namespace scope variables and variable templates; 1014 /// \li static and non-static class data members and member templates; 1015 /// \li enumerators. 1016 VariableKind, 1017 1018 /// A C++ namespace. 1019 NamespaceKind, 1020 1021 /// A C++ typedef-name (a 'typedef' decl specifier or alias-declaration), 1022 /// see \c TypedefNameDecl. 1023 TypedefKind, 1024 1025 /// An enumeration or scoped enumeration. 1026 EnumKind 1027 }; 1028 1029 /// What kind of template specialization \c CommentDecl is. 1030 enum TemplateDeclKind { 1031 NotTemplate, 1032 Template, 1033 TemplateSpecialization, 1034 TemplatePartialSpecialization 1035 }; 1036 1037 /// If false, only \c CommentDecl is valid. 1038 LLVM_PREFERRED_TYPE(bool) 1039 unsigned IsFilled : 1; 1040 1041 /// Simplified kind of \c CommentDecl, see \c DeclKind enum. 1042 LLVM_PREFERRED_TYPE(DeclKind) 1043 unsigned Kind : 3; 1044 1045 /// Is \c CommentDecl a template declaration. 1046 LLVM_PREFERRED_TYPE(TemplateDeclKind) 1047 unsigned TemplateKind : 2; 1048 1049 /// Is \c CommentDecl an ObjCMethodDecl. 1050 LLVM_PREFERRED_TYPE(bool) 1051 unsigned IsObjCMethod : 1; 1052 1053 /// Is \c CommentDecl a non-static member function of C++ class or 1054 /// instance method of ObjC class. 1055 /// Can be true only if \c IsFunctionDecl is true. 1056 LLVM_PREFERRED_TYPE(bool) 1057 unsigned IsInstanceMethod : 1; 1058 1059 /// Is \c CommentDecl a static member function of C++ class or 1060 /// class method of ObjC class. 1061 /// Can be true only if \c IsFunctionDecl is true. 1062 LLVM_PREFERRED_TYPE(bool) 1063 unsigned IsClassMethod : 1; 1064 1065 /// Is \c CommentDecl something we consider a "function" that's variadic. 1066 LLVM_PREFERRED_TYPE(bool) 1067 unsigned IsVariadic : 1; 1068 1069 void fill(); 1070 1071 DeclKind getKind() const LLVM_READONLY { 1072 return static_cast<DeclKind>(Kind); 1073 } 1074 1075 TemplateDeclKind getTemplateKind() const LLVM_READONLY { 1076 return static_cast<TemplateDeclKind>(TemplateKind); 1077 } 1078 1079 bool involvesFunctionType() const { return !ReturnType.isNull(); } 1080 }; 1081 1082 /// A full comment attached to a declaration, contains block content. 1083 class FullComment : public Comment { 1084 ArrayRef<BlockContentComment *> Blocks; 1085 DeclInfo *ThisDeclInfo; 1086 1087 public: 1088 FullComment(ArrayRef<BlockContentComment *> Blocks, DeclInfo *D) 1089 : Comment(CommentKind::FullComment, SourceLocation(), SourceLocation()), 1090 Blocks(Blocks), ThisDeclInfo(D) { 1091 if (Blocks.empty()) 1092 return; 1093 1094 setSourceRange( 1095 SourceRange(Blocks.front()->getBeginLoc(), Blocks.back()->getEndLoc())); 1096 setLocation(Blocks.front()->getBeginLoc()); 1097 } 1098 1099 static bool classof(const Comment *C) { 1100 return C->getCommentKind() == CommentKind::FullComment; 1101 } 1102 1103 child_iterator child_begin() const { 1104 return reinterpret_cast<child_iterator>(Blocks.begin()); 1105 } 1106 1107 child_iterator child_end() const { 1108 return reinterpret_cast<child_iterator>(Blocks.end()); 1109 } 1110 1111 const Decl *getDecl() const LLVM_READONLY { 1112 return ThisDeclInfo->CommentDecl; 1113 } 1114 1115 const DeclInfo *getDeclInfo() const LLVM_READONLY { 1116 if (!ThisDeclInfo->IsFilled) 1117 ThisDeclInfo->fill(); 1118 return ThisDeclInfo; 1119 } 1120 1121 ArrayRef<BlockContentComment *> getBlocks() const { return Blocks; } 1122 1123 }; 1124 } // end namespace comments 1125 } // end namespace clang 1126 1127 #endif 1128 1129