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 |
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 | = 0, |
44 | |
45 | /// Command started with an 'at' character: |
46 | /// \code |
47 | /// @foo |
48 | /// \endcode |
49 | = 1 |
50 | }; |
51 | |
52 | enum class { |
53 | = 0, |
54 | #define (CLASS, PARENT) CLASS, |
55 | #define (BASE, FIRST, LAST) \ |
56 | First##BASE##Constant = FIRST, Last##BASE##Constant = LAST, |
57 | #define (BASE, FIRST, LAST) \ |
58 | First##BASE##Constant = FIRST, Last##BASE##Constant = LAST |
59 | #define (COMMENT) |
60 | #include "clang/AST/CommentNodes.inc" |
61 | }; |
62 | |
63 | /// Any part of the comment. |
64 | /// Abstract class. |
65 | class { |
66 | protected: |
67 | /// Preferred location to show caret. |
68 | SourceLocation ; |
69 | |
70 | /// Source range of this AST node. |
71 | SourceRange ; |
72 | |
73 | class { |
74 | friend class Comment; |
75 | |
76 | /// Type of this AST node. |
77 | LLVM_PREFERRED_TYPE(CommentKind) |
78 | unsigned : 8; |
79 | }; |
80 | enum { = 8 }; |
81 | |
82 | class { |
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 : 1; |
92 | }; |
93 | enum { = NumCommentBits + 1 }; |
94 | |
95 | class { |
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 : 1; |
104 | |
105 | /// True if this comment AST node contains only whitespace. |
106 | LLVM_PREFERRED_TYPE(bool) |
107 | mutable unsigned : 1; |
108 | }; |
109 | enum { = 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 { |
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 : 1; |
135 | }; |
136 | enum { = NumInlineContentCommentBits + 1 }; |
137 | |
138 | class { |
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 : 1; |
148 | }; |
149 | enum { = NumHTMLTagCommentBits + 1 }; |
150 | |
151 | class { |
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 : 1; |
160 | |
161 | /// True if this comment AST node contains only whitespace. |
162 | LLVM_PREFERRED_TYPE(bool) |
163 | mutable unsigned : 1; |
164 | }; |
165 | enum { = 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 ; |
202 | InlineContentCommentBitfields ; |
203 | TextCommentBitfields ; |
204 | InlineCommandCommentBitfields InlineCommandCommentBits; |
205 | HTMLTagCommentBitfields ; |
206 | HTMLStartTagCommentBitfields ; |
207 | ParagraphCommentBitfields ; |
208 | BlockCommandCommentBitfields BlockCommandCommentBits; |
209 | ParamCommandCommentBitfields ParamCommandCommentBits; |
210 | }; |
211 | |
212 | void (SourceRange SR) { |
213 | Range = SR; |
214 | } |
215 | |
216 | void (SourceLocation L) { |
217 | Loc = L; |
218 | } |
219 | |
220 | public: |
221 | struct { |
222 | SourceRange ; |
223 | StringRef ; |
224 | }; |
225 | |
226 | (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 () const { |
234 | return static_cast<CommentKind>(CommentBits.Kind); |
235 | } |
236 | |
237 | const char *() const; |
238 | |
239 | void () const; |
240 | void () const; |
241 | void (raw_ostream &OS, const ASTContext &Context) const; |
242 | |
243 | SourceRange () const LLVM_READONLY { return Range; } |
244 | |
245 | SourceLocation () const LLVM_READONLY { return Range.getBegin(); } |
246 | |
247 | SourceLocation () const LLVM_READONLY { return Range.getEnd(); } |
248 | |
249 | SourceLocation () const LLVM_READONLY { return Loc; } |
250 | |
251 | typedef Comment * const *; |
252 | |
253 | child_iterator () const; |
254 | child_iterator () const; |
255 | |
256 | // TODO: const child iterator |
257 | |
258 | unsigned () const { |
259 | return child_end() - child_begin(); |
260 | } |
261 | }; |
262 | |
263 | /// Inline content (contained within a block). |
264 | /// Abstract class. |
265 | class : public Comment { |
266 | protected: |
267 | (CommentKind K, |
268 | SourceLocation LocBegin, |
269 | SourceLocation LocEnd) : |
270 | Comment(K, LocBegin, LocEnd) { |
271 | InlineContentCommentBits.HasTrailingNewline = 0; |
272 | } |
273 | |
274 | public: |
275 | static bool (const Comment *C) { |
276 | return C->getCommentKind() >= |
277 | CommentKind::FirstInlineContentCommentConstant && |
278 | C->getCommentKind() <= CommentKind::LastInlineContentCommentConstant; |
279 | } |
280 | |
281 | void () { |
282 | InlineContentCommentBits.HasTrailingNewline = 1; |
283 | } |
284 | |
285 | bool () const { |
286 | return InlineContentCommentBits.HasTrailingNewline; |
287 | } |
288 | }; |
289 | |
290 | /// Plain text. |
291 | class : public InlineContentComment { |
292 | StringRef ; |
293 | |
294 | public: |
295 | (SourceLocation LocBegin, SourceLocation LocEnd, StringRef Text) |
296 | : InlineContentComment(CommentKind::TextComment, LocBegin, LocEnd), |
297 | Text(Text) { |
298 | TextCommentBits.IsWhitespaceValid = false; |
299 | } |
300 | |
301 | static bool (const Comment *C) { |
302 | return C->getCommentKind() == CommentKind::TextComment; |
303 | } |
304 | |
305 | child_iterator () const { return nullptr; } |
306 | |
307 | child_iterator () const { return nullptr; } |
308 | |
309 | StringRef () const LLVM_READONLY { return Text; } |
310 | |
311 | bool () 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 () 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(CommandID: 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 : public InlineContentComment { |
392 | protected: |
393 | StringRef ; |
394 | SourceRange ; |
395 | |
396 | (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 (const Comment *C) { |
411 | return C->getCommentKind() >= CommentKind::FirstHTMLTagCommentConstant && |
412 | C->getCommentKind() <= CommentKind::LastHTMLTagCommentConstant; |
413 | } |
414 | |
415 | StringRef () const LLVM_READONLY { return TagName; } |
416 | |
417 | SourceRange () const LLVM_READONLY { |
418 | SourceLocation L = getLocation(); |
419 | return SourceRange(L.getLocWithOffset(Offset: 1), |
420 | L.getLocWithOffset(Offset: 1 + TagName.size())); |
421 | } |
422 | |
423 | bool () const { |
424 | return HTMLTagCommentBits.IsMalformed; |
425 | } |
426 | |
427 | void () { |
428 | HTMLTagCommentBits.IsMalformed = 1; |
429 | } |
430 | }; |
431 | |
432 | /// An opening HTML tag with attributes. |
433 | class : public HTMLTagComment { |
434 | public: |
435 | class { |
436 | public: |
437 | SourceLocation ; |
438 | StringRef ; |
439 | |
440 | SourceLocation ; |
441 | |
442 | SourceRange ; |
443 | StringRef ; |
444 | |
445 | () { } |
446 | |
447 | (SourceLocation NameLocBegin, StringRef Name) |
448 | : NameLocBegin(NameLocBegin), Name(Name), EqualsLoc(SourceLocation()) {} |
449 | |
450 | (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 () const { |
456 | return NameLocBegin.getLocWithOffset(Offset: Name.size()); |
457 | } |
458 | |
459 | SourceRange () const { |
460 | return SourceRange(NameLocBegin, getNameLocEnd()); |
461 | } |
462 | }; |
463 | |
464 | private: |
465 | ArrayRef<Attribute> ; |
466 | |
467 | public: |
468 | (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 (const Comment *C) { |
477 | return C->getCommentKind() == CommentKind::HTMLStartTagComment; |
478 | } |
479 | |
480 | child_iterator () const { return nullptr; } |
481 | |
482 | child_iterator () const { return nullptr; } |
483 | |
484 | unsigned () const { |
485 | return Attributes.size(); |
486 | } |
487 | |
488 | const Attribute &(unsigned Idx) const { |
489 | return Attributes[Idx]; |
490 | } |
491 | |
492 | void (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 (SourceLocation GreaterLoc) { |
506 | Range.setEnd(GreaterLoc); |
507 | } |
508 | |
509 | bool () const { |
510 | return HTMLStartTagCommentBits.IsSelfClosing; |
511 | } |
512 | |
513 | void () { |
514 | HTMLStartTagCommentBits.IsSelfClosing = true; |
515 | } |
516 | }; |
517 | |
518 | /// A closing HTML tag. |
519 | class : public HTMLTagComment { |
520 | public: |
521 | (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 (const Comment *C) { |
528 | return C->getCommentKind() == CommentKind::HTMLEndTagComment; |
529 | } |
530 | |
531 | child_iterator () const { return nullptr; } |
532 | |
533 | child_iterator () const { return nullptr; } |
534 | }; |
535 | |
536 | /// Block content (contains inline content). |
537 | /// Abstract class. |
538 | class : public Comment { |
539 | protected: |
540 | (CommentKind K, |
541 | SourceLocation LocBegin, |
542 | SourceLocation LocEnd) : |
543 | Comment(K, LocBegin, LocEnd) |
544 | { } |
545 | |
546 | public: |
547 | static bool (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 : public BlockContentComment { |
556 | ArrayRef<InlineContentComment *> ; |
557 | |
558 | public: |
559 | (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 (const Comment *C) { |
577 | return C->getCommentKind() == CommentKind::ParagraphComment; |
578 | } |
579 | |
580 | child_iterator () const { |
581 | return reinterpret_cast<child_iterator>(Content.begin()); |
582 | } |
583 | |
584 | child_iterator () const { |
585 | return reinterpret_cast<child_iterator>(Content.end()); |
586 | } |
587 | |
588 | bool () 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 () 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(CommandID: 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 : public Comment { |
855 | StringRef ; |
856 | |
857 | public: |
858 | (SourceLocation LocBegin, StringRef Text) |
859 | : Comment(CommentKind::VerbatimBlockLineComment, LocBegin, |
860 | LocBegin.getLocWithOffset(Text.size())), |
861 | Text(Text) {} |
862 | |
863 | static bool (const Comment *C) { |
864 | return C->getCommentKind() == CommentKind::VerbatimBlockLineComment; |
865 | } |
866 | |
867 | child_iterator () const { return nullptr; } |
868 | |
869 | child_iterator () const { return nullptr; } |
870 | |
871 | StringRef () 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 : public BlockCommandComment { |
880 | protected: |
881 | StringRef ; |
882 | SourceLocation ; |
883 | ArrayRef<VerbatimBlockLineComment *> ; |
884 | |
885 | public: |
886 | (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 (const Comment *C) { |
894 | return C->getCommentKind() == CommentKind::VerbatimBlockComment; |
895 | } |
896 | |
897 | child_iterator () const { |
898 | return reinterpret_cast<child_iterator>(Lines.begin()); |
899 | } |
900 | |
901 | child_iterator () const { |
902 | return reinterpret_cast<child_iterator>(Lines.end()); |
903 | } |
904 | |
905 | void (StringRef Name, SourceLocation LocBegin) { |
906 | CloseName = Name; |
907 | CloseNameLocBegin = LocBegin; |
908 | } |
909 | |
910 | void (ArrayRef<VerbatimBlockLineComment *> L) { |
911 | Lines = L; |
912 | } |
913 | |
914 | StringRef () const { |
915 | return CloseName; |
916 | } |
917 | |
918 | unsigned () const { |
919 | return Lines.size(); |
920 | } |
921 | |
922 | StringRef (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 : public BlockCommandComment { |
931 | protected: |
932 | StringRef ; |
933 | SourceLocation ; |
934 | |
935 | public: |
936 | (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 (const Comment *C) { |
945 | return C->getCommentKind() == CommentKind::VerbatimLineComment; |
946 | } |
947 | |
948 | child_iterator () const { return nullptr; } |
949 | |
950 | child_iterator () const { return nullptr; } |
951 | |
952 | StringRef () const { |
953 | return Text; |
954 | } |
955 | |
956 | SourceRange () const { |
957 | return SourceRange(TextBegin, getEndLoc()); |
958 | } |
959 | }; |
960 | |
961 | /// Information about the declaration, useful to clients of FullComment. |
962 | struct { |
963 | /// Declaration the comment is actually attached to (in the source). |
964 | /// Should not be NULL. |
965 | const Decl *; |
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 *; |
976 | |
977 | /// Parameters that can be referenced by \\param if \c CommentDecl is something |
978 | /// that we consider a "function". |
979 | ArrayRef<const ParmVarDecl *> ; |
980 | |
981 | /// Function return type if \c CommentDecl is something that we consider |
982 | /// a "function". |
983 | QualType ; |
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 *; |
989 | |
990 | /// A simplified description of \c CommentDecl kind that should be good enough |
991 | /// for documentation rendering purposes. |
992 | enum { |
993 | /// Everything else not explicitly mentioned below. |
994 | , |
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 | , |
1005 | |
1006 | /// Something that we consider a "class": |
1007 | /// \li class/struct, |
1008 | /// \li class template, |
1009 | /// \li class template (partial) specialization. |
1010 | , |
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 | , |
1017 | |
1018 | /// A C++ namespace. |
1019 | , |
1020 | |
1021 | /// A C++ typedef-name (a 'typedef' decl specifier or alias-declaration), |
1022 | /// see \c TypedefNameDecl. |
1023 | , |
1024 | |
1025 | /// An enumeration or scoped enumeration. |
1026 | |
1027 | }; |
1028 | |
1029 | /// What kind of template specialization \c CommentDecl is. |
1030 | enum { |
1031 | , |
1032 | , |
1033 | , |
1034 | |
1035 | }; |
1036 | |
1037 | /// If false, only \c CommentDecl is valid. |
1038 | LLVM_PREFERRED_TYPE(bool) |
1039 | unsigned : 1; |
1040 | |
1041 | /// Simplified kind of \c CommentDecl, see \c DeclKind enum. |
1042 | LLVM_PREFERRED_TYPE(DeclKind) |
1043 | unsigned : 3; |
1044 | |
1045 | /// Is \c CommentDecl a template declaration. |
1046 | LLVM_PREFERRED_TYPE(TemplateDeclKind) |
1047 | unsigned : 2; |
1048 | |
1049 | /// Is \c CommentDecl an ObjCMethodDecl. |
1050 | LLVM_PREFERRED_TYPE(bool) |
1051 | unsigned : 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 : 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 : 1; |
1064 | |
1065 | /// Is \c CommentDecl something we consider a "function" that's variadic. |
1066 | LLVM_PREFERRED_TYPE(bool) |
1067 | unsigned : 1; |
1068 | |
1069 | void (); |
1070 | |
1071 | DeclKind () const LLVM_READONLY { |
1072 | return static_cast<DeclKind>(Kind); |
1073 | } |
1074 | |
1075 | TemplateDeclKind () const LLVM_READONLY { |
1076 | return static_cast<TemplateDeclKind>(TemplateKind); |
1077 | } |
1078 | |
1079 | bool () const { return !ReturnType.isNull(); } |
1080 | }; |
1081 | |
1082 | /// A full comment attached to a declaration, contains block content. |
1083 | class : public Comment { |
1084 | ArrayRef<BlockContentComment *> ; |
1085 | DeclInfo *; |
1086 | |
1087 | public: |
1088 | (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 (const Comment *C) { |
1100 | return C->getCommentKind() == CommentKind::FullComment; |
1101 | } |
1102 | |
1103 | child_iterator () const { |
1104 | return reinterpret_cast<child_iterator>(Blocks.begin()); |
1105 | } |
1106 | |
1107 | child_iterator () const { |
1108 | return reinterpret_cast<child_iterator>(Blocks.end()); |
1109 | } |
1110 | |
1111 | const Decl *() const LLVM_READONLY { |
1112 | return ThisDeclInfo->CommentDecl; |
1113 | } |
1114 | |
1115 | const DeclInfo *() const LLVM_READONLY { |
1116 | if (!ThisDeclInfo->IsFilled) |
1117 | ThisDeclInfo->fill(); |
1118 | return ThisDeclInfo; |
1119 | } |
1120 | |
1121 | ArrayRef<BlockContentComment *> () const { return Blocks; } |
1122 | |
1123 | }; |
1124 | } // end namespace comments |
1125 | } // end namespace clang |
1126 | |
1127 | #endif |
1128 | |
1129 | |