blob: 57bab1854606bbc8dd4ac93d9044248e5616fd7c [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;
Fariborz Jahanianf843a582013-01-31 23:12:39 +0000414 const BlockContentComment *Headerfile;
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000415 const ParagraphComment *FirstParagraph;
416 const BlockCommandComment *Returns;
417 SmallVector<const ParamCommandComment *, 8> Params;
418 SmallVector<const TParamCommandComment *, 4> TParams;
419 SmallVector<const BlockContentComment *, 8> MiscBlocks;
420};
421
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000422FullCommentParts::FullCommentParts(const FullComment *C,
423 const CommandTraits &Traits) :
Fariborz Jahanianf843a582013-01-31 23:12:39 +0000424 Brief(NULL), Headerfile(NULL), FirstParagraph(NULL), Returns(NULL) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000425 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
426 I != E; ++I) {
427 const Comment *Child = *I;
428 if (!Child)
429 continue;
430 switch (Child->getCommentKind()) {
431 case Comment::NoCommentKind:
432 continue;
433
434 case Comment::ParagraphCommentKind: {
435 const ParagraphComment *PC = cast<ParagraphComment>(Child);
436 if (PC->isWhitespace())
437 break;
438 if (!FirstParagraph)
439 FirstParagraph = PC;
440
441 MiscBlocks.push_back(PC);
442 break;
443 }
444
445 case Comment::BlockCommandCommentKind: {
446 const BlockCommandComment *BCC = cast<BlockCommandComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000447 const CommandInfo *Info = Traits.getCommandInfo(BCC->getCommandID());
448 if (!Brief && Info->IsBriefCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000449 Brief = BCC;
450 break;
451 }
Fariborz Jahanianf843a582013-01-31 23:12:39 +0000452 if (!Headerfile && Info->IsHeaderfileCommand) {
453 Headerfile = BCC;
454 break;
455 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000456 if (!Returns && Info->IsReturnsCommand) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000457 Returns = BCC;
458 break;
459 }
460 MiscBlocks.push_back(BCC);
461 break;
462 }
463
464 case Comment::ParamCommandCommentKind: {
465 const ParamCommandComment *PCC = cast<ParamCommandComment>(Child);
466 if (!PCC->hasParamName())
467 break;
468
469 if (!PCC->isDirectionExplicit() && !PCC->hasNonWhitespaceParagraph())
470 break;
471
472 Params.push_back(PCC);
473 break;
474 }
475
476 case Comment::TParamCommandCommentKind: {
477 const TParamCommandComment *TPCC = cast<TParamCommandComment>(Child);
478 if (!TPCC->hasParamName())
479 break;
480
481 if (!TPCC->hasNonWhitespaceParagraph())
482 break;
483
484 TParams.push_back(TPCC);
485 break;
486 }
487
488 case Comment::VerbatimBlockCommentKind:
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000489 MiscBlocks.push_back(cast<BlockCommandComment>(Child));
490 break;
491
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000492 case Comment::VerbatimLineCommentKind: {
493 const VerbatimLineComment *VLC = cast<VerbatimLineComment>(Child);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000494 const CommandInfo *Info = Traits.getCommandInfo(VLC->getCommandID());
495 if (!Info->IsDeclarationCommand)
Dmitri Gribenko62290ae2012-08-09 18:20:29 +0000496 MiscBlocks.push_back(VLC);
497 break;
498 }
499
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000500 case Comment::TextCommentKind:
501 case Comment::InlineCommandCommentKind:
502 case Comment::HTMLStartTagCommentKind:
503 case Comment::HTMLEndTagCommentKind:
504 case Comment::VerbatimBlockLineCommentKind:
505 case Comment::FullCommentKind:
506 llvm_unreachable("AST node of this kind can't be a child of "
507 "a FullComment");
508 }
509 }
510
511 // Sort params in order they are declared in the function prototype.
512 // Unresolved parameters are put at the end of the list in the same order
513 // they were seen in the comment.
514 std::stable_sort(Params.begin(), Params.end(),
515 ParamCommandCommentCompareIndex());
516
517 std::stable_sort(TParams.begin(), TParams.end(),
518 TParamCommandCommentComparePosition());
519}
520
521void PrintHTMLStartTagComment(const HTMLStartTagComment *C,
522 llvm::raw_svector_ostream &Result) {
523 Result << "<" << C->getTagName();
524
525 if (C->getNumAttrs() != 0) {
526 for (unsigned i = 0, e = C->getNumAttrs(); i != e; i++) {
527 Result << " ";
528 const HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
529 Result << Attr.Name;
530 if (!Attr.Value.empty())
531 Result << "=\"" << Attr.Value << "\"";
532 }
533 }
534
535 if (!C->isSelfClosing())
536 Result << ">";
537 else
538 Result << "/>";
539}
540
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000541class CommentASTToHTMLConverter :
542 public ConstCommentVisitor<CommentASTToHTMLConverter> {
543public:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000544 /// \param Str accumulator for HTML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000545 CommentASTToHTMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000546 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000547 const CommandTraits &Traits) :
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000548 FC(FC), Result(Str), Traits(Traits)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000549 { }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000550
551 // Inline content.
552 void visitTextComment(const TextComment *C);
553 void visitInlineCommandComment(const InlineCommandComment *C);
554 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
555 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
556
557 // Block content.
558 void visitParagraphComment(const ParagraphComment *C);
559 void visitBlockCommandComment(const BlockCommandComment *C);
560 void visitParamCommandComment(const ParamCommandComment *C);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000561 void visitTParamCommandComment(const TParamCommandComment *C);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000562 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
563 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
564 void visitVerbatimLineComment(const VerbatimLineComment *C);
565
566 void visitFullComment(const FullComment *C);
567
568 // Helpers.
569
570 /// Convert a paragraph that is not a block by itself (an argument to some
571 /// command).
572 void visitNonStandaloneParagraphComment(const ParagraphComment *C);
573
574 void appendToResultWithHTMLEscaping(StringRef S);
575
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000576private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000577 const FullComment *FC;
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000578 /// Output stream for HTML.
579 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000580
581 const CommandTraits &Traits;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000582};
583} // end unnamed namespace
584
585void CommentASTToHTMLConverter::visitTextComment(const TextComment *C) {
586 appendToResultWithHTMLEscaping(C->getText());
587}
588
589void CommentASTToHTMLConverter::visitInlineCommandComment(
590 const InlineCommandComment *C) {
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000591 // Nothing to render if no arguments supplied.
592 if (C->getNumArgs() == 0)
593 return;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000594
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000595 // Nothing to render if argument is empty.
596 StringRef Arg0 = C->getArgText(0);
597 if (Arg0.empty())
598 return;
599
600 switch (C->getRenderKind()) {
601 case InlineCommandComment::RenderNormal:
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000602 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
603 appendToResultWithHTMLEscaping(C->getArgText(i));
604 Result << " ";
605 }
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000606 return;
607
608 case InlineCommandComment::RenderBold:
609 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000610 Result << "<b>";
611 appendToResultWithHTMLEscaping(Arg0);
612 Result << "</b>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000613 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000614 case InlineCommandComment::RenderMonospaced:
615 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000616 Result << "<tt>";
617 appendToResultWithHTMLEscaping(Arg0);
618 Result<< "</tt>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000619 return;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000620 case InlineCommandComment::RenderEmphasized:
621 assert(C->getNumArgs() == 1);
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000622 Result << "<em>";
623 appendToResultWithHTMLEscaping(Arg0);
624 Result << "</em>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000625 return;
626 }
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000627}
628
629void CommentASTToHTMLConverter::visitHTMLStartTagComment(
630 const HTMLStartTagComment *C) {
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000631 PrintHTMLStartTagComment(C, Result);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000632}
633
634void CommentASTToHTMLConverter::visitHTMLEndTagComment(
635 const HTMLEndTagComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000636 Result << "</" << C->getTagName() << ">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000637}
638
639void CommentASTToHTMLConverter::visitParagraphComment(
640 const ParagraphComment *C) {
641 if (C->isWhitespace())
642 return;
643
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000644 Result << "<p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000645 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
646 I != E; ++I) {
647 visit(*I);
648 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000649 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000650}
651
652void CommentASTToHTMLConverter::visitBlockCommandComment(
653 const BlockCommandComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000654 const CommandInfo *Info = Traits.getCommandInfo(C->getCommandID());
655 if (Info->IsBriefCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000656 Result << "<p class=\"para-brief\">";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000657 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000658 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000659 return;
660 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000661 if (Info->IsReturnsCommand) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000662 Result << "<p class=\"para-returns\">"
663 "<span class=\"word-returns\">Returns</span> ";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000664 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000665 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000666 return;
667 }
668 // We don't know anything about this command. Just render the paragraph.
669 visit(C->getParagraph());
670}
671
672void CommentASTToHTMLConverter::visitParamCommandComment(
673 const ParamCommandComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000674 if (C->isParamIndexValid()) {
675 Result << "<dt class=\"param-name-index-"
676 << C->getParamIndex()
677 << "\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000678 appendToResultWithHTMLEscaping(C->getParamName(FC));
679 } else {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000680 Result << "<dt class=\"param-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000681 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
682 }
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000683 Result << "</dt>";
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000684
685 if (C->isParamIndexValid()) {
686 Result << "<dd class=\"param-descr-index-"
687 << C->getParamIndex()
688 << "\">";
689 } else
690 Result << "<dd class=\"param-descr-index-invalid\">";
691
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000692 visitNonStandaloneParagraphComment(C->getParagraph());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000693 Result << "</dd>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000694}
695
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000696void CommentASTToHTMLConverter::visitTParamCommandComment(
697 const TParamCommandComment *C) {
698 if (C->isPositionValid()) {
699 if (C->getDepth() == 1)
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000700 Result << "<dt class=\"tparam-name-index-"
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000701 << C->getIndex(0)
702 << "\">";
703 else
Dmitri Gribenko6a425522012-08-01 23:47:30 +0000704 Result << "<dt class=\"tparam-name-index-other\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000705 appendToResultWithHTMLEscaping(C->getParamName(FC));
706 } else {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000707 Result << "<dt class=\"tparam-name-index-invalid\">";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000708 appendToResultWithHTMLEscaping(C->getParamNameAsWritten());
709 }
710
Dmitri Gribenko59500fe2012-08-01 00:21:12 +0000711 Result << "</dt>";
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000712
713 if (C->isPositionValid()) {
714 if (C->getDepth() == 1)
715 Result << "<dd class=\"tparam-descr-index-"
716 << C->getIndex(0)
717 << "\">";
718 else
719 Result << "<dd class=\"tparam-descr-index-other\">";
720 } else
721 Result << "<dd class=\"tparam-descr-index-invalid\">";
722
723 visitNonStandaloneParagraphComment(C->getParagraph());
724 Result << "</dd>";
725}
726
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000727void CommentASTToHTMLConverter::visitVerbatimBlockComment(
728 const VerbatimBlockComment *C) {
729 unsigned NumLines = C->getNumLines();
730 if (NumLines == 0)
731 return;
732
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000733 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000734 for (unsigned i = 0; i != NumLines; ++i) {
735 appendToResultWithHTMLEscaping(C->getText(i));
736 if (i + 1 != NumLines)
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000737 Result << '\n';
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000738 }
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000739 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000740}
741
742void CommentASTToHTMLConverter::visitVerbatimBlockLineComment(
743 const VerbatimBlockLineComment *C) {
744 llvm_unreachable("should not see this AST node");
745}
746
747void CommentASTToHTMLConverter::visitVerbatimLineComment(
748 const VerbatimLineComment *C) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000749 Result << "<pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000750 appendToResultWithHTMLEscaping(C->getText());
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000751 Result << "</pre>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000752}
753
754void CommentASTToHTMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000755 FullCommentParts Parts(C, Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000756
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000757 bool FirstParagraphIsBrief = false;
Fariborz Jahanianf843a582013-01-31 23:12:39 +0000758 if (Parts.Headerfile)
759 visit(Parts.Headerfile);
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000760 if (Parts.Brief)
761 visit(Parts.Brief);
762 else if (Parts.FirstParagraph) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000763 Result << "<p class=\"para-brief\">";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000764 visitNonStandaloneParagraphComment(Parts.FirstParagraph);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000765 Result << "</p>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000766 FirstParagraphIsBrief = true;
767 }
768
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000769 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
770 const Comment *C = Parts.MiscBlocks[i];
771 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000772 continue;
773 visit(C);
774 }
775
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000776 if (Parts.TParams.size() != 0) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000777 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000778 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
779 visit(Parts.TParams[i]);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000780 Result << "</dl>";
781 }
782
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000783 if (Parts.Params.size() != 0) {
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000784 Result << "<dl>";
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000785 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
786 visit(Parts.Params[i]);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000787 Result << "</dl>";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000788 }
789
Dmitri Gribenko2ff84b52012-08-01 22:48:16 +0000790 if (Parts.Returns)
791 visit(Parts.Returns);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000792
793 Result.flush();
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000794}
795
796void CommentASTToHTMLConverter::visitNonStandaloneParagraphComment(
797 const ParagraphComment *C) {
798 if (!C)
799 return;
800
801 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
802 I != E; ++I) {
803 visit(*I);
804 }
805}
806
807void CommentASTToHTMLConverter::appendToResultWithHTMLEscaping(StringRef S) {
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000808 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
809 const char C = *I;
810 switch (C) {
811 case '&':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000812 Result << "&amp;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000813 break;
814 case '<':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000815 Result << "&lt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000816 break;
817 case '>':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000818 Result << "&gt;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000819 break;
820 case '"':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000821 Result << "&quot;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000822 break;
823 case '\'':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000824 Result << "&#39;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000825 break;
826 case '/':
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000827 Result << "&#47;";
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000828 break;
829 default:
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000830 Result << C;
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000831 break;
832 }
833 }
834}
835
836extern "C" {
837
838CXString clang_HTMLTagComment_getAsString(CXComment CXC) {
839 const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC);
840 if (!HTC)
841 return createCXString((const char *) 0);
842
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000843 SmallString<128> HTML;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000844 CommentASTToHTMLConverter Converter(0, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000845 Converter.visit(HTC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000846 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000847}
848
849CXString clang_FullComment_getAsHTML(CXComment CXC) {
850 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
851 if (!FC)
852 return createCXString((const char *) 0);
853
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000854 SmallString<1024> HTML;
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000855 CommentASTToHTMLConverter Converter(FC, HTML, getCommandTraits(CXC));
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000856 Converter.visit(FC);
Dmitri Gribenko3e63d332012-07-21 01:47:43 +0000857 return createCXString(HTML.str(), /* DupString = */ true);
Dmitri Gribenkoae99b752012-07-20 21:34:34 +0000858}
859
860} // end extern "C"
861
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000862namespace {
863class CommentASTToXMLConverter :
864 public ConstCommentVisitor<CommentASTToXMLConverter> {
865public:
866 /// \param Str accumulator for XML.
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000867 CommentASTToXMLConverter(const FullComment *FC,
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000868 SmallVectorImpl<char> &Str,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000869 const CommandTraits &Traits,
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000870 const SourceManager &SM,
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000871 SimpleFormatContext &SFC,
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000872 unsigned FUID) :
873 FC(FC), Result(Str), Traits(Traits), SM(SM),
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000874 FormatRewriterContext(SFC),
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000875 FormatInMemoryUniqueId(FUID) { }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000876
877 // Inline content.
878 void visitTextComment(const TextComment *C);
879 void visitInlineCommandComment(const InlineCommandComment *C);
880 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
881 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
882
883 // Block content.
884 void visitParagraphComment(const ParagraphComment *C);
885 void visitBlockCommandComment(const BlockCommandComment *C);
886 void visitParamCommandComment(const ParamCommandComment *C);
887 void visitTParamCommandComment(const TParamCommandComment *C);
888 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
889 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
890 void visitVerbatimLineComment(const VerbatimLineComment *C);
891
892 void visitFullComment(const FullComment *C);
893
894 // Helpers.
895 void appendToResultWithXMLEscaping(StringRef S);
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000896
897 void formatTextOfDeclaration(const DeclInfo *DI,
898 SmallString<128> &Declaration);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000899
900private:
Dmitri Gribenko8cfabf22012-10-19 16:51:38 +0000901 const FullComment *FC;
902
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000903 /// Output stream for XML.
904 llvm::raw_svector_ostream Result;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000905
906 const CommandTraits &Traits;
907 const SourceManager &SM;
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000908 SimpleFormatContext &FormatRewriterContext;
909 unsigned FormatInMemoryUniqueId;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000910};
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000911
912void getSourceTextOfDeclaration(const DeclInfo *ThisDecl,
913 SmallVectorImpl<char> &Str) {
914 ASTContext &Context = ThisDecl->CurrentDecl->getASTContext();
915 const LangOptions &LangOpts = Context.getLangOpts();
916 llvm::raw_svector_ostream OS(Str);
917 PrintingPolicy PPolicy(LangOpts);
Fariborz Jahanian40902d82012-12-19 23:36:00 +0000918 PPolicy.PolishForDeclaration = true;
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000919 PPolicy.TerseOutput = true;
920 ThisDecl->CurrentDecl->print(OS, PPolicy,
Dmitri Gribenko27c2cb22013-01-07 18:45:48 +0000921 /*Indentation*/0, /*PrintInstantiation*/false);
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000922}
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000923
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000924void CommentASTToXMLConverter::formatTextOfDeclaration(
925 const DeclInfo *DI,
926 SmallString<128> &Declaration) {
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000927 // FIXME. formatting API expects null terminated input string.
928 // There might be more efficient way of doing this.
929 std::string StringDecl = Declaration.str();
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000930
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000931 // Formatter specific code.
932 // Form a unique in memory buffer name.
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000933 SmallString<128> filename;
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000934 filename += "xmldecl";
935 filename += llvm::utostr(FormatInMemoryUniqueId);
936 filename += ".xd";
937 FileID ID = FormatRewriterContext.createInMemoryFile(filename, StringDecl);
938 SourceLocation Start =
939 FormatRewriterContext.Sources.getLocForStartOfFile(ID).getLocWithOffset(0);
940 unsigned Length = Declaration.size();
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000941
Fariborz Jahanian7c106832012-12-19 00:01:48 +0000942 std::vector<CharSourceRange>
943 Ranges(1, CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length)));
944 ASTContext &Context = DI->CurrentDecl->getASTContext();
945 const LangOptions &LangOpts = Context.getLangOpts();
946 Lexer Lex(ID, FormatRewriterContext.Sources.getBuffer(ID),
947 FormatRewriterContext.Sources, LangOpts);
948 tooling::Replacements Replace =
949 reformat(format::getLLVMStyle(), Lex, FormatRewriterContext.Sources, Ranges);
950 applyAllReplacements(Replace, FormatRewriterContext.Rewrite);
951 Declaration = FormatRewriterContext.getRewrittenText(ID);
Fariborz Jahanian88b95212012-12-18 23:02:59 +0000952}
Dmitri Gribenko7c984992012-10-25 18:28:26 +0000953
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +0000954} // end unnamed namespace
955
956void CommentASTToXMLConverter::visitTextComment(const TextComment *C) {
957 appendToResultWithXMLEscaping(C->getText());
958}
959
960void CommentASTToXMLConverter::visitInlineCommandComment(const InlineCommandComment *C) {
961 // Nothing to render if no arguments supplied.
962 if (C->getNumArgs() == 0)
963 return;
964
965 // Nothing to render if argument is empty.
966 StringRef Arg0 = C->getArgText(0);
967 if (Arg0.empty())
968 return;
969
970 switch (C->getRenderKind()) {
971 case InlineCommandComment::RenderNormal:
972 for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) {
973 appendToResultWithXMLEscaping(C->getArgText(i));
974 Result << " ";
975 }
976 return;
977 case InlineCommandComment::RenderBold:
978 assert(C->getNumArgs() == 1);
979 Result << "<bold>";
980 appendToResultWithXMLEscaping(Arg0);
981 Result << "</bold>";
982 return;
983 case InlineCommandComment::RenderMonospaced:
984 assert(C->getNumArgs() == 1);
985 Result << "<monospaced>";
986 appendToResultWithXMLEscaping(Arg0);
987 Result << "</monospaced>";
988 return;
989 case InlineCommandComment::RenderEmphasized:
990 assert(C->getNumArgs() == 1);
991 Result << "<emphasized>";
992 appendToResultWithXMLEscaping(Arg0);
993 Result << "</emphasized>";
994 return;
995 }
996}
997
998void CommentASTToXMLConverter::visitHTMLStartTagComment(const HTMLStartTagComment *C) {
999 Result << "<rawHTML><![CDATA[";
1000 PrintHTMLStartTagComment(C, Result);
1001 Result << "]]></rawHTML>";
1002}
1003
1004void CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
1005 Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
1006}
1007
1008void CommentASTToXMLConverter::visitParagraphComment(const ParagraphComment *C) {
1009 if (C->isWhitespace())
1010 return;
1011
1012 Result << "<Para>";
1013 for (Comment::child_iterator I = C->child_begin(), E = C->child_end();
1014 I != E; ++I) {
1015 visit(*I);
1016 }
1017 Result << "</Para>";
1018}
1019
1020void CommentASTToXMLConverter::visitBlockCommandComment(const BlockCommandComment *C) {
1021 visit(C->getParagraph());
1022}
1023
1024void CommentASTToXMLConverter::visitParamCommandComment(const ParamCommandComment *C) {
1025 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001026 appendToResultWithXMLEscaping(C->isParamIndexValid() ? C->getParamName(FC)
1027 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001028 Result << "</Name>";
1029
1030 if (C->isParamIndexValid())
1031 Result << "<Index>" << C->getParamIndex() << "</Index>";
1032
1033 Result << "<Direction isExplicit=\"" << C->isDirectionExplicit() << "\">";
1034 switch (C->getDirection()) {
1035 case ParamCommandComment::In:
1036 Result << "in";
1037 break;
1038 case ParamCommandComment::Out:
1039 Result << "out";
1040 break;
1041 case ParamCommandComment::InOut:
1042 Result << "in,out";
1043 break;
1044 }
1045 Result << "</Direction><Discussion>";
1046 visit(C->getParagraph());
1047 Result << "</Discussion></Parameter>";
1048}
1049
1050void CommentASTToXMLConverter::visitTParamCommandComment(
1051 const TParamCommandComment *C) {
1052 Result << "<Parameter><Name>";
Fariborz Jahanian262e60c2012-10-18 21:42:42 +00001053 appendToResultWithXMLEscaping(C->isPositionValid() ? C->getParamName(FC)
1054 : C->getParamNameAsWritten());
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001055 Result << "</Name>";
1056
1057 if (C->isPositionValid() && C->getDepth() == 1) {
1058 Result << "<Index>" << C->getIndex(0) << "</Index>";
1059 }
1060
1061 Result << "<Discussion>";
1062 visit(C->getParagraph());
1063 Result << "</Discussion></Parameter>";
1064}
1065
1066void CommentASTToXMLConverter::visitVerbatimBlockComment(
1067 const VerbatimBlockComment *C) {
1068 unsigned NumLines = C->getNumLines();
1069 if (NumLines == 0)
1070 return;
1071
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001072 Result << llvm::StringSwitch<const char *>(C->getCommandName(Traits))
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001073 .Case("code", "<Verbatim xml:space=\"preserve\" kind=\"code\">")
1074 .Default("<Verbatim xml:space=\"preserve\" kind=\"verbatim\">");
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001075 for (unsigned i = 0; i != NumLines; ++i) {
1076 appendToResultWithXMLEscaping(C->getText(i));
1077 if (i + 1 != NumLines)
1078 Result << '\n';
1079 }
1080 Result << "</Verbatim>";
1081}
1082
1083void CommentASTToXMLConverter::visitVerbatimBlockLineComment(
1084 const VerbatimBlockLineComment *C) {
1085 llvm_unreachable("should not see this AST node");
1086}
1087
1088void CommentASTToXMLConverter::visitVerbatimLineComment(
1089 const VerbatimLineComment *C) {
Dmitri Gribenko6cd44202012-08-08 22:10:24 +00001090 Result << "<Verbatim xml:space=\"preserve\" kind=\"verbatim\">";
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001091 appendToResultWithXMLEscaping(C->getText());
1092 Result << "</Verbatim>";
1093}
1094
1095void CommentASTToXMLConverter::visitFullComment(const FullComment *C) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001096 FullCommentParts Parts(C, Traits);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001097
1098 const DeclInfo *DI = C->getDeclInfo();
1099 StringRef RootEndTag;
1100 if (DI) {
1101 switch (DI->getKind()) {
1102 case DeclInfo::OtherKind:
1103 RootEndTag = "</Other>";
1104 Result << "<Other";
1105 break;
1106 case DeclInfo::FunctionKind:
1107 RootEndTag = "</Function>";
1108 Result << "<Function";
1109 switch (DI->TemplateKind) {
1110 case DeclInfo::NotTemplate:
1111 break;
1112 case DeclInfo::Template:
1113 Result << " templateKind=\"template\"";
1114 break;
1115 case DeclInfo::TemplateSpecialization:
1116 Result << " templateKind=\"specialization\"";
1117 break;
1118 case DeclInfo::TemplatePartialSpecialization:
1119 llvm_unreachable("partial specializations of functions "
1120 "are not allowed in C++");
1121 }
1122 if (DI->IsInstanceMethod)
1123 Result << " isInstanceMethod=\"1\"";
1124 if (DI->IsClassMethod)
1125 Result << " isClassMethod=\"1\"";
1126 break;
1127 case DeclInfo::ClassKind:
1128 RootEndTag = "</Class>";
1129 Result << "<Class";
1130 switch (DI->TemplateKind) {
1131 case DeclInfo::NotTemplate:
1132 break;
1133 case DeclInfo::Template:
1134 Result << " templateKind=\"template\"";
1135 break;
1136 case DeclInfo::TemplateSpecialization:
1137 Result << " templateKind=\"specialization\"";
1138 break;
1139 case DeclInfo::TemplatePartialSpecialization:
1140 Result << " templateKind=\"partialSpecialization\"";
1141 break;
1142 }
1143 break;
1144 case DeclInfo::VariableKind:
1145 RootEndTag = "</Variable>";
1146 Result << "<Variable";
1147 break;
1148 case DeclInfo::NamespaceKind:
1149 RootEndTag = "</Namespace>";
1150 Result << "<Namespace";
1151 break;
1152 case DeclInfo::TypedefKind:
1153 RootEndTag = "</Typedef>";
1154 Result << "<Typedef";
1155 break;
Dmitri Gribenkocff339a2012-08-07 18:59:04 +00001156 case DeclInfo::EnumKind:
1157 RootEndTag = "</Enum>";
1158 Result << "<Enum";
1159 break;
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001160 }
1161
1162 {
1163 // Print line and column number.
Fariborz Jahanian1bfb00d2012-10-17 21:58:03 +00001164 SourceLocation Loc = DI->CurrentDecl->getLocation();
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001165 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
1166 FileID FID = LocInfo.first;
1167 unsigned FileOffset = LocInfo.second;
1168
1169 if (!FID.isInvalid()) {
1170 if (const FileEntry *FE = SM.getFileEntryForID(FID)) {
1171 Result << " file=\"";
1172 appendToResultWithXMLEscaping(FE->getName());
1173 Result << "\"";
1174 }
1175 Result << " line=\"" << SM.getLineNumber(FID, FileOffset)
1176 << "\" column=\"" << SM.getColumnNumber(FID, FileOffset)
1177 << "\"";
1178 }
1179 }
1180
1181 // Finish the root tag.
1182 Result << ">";
1183
1184 bool FoundName = false;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001185 if (const NamedDecl *ND = dyn_cast<NamedDecl>(DI->CommentDecl)) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001186 if (DeclarationName DeclName = ND->getDeclName()) {
1187 Result << "<Name>";
1188 std::string Name = DeclName.getAsString();
1189 appendToResultWithXMLEscaping(Name);
1190 FoundName = true;
1191 Result << "</Name>";
1192 }
1193 }
1194 if (!FoundName)
1195 Result << "<Name>&lt;anonymous&gt;</Name>";
1196
1197 {
1198 // Print USR.
1199 SmallString<128> USR;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001200 cxcursor::getDeclCursorUSR(DI->CommentDecl, USR);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001201 if (!USR.empty()) {
1202 Result << "<USR>";
1203 appendToResultWithXMLEscaping(USR);
1204 Result << "</USR>";
1205 }
1206 }
1207 } else {
1208 // No DeclInfo -- just emit some root tag and name tag.
1209 RootEndTag = "</Other>";
1210 Result << "<Other><Name>unknown</Name>";
1211 }
Fariborz Jahanianf843a582013-01-31 23:12:39 +00001212
1213 if (Parts.Headerfile) {
1214 Result << "<Headerfile>";
1215 visit(Parts.Headerfile);
1216 Result << "</Headerfile>";
1217 }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001218
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001219 {
1220 // Pretty-print the declaration.
1221 Result << "<Declaration>";
1222 SmallString<128> Declaration;
1223 getSourceTextOfDeclaration(DI, Declaration);
Fariborz Jahanian7c106832012-12-19 00:01:48 +00001224 formatTextOfDeclaration(DI, Declaration);
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001225 appendToResultWithXMLEscaping(Declaration);
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001226
Dmitri Gribenko7c984992012-10-25 18:28:26 +00001227 Result << "</Declaration>";
1228 }
1229
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001230 bool FirstParagraphIsBrief = false;
1231 if (Parts.Brief) {
1232 Result << "<Abstract>";
1233 visit(Parts.Brief);
1234 Result << "</Abstract>";
1235 } else if (Parts.FirstParagraph) {
1236 Result << "<Abstract>";
1237 visit(Parts.FirstParagraph);
1238 Result << "</Abstract>";
1239 FirstParagraphIsBrief = true;
1240 }
Fariborz Jahanianf843a582013-01-31 23:12:39 +00001241
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001242 if (Parts.TParams.size() != 0) {
1243 Result << "<TemplateParameters>";
1244 for (unsigned i = 0, e = Parts.TParams.size(); i != e; ++i)
1245 visit(Parts.TParams[i]);
1246 Result << "</TemplateParameters>";
1247 }
1248
1249 if (Parts.Params.size() != 0) {
1250 Result << "<Parameters>";
1251 for (unsigned i = 0, e = Parts.Params.size(); i != e; ++i)
1252 visit(Parts.Params[i]);
1253 Result << "</Parameters>";
1254 }
1255
1256 if (Parts.Returns) {
1257 Result << "<ResultDiscussion>";
1258 visit(Parts.Returns);
1259 Result << "</ResultDiscussion>";
1260 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001261
Fariborz Jahanianbf967be2012-10-10 18:34:52 +00001262 if (DI->CommentDecl->hasAttrs()) {
1263 const AttrVec &Attrs = DI->CommentDecl->getAttrs();
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001264 for (unsigned i = 0, e = Attrs.size(); i != e; i++) {
1265 const AvailabilityAttr *AA = dyn_cast<AvailabilityAttr>(Attrs[i]);
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001266 if (!AA) {
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001267 if (const DeprecatedAttr *DA = dyn_cast<DeprecatedAttr>(Attrs[i])) {
1268 if (DA->getMessage().empty())
1269 Result << "<Deprecated/>";
1270 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001271 Result << "<Deprecated>";
1272 appendToResultWithXMLEscaping(DA->getMessage());
1273 Result << "</Deprecated>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001274 }
1275 }
1276 else if (const UnavailableAttr *UA = dyn_cast<UnavailableAttr>(Attrs[i])) {
1277 if (UA->getMessage().empty())
1278 Result << "<Unavailable/>";
1279 else {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001280 Result << "<Unavailable>";
1281 appendToResultWithXMLEscaping(UA->getMessage());
1282 Result << "</Unavailable>";
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001283 }
1284 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001285 continue;
Fariborz Jahanian2a465332012-10-02 20:05:47 +00001286 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001287
1288 // 'availability' attribute.
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001289 Result << "<Availability";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001290 StringRef Distribution;
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001291 if (AA->getPlatform()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001292 Distribution = AvailabilityAttr::getPrettyPlatformName(
1293 AA->getPlatform()->getName());
1294 if (Distribution.empty())
1295 Distribution = AA->getPlatform()->getName();
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001296 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001297 Result << " distribution=\"" << Distribution << "\">";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001298 VersionTuple IntroducedInVersion = AA->getIntroduced();
1299 if (!IntroducedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001300 Result << "<IntroducedInVersion>"
1301 << IntroducedInVersion.getAsString()
1302 << "</IntroducedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001303 }
1304 VersionTuple DeprecatedInVersion = AA->getDeprecated();
1305 if (!DeprecatedInVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001306 Result << "<DeprecatedInVersion>"
1307 << DeprecatedInVersion.getAsString()
1308 << "</DeprecatedInVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001309 }
1310 VersionTuple RemovedAfterVersion = AA->getObsoleted();
1311 if (!RemovedAfterVersion.empty()) {
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001312 Result << "<RemovedAfterVersion>"
1313 << RemovedAfterVersion.getAsString()
1314 << "</RemovedAfterVersion>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001315 }
1316 StringRef DeprecationSummary = AA->getMessage();
1317 if (!DeprecationSummary.empty()) {
Dmitri Gribenko7d9c9752012-10-03 09:04:56 +00001318 Result << "<DeprecationSummary>";
1319 appendToResultWithXMLEscaping(DeprecationSummary);
1320 Result << "</DeprecationSummary>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001321 }
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001322 if (AA->getUnavailable())
Fariborz Jahanian8da68b82012-10-02 23:01:04 +00001323 Result << "<Unavailable/>";
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001324 Result << "</Availability>";
Fariborz Jahanian257e2e82012-09-28 22:35:49 +00001325 }
1326 }
Fariborz Jahanianfaab5612012-10-01 18:42:25 +00001327
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001328 {
1329 bool StartTagEmitted = false;
1330 for (unsigned i = 0, e = Parts.MiscBlocks.size(); i != e; ++i) {
1331 const Comment *C = Parts.MiscBlocks[i];
1332 if (FirstParagraphIsBrief && C == Parts.FirstParagraph)
1333 continue;
1334 if (!StartTagEmitted) {
1335 Result << "<Discussion>";
1336 StartTagEmitted = true;
1337 }
1338 visit(C);
1339 }
1340 if (StartTagEmitted)
1341 Result << "</Discussion>";
1342 }
1343
1344 Result << RootEndTag;
1345
1346 Result.flush();
1347}
1348
1349void CommentASTToXMLConverter::appendToResultWithXMLEscaping(StringRef S) {
1350 for (StringRef::iterator I = S.begin(), E = S.end(); I != E; ++I) {
1351 const char C = *I;
1352 switch (C) {
1353 case '&':
1354 Result << "&amp;";
1355 break;
1356 case '<':
1357 Result << "&lt;";
1358 break;
1359 case '>':
1360 Result << "&gt;";
1361 break;
1362 case '"':
1363 Result << "&quot;";
1364 break;
1365 case '\'':
1366 Result << "&apos;";
1367 break;
1368 default:
1369 Result << C;
1370 break;
1371 }
1372 }
1373}
1374
1375extern "C" {
1376
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001377CXString clang_FullComment_getAsXML(CXComment CXC) {
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001378 const FullComment *FC = getASTNodeAs<FullComment>(CXC);
1379 if (!FC)
1380 return createCXString((const char *) 0);
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001381 ASTContext &Context = FC->getDeclInfo()->CurrentDecl->getASTContext();
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +00001382 CXTranslationUnit TU = CXC.TranslationUnit;
Dmitri Gribenko5694feb2013-01-26 18:53:38 +00001383 SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
Dmitri Gribenko337ee242013-01-26 21:39:50 +00001384
1385 if (!TU->FormatContext) {
1386 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
Fariborz Jahanian8a68da12012-12-19 00:35:23 +00001387 } else if ((TU->FormatInMemoryUniqueId % 1000) == 0) {
Fariborz Jahanian7c106832012-12-19 00:01:48 +00001388 // Delete after some number of iterators, so the buffers don't grow
1389 // too large.
Dmitri Gribenko337ee242013-01-26 21:39:50 +00001390 delete TU->FormatContext;
1391 TU->FormatContext = new SimpleFormatContext(Context.getLangOpts());
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001392 }
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001393
1394 SmallString<1024> XML;
Fariborz Jahanian88b95212012-12-18 23:02:59 +00001395 CommentASTToXMLConverter Converter(FC, XML, getCommandTraits(CXC), SM,
Dmitri Gribenko337ee242013-01-26 21:39:50 +00001396 *TU->FormatContext,
1397 TU->FormatInMemoryUniqueId++);
Dmitri Gribenkof303d4c2012-08-07 17:54:38 +00001398 Converter.visit(FC);
1399 return createCXString(XML.str(), /* DupString = */ true);
1400}
1401
1402} // end extern "C"
1403