blob: caf6a857f09b9fa5151a9b21f253f040c81626ef [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"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000015#include "CXComment.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000016#include "CXCursor.h"
Chandler Carruthf59edb92012-12-04 09:25:21 +000017#include "CXString.h"
Fariborz Jahanian88b95212012-12-18 23:02:59 +000018#include "SimpleFormatContext.h"
Dmitri Gribenkod1db1252012-08-09 17:33:20 +000019#include "clang/AST/CommentCommandTraits.h"
Chandler Carruthf59edb92012-12-04 09:25:21 +000020#include "clang/AST/CommentVisitor.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000021#include "clang/AST/Decl.h"
Chandler Carruthf59edb92012-12-04 09:25:21 +000022#include "clang/AST/PrettyPrinter.h"
Fariborz Jahanian88b95212012-12-18 23:02:59 +000023#include "llvm/ADT/StringExtras.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000024#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000025#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000026#include "llvm/Support/raw_ostream.h"
Fariborz Jahanian88b95212012-12-18 23:02:59 +000027#include "clang/Format/Format.h"
28#include "clang/Lex/Lexer.h"
Dmitri Gribenko221a6d72012-07-30 17:49:32 +000029#include <climits>
30
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000031using namespace clang;
32using namespace clang::cxstring;
33using namespace clang::comments;
34using namespace clang::cxcomment;
35
36extern "C" {
37
38enum CXCommentKind clang_Comment_getKind(CXComment CXC) {
39 const Comment *C = getASTNode(CXC);
40 if (!C)
41 return CXComment_Null;
42
43 switch (C->getCommentKind()) {
44 case Comment::NoCommentKind:
45 return CXComment_Null;
46
47 case Comment::TextCommentKind:
48 return CXComment_Text;
49
50 case Comment::InlineCommandCommentKind:
51 return CXComment_InlineCommand;
52
53 case Comment::HTMLStartTagCommentKind:
54 return CXComment_HTMLStartTag;
55
56 case Comment::HTMLEndTagCommentKind:
57 return CXComment_HTMLEndTag;
58
59 case Comment::ParagraphCommentKind:
60 return CXComment_Paragraph;
61
62 case Comment::BlockCommandCommentKind:
63 return CXComment_BlockCommand;
64
65 case Comment::ParamCommandCommentKind:
66 return CXComment_ParamCommand;
67
Dmitri Gribenko96b09862012-07-31 22:37:06 +000068 case Comment::TParamCommandCommentKind:
69 return CXComment_TParamCommand;
70
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000071 case Comment::VerbatimBlockCommentKind:
72 return CXComment_VerbatimBlockCommand;
73
74 case Comment::VerbatimBlockLineCommentKind:
75 return CXComment_VerbatimBlockLine;
76
77 case Comment::VerbatimLineCommentKind:
78 return CXComment_VerbatimLine;
79
80 case Comment::FullCommentKind:
81 return CXComment_FullComment;
82 }
83 llvm_unreachable("unknown CommentKind");
84}
85
86unsigned clang_Comment_getNumChildren(CXComment CXC) {
87 const Comment *C = getASTNode(CXC);
88 if (!C)
89 return 0;
90
91 return C->child_count();
92}
93
94CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) {
95 const Comment *C = getASTNode(CXC);
96 if (!C || ChildIdx >= C->child_count())
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000097 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000098
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000099 return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000100}
101
102unsigned clang_Comment_isWhitespace(CXComment CXC) {
103 const Comment *C = getASTNode(CXC);
104 if (!C)
105 return false;
106
107 if (const TextComment *TC = dyn_cast<TextComment>(C))
108 return TC->isWhitespace();
109
110 if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C))
111 return PC->isWhitespace();
112
113 return false;
114}
115
116unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) {
117 const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC);
118 if (!ICC)
119 return false;
120
121 return ICC->hasTrailingNewline();
122}
123
124CXString clang_TextComment_getText(CXComment CXC) {
125 const TextComment *TC = getASTNodeAs<TextComment>(CXC);
126 if (!TC)
127 return createCXString((const char *) 0);
128
129 return createCXString(TC->getText(), /*DupString=*/ false);
130}
131
132CXString clang_InlineCommandComment_getCommandName(CXComment CXC) {
133 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
134 if (!ICC)
135 return createCXString((const char *) 0);
136
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000137 const CommandTraits &Traits = getCommandTraits(CXC);
138 return createCXString(ICC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000139}
140
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000141enum CXCommentInlineCommandRenderKind
142clang_InlineCommandComment_getRenderKind(CXComment CXC) {
143 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
144 if (!ICC)
145 return CXCommentInlineCommandRenderKind_Normal;
146
147 switch (ICC->getRenderKind()) {
148 case InlineCommandComment::RenderNormal:
149 return CXCommentInlineCommandRenderKind_Normal;
150
151 case InlineCommandComment::RenderBold:
152 return CXCommentInlineCommandRenderKind_Bold;
153
154 case InlineCommandComment::RenderMonospaced:
155 return CXCommentInlineCommandRenderKind_Monospaced;
156
157 case InlineCommandComment::RenderEmphasized:
158 return CXCommentInlineCommandRenderKind_Emphasized;
159 }
160 llvm_unreachable("unknown InlineCommandComment::RenderKind");
161}
162
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000163unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) {
164 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
165 if (!ICC)
166 return 0;
167
168 return ICC->getNumArgs();
169}
170
171CXString clang_InlineCommandComment_getArgText(CXComment CXC,
172 unsigned ArgIdx) {
173 const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC);
174 if (!ICC || ArgIdx >= ICC->getNumArgs())
175 return createCXString((const char *) 0);
176
177 return createCXString(ICC->getArgText(ArgIdx), /*DupString=*/ false);
178}
179
180CXString clang_HTMLTagComment_getTagName(CXComment CXC) {
181 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
182 if (!HTC)
183 return createCXString((const char *) 0);
184
185 return createCXString(HTC->getTagName(), /*DupString=*/ false);
186}
187
188unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) {
189 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
190 if (!HST)
191 return false;
192
193 return HST->isSelfClosing();
194}
195
196unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) {
197 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
198 if (!HST)
199 return 0;
200
201 return HST->getNumAttrs();
202}
203
204CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) {
205 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
206 if (!HST || AttrIdx >= HST->getNumAttrs())
207 return createCXString((const char *) 0);
208
209 return createCXString(HST->getAttr(AttrIdx).Name, /*DupString=*/ false);
210}
211
212CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) {
213 const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC);
214 if (!HST || AttrIdx >= HST->getNumAttrs())
215 return createCXString((const char *) 0);
216
217 return createCXString(HST->getAttr(AttrIdx).Value, /*DupString=*/ false);
218}
219
220CXString clang_BlockCommandComment_getCommandName(CXComment CXC) {
221 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
222 if (!BCC)
223 return createCXString((const char *) 0);
224
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000225 const CommandTraits &Traits = getCommandTraits(CXC);
226 return createCXString(BCC->getCommandName(Traits), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000227}
228
229unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) {
230 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
231 if (!BCC)
232 return 0;
233
234 return BCC->getNumArgs();
235}
236
237CXString clang_BlockCommandComment_getArgText(CXComment CXC,
238 unsigned ArgIdx) {
239 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
240 if (!BCC || ArgIdx >= BCC->getNumArgs())
241 return createCXString((const char *) 0);
242
243 return createCXString(BCC->getArgText(ArgIdx), /*DupString=*/ false);
244}
245
246CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) {
247 const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC);
248 if (!BCC)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000249 return createCXComment(NULL, NULL);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000250
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000251 return createCXComment(BCC->getParagraph(), CXC.TranslationUnit);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000252}
253
254CXString clang_ParamCommandComment_getParamName(CXComment CXC) {
255 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenko1f8c5292012-07-23 19:41:49 +0000256 if (!PCC || !PCC->hasParamName())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000257 return createCXString((const char *) 0);
258
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000259 return createCXString(PCC->getParamNameAsWritten(), /*DupString=*/ false);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000260}
261
262unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) {
263 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
264 if (!PCC)
265 return false;
266
267 return PCC->isParamIndexValid();
268}
269
270unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) {
271 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000272 if (!PCC || !PCC->isParamIndexValid())
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000273 return ParamCommandComment::InvalidParamIndex;
274
275 return PCC->getParamIndex();
276}
277
278unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) {
279 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
280 if (!PCC)
281 return false;
282
283 return PCC->isDirectionExplicit();
284}
285
286enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection(
287 CXComment CXC) {
288 const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC);
289 if (!PCC)
290 return CXCommentParamPassDirection_In;
291
292 switch (PCC->getDirection()) {
293 case ParamCommandComment::In:
294 return CXCommentParamPassDirection_In;
295
296 case ParamCommandComment::Out:
297 return CXCommentParamPassDirection_Out;
298
299 case ParamCommandComment::InOut:
300 return CXCommentParamPassDirection_InOut;
301 }
302 llvm_unreachable("unknown ParamCommandComment::PassDirection");
303}
304
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000305CXString clang_TParamCommandComment_getParamName(CXComment CXC) {
306 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
307 if (!TPCC || !TPCC->hasParamName())
308 return createCXString((const char *) 0);
309
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000310 return createCXString(TPCC->getParamNameAsWritten(), /*DupString=*/ false);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000311}
312
313unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) {
314 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
315 if (!TPCC)
316 return false;
317
318 return TPCC->isPositionValid();
319}
320
321unsigned clang_TParamCommandComment_getDepth(CXComment CXC) {
322 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
323 if (!TPCC || !TPCC->isPositionValid())
324 return 0;
325
326 return TPCC->getDepth();
327}
328
329unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) {
330 const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC);
331 if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth())
332 return 0;
333
334 return TPCC->getIndex(Depth);
335}
336
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000337CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) {
338 const VerbatimBlockLineComment *VBL =
339 getASTNodeAs<VerbatimBlockLineComment>(CXC);
340 if (!VBL)
341 return createCXString((const char *) 0);
342
343 return createCXString(VBL->getText(), /*DupString=*/ false);
344}
345
346CXString clang_VerbatimLineComment_getText(CXComment CXC) {
347 const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC);
348 if (!VLC)
349 return createCXString((const char *) 0);
350
351 return createCXString(VLC->getText(), /*DupString=*/ false);
352}
353
354} // end extern "C"
355
356//===----------------------------------------------------------------------===//
357// Helpers for converting comment AST to HTML.
358//===----------------------------------------------------------------------===//
359
360namespace {
361
Dmitri Gribenkoe5db09c2012-07-30 19:47:34 +0000362/// This comparison will sort parameters with valid index by index and
363/// invalid (unresolved) parameters last.
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000364class ParamCommandCommentCompareIndex {
365public:
366 bool operator()(const ParamCommandComment *LHS,
367 const ParamCommandComment *RHS) const {
Dmitri Gribenkob7403162012-07-30 17:38:19 +0000368 unsigned LHSIndex = UINT_MAX;
369 unsigned RHSIndex = UINT_MAX;
370 if (LHS->isParamIndexValid())
371 LHSIndex = LHS->getParamIndex();
372 if (RHS->isParamIndexValid())
373 RHSIndex = RHS->getParamIndex();
374
375 return LHSIndex < RHSIndex;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000376 }
377};
378
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000379/// This comparison will sort template parameters in the following order:
380/// \li real template parameters (depth = 1) in index order;
381/// \li all other names (depth > 1);
382/// \li unresolved names.
383class TParamCommandCommentComparePosition {
384public:
385 bool operator()(const TParamCommandComment *LHS,
386 const TParamCommandComment *RHS) const {
387 // Sort unresolved names last.
388 if (!LHS->isPositionValid())
389 return false;
390 if (!RHS->isPositionValid())
391 return true;
392
393 if (LHS->getDepth() > 1)
394 return false;
395 if (RHS->getDepth() > 1)
396 return true;
397
398 // Sort template parameters in index order.
399 if (LHS->getDepth() == 1 && RHS->getDepth() == 1)
400 return LHS->getIndex(0) < RHS->getIndex(0);
401
402 // Leave all other names in source order.
403 return true;
404 }
405};
406
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000407/// Separate parts of a FullComment.
408struct FullCommentParts {
409 /// Take a full comment apart and initialize members accordingly.
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000410 FullCommentParts(const FullComment *C,
411 const CommandTraits &Traits);
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000412
413 const BlockContentComment *Brief;
414 const ParagraphComment *FirstParagraph;
415 const BlockCommandComment *Returns;
416 SmallVector<const ParamCommandComment *, 8> Params;
417 SmallVector<const TParamCommandComment *, 4> TParams;
418 SmallVector<const BlockContentComment *, 8> MiscBlocks;
419};
420
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000421FullCommentParts::FullCommentParts(const FullComment *C,
422 const CommandTraits &Traits) :
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000423 Brief(NULL), FirstParagraph(NULL), Returns(NULL) {
424 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
425 I != E; ++I) {
426 const Comment *Child = *I;
427 if (!Child)
428 continue;
429 switch (Child->getCommentKind()) {
430 case Comment::NoCommentKind:
431 continue;
432
433 case Comment::ParagraphCommentKind: {
434 const ParagraphComment *PC = cast<ParagraphComment>(Child);
435 if (PC->isWhitespace())
436 break;
437 if (!FirstParagraph)
438 FirstParagraph = PC;
439
440 MiscBlocks.push_back(PC);
441 break;
442 }
443
444 case Comment::BlockCommandCommentKind: {
445 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000446 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
447 if (!Brief && Info->IsBriefCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000448 Brief = BCC;
449 break;
450 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000451 if (!Returns && Info->IsReturnsCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000452 Returns = BCC;
453 break;
454 }
455 MiscBlocks.push_back(BCC);
456 break;
457 }
458
459 case Comment::ParamCommandCommentKind: {
460 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
461 if (!PCC->hasParamName())
462 break;
463
464 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
465 break;
466
467 Params.push_back(PCC);
468 break;
469 }
470
471 case Comment::TParamCommandCommentKind: {
472 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
473 if (!TPCC->hasParamName())
474 break;
475
476 if (!TPCC->hasNonWhitespaceParagraph())
477 break;
478
479 TParams.push_back(TPCC);
480 break;
481 }
482
483 case Comment::VerbatimBlockCommentKind:
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000484 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
485 break;
486
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000487 case Comment::VerbatimLineCommentKind: {
488 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000489 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
490 if (!Info->IsDeclarationCommand)
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000491 MiscBlocks.push_back(VLC);
492 break;
493 }
494
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000495 case Comment::TextCommentKind:
496 case Comment::InlineCommandCommentKind:
497 case Comment::HTMLStartTagCommentKind:
498 case Comment::HTMLEndTagCommentKind:
499 case Comment::VerbatimBlockLineCommentKind:
500 case Comment::FullCommentKind:
501 llvm_unreachable("AST node of this kind can't be a child of "
502 "a FullComment");
503 }
504 }
505
506 // Sort params in order they are declared in the function prototype.
507 // Unresolved parameters are put at the end of the list in the same order
508 // they were seen in the comment.
509 std::stable_sort(Params.begin(), Params.end(),
510 ParamCommandCommentCompareIndex());
511
512 std::stable_sort(TParams.begin(), TParams.end(),
513 TParamCommandCommentComparePosition());
514}
515
516void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
517 llvm::raw_svector_ostream &Result) {
518 Result << "<" << C->getTagName();
519
520 if (C->getNumAttrs() != 0) {
521 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
522 Result << " ";
523 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
524 Result << Attr.Name;
525 if (!Attr.Value.empty())
526 Result << "=\"" << Attr.Value << "\"";
527 }
528 }
529
530 if (!C->isSelfClosing())
531 Result << ">";
532 else
533 Result << "/>";
534}
535
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000536class CommentASTToHTMLConverter :
537 public ConstCommentVisitor<CommentASTToHTMLConverter> {
538public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000539 /// \param Str accumulator for HTML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000540 CommentASTToHTMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000541 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000542 const CommandTraits &Traits) :
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000543 FC(FC), Result(Str), Traits(Traits)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000544 { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000545
546 // Inline content.
547 void visitTextComment(const TextComment *C);
548 void visitInlineCommandComment(const InlineCommandComment *C);
549 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
550 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
551
552 // Block content.
553 void visitParagraphComment(const ParagraphComment *C);
554 void visitBlockCommandComment(const BlockCommandComment *C);
555 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000556 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000557 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
558 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
559 void visitVerbatimLineComment(const VerbatimLineComment *C);
560
561 void visitFullComment(const FullComment *C);
562
563 // Helpers.
564
565 /// Convert a paragraph that is not a block by itself (an argument to some
566 /// command).
567 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
568
569 void appendToResultWithHTMLEscaping(StringRef S);
570
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000571private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000572 const FullComment *FC;
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000573 /// Output stream for HTML.
574 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000575
576 const CommandTraits &Traits;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000577};
578} // end unnamed namespace
579
580void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
581 appendToResultWithHTMLEscaping(C->getText());
582}
583
584void CommentASTToHTMLConverter::visitInlineCommandComment(
585 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000586 // Nothing to render if no arguments supplied.
587 if (C->getNumArgs() == 0)
588 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000589
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000590 // Nothing to render if argument is empty.
591 StringRef Arg0 = C->getArgText(0);
592 if (Arg0.empty())
593 return;
594
595 switch (C->getRenderKind()) {
596 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000597 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
598 appendToResultWithHTMLEscaping(C->getArgText(i));
599 Result << " ";
600 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000601 return;
602
603 case InlineCommandComment::RenderBold:
604 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000605 Result << "<b>";
606 appendToResultWithHTMLEscaping(Arg0);
607 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000608 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000609 case InlineCommandComment::RenderMonospaced:
610 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000611 Result << "<tt>";
612 appendToResultWithHTMLEscaping(Arg0);
613 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000614 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000615 case InlineCommandComment::RenderEmphasized:
616 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000617 Result << "<em>";
618 appendToResultWithHTMLEscaping(Arg0);
619 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000620 return;
621 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000622}
623
624void CommentASTToHTMLConverter::visitHTMLStartTagComment(
625 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000626 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000627}
628
629void CommentASTToHTMLConverter::visitHTMLEndTagComment(
630 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000631 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000632}
633
634void CommentASTToHTMLConverter::visitParagraphComment(
635 const ParagraphComment *C) {
636 if (C->isWhitespace())
637 return;
638
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000639 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000640 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
641 I != E; ++I) {
642 visit(*I);
643 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000644 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000645}
646
647void CommentASTToHTMLConverter::visitBlockCommandComment(
648 const BlockCommandComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000649 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
650 if (Info->IsBriefCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000651 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000652 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000653 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000654 return;
655 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000656 if (Info->IsReturnsCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000657 Result << "<p class=\"para-returns\">"
658 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000659 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000660 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000661 return;
662 }
663 // We don't know anything about this command. Just render the paragraph.
664 visit(C->getParagraph());
665}
666
667void CommentASTToHTMLConverter::visitParamCommandComment(
668 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000669 if (C->isParamIndexValid()) {
670 Result << "<dt class=\"param-name-index-"
671 << C->getParamIndex()
672 << "\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000673 appendToResultWithHTMLEscaping(C->getParamName(FC));
674 } else {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000675 Result << "<dt class=\"param-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000676 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
677 }
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000678 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000679
680 if (C->isParamIndexValid()) {
681 Result << "<dd class=\"param-descr-index-"
682 << C->getParamIndex()
683 << "\">";
684 } else
685 Result << "<dd class=\"param-descr-index-invalid\">";
686
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000687 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000688 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000689}
690
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000691void CommentASTToHTMLConverter::visitTParamCommandComment(
692 const TParamCommandComment *C) {
693 if (C->isPositionValid()) {
694 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000695 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000696 << C->getIndex(0)
697 << "\">";
698 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000699 Result << "<dt class=\"tparam-name-index-other\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000700 appendToResultWithHTMLEscaping(C->getParamName(FC));
701 } else {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000702 Result << "<dt class=\"tparam-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000703 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
704 }
705
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000706 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000707
708 if (C->isPositionValid()) {
709 if (C->getDepth() == 1)
710 Result << "<dd class=\"tparam-descr-index-"
711 << C->getIndex(0)
712 << "\">";
713 else
714 Result << "<dd class=\"tparam-descr-index-other\">";
715 } else
716 Result << "<dd class=\"tparam-descr-index-invalid\">";
717
718 visitNonStandaloneParagraphComment(C->getParagraph());
719 Result << "</dd>";
720}
721
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000722void CommentASTToHTMLConverter::visitVerbatimBlockComment(
723 const VerbatimBlockComment *C) {
724 unsigned NumLines = C->getNumLines();
725 if (NumLines == 0)
726 return;
727
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000728 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000729 for (unsigned i = 0; i != NumLines; ++i) {
730 appendToResultWithHTMLEscaping(C->getText(i));
731 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000732 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000733 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000734 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000735}
736
737void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
738 const VerbatimBlockLineComment *C) {
739 llvm_unreachable("should not see this AST node");
740}
741
742void CommentASTToHTMLConverter::visitVerbatimLineComment(
743 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000744 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000745 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000746 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000747}
748
749void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000750 FullCommentParts Parts(C, Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000751
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000752 bool FirstParagraphIsBrief = false;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000753 if (Parts.Brief)
754 visit(Parts.Brief);
755 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000756 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000757 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000758 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000759 FirstParagraphIsBrief = true;
760 }
761
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000762 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
763 const Comment *C = Parts.MiscBlocks[i];
764 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000765 continue;
766 visit(C);
767 }
768
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000769 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000770 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000771 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
772 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000773 Result << "</dl>";
774 }
775
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000776 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000777 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000778 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
779 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000780 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000781 }
782
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000783 if (Parts.Returns)
784 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000785
786 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000787}
788
789void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
790 const ParagraphComment *C) {
791 if (!C)
792 return;
793
794 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
795 I != E; ++I) {
796 visit(*I);
797 }
798}
799
800void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000801 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
802 const char C = *I;
803 switch (C) {
804 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000805 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000806 break;
807 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000808 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000809 break;
810 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000811 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000812 break;
813 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000814 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000815 break;
816 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000817 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000818 break;
819 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000820 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000821 break;
822 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000823 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000824 break;
825 }
826 }
827}
828
829extern "C" {
830
831CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
832 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
833 if (!HTC)
834 return createCXString((const char *) 0);
835
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000836 SmallString<128> HTML;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000837 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000838 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000839 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000840}
841
842CXString clang_FullComment_getAsHTML(CXComment CXC) {
843 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
844 if (!FC)
845 return createCXString((const char *) 0);
846
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000847 SmallString<1024> HTML;
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000848 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000849 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000850 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000851}
852
853} // end extern "C"
854
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000855namespace {
856class CommentASTToXMLConverter :
857 public ConstCommentVisitor<CommentASTToXMLConverter> {
858public:
859 /// \param Str accumulator for XML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000860 CommentASTToXMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000861 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000862 const CommandTraits &Traits,
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000863 const SourceManager &SM,
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000864 SimpleFormatContext &SFC,
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000865 unsigned FUID) :
866 FC(FC), Result(Str), Traits(Traits), SM(SM),
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000867 FormatRewriterContext(SFC),
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000868 FormatInMemoryUniqueId(FUID) { }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000869
870 // Inline content.
871 void visitTextComment(const TextComment *C);
872 void visitInlineCommandComment(const InlineCommandComment *C);
873 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
874 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
875
876 // Block content.
877 void visitParagraphComment(const ParagraphComment *C);
878 void visitBlockCommandComment(const BlockCommandComment *C);
879 void visitParamCommandComment(const ParamCommandComment *C);
880 void visitTParamCommandComment(const TParamCommandComment *C);
881 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
882 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
883 void visitVerbatimLineComment(const VerbatimLineComment *C);
884
885 void visitFullComment(const FullComment *C);
886
887 // Helpers.
888 void appendToResultWithXMLEscaping(StringRef S);
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000889
890 void formatTextOfDeclaration(const DeclInfo *DI,
891 SmallString<128> &Declaration);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000892
893private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000894 const FullComment *FC;
895
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000896 /// Output stream for XML.
897 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000898
899 const CommandTraits &Traits;
900 const SourceManager &SM;
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000901 SimpleFormatContext &FormatRewriterContext;
902 unsigned FormatInMemoryUniqueId;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000903};
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000904
905void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
906 SmallVectorImpl<char> &Str) {
907 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
908 const LangOptions &LangOpts = Context.getLangOpts();
909 llvm::raw_svector_ostream OS(Str);
910 PrintingPolicy PPolicy(LangOpts);
Fariborz Jahanian40902d82012-12-19 23:36:00 +0000911 PPolicy.PolishForDeclaration = true;
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000912 PPolicy.TerseOutput = true;
913 ThisDecl->CurrentDecl->print(OS, PPolicy,
914 /*Indentation*/0, /*PrintInstantiation*/true);
915}
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000916
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000917void CommentASTToXMLConverter::formatTextOfDeclaration(
918 const DeclInfo *DI,
919 SmallString<128> &Declaration) {
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000920 // FIXME. This conditional is TEMPORARY. We don't want to break multiple
921 // large tests each time Format.cpp changes. This condition will
922 // go away and formatting will happen for all declarations.
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000923 if (!getenv("LIBCLANG_ACTIVATE_FORMAT"))
924 return;
925
926 // FIXME. formatting API expects null terminated input string.
927 // There might be more efficient way of doing this.
928 std::string StringDecl = Declaration.str();
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000929
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000930 // Formatter specific code.
931 // Form a unique in memory buffer name.
932 llvm::SmallString<128> filename;
933 filename += "xmldecl";
934 filename += llvm::utostr(FormatInMemoryUniqueId);
935 filename += ".xd";
936 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
937 SourceLocation Start =
938 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
939 unsigned Length = Declaration.size();
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000940
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000941 std::vector<CharSourceRange>
942 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
943 ASTContext &Context = DI->CurrentDecl->getASTContext();
944 const LangOptions &LangOpts = Context.getLangOpts();
945 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
946 FormatRewriterContext.Sources, LangOpts);
947 tooling::Replacements Replace =
948 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
949 applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
950 Declaration = FormatRewriterContext.getRewrittenText(ID);
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000951}
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000952
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000953} // end unnamed namespace
954
955void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
956 appendToResultWithXMLEscaping(C->getText());
957}
958
959void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
960 // Nothing to render if no arguments supplied.
961 if (C->getNumArgs() == 0)
962 return;
963
964 // Nothing to render if argument is empty.
965 StringRef Arg0 = C->getArgText(0);
966 if (Arg0.empty())
967 return;
968
969 switch (C->getRenderKind()) {
970 case InlineCommandComment::RenderNormal:
971 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
972 appendToResultWithXMLEscaping(C->getArgText(i));
973 Result << " ";
974 }
975 return;
976 case InlineCommandComment::RenderBold:
977 assert(C->getNumArgs() == 1);
978 Result << "<bold>";
979 appendToResultWithXMLEscaping(Arg0);
980 Result << "</bold>";
981 return;
982 case InlineCommandComment::RenderMonospaced:
983 assert(C->getNumArgs() == 1);
984 Result << "<monospaced>";
985 appendToResultWithXMLEscaping(Arg0);
986 Result << "</monospaced>";
987 return;
988 case InlineCommandComment::RenderEmphasized:
989 assert(C->getNumArgs() == 1);
990 Result << "<emphasized>";
991 appendToResultWithXMLEscaping(Arg0);
992 Result << "</emphasized>";
993 return;
994 }
995}
996
997void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
998 Result << "<rawHTML><![CDATA[";
999 PrintHTMLStartTagComment(C, Result);
1000 Result << "]]></rawHTML>";
1001}
1002
1003void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
1004 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
1005}
1006
1007void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
1008 if (C->isWhitespace())
1009 return;
1010
1011 Result << "<Para>";
1012 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
1013 I != E; ++I) {
1014 visit(*I);
1015 }
1016 Result << "</Para>";
1017}
1018
1019void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
1020 visit(C->getParagraph());
1021}
1022
1023void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
1024 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001025 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
1026 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001027 Result << "</Name>";
1028
1029 if (C->isParamIndexValid())
1030 Result << "<Index>" << C->getParamIndex() << "</Index>";
1031
1032 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
1033 switch (C->getDirection()) {
1034 case ParamCommandComment::In:
1035 Result << "in";
1036 break;
1037 case ParamCommandComment::Out:
1038 Result << "out";
1039 break;
1040 case ParamCommandComment::InOut:
1041 Result << "in,out";
1042 break;
1043 }
1044 Result << "</Direction><Discussion>";
1045 visit(C->getParagraph());
1046 Result << "</Discussion></Parameter>";
1047}
1048
1049void CommentASTToXMLConverter::visitTParamCommandComment(
1050 const TParamCommandComment *C) {
1051 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001052 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
1053 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001054 Result << "</Name>";
1055
1056 if (C->isPositionValid() && C->getDepth() == 1) {
1057 Result << "<Index>" << C->getIndex(0) << "</Index>";
1058 }
1059
1060 Result << "<Discussion>";
1061 visit(C->getParagraph());
1062 Result << "</Discussion></Parameter>";
1063}
1064
1065void CommentASTToXMLConverter::visitVerbatimBlockComment(
1066 const VerbatimBlockComment *C) {
1067 unsigned NumLines = C->getNumLines();
1068 if (NumLines == 0)
1069 return;
1070
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001071 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001072 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1073 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001074 for (unsigned i = 0; i != NumLines; ++i) {
1075 appendToResultWithXMLEscaping(C->getText(i));
1076 if (i + 1 != NumLines)
1077 Result << '\n';
1078 }
1079 Result << "</Verbatim>";
1080}
1081
1082void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1083 const VerbatimBlockLineComment *C) {
1084 llvm_unreachable("should not see this AST node");
1085}
1086
1087void CommentASTToXMLConverter::visitVerbatimLineComment(
1088 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001089 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001090 appendToResultWithXMLEscaping(C->getText());
1091 Result << "</Verbatim>";
1092}
1093
1094void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001095 FullCommentParts Parts(C, Traits);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001096
1097 const DeclInfo *DI = C->getDeclInfo();
1098 StringRef RootEndTag;
1099 if (DI) {
1100 switch (DI->getKind()) {
1101 case DeclInfo::OtherKind:
1102 RootEndTag = "</Other>";
1103 Result << "<Other";
1104 break;
1105 case DeclInfo::FunctionKind:
1106 RootEndTag = "</Function>";
1107 Result << "<Function";
1108 switch (DI->TemplateKind) {
1109 case DeclInfo::NotTemplate:
1110 break;
1111 case DeclInfo::Template:
1112 Result << " templateKind=\"template\"";
1113 break;
1114 case DeclInfo::TemplateSpecialization:
1115 Result << " templateKind=\"specialization\"";
1116 break;
1117 case DeclInfo::TemplatePartialSpecialization:
1118 llvm_unreachable("partial specializations of functions "
1119 "are not allowed in C++");
1120 }
1121 if (DI->IsInstanceMethod)
1122 Result << " isInstanceMethod=\"1\"";
1123 if (DI->IsClassMethod)
1124 Result << " isClassMethod=\"1\"";
1125 break;
1126 case DeclInfo::ClassKind:
1127 RootEndTag = "</Class>";
1128 Result << "<Class";
1129 switch (DI->TemplateKind) {
1130 case DeclInfo::NotTemplate:
1131 break;
1132 case DeclInfo::Template:
1133 Result << " templateKind=\"template\"";
1134 break;
1135 case DeclInfo::TemplateSpecialization:
1136 Result << " templateKind=\"specialization\"";
1137 break;
1138 case DeclInfo::TemplatePartialSpecialization:
1139 Result << " templateKind=\"partialSpecialization\"";
1140 break;
1141 }
1142 break;
1143 case DeclInfo::VariableKind:
1144 RootEndTag = "</Variable>";
1145 Result << "<Variable";
1146 break;
1147 case DeclInfo::NamespaceKind:
1148 RootEndTag = "</Namespace>";
1149 Result << "<Namespace";
1150 break;
1151 case DeclInfo::TypedefKind:
1152 RootEndTag = "</Typedef>";
1153 Result << "<Typedef";
1154 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001155 case DeclInfo::EnumKind:
1156 RootEndTag = "</Enum>";
1157 Result << "<Enum";
1158 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001159 }
1160
1161 {
1162 // Print line and column number.
Fariborz Jahanian1bfb00d2012-10-17 21:58:03 +00001163 SourceLocation Loc = DI->CurrentDecl->getLocation();
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001164 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1165 FileID FID = LocInfo.first;
1166 unsigned FileOffset = LocInfo.second;
1167
1168 if (!FID.isInvalid()) {
1169 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1170 Result << " file=\"";
1171 appendToResultWithXMLEscaping(FE->getName());
1172 Result << "\"";
1173 }
1174 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1175 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1176 << "\"";
1177 }
1178 }
1179
1180 // Finish the root tag.
1181 Result << ">";
1182
1183 bool FoundName = false;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001184 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001185 if (DeclarationName DeclName = ND->getDeclName()) {
1186 Result << "<Name>";
1187 std::string Name = DeclName.getAsString();
1188 appendToResultWithXMLEscaping(Name);
1189 FoundName = true;
1190 Result << "</Name>";
1191 }
1192 }
1193 if (!FoundName)
1194 Result << "<Name>&lt;anonymous&gt;</Name>";
1195
1196 {
1197 // Print USR.
1198 SmallString<128> USR;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001199 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001200 if (!USR.empty()) {
1201 Result << "<USR>";
1202 appendToResultWithXMLEscaping(USR);
1203 Result << "</USR>";
1204 }
1205 }
1206 } else {
1207 // No DeclInfo -- just emit some root tag and name tag.
1208 RootEndTag = "</Other>";
1209 Result << "<Other><Name>unknown</Name>";
1210 }
1211
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001212 {
1213 // Pretty-print the declaration.
1214 Result << "<Declaration>";
1215 SmallString<128> Declaration;
1216 getSourceTextOfDeclaration(DI, Declaration);
Fariborz Jahanian7c106832012-12-19 00:01:48 +00001217 formatTextOfDeclaration(DI, Declaration);
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001218 appendToResultWithXMLEscaping(Declaration);
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001219
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001220 Result << "</Declaration>";
1221 }
1222
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001223 bool FirstParagraphIsBrief = false;
1224 if (Parts.Brief) {
1225 Result << "<Abstract>";
1226 visit(Parts.Brief);
1227 Result << "</Abstract>";
1228 } else if (Parts.FirstParagraph) {
1229 Result << "<Abstract>";
1230 visit(Parts.FirstParagraph);
1231 Result << "</Abstract>";
1232 FirstParagraphIsBrief = true;
1233 }
1234
1235 if (Parts.TParams.size() != 0) {
1236 Result << "<TemplateParameters>";
1237 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1238 visit(Parts.TParams[i]);
1239 Result << "</TemplateParameters>";
1240 }
1241
1242 if (Parts.Params.size() != 0) {
1243 Result << "<Parameters>";
1244 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1245 visit(Parts.Params[i]);
1246 Result << "</Parameters>";
1247 }
1248
1249 if (Parts.Returns) {
1250 Result << "<ResultDiscussion>";
1251 visit(Parts.Returns);
1252 Result << "</ResultDiscussion>";
1253 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001254
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001255 if (DI->CommentDecl->hasAttrs()) {
1256 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001257 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1258 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001259 if (!AA) {
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001260 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1261 if (DA->getMessage().empty())
1262 Result << "<Deprecated/>";
1263 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001264 Result << "<Deprecated>";
1265 appendToResultWithXMLEscaping(DA->getMessage());
1266 Result << "</Deprecated>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001267 }
1268 }
1269 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1270 if (UA->getMessage().empty())
1271 Result << "<Unavailable/>";
1272 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001273 Result << "<Unavailable>";
1274 appendToResultWithXMLEscaping(UA->getMessage());
1275 Result << "</Unavailable>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001276 }
1277 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001278 continue;
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001279 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001280
1281 // 'availability' attribute.
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001282 Result << "<Availability";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001283 StringRef Distribution;
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001284 if (AA->getPlatform()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001285 Distribution = AvailabilityAttr::getPrettyPlatformName(
1286 AA->getPlatform()->getName());
1287 if (Distribution.empty())
1288 Distribution = AA->getPlatform()->getName();
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001289 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001290 Result << " distribution=\"" << Distribution << "\">";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001291 VersionTuple IntroducedInVersion = AA->getIntroduced();
1292 if (!IntroducedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001293 Result << "<IntroducedInVersion>"
1294 << IntroducedInVersion.getAsString()
1295 << "</IntroducedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001296 }
1297 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1298 if (!DeprecatedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001299 Result << "<DeprecatedInVersion>"
1300 << DeprecatedInVersion.getAsString()
1301 << "</DeprecatedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001302 }
1303 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1304 if (!RemovedAfterVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001305 Result << "<RemovedAfterVersion>"
1306 << RemovedAfterVersion.getAsString()
1307 << "</RemovedAfterVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001308 }
1309 StringRef DeprecationSummary = AA->getMessage();
1310 if (!DeprecationSummary.empty()) {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001311 Result << "<DeprecationSummary>";
1312 appendToResultWithXMLEscaping(DeprecationSummary);
1313 Result << "</DeprecationSummary>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001314 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001315 if (AA->getUnavailable())
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001316 Result << "<Unavailable/>";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001317 Result << "</Availability>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001318 }
1319 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001320
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001321 {
1322 bool StartTagEmitted = false;
1323 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1324 const Comment *C = Parts.MiscBlocks[i];
1325 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1326 continue;
1327 if (!StartTagEmitted) {
1328 Result << "<Discussion>";
1329 StartTagEmitted = true;
1330 }
1331 visit(C);
1332 }
1333 if (StartTagEmitted)
1334 Result << "</Discussion>";
1335 }
1336
1337 Result << RootEndTag;
1338
1339 Result.flush();
1340}
1341
1342void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1343 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1344 const char C = *I;
1345 switch (C) {
1346 case '&':
1347 Result << "&amp;";
1348 break;
1349 case '<':
1350 Result << "&lt;";
1351 break;
1352 case '>':
1353 Result << "&gt;";
1354 break;
1355 case '"':
1356 Result << "&quot;";
1357 break;
1358 case '\'':
1359 Result << "&apos;";
1360 break;
1361 default:
1362 Result << C;
1363 break;
1364 }
1365 }
1366}
1367
1368extern "C" {
1369
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001370CXString clang_FullComment_getAsXML(CXComment CXC) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001371 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1372 if (!FC)
1373 return createCXString((const char *) 0);
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001374 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001375 CXTranslationUnit TU = CXC.TranslationUnit;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001376 SourceManager &SM = static_cast<ASTUnit *>(TU->TUData)->getSourceManager();
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001377
1378 SimpleFormatContext *SFC =
1379 static_cast<SimpleFormatContext*>(TU->FormatContext);
1380 if (!SFC) {
1381 SFC = new SimpleFormatContext(Context.getLangOpts());
1382 TU->FormatContext = SFC;
Fariborz Jahanian8a68da12012-12-19 00:35:23 +00001383 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
Fariborz Jahanian7c106832012-12-19 00:01:48 +00001384 // Delete after some number of iterators, so the buffers don't grow
1385 // too large.
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001386 delete SFC;
1387 SFC = new SimpleFormatContext(Context.getLangOpts());
1388 TU->FormatContext = SFC;
1389 }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001390
1391 SmallString<1024> XML;
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001392 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
1393 *SFC, TU->FormatInMemoryUniqueId++);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001394 Converter.visit(FC);
1395 return createCXString(XML.str(), /* DupString = */ true);
1396}
1397
1398} // end extern "C"
1399