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