blob: 5fac0d8779798be88a480dca9a903ef065ae45b6 [file] [log] [blame]
Dmitri Gribenkoae99b752012-07-20 21:34:34 +00001//===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===//
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 all libclang APIs related to walking comment AST.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang-c/Index.h"
15#include "CXString.h"
16#include "CXComment.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000017#include "CXCursor.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000018
19#include "clang/AST/CommentVisitor.h"
Dmitri Gribenkod1db1252012-08-09 17:33:20 +000020#include "clang/AST/CommentCommandTraits.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000021#include "clang/AST/Decl.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000022
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000023#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000024#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000025#include "llvm/Support/raw_ostream.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000026
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000027#include <climits>
28
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000029using namespace clang;
30using namespace clang::cxstring;
31using namespace clang::comments;
32using namespace clang::cxcomment;
33
34extern "C" {
35
36enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
37 const Comment *C = getASTNode(CXC);
38 if (!C)
39 return CXComment_Null;
40
41 switch (C->getCommentKind()) {
42 case Comment::NoCommentKind:
43 return CXComment_Null;
44
45 case Comment::TextCommentKind:
46 return CXComment_Text;
47
48 case Comment::InlineCommandCommentKind:
49 return CXComment_InlineCommand;
50
51 case Comment::HTMLStartTagCommentKind:
52 return CXComment_HTMLStartTag;
53
54 case Comment::HTMLEndTagCommentKind:
55 return CXComment_HTMLEndTag;
56
57 case Comment::ParagraphCommentKind:
58 return CXComment_Paragraph;
59
60 case Comment::BlockCommandCommentKind:
61 return CXComment_BlockCommand;
62
63 case Comment::ParamCommandCommentKind:
64 return CXComment_ParamCommand;
65
Dmitri Gribenko96b09862012-07-31 22:37:06 +000066 case Comment::TParamCommandCommentKind:
67 return CXComment_TParamCommand;
68
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000069 case Comment::VerbatimBlockCommentKind:
70 return CXComment_VerbatimBlockCommand;
71
72 case Comment::VerbatimBlockLineCommentKind:
73 return CXComment_VerbatimBlockLine;
74
75 case Comment::VerbatimLineCommentKind:
76 return CXComment_VerbatimLine;
77
78 case Comment::FullCommentKind:
79 return CXComment_FullComment;
80 }
81 llvm_unreachable("unknown CommentKind");
82}
83
84unsigned clang_Comment_getNumChildren(CXComment CXC) {
85 const Comment *C = getASTNode(CXC);
86 if (!C)
87 return 0;
88
89 return C->child_count();
90}
91
92CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
93 const Comment *C = getASTNode(CXC);
94 if (!C || ChildIdx >= C->child_count())
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000095 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000096
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000097 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000098}
99
100unsigned clang_Comment_isWhitespace(CXComment CXC) {
101 const Comment *C = getASTNode(CXC);
102 if (!C)
103 return false;
104
105 if (const TextComment *TC = dyn_cast<TextComment>(C))
106 return TC->isWhitespace();
107
108 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
109 return PC->isWhitespace();
110
111 return false;
112}
113
114unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
115 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
116 if (!ICC)
117 return false;
118
119 return ICC->hasTrailingNewline();
120}
121
122CXString clang_TextComment_getText(CXComment CXC) {
123 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
124 if (!TC)
125 return createCXString((const char *) 0);
126
127 return createCXString(TC->getText(), /*DupString=*/ false);
128}
129
130CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
131 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
132 if (!ICC)
133 return createCXString((const char *) 0);
134
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000135 const CommandTraits &Traits = getCommandTraits(CXC);
136 return createCXString(ICC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000137}
138
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000139enum CXCommentInlineCommandRenderKind
140clang_InlineCommandComment_getRenderKind(CXComment CXC) {
141 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
142 if (!ICC)
143 return CXCommentInlineCommandRenderKind_Normal;
144
145 switch (ICC->getRenderKind()) {
146 case InlineCommandComment::RenderNormal:
147 return CXCommentInlineCommandRenderKind_Normal;
148
149 case InlineCommandComment::RenderBold:
150 return CXCommentInlineCommandRenderKind_Bold;
151
152 case InlineCommandComment::RenderMonospaced:
153 return CXCommentInlineCommandRenderKind_Monospaced;
154
155 case InlineCommandComment::RenderEmphasized:
156 return CXCommentInlineCommandRenderKind_Emphasized;
157 }
158 llvm_unreachable("unknown InlineCommandComment::RenderKind");
159}
160
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000161unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
162 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
163 if (!ICC)
164 return 0;
165
166 return ICC->getNumArgs();
167}
168
169CXString clang_InlineCommandComment_getArgText(CXComment CXC,
170 unsigned ArgIdx) {
171 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
172 if (!ICC || ArgIdx >= ICC->getNumArgs())
173 return createCXString((const char *) 0);
174
175 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
176}
177
178CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
179 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
180 if (!HTC)
181 return createCXString((const char *) 0);
182
183 return createCXString(HTC->getTagName(), /*DupString=*/ false);
184}
185
186unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
187 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
188 if (!HST)
189 return false;
190
191 return HST->isSelfClosing();
192}
193
194unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
195 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
196 if (!HST)
197 return 0;
198
199 return HST->getNumAttrs();
200}
201
202CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
203 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
204 if (!HST || AttrIdx >= HST->getNumAttrs())
205 return createCXString((const char *) 0);
206
207 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
208}
209
210CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
211 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
212 if (!HST || AttrIdx >= HST->getNumAttrs())
213 return createCXString((const char *) 0);
214
215 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
216}
217
218CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
219 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
220 if (!BCC)
221 return createCXString((const char *) 0);
222
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000223 const CommandTraits &Traits = getCommandTraits(CXC);
224 return createCXString(BCC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000225}
226
227unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
228 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
229 if (!BCC)
230 return 0;
231
232 return BCC->getNumArgs();
233}
234
235CXString clang_BlockCommandComment_getArgText(CXComment CXC,
236 unsigned ArgIdx) {
237 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
238 if (!BCC || ArgIdx >= BCC->getNumArgs())
239 return createCXString((const char *) 0);
240
241 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
242}
243
244CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
245 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
246 if (!BCC)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000247 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000248
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000249 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000250}
251
252CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
253 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000254 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000255 return createCXString((const char *) 0);
256
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000257 return createCXString(PCC->getParamName(0), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000258}
259
260unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
261 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
262 if (!PCC)
263 return false;
264
265 return PCC->isParamIndexValid();
266}
267
268unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
269 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000270 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000271 return ParamCommandComment::InvalidParamIndex;
272
273 return PCC->getParamIndex();
274}
275
276unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
277 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
278 if (!PCC)
279 return false;
280
281 return PCC->isDirectionExplicit();
282}
283
284enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
285 CXComment CXC) {
286 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
287 if (!PCC)
288 return CXCommentParamPassDirection_In;
289
290 switch (PCC->getDirection()) {
291 case ParamCommandComment::In:
292 return CXCommentParamPassDirection_In;
293
294 case ParamCommandComment::Out:
295 return CXCommentParamPassDirection_Out;
296
297 case ParamCommandComment::InOut:
298 return CXCommentParamPassDirection_InOut;
299 }
300 llvm_unreachable("unknown ParamCommandComment::PassDirection");
301}
302
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000303CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
304 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
305 if (!TPCC || !TPCC->hasParamName())
306 return createCXString((const char *) 0);
307
308 return createCXString(TPCC->getParamName(), /*DupString=*/ false);
309}
310
311unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
312 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
313 if (!TPCC)
314 return false;
315
316 return TPCC->isPositionValid();
317}
318
319unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
320 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
321 if (!TPCC || !TPCC->isPositionValid())
322 return 0;
323
324 return TPCC->getDepth();
325}
326
327unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
328 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
329 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
330 return 0;
331
332 return TPCC->getIndex(Depth);
333}
334
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000335CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
336 const VerbatimBlockLineComment *VBL =
337 getASTNodeAs<VerbatimBlockLineComment>(CXC);
338 if (!VBL)
339 return createCXString((const char *) 0);
340
341 return createCXString(VBL->getText(), /*DupString=*/ false);
342}
343
344CXString clang_VerbatimLineComment_getText(CXComment CXC) {
345 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
346 if (!VLC)
347 return createCXString((const char *) 0);
348
349 return createCXString(VLC->getText(), /*DupString=*/ false);
350}
351
352} // end extern "C"
353
354//===----------------------------------------------------------------------===//
355// Helpers for converting comment AST to HTML.
356//===----------------------------------------------------------------------===//
357
358namespace {
359
Dmitri Gribenkoe5db09c2012-07-30 19:47:34 +0000360/// This comparison will sort parameters with valid index by index and
361/// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000362class ParamCommandCommentCompareIndex {
363public:
364 bool operator()(const ParamCommandComment *LHS,
365 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000366 unsigned LHSIndex = UINT_MAX;
367 unsigned RHSIndex = UINT_MAX;
368 if (LHS->isParamIndexValid())
369 LHSIndex = LHS->getParamIndex();
370 if (RHS->isParamIndexValid())
371 RHSIndex = RHS->getParamIndex();
372
373 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000374 }
375};
376
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000377/// This comparison will sort template parameters in the following order:
378/// \li real template parameters (depth = 1) in index order;
379/// \li all other names (depth > 1);
380/// \li unresolved names.
381class TParamCommandCommentComparePosition {
382public:
383 bool operator()(const TParamCommandComment *LHS,
384 const TParamCommandComment *RHS) const {
385 // Sort unresolved names last.
386 if (!LHS->isPositionValid())
387 return false;
388 if (!RHS->isPositionValid())
389 return true;
390
391 if (LHS->getDepth() > 1)
392 return false;
393 if (RHS->getDepth() > 1)
394 return true;
395
396 // Sort template parameters in index order.
397 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
398 return LHS->getIndex(0) < RHS->getIndex(0);
399
400 // Leave all other names in source order.
401 return true;
402 }
403};
404
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000405/// Separate parts of a FullComment.
406struct FullCommentParts {
407 /// Take a full comment apart and initialize members accordingly.
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000408 FullCommentParts(const FullComment *C,
409 const CommandTraits &Traits);
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000410
411 const BlockContentComment *Brief;
412 const ParagraphComment *FirstParagraph;
413 const BlockCommandComment *Returns;
414 SmallVector<const ParamCommandComment *, 8> Params;
415 SmallVector<const TParamCommandComment *, 4> TParams;
416 SmallVector<const BlockContentComment *, 8> MiscBlocks;
417};
418
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000419FullCommentParts::FullCommentParts(const FullComment *C,
420 const CommandTraits &Traits) :
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000421 Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
422 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
423 I != E; ++I) {
424 const Comment *Child = *I;
425 if (!Child)
426 continue;
427 switch (Child->getCommentKind()) {
428 case Comment::NoCommentKind:
429 continue;
430
431 case Comment::ParagraphCommentKind: {
432 const ParagraphComment *PC = cast<ParagraphComment>(Child);
433 if (PC->isWhitespace())
434 break;
435 if (!FirstParagraph)
436 FirstParagraph = PC;
437
438 MiscBlocks.push_back(PC);
439 break;
440 }
441
442 case Comment::BlockCommandCommentKind: {
443 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000444 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
445 if (!Brief && Info->IsBriefCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000446 Brief = BCC;
447 break;
448 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000449 if (!Returns && Info->IsReturnsCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000450 Returns = BCC;
451 break;
452 }
453 MiscBlocks.push_back(BCC);
454 break;
455 }
456
457 case Comment::ParamCommandCommentKind: {
458 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
459 if (!PCC->hasParamName())
460 break;
461
462 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
463 break;
464
465 Params.push_back(PCC);
466 break;
467 }
468
469 case Comment::TParamCommandCommentKind: {
470 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
471 if (!TPCC->hasParamName())
472 break;
473
474 if (!TPCC->hasNonWhitespaceParagraph())
475 break;
476
477 TParams.push_back(TPCC);
478 break;
479 }
480
481 case Comment::VerbatimBlockCommentKind:
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000482 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
483 break;
484
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000485 case Comment::VerbatimLineCommentKind: {
486 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000487 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
488 if (!Info->IsDeclarationCommand)
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000489 MiscBlocks.push_back(VLC);
490 break;
491 }
492
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000493 case Comment::TextCommentKind:
494 case Comment::InlineCommandCommentKind:
495 case Comment::HTMLStartTagCommentKind:
496 case Comment::HTMLEndTagCommentKind:
497 case Comment::VerbatimBlockLineCommentKind:
498 case Comment::FullCommentKind:
499 llvm_unreachable("AST node of this kind can't be a child of "
500 "a FullComment");
501 }
502 }
503
504 // Sort params in order they are declared in the function prototype.
505 // Unresolved parameters are put at the end of the list in the same order
506 // they were seen in the comment.
507 std::stable_sort(Params.begin(), Params.end(),
508 ParamCommandCommentCompareIndex());
509
510 std::stable_sort(TParams.begin(), TParams.end(),
511 TParamCommandCommentComparePosition());
512}
513
514void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
515 llvm::raw_svector_ostream &Result) {
516 Result << "<" << C->getTagName();
517
518 if (C->getNumAttrs() != 0) {
519 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
520 Result << " ";
521 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
522 Result << Attr.Name;
523 if (!Attr.Value.empty())
524 Result << "=\"" << Attr.Value << "\"";
525 }
526 }
527
528 if (!C->isSelfClosing())
529 Result << ">";
530 else
531 Result << "/>";
532}
533
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000534class CommentASTToHTMLConverter :
535 public ConstCommentVisitor<CommentASTToHTMLConverter> {
536public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000537 /// \param Str accumulator for HTML.
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000538 CommentASTToHTMLConverter(FullComment *FC,
539 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000540 const CommandTraits &Traits) :
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000541 FC(FC), Result(Str), Traits(Traits)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000542 { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000543
544 // Inline content.
545 void visitTextComment(const TextComment *C);
546 void visitInlineCommandComment(const InlineCommandComment *C);
547 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
548 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
549
550 // Block content.
551 void visitParagraphComment(const ParagraphComment *C);
552 void visitBlockCommandComment(const BlockCommandComment *C);
553 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000554 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000555 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
556 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
557 void visitVerbatimLineComment(const VerbatimLineComment *C);
558
559 void visitFullComment(const FullComment *C);
560
561 // Helpers.
562
563 /// Convert a paragraph that is not a block by itself (an argument to some
564 /// command).
565 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
566
567 void appendToResultWithHTMLEscaping(StringRef S);
568
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000569private:
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000570 FullComment *FC;
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000571 /// Output stream for HTML.
572 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000573
574 const CommandTraits &Traits;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000575};
576} // end unnamed namespace
577
578void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
579 appendToResultWithHTMLEscaping(C->getText());
580}
581
582void CommentASTToHTMLConverter::visitInlineCommandComment(
583 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000584 // Nothing to render if no arguments supplied.
585 if (C->getNumArgs() == 0)
586 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000587
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000588 // Nothing to render if argument is empty.
589 StringRef Arg0 = C->getArgText(0);
590 if (Arg0.empty())
591 return;
592
593 switch (C->getRenderKind()) {
594 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000595 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
596 appendToResultWithHTMLEscaping(C->getArgText(i));
597 Result << " ";
598 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000599 return;
600
601 case InlineCommandComment::RenderBold:
602 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000603 Result << "<b>";
604 appendToResultWithHTMLEscaping(Arg0);
605 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000606 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000607 case InlineCommandComment::RenderMonospaced:
608 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000609 Result << "<tt>";
610 appendToResultWithHTMLEscaping(Arg0);
611 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000612 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000613 case InlineCommandComment::RenderEmphasized:
614 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000615 Result << "<em>";
616 appendToResultWithHTMLEscaping(Arg0);
617 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000618 return;
619 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000620}
621
622void CommentASTToHTMLConverter::visitHTMLStartTagComment(
623 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000624 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000625}
626
627void CommentASTToHTMLConverter::visitHTMLEndTagComment(
628 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000629 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000630}
631
632void CommentASTToHTMLConverter::visitParagraphComment(
633 const ParagraphComment *C) {
634 if (C->isWhitespace())
635 return;
636
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000637 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000638 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
639 I != E; ++I) {
640 visit(*I);
641 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000642 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000643}
644
645void CommentASTToHTMLConverter::visitBlockCommandComment(
646 const BlockCommandComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000647 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
648 if (Info->IsBriefCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000649 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000650 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000651 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000652 return;
653 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000654 if (Info->IsReturnsCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000655 Result << "<p class=\"para-returns\">"
656 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000657 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000658 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000659 return;
660 }
661 // We don't know anything about this command. Just render the paragraph.
662 visit(C->getParagraph());
663}
664
665void CommentASTToHTMLConverter::visitParamCommandComment(
666 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000667 if (C->isParamIndexValid()) {
668 Result << "<dt class=\"param-name-index-"
669 << C->getParamIndex()
670 << "\">";
671 } else
672 Result << "<dt class=\"param-name-index-invalid\">";
673
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000674 appendToResultWithHTMLEscaping(C->getParamName(FC->getDeclForCommentLookup()));
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000675 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000676
677 if (C->isParamIndexValid()) {
678 Result << "<dd class=\"param-descr-index-"
679 << C->getParamIndex()
680 << "\">";
681 } else
682 Result << "<dd class=\"param-descr-index-invalid\">";
683
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000684 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000685 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000686}
687
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000688void CommentASTToHTMLConverter::visitTParamCommandComment(
689 const TParamCommandComment *C) {
690 if (C->isPositionValid()) {
691 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000692 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000693 << C->getIndex(0)
694 << "\">";
695 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000696 Result << "<dt class=\"tparam-name-index-other\">";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000697 } else
698 Result << "<dt class=\"tparam-name-index-invalid\">";
699
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000700 appendToResultWithHTMLEscaping(C->getParamName());
701 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000702
703 if (C->isPositionValid()) {
704 if (C->getDepth() == 1)
705 Result << "<dd class=\"tparam-descr-index-"
706 << C->getIndex(0)
707 << "\">";
708 else
709 Result << "<dd class=\"tparam-descr-index-other\">";
710 } else
711 Result << "<dd class=\"tparam-descr-index-invalid\">";
712
713 visitNonStandaloneParagraphComment(C->getParagraph());
714 Result << "</dd>";
715}
716
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000717void CommentASTToHTMLConverter::visitVerbatimBlockComment(
718 const VerbatimBlockComment *C) {
719 unsigned NumLines = C->getNumLines();
720 if (NumLines == 0)
721 return;
722
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000723 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000724 for (unsigned i = 0; i != NumLines; ++i) {
725 appendToResultWithHTMLEscaping(C->getText(i));
726 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000727 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000728 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000729 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000730}
731
732void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
733 const VerbatimBlockLineComment *C) {
734 llvm_unreachable("should not see this AST node");
735}
736
737void CommentASTToHTMLConverter::visitVerbatimLineComment(
738 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000739 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000740 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000741 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000742}
743
744void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000745 FullCommentParts Parts(C, Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000746
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000747 bool FirstParagraphIsBrief = false;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000748 if (Parts.Brief)
749 visit(Parts.Brief);
750 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000751 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000752 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000753 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000754 FirstParagraphIsBrief = true;
755 }
756
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000757 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
758 const Comment *C = Parts.MiscBlocks[i];
759 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000760 continue;
761 visit(C);
762 }
763
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000764 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000765 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000766 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
767 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000768 Result << "</dl>";
769 }
770
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000771 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000772 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000773 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
774 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000775 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000776 }
777
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000778 if (Parts.Returns)
779 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000780
781 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000782}
783
784void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
785 const ParagraphComment *C) {
786 if (!C)
787 return;
788
789 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
790 I != E; ++I) {
791 visit(*I);
792 }
793}
794
795void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000796 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
797 const char C = *I;
798 switch (C) {
799 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000800 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000801 break;
802 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000803 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000804 break;
805 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000806 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000807 break;
808 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000809 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000810 break;
811 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000812 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000813 break;
814 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000815 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000816 break;
817 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000818 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000819 break;
820 }
821 }
822}
823
824extern "C" {
825
826CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
827 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
828 if (!HTC)
829 return createCXString((const char *) 0);
830
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000831 SmallString<128> HTML;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000832 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000833 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000834 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000835}
836
837CXString clang_FullComment_getAsHTML(CXComment CXC) {
838 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
839 if (!FC)
840 return createCXString((const char *) 0);
841
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000842 SmallString<1024> HTML;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000843 CommentASTToHTMLConverter Converter(const_cast<FullComment *>(FC),
844 HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000845 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000846 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000847}
848
849} // end extern "C"
850
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000851namespace {
852class CommentASTToXMLConverter :
853 public ConstCommentVisitor<CommentASTToXMLConverter> {
854public:
855 /// \param Str accumulator for XML.
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000856 CommentASTToXMLConverter(FullComment *FC,
857 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000858 const CommandTraits &Traits,
859 const SourceManager &SM) :
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000860 FC(FC), Result(Str), Traits(Traits), SM(SM) { }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000861
862 // Inline content.
863 void visitTextComment(const TextComment *C);
864 void visitInlineCommandComment(const InlineCommandComment *C);
865 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
866 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
867
868 // Block content.
869 void visitParagraphComment(const ParagraphComment *C);
870 void visitBlockCommandComment(const BlockCommandComment *C);
871 void visitParamCommandComment(const ParamCommandComment *C);
872 void visitTParamCommandComment(const TParamCommandComment *C);
873 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
874 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
875 void visitVerbatimLineComment(const VerbatimLineComment *C);
876
877 void visitFullComment(const FullComment *C);
878
879 // Helpers.
880 void appendToResultWithXMLEscaping(StringRef S);
881
882private:
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000883 FullComment *FC;
884
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000885 /// Output stream for XML.
886 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000887
888 const CommandTraits &Traits;
889 const SourceManager &SM;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000890};
891} // end unnamed namespace
892
893void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
894 appendToResultWithXMLEscaping(C->getText());
895}
896
897void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
898 // Nothing to render if no arguments supplied.
899 if (C->getNumArgs() == 0)
900 return;
901
902 // Nothing to render if argument is empty.
903 StringRef Arg0 = C->getArgText(0);
904 if (Arg0.empty())
905 return;
906
907 switch (C->getRenderKind()) {
908 case InlineCommandComment::RenderNormal:
909 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
910 appendToResultWithXMLEscaping(C->getArgText(i));
911 Result << " ";
912 }
913 return;
914 case InlineCommandComment::RenderBold:
915 assert(C->getNumArgs() == 1);
916 Result << "<bold>";
917 appendToResultWithXMLEscaping(Arg0);
918 Result << "</bold>";
919 return;
920 case InlineCommandComment::RenderMonospaced:
921 assert(C->getNumArgs() == 1);
922 Result << "<monospaced>";
923 appendToResultWithXMLEscaping(Arg0);
924 Result << "</monospaced>";
925 return;
926 case InlineCommandComment::RenderEmphasized:
927 assert(C->getNumArgs() == 1);
928 Result << "<emphasized>";
929 appendToResultWithXMLEscaping(Arg0);
930 Result << "</emphasized>";
931 return;
932 }
933}
934
935void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
936 Result << "<rawHTML><![CDATA[";
937 PrintHTMLStartTagComment(C, Result);
938 Result << "]]></rawHTML>";
939}
940
941void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
942 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
943}
944
945void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
946 if (C->isWhitespace())
947 return;
948
949 Result << "<Para>";
950 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
951 I != E; ++I) {
952 visit(*I);
953 }
954 Result << "</Para>";
955}
956
957void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
958 visit(C->getParagraph());
959}
960
961void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
962 Result << "<Parameter><Name>";
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000963 appendToResultWithXMLEscaping(C->getParamName(FC->getDeclForCommentLookup()));
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000964 Result << "</Name>";
965
966 if (C->isParamIndexValid())
967 Result << "<Index>" << C->getParamIndex() << "</Index>";
968
969 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
970 switch (C->getDirection()) {
971 case ParamCommandComment::In:
972 Result << "in";
973 break;
974 case ParamCommandComment::Out:
975 Result << "out";
976 break;
977 case ParamCommandComment::InOut:
978 Result << "in,out";
979 break;
980 }
981 Result << "</Direction><Discussion>";
982 visit(C->getParagraph());
983 Result << "</Discussion></Parameter>";
984}
985
986void CommentASTToXMLConverter::visitTParamCommandComment(
987 const TParamCommandComment *C) {
988 Result << "<Parameter><Name>";
989 appendToResultWithXMLEscaping(C->getParamName());
990 Result << "</Name>";
991
992 if (C->isPositionValid() && C->getDepth() == 1) {
993 Result << "<Index>" << C->getIndex(0) << "</Index>";
994 }
995
996 Result << "<Discussion>";
997 visit(C->getParagraph());
998 Result << "</Discussion></Parameter>";
999}
1000
1001void CommentASTToXMLConverter::visitVerbatimBlockComment(
1002 const VerbatimBlockComment *C) {
1003 unsigned NumLines = C->getNumLines();
1004 if (NumLines == 0)
1005 return;
1006
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001007 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001008 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1009 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001010 for (unsigned i = 0; i != NumLines; ++i) {
1011 appendToResultWithXMLEscaping(C->getText(i));
1012 if (i + 1 != NumLines)
1013 Result << '\n';
1014 }
1015 Result << "</Verbatim>";
1016}
1017
1018void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1019 const VerbatimBlockLineComment *C) {
1020 llvm_unreachable("should not see this AST node");
1021}
1022
1023void CommentASTToXMLConverter::visitVerbatimLineComment(
1024 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001025 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001026 appendToResultWithXMLEscaping(C->getText());
1027 Result << "</Verbatim>";
1028}
1029
1030void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001031 FullCommentParts Parts(C, Traits);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001032
1033 const DeclInfo *DI = C->getDeclInfo();
1034 StringRef RootEndTag;
1035 if (DI) {
1036 switch (DI->getKind()) {
1037 case DeclInfo::OtherKind:
1038 RootEndTag = "</Other>";
1039 Result << "<Other";
1040 break;
1041 case DeclInfo::FunctionKind:
1042 RootEndTag = "</Function>";
1043 Result << "<Function";
1044 switch (DI->TemplateKind) {
1045 case DeclInfo::NotTemplate:
1046 break;
1047 case DeclInfo::Template:
1048 Result << " templateKind=\"template\"";
1049 break;
1050 case DeclInfo::TemplateSpecialization:
1051 Result << " templateKind=\"specialization\"";
1052 break;
1053 case DeclInfo::TemplatePartialSpecialization:
1054 llvm_unreachable("partial specializations of functions "
1055 "are not allowed in C++");
1056 }
1057 if (DI->IsInstanceMethod)
1058 Result << " isInstanceMethod=\"1\"";
1059 if (DI->IsClassMethod)
1060 Result << " isClassMethod=\"1\"";
1061 break;
1062 case DeclInfo::ClassKind:
1063 RootEndTag = "</Class>";
1064 Result << "<Class";
1065 switch (DI->TemplateKind) {
1066 case DeclInfo::NotTemplate:
1067 break;
1068 case DeclInfo::Template:
1069 Result << " templateKind=\"template\"";
1070 break;
1071 case DeclInfo::TemplateSpecialization:
1072 Result << " templateKind=\"specialization\"";
1073 break;
1074 case DeclInfo::TemplatePartialSpecialization:
1075 Result << " templateKind=\"partialSpecialization\"";
1076 break;
1077 }
1078 break;
1079 case DeclInfo::VariableKind:
1080 RootEndTag = "</Variable>";
1081 Result << "<Variable";
1082 break;
1083 case DeclInfo::NamespaceKind:
1084 RootEndTag = "</Namespace>";
1085 Result << "<Namespace";
1086 break;
1087 case DeclInfo::TypedefKind:
1088 RootEndTag = "</Typedef>";
1089 Result << "<Typedef";
1090 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001091 case DeclInfo::EnumKind:
1092 RootEndTag = "</Enum>";
1093 Result << "<Enum";
1094 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001095 }
1096
1097 {
1098 // Print line and column number.
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001099 SourceLocation Loc = DI->CommentDecl->getLocation();
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001100 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1101 FileID FID = LocInfo.first;
1102 unsigned FileOffset = LocInfo.second;
1103
1104 if (!FID.isInvalid()) {
1105 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1106 Result << " file=\"";
1107 appendToResultWithXMLEscaping(FE->getName());
1108 Result << "\"";
1109 }
1110 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1111 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1112 << "\"";
1113 }
1114 }
1115
1116 // Finish the root tag.
1117 Result << ">";
1118
1119 bool FoundName = false;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001120 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001121 if (DeclarationName DeclName = ND->getDeclName()) {
1122 Result << "<Name>";
1123 std::string Name = DeclName.getAsString();
1124 appendToResultWithXMLEscaping(Name);
1125 FoundName = true;
1126 Result << "</Name>";
1127 }
1128 }
1129 if (!FoundName)
1130 Result << "<Name>&lt;anonymous&gt;</Name>";
1131
1132 {
1133 // Print USR.
1134 SmallString<128> USR;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001135 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001136 if (!USR.empty()) {
1137 Result << "<USR>";
1138 appendToResultWithXMLEscaping(USR);
1139 Result << "</USR>";
1140 }
1141 }
1142 } else {
1143 // No DeclInfo -- just emit some root tag and name tag.
1144 RootEndTag = "</Other>";
1145 Result << "<Other><Name>unknown</Name>";
1146 }
1147
1148 bool FirstParagraphIsBrief = false;
1149 if (Parts.Brief) {
1150 Result << "<Abstract>";
1151 visit(Parts.Brief);
1152 Result << "</Abstract>";
1153 } else if (Parts.FirstParagraph) {
1154 Result << "<Abstract>";
1155 visit(Parts.FirstParagraph);
1156 Result << "</Abstract>";
1157 FirstParagraphIsBrief = true;
1158 }
1159
1160 if (Parts.TParams.size() != 0) {
1161 Result << "<TemplateParameters>";
1162 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1163 visit(Parts.TParams[i]);
1164 Result << "</TemplateParameters>";
1165 }
1166
1167 if (Parts.Params.size() != 0) {
1168 Result << "<Parameters>";
1169 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1170 visit(Parts.Params[i]);
1171 Result << "</Parameters>";
1172 }
1173
1174 if (Parts.Returns) {
1175 Result << "<ResultDiscussion>";
1176 visit(Parts.Returns);
1177 Result << "</ResultDiscussion>";
1178 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001179
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001180 if (DI->CommentDecl->hasAttrs()) {
1181 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001182 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1183 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001184 if (!AA) {
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001185 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1186 if (DA->getMessage().empty())
1187 Result << "<Deprecated/>";
1188 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001189 Result << "<Deprecated>";
1190 appendToResultWithXMLEscaping(DA->getMessage());
1191 Result << "</Deprecated>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001192 }
1193 }
1194 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1195 if (UA->getMessage().empty())
1196 Result << "<Unavailable/>";
1197 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001198 Result << "<Unavailable>";
1199 appendToResultWithXMLEscaping(UA->getMessage());
1200 Result << "</Unavailable>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001201 }
1202 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001203 continue;
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001204 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001205
1206 // 'availability' attribute.
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001207 Result << "<Availability";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001208 StringRef Distribution;
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001209 if (AA->getPlatform()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001210 Distribution = AvailabilityAttr::getPrettyPlatformName(
1211 AA->getPlatform()->getName());
1212 if (Distribution.empty())
1213 Distribution = AA->getPlatform()->getName();
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001214 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001215 Result << " distribution=\"" << Distribution << "\">";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001216 VersionTuple IntroducedInVersion = AA->getIntroduced();
1217 if (!IntroducedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001218 Result << "<IntroducedInVersion>"
1219 << IntroducedInVersion.getAsString()
1220 << "</IntroducedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001221 }
1222 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1223 if (!DeprecatedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001224 Result << "<DeprecatedInVersion>"
1225 << DeprecatedInVersion.getAsString()
1226 << "</DeprecatedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001227 }
1228 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1229 if (!RemovedAfterVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001230 Result << "<RemovedAfterVersion>"
1231 << RemovedAfterVersion.getAsString()
1232 << "</RemovedAfterVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001233 }
1234 StringRef DeprecationSummary = AA->getMessage();
1235 if (!DeprecationSummary.empty()) {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001236 Result << "<DeprecationSummary>";
1237 appendToResultWithXMLEscaping(DeprecationSummary);
1238 Result << "</DeprecationSummary>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001239 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001240 if (AA->getUnavailable())
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001241 Result << "<Unavailable/>";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001242 Result << "</Availability>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001243 }
1244 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001245
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001246 {
1247 bool StartTagEmitted = false;
1248 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1249 const Comment *C = Parts.MiscBlocks[i];
1250 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1251 continue;
1252 if (!StartTagEmitted) {
1253 Result << "<Discussion>";
1254 StartTagEmitted = true;
1255 }
1256 visit(C);
1257 }
1258 if (StartTagEmitted)
1259 Result << "</Discussion>";
1260 }
1261
1262 Result << RootEndTag;
1263
1264 Result.flush();
1265}
1266
1267void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1268 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1269 const char C = *I;
1270 switch (C) {
1271 case '&':
1272 Result << "&amp;";
1273 break;
1274 case '<':
1275 Result << "&lt;";
1276 break;
1277 case '>':
1278 Result << "&gt;";
1279 break;
1280 case '"':
1281 Result << "&quot;";
1282 break;
1283 case '\'':
1284 Result << "&apos;";
1285 break;
1286 default:
1287 Result << C;
1288 break;
1289 }
1290 }
1291}
1292
1293extern "C" {
1294
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001295CXString clang_FullComment_getAsXML(CXComment CXC) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001296 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1297 if (!FC)
1298 return createCXString((const char *) 0);
1299
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001300 CXTranslationUnit TU = CXC.TranslationUnit;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001301 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
1302
1303 SmallString<1024> XML;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001304 CommentASTToXMLConverter Converter(const_cast<FullComment *>(FC), XML,
1305 getCommandTraits(CXC), SM);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001306 Converter.visit(FC);
1307 return createCXString(XML.str(), /* DupString = */ true);
1308}
1309
1310} // end extern "C"
1311