blob: 0fcd7c216664e2de1e530979270f74084f1d02a4 [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"
Chandler Carruthb99083e2013-01-02 10:28:36 +000023#include "clang/Format/Format.h"
24#include "clang/Lex/Lexer.h"
Fariborz Jahanian88b95212012-12-18 23:02:59 +000025#include "llvm/ADT/StringExtras.h"
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +000026#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenkoae99b752012-07-20 21:34:34 +000027#include "llvm/Support/ErrorHandling.h"
Dmitri Gribenko3e63d332012-07-21 01:47:43 +000028#include "llvm/Support/raw_ostream.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,
Dmitri Gribenko27c2cb22013-01-07 18:45:48 +0000914 /*Indentation*/0, /*PrintInstantiation*/false);
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000915}
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 Jahanian7c106832012-12-19 00:01:48 +0000920 // FIXME. formatting API expects null terminated input string.
921 // There might be more efficient way of doing this.
922 std::string StringDecl = Declaration.str();
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000923
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000924 // Formatter specific code.
925 // Form a unique in memory buffer name.
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000926 SmallString<128> filename;
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000927 filename += "xmldecl";
928 filename += llvm::utostr(FormatInMemoryUniqueId);
929 filename += ".xd";
930 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
931 SourceLocation Start =
932 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
933 unsigned Length = Declaration.size();
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000934
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000935 std::vector<CharSourceRange>
936 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
937 ASTContext &Context = DI->CurrentDecl->getASTContext();
938 const LangOptions &LangOpts = Context.getLangOpts();
939 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
940 FormatRewriterContext.Sources, LangOpts);
941 tooling::Replacements Replace =
942 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
943 applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
944 Declaration = FormatRewriterContext.getRewrittenText(ID);
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000945}
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000946
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000947} // end unnamed namespace
948
949void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
950 appendToResultWithXMLEscaping(C->getText());
951}
952
953void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
954 // Nothing to render if no arguments supplied.
955 if (C->getNumArgs() == 0)
956 return;
957
958 // Nothing to render if argument is empty.
959 StringRef Arg0 = C->getArgText(0);
960 if (Arg0.empty())
961 return;
962
963 switch (C->getRenderKind()) {
964 case InlineCommandComment::RenderNormal:
965 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
966 appendToResultWithXMLEscaping(C->getArgText(i));
967 Result << " ";
968 }
969 return;
970 case InlineCommandComment::RenderBold:
971 assert(C->getNumArgs() == 1);
972 Result << "<bold>";
973 appendToResultWithXMLEscaping(Arg0);
974 Result << "</bold>";
975 return;
976 case InlineCommandComment::RenderMonospaced:
977 assert(C->getNumArgs() == 1);
978 Result << "<monospaced>";
979 appendToResultWithXMLEscaping(Arg0);
980 Result << "</monospaced>";
981 return;
982 case InlineCommandComment::RenderEmphasized:
983 assert(C->getNumArgs() == 1);
984 Result << "<emphasized>";
985 appendToResultWithXMLEscaping(Arg0);
986 Result << "</emphasized>";
987 return;
988 }
989}
990
991void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
992 Result << "<rawHTML><![CDATA[";
993 PrintHTMLStartTagComment(C, Result);
994 Result << "]]></rawHTML>";
995}
996
997void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
998 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
999}
1000
1001void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
1002 if (C->isWhitespace())
1003 return;
1004
1005 Result << "<Para>";
1006 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
1007 I != E; ++I) {
1008 visit(*I);
1009 }
1010 Result << "</Para>";
1011}
1012
1013void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
1014 visit(C->getParagraph());
1015}
1016
1017void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
1018 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001019 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
1020 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001021 Result << "</Name>";
1022
1023 if (C->isParamIndexValid())
1024 Result << "<Index>" << C->getParamIndex() << "</Index>";
1025
1026 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
1027 switch (C->getDirection()) {
1028 case ParamCommandComment::In:
1029 Result << "in";
1030 break;
1031 case ParamCommandComment::Out:
1032 Result << "out";
1033 break;
1034 case ParamCommandComment::InOut:
1035 Result << "in,out";
1036 break;
1037 }
1038 Result << "</Direction><Discussion>";
1039 visit(C->getParagraph());
1040 Result << "</Discussion></Parameter>";
1041}
1042
1043void CommentASTToXMLConverter::visitTParamCommandComment(
1044 const TParamCommandComment *C) {
1045 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001046 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
1047 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001048 Result << "</Name>";
1049
1050 if (C->isPositionValid() && C->getDepth() == 1) {
1051 Result << "<Index>" << C->getIndex(0) << "</Index>";
1052 }
1053
1054 Result << "<Discussion>";
1055 visit(C->getParagraph());
1056 Result << "</Discussion></Parameter>";
1057}
1058
1059void CommentASTToXMLConverter::visitVerbatimBlockComment(
1060 const VerbatimBlockComment *C) {
1061 unsigned NumLines = C->getNumLines();
1062 if (NumLines == 0)
1063 return;
1064
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001065 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001066 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1067 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001068 for (unsigned i = 0; i != NumLines; ++i) {
1069 appendToResultWithXMLEscaping(C->getText(i));
1070 if (i + 1 != NumLines)
1071 Result << '\n';
1072 }
1073 Result << "</Verbatim>";
1074}
1075
1076void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1077 const VerbatimBlockLineComment *C) {
1078 llvm_unreachable("should not see this AST node");
1079}
1080
1081void CommentASTToXMLConverter::visitVerbatimLineComment(
1082 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001083 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001084 appendToResultWithXMLEscaping(C->getText());
1085 Result << "</Verbatim>";
1086}
1087
1088void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001089 FullCommentParts Parts(C, Traits);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001090
1091 const DeclInfo *DI = C->getDeclInfo();
1092 StringRef RootEndTag;
1093 if (DI) {
1094 switch (DI->getKind()) {
1095 case DeclInfo::OtherKind:
1096 RootEndTag = "</Other>";
1097 Result << "<Other";
1098 break;
1099 case DeclInfo::FunctionKind:
1100 RootEndTag = "</Function>";
1101 Result << "<Function";
1102 switch (DI->TemplateKind) {
1103 case DeclInfo::NotTemplate:
1104 break;
1105 case DeclInfo::Template:
1106 Result << " templateKind=\"template\"";
1107 break;
1108 case DeclInfo::TemplateSpecialization:
1109 Result << " templateKind=\"specialization\"";
1110 break;
1111 case DeclInfo::TemplatePartialSpecialization:
1112 llvm_unreachable("partial specializations of functions "
1113 "are not allowed in C++");
1114 }
1115 if (DI->IsInstanceMethod)
1116 Result << " isInstanceMethod=\"1\"";
1117 if (DI->IsClassMethod)
1118 Result << " isClassMethod=\"1\"";
1119 break;
1120 case DeclInfo::ClassKind:
1121 RootEndTag = "</Class>";
1122 Result << "<Class";
1123 switch (DI->TemplateKind) {
1124 case DeclInfo::NotTemplate:
1125 break;
1126 case DeclInfo::Template:
1127 Result << " templateKind=\"template\"";
1128 break;
1129 case DeclInfo::TemplateSpecialization:
1130 Result << " templateKind=\"specialization\"";
1131 break;
1132 case DeclInfo::TemplatePartialSpecialization:
1133 Result << " templateKind=\"partialSpecialization\"";
1134 break;
1135 }
1136 break;
1137 case DeclInfo::VariableKind:
1138 RootEndTag = "</Variable>";
1139 Result << "<Variable";
1140 break;
1141 case DeclInfo::NamespaceKind:
1142 RootEndTag = "</Namespace>";
1143 Result << "<Namespace";
1144 break;
1145 case DeclInfo::TypedefKind:
1146 RootEndTag = "</Typedef>";
1147 Result << "<Typedef";
1148 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001149 case DeclInfo::EnumKind:
1150 RootEndTag = "</Enum>";
1151 Result << "<Enum";
1152 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001153 }
1154
1155 {
1156 // Print line and column number.
Fariborz Jahanian1bfb00d2012-10-17 21:58:03 +00001157 SourceLocation Loc = DI->CurrentDecl->getLocation();
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001158 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1159 FileID FID = LocInfo.first;
1160 unsigned FileOffset = LocInfo.second;
1161
1162 if (!FID.isInvalid()) {
1163 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1164 Result << " file=\"";
1165 appendToResultWithXMLEscaping(FE->getName());
1166 Result << "\"";
1167 }
1168 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1169 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1170 << "\"";
1171 }
1172 }
1173
1174 // Finish the root tag.
1175 Result << ">";
1176
1177 bool FoundName = false;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001178 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001179 if (DeclarationName DeclName = ND->getDeclName()) {
1180 Result << "<Name>";
1181 std::string Name = DeclName.getAsString();
1182 appendToResultWithXMLEscaping(Name);
1183 FoundName = true;
1184 Result << "</Name>";
1185 }
1186 }
1187 if (!FoundName)
1188 Result << "<Name>&lt;anonymous&gt;</Name>";
1189
1190 {
1191 // Print USR.
1192 SmallString<128> USR;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001193 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001194 if (!USR.empty()) {
1195 Result << "<USR>";
1196 appendToResultWithXMLEscaping(USR);
1197 Result << "</USR>";
1198 }
1199 }
1200 } else {
1201 // No DeclInfo -- just emit some root tag and name tag.
1202 RootEndTag = "</Other>";
1203 Result << "<Other><Name>unknown</Name>";
1204 }
1205
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001206 {
1207 // Pretty-print the declaration.
1208 Result << "<Declaration>";
1209 SmallString<128> Declaration;
1210 getSourceTextOfDeclaration(DI, Declaration);
Fariborz Jahanian7c106832012-12-19 00:01:48 +00001211 formatTextOfDeclaration(DI, Declaration);
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001212 appendToResultWithXMLEscaping(Declaration);
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001213
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001214 Result << "</Declaration>";
1215 }
1216
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001217 bool FirstParagraphIsBrief = false;
1218 if (Parts.Brief) {
1219 Result << "<Abstract>";
1220 visit(Parts.Brief);
1221 Result << "</Abstract>";
1222 } else if (Parts.FirstParagraph) {
1223 Result << "<Abstract>";
1224 visit(Parts.FirstParagraph);
1225 Result << "</Abstract>";
1226 FirstParagraphIsBrief = true;
1227 }
1228
1229 if (Parts.TParams.size() != 0) {
1230 Result << "<TemplateParameters>";
1231 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1232 visit(Parts.TParams[i]);
1233 Result << "</TemplateParameters>";
1234 }
1235
1236 if (Parts.Params.size() != 0) {
1237 Result << "<Parameters>";
1238 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1239 visit(Parts.Params[i]);
1240 Result << "</Parameters>";
1241 }
1242
1243 if (Parts.Returns) {
1244 Result << "<ResultDiscussion>";
1245 visit(Parts.Returns);
1246 Result << "</ResultDiscussion>";
1247 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001248
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001249 if (DI->CommentDecl->hasAttrs()) {
1250 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001251 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1252 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001253 if (!AA) {
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001254 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1255 if (DA->getMessage().empty())
1256 Result << "<Deprecated/>";
1257 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001258 Result << "<Deprecated>";
1259 appendToResultWithXMLEscaping(DA->getMessage());
1260 Result << "</Deprecated>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001261 }
1262 }
1263 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1264 if (UA->getMessage().empty())
1265 Result << "<Unavailable/>";
1266 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001267 Result << "<Unavailable>";
1268 appendToResultWithXMLEscaping(UA->getMessage());
1269 Result << "</Unavailable>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001270 }
1271 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001272 continue;
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001273 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001274
1275 // 'availability' attribute.
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001276 Result << "<Availability";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001277 StringRef Distribution;
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001278 if (AA->getPlatform()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001279 Distribution = AvailabilityAttr::getPrettyPlatformName(
1280 AA->getPlatform()->getName());
1281 if (Distribution.empty())
1282 Distribution = AA->getPlatform()->getName();
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001283 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001284 Result << " distribution=\"" << Distribution << "\">";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001285 VersionTuple IntroducedInVersion = AA->getIntroduced();
1286 if (!IntroducedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001287 Result << "<IntroducedInVersion>"
1288 << IntroducedInVersion.getAsString()
1289 << "</IntroducedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001290 }
1291 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1292 if (!DeprecatedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001293 Result << "<DeprecatedInVersion>"
1294 << DeprecatedInVersion.getAsString()
1295 << "</DeprecatedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001296 }
1297 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1298 if (!RemovedAfterVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001299 Result << "<RemovedAfterVersion>"
1300 << RemovedAfterVersion.getAsString()
1301 << "</RemovedAfterVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001302 }
1303 StringRef DeprecationSummary = AA->getMessage();
1304 if (!DeprecationSummary.empty()) {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001305 Result << "<DeprecationSummary>";
1306 appendToResultWithXMLEscaping(DeprecationSummary);
1307 Result << "</DeprecationSummary>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001308 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001309 if (AA->getUnavailable())
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001310 Result << "<Unavailable/>";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001311 Result << "</Availability>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001312 }
1313 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001314
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001315 {
1316 bool StartTagEmitted = false;
1317 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1318 const Comment *C = Parts.MiscBlocks[i];
1319 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1320 continue;
1321 if (!StartTagEmitted) {
1322 Result << "<Discussion>";
1323 StartTagEmitted = true;
1324 }
1325 visit(C);
1326 }
1327 if (StartTagEmitted)
1328 Result << "</Discussion>";
1329 }
1330
1331 Result << RootEndTag;
1332
1333 Result.flush();
1334}
1335
1336void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1337 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1338 const char C = *I;
1339 switch (C) {
1340 case '&':
1341 Result << "&amp;";
1342 break;
1343 case '<':
1344 Result << "&lt;";
1345 break;
1346 case '>':
1347 Result << "&gt;";
1348 break;
1349 case '"':
1350 Result << "&quot;";
1351 break;
1352 case '\'':
1353 Result << "&apos;";
1354 break;
1355 default:
1356 Result << C;
1357 break;
1358 }
1359 }
1360}
1361
1362extern "C" {
1363
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001364CXString clang_FullComment_getAsXML(CXComment CXC) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001365 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1366 if (!FC)
1367 return createCXString((const char *) 0);
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001368 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001369 CXTranslationUnit TU = CXC.TranslationUnit;
Dmitri Gribenko5694feb2013-01-26 18:53:38 +00001370 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
Dmitri Gribenko337ee242013-01-26 21:39:50 +00001371
1372 if (!TU->FormatContext) {
1373 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
Fariborz Jahanian8a68da12012-12-19 00:35:23 +00001374 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
Fariborz Jahanian7c106832012-12-19 00:01:48 +00001375 // Delete after some number of iterators, so the buffers don't grow
1376 // too large.
Dmitri Gribenko337ee242013-01-26 21:39:50 +00001377 delete TU->FormatContext;
1378 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001379 }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001380
1381 SmallString<1024> XML;
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001382 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
Dmitri Gribenko337ee242013-01-26 21:39:50 +00001383 *TU->FormatContext,
1384 TU->FormatInMemoryUniqueId++);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001385 Converter.visit(FC);
1386 return createCXString(XML.str(), /* DupString = */ true);
1387}
1388
1389} // end extern "C"
1390