CommentParser.cpp revision 96b098674908eaa59a9128f3305cda6fbbdad563
1//===- unittests/AST/CommentParser.cpp ------ Comment parser tests --------===//
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#include "clang/Basic/SourceManager.h"
11#include "clang/Basic/FileManager.h"
12#include "clang/Basic/Diagnostic.h"
13#include "clang/AST/Comment.h"
14#include "clang/AST/CommentLexer.h"
15#include "clang/AST/CommentParser.h"
16#include "clang/AST/CommentSema.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/Support/Allocator.h"
19#include <vector>
20
21#include "gtest/gtest.h"
22
23using namespace llvm;
24using namespace clang;
25
26namespace clang {
27namespace comments {
28
29namespace {
30
31const bool DEBUG = true;
32
33class CommentParserTest : public ::testing::Test {
34protected:
35  CommentParserTest()
36    : FileMgr(FileMgrOpts),
37      DiagID(new DiagnosticIDs()),
38      Diags(DiagID, new IgnoringDiagConsumer()),
39      SourceMgr(Diags, FileMgr) {
40  }
41
42  FileSystemOptions FileMgrOpts;
43  FileManager FileMgr;
44  IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
45  DiagnosticsEngine Diags;
46  SourceManager SourceMgr;
47  llvm::BumpPtrAllocator Allocator;
48
49  FullComment *parseString(const char *Source);
50};
51
52FullComment *CommentParserTest::parseString(const char *Source) {
53  MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(Source);
54  FileID File = SourceMgr.createFileIDForMemBuffer(Buf);
55  SourceLocation Begin = SourceMgr.getLocForStartOfFile(File);
56
57  comments::Lexer L(Allocator, Begin, CommentOptions(),
58                    Source, Source + strlen(Source));
59
60  comments::Sema S(Allocator, SourceMgr, Diags);
61  comments::Parser P(L, S, Allocator, SourceMgr, Diags);
62  comments::FullComment *FC = P.parseFullComment();
63
64  if (DEBUG) {
65    llvm::errs() << "=== Source:\n" << Source << "\n=== AST:\n";
66    FC->dump(SourceMgr);
67  }
68
69  Token Tok;
70  L.lex(Tok);
71  if (Tok.is(tok::eof))
72    return FC;
73  else
74    return NULL;
75}
76
77::testing::AssertionResult HasChildCount(const Comment *C, size_t Count) {
78  if (!C)
79    return ::testing::AssertionFailure() << "Comment is NULL";
80
81  if (Count != C->child_count())
82    return ::testing::AssertionFailure()
83        << "Count = " << Count
84        << ", child_count = " << C->child_count();
85
86  return ::testing::AssertionSuccess();
87}
88
89template <typename T>
90::testing::AssertionResult GetChildAt(const Comment *C,
91                                      size_t Idx,
92                                      T *&Child) {
93  if (!C)
94    return ::testing::AssertionFailure() << "Comment is NULL";
95
96  if (Idx >= C->child_count())
97    return ::testing::AssertionFailure()
98        << "Idx out of range.  Idx = " << Idx
99        << ", child_count = " << C->child_count();
100
101  Comment::child_iterator I = C->child_begin() + Idx;
102  Comment *CommentChild = *I;
103  if (!CommentChild)
104    return ::testing::AssertionFailure() << "Child is NULL";
105
106  Child = dyn_cast<T>(CommentChild);
107  if (!Child)
108    return ::testing::AssertionFailure()
109        << "Child is not of requested type, but a "
110        << CommentChild->getCommentKindName();
111
112  return ::testing::AssertionSuccess();
113}
114
115::testing::AssertionResult HasTextAt(const Comment *C,
116                                     size_t Idx,
117                                     StringRef Text) {
118  TextComment *TC;
119  ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
120  if (!AR)
121    return AR;
122
123  StringRef ActualText = TC->getText();
124  if (ActualText != Text)
125    return ::testing::AssertionFailure()
126        << "TextComment has text \"" << ActualText.str() << "\", "
127           "expected \"" << Text.str() << "\"";
128
129  if (TC->hasTrailingNewline())
130    return ::testing::AssertionFailure()
131        << "TextComment has a trailing newline";
132
133  return ::testing::AssertionSuccess();
134}
135
136::testing::AssertionResult HasTextWithNewlineAt(const Comment *C,
137                                                size_t Idx,
138                                                StringRef Text) {
139  TextComment *TC;
140  ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
141  if (!AR)
142    return AR;
143
144  StringRef ActualText = TC->getText();
145  if (ActualText != Text)
146    return ::testing::AssertionFailure()
147        << "TextComment has text \"" << ActualText.str() << "\", "
148           "expected \"" << Text.str() << "\"";
149
150  if (!TC->hasTrailingNewline())
151    return ::testing::AssertionFailure()
152        << "TextComment has no trailing newline";
153
154  return ::testing::AssertionSuccess();
155}
156
157::testing::AssertionResult HasBlockCommandAt(const Comment *C,
158                                             size_t Idx,
159                                             BlockCommandComment *&BCC,
160                                             StringRef Name,
161                                             ParagraphComment *&Paragraph) {
162  ::testing::AssertionResult AR = GetChildAt(C, Idx, BCC);
163  if (!AR)
164    return AR;
165
166  StringRef ActualName = BCC->getCommandName();
167  if (ActualName != Name)
168    return ::testing::AssertionFailure()
169        << "BlockCommandComment has name \"" << ActualName.str() << "\", "
170           "expected \"" << Name.str() << "\"";
171
172  Paragraph = BCC->getParagraph();
173
174  return ::testing::AssertionSuccess();
175}
176
177::testing::AssertionResult HasParamCommandAt(
178                              const Comment *C,
179                              size_t Idx,
180                              ParamCommandComment *&PCC,
181                              StringRef CommandName,
182                              ParamCommandComment::PassDirection Direction,
183                              bool IsDirectionExplicit,
184                              StringRef ParamName,
185                              ParagraphComment *&Paragraph) {
186  ::testing::AssertionResult AR = GetChildAt(C, Idx, PCC);
187  if (!AR)
188    return AR;
189
190  StringRef ActualCommandName = PCC->getCommandName();
191  if (ActualCommandName != CommandName)
192    return ::testing::AssertionFailure()
193        << "ParamCommandComment has name \"" << ActualCommandName.str() << "\", "
194           "expected \"" << CommandName.str() << "\"";
195
196  if (PCC->getDirection() != Direction)
197    return ::testing::AssertionFailure()
198        << "ParamCommandComment has direction " << PCC->getDirection() << ", "
199           "expected " << Direction;
200
201  if (PCC->isDirectionExplicit() != IsDirectionExplicit)
202    return ::testing::AssertionFailure()
203        << "ParamCommandComment has "
204        << (PCC->isDirectionExplicit() ? "explicit" : "implicit")
205        << " direction, "
206           "expected " << (IsDirectionExplicit ? "explicit" : "implicit");
207
208  if (!PCC->hasParamName())
209    return ::testing::AssertionFailure()
210        << "ParamCommandComment has no parameter name";
211
212  StringRef ActualParamName = PCC->getParamName();
213  if (ActualParamName != ParamName)
214    return ::testing::AssertionFailure()
215        << "ParamCommandComment has parameter name \"" << ActualParamName.str()
216        << "\", "
217           "expected \"" << ParamName.str() << "\"";
218
219  Paragraph = PCC->getParagraph();
220
221  return ::testing::AssertionSuccess();
222}
223
224::testing::AssertionResult HasTParamCommandAt(
225                              const Comment *C,
226                              size_t Idx,
227                              TParamCommandComment *&TPCC,
228                              StringRef CommandName,
229                              StringRef ParamName,
230                              ParagraphComment *&Paragraph) {
231  ::testing::AssertionResult AR = GetChildAt(C, Idx, TPCC);
232  if (!AR)
233    return AR;
234
235  StringRef ActualCommandName = TPCC->getCommandName();
236  if (ActualCommandName != CommandName)
237    return ::testing::AssertionFailure()
238        << "TParamCommandComment has name \"" << ActualCommandName.str() << "\", "
239           "expected \"" << CommandName.str() << "\"";
240
241  if (!TPCC->hasParamName())
242    return ::testing::AssertionFailure()
243        << "TParamCommandComment has no parameter name";
244
245  StringRef ActualParamName = TPCC->getParamName();
246  if (ActualParamName != ParamName)
247    return ::testing::AssertionFailure()
248        << "TParamCommandComment has parameter name \"" << ActualParamName.str()
249        << "\", "
250           "expected \"" << ParamName.str() << "\"";
251
252  Paragraph = TPCC->getParagraph();
253
254  return ::testing::AssertionSuccess();
255}
256
257::testing::AssertionResult HasInlineCommandAt(const Comment *C,
258                                              size_t Idx,
259                                              InlineCommandComment *&ICC,
260                                              StringRef Name) {
261  ::testing::AssertionResult AR = GetChildAt(C, Idx, ICC);
262  if (!AR)
263    return AR;
264
265  StringRef ActualName = ICC->getCommandName();
266  if (ActualName != Name)
267    return ::testing::AssertionFailure()
268        << "InlineCommandComment has name \"" << ActualName.str() << "\", "
269           "expected \"" << Name.str() << "\"";
270
271  return ::testing::AssertionSuccess();
272}
273
274struct NoArgs {};
275
276::testing::AssertionResult HasInlineCommandAt(const Comment *C,
277                                              size_t Idx,
278                                              InlineCommandComment *&ICC,
279                                              StringRef Name,
280                                              NoArgs) {
281  ::testing::AssertionResult AR = HasInlineCommandAt(C, Idx, ICC, Name);
282  if (!AR)
283    return AR;
284
285  if (ICC->getNumArgs() != 0)
286    return ::testing::AssertionFailure()
287        << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
288           "expected 0";
289
290  return ::testing::AssertionSuccess();
291}
292
293::testing::AssertionResult HasInlineCommandAt(const Comment *C,
294                                              size_t Idx,
295                                              InlineCommandComment *&ICC,
296                                              StringRef Name,
297                                              StringRef Arg) {
298  ::testing::AssertionResult AR = HasInlineCommandAt(C, Idx, ICC, Name);
299  if (!AR)
300    return AR;
301
302  if (ICC->getNumArgs() != 1)
303    return ::testing::AssertionFailure()
304        << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
305           "expected 1";
306
307  StringRef ActualArg = ICC->getArgText(0);
308  if (ActualArg != Arg)
309    return ::testing::AssertionFailure()
310        << "InlineCommandComment has argument \"" << ActualArg.str() << "\", "
311           "expected \"" << Arg.str() << "\"";
312
313  return ::testing::AssertionSuccess();
314}
315
316::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
317                                             size_t Idx,
318                                             HTMLStartTagComment *&HST,
319                                             StringRef TagName) {
320  ::testing::AssertionResult AR = GetChildAt(C, Idx, HST);
321  if (!AR)
322    return AR;
323
324  StringRef ActualTagName = HST->getTagName();
325  if (ActualTagName != TagName)
326    return ::testing::AssertionFailure()
327        << "HTMLStartTagComment has name \"" << ActualTagName.str() << "\", "
328           "expected \"" << TagName.str() << "\"";
329
330  return ::testing::AssertionSuccess();
331}
332
333struct SelfClosing {};
334
335::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
336                                             size_t Idx,
337                                             HTMLStartTagComment *&HST,
338                                             StringRef TagName,
339                                             SelfClosing) {
340  ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
341  if (!AR)
342    return AR;
343
344  if (!HST->isSelfClosing())
345    return ::testing::AssertionFailure()
346        << "HTMLStartTagComment is not self-closing";
347
348  return ::testing::AssertionSuccess();
349}
350
351
352struct NoAttrs {};
353
354::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
355                                             size_t Idx,
356                                             HTMLStartTagComment *&HST,
357                                             StringRef TagName,
358                                             NoAttrs) {
359  ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
360  if (!AR)
361    return AR;
362
363  if (HST->isSelfClosing())
364    return ::testing::AssertionFailure()
365        << "HTMLStartTagComment is self-closing";
366
367  if (HST->getNumAttrs() != 0)
368    return ::testing::AssertionFailure()
369        << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
370           "expected 0";
371
372  return ::testing::AssertionSuccess();
373}
374
375::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
376                                             size_t Idx,
377                                             HTMLStartTagComment *&HST,
378                                             StringRef TagName,
379                                             StringRef AttrName,
380                                             StringRef AttrValue) {
381  ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
382  if (!AR)
383    return AR;
384
385  if (HST->isSelfClosing())
386    return ::testing::AssertionFailure()
387        << "HTMLStartTagComment is self-closing";
388
389  if (HST->getNumAttrs() != 1)
390    return ::testing::AssertionFailure()
391        << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
392           "expected 1";
393
394  StringRef ActualName = HST->getAttr(0).Name;
395  if (ActualName != AttrName)
396    return ::testing::AssertionFailure()
397        << "HTMLStartTagComment has attr \"" << ActualName.str() << "\", "
398           "expected \"" << AttrName.str() << "\"";
399
400  StringRef ActualValue = HST->getAttr(0).Value;
401  if (ActualValue != AttrValue)
402    return ::testing::AssertionFailure()
403        << "HTMLStartTagComment has attr value \"" << ActualValue.str() << "\", "
404           "expected \"" << AttrValue.str() << "\"";
405
406  return ::testing::AssertionSuccess();
407}
408
409::testing::AssertionResult HasHTMLEndTagAt(const Comment *C,
410                                           size_t Idx,
411                                           HTMLEndTagComment *&HET,
412                                           StringRef TagName) {
413  ::testing::AssertionResult AR = GetChildAt(C, Idx, HET);
414  if (!AR)
415    return AR;
416
417  StringRef ActualTagName = HET->getTagName();
418  if (ActualTagName != TagName)
419    return ::testing::AssertionFailure()
420        << "HTMLEndTagComment has name \"" << ActualTagName.str() << "\", "
421           "expected \"" << TagName.str() << "\"";
422
423  return ::testing::AssertionSuccess();
424}
425
426::testing::AssertionResult HasParagraphCommentAt(const Comment *C,
427                                                 size_t Idx,
428                                                 StringRef Text) {
429  ParagraphComment *PC;
430
431  {
432    ::testing::AssertionResult AR = GetChildAt(C, Idx, PC);
433    if (!AR)
434      return AR;
435  }
436
437  {
438    ::testing::AssertionResult AR = HasChildCount(PC, 1);
439    if (!AR)
440      return AR;
441  }
442
443  {
444    ::testing::AssertionResult AR = HasTextAt(PC, 0, Text);
445    if (!AR)
446      return AR;
447  }
448
449  return ::testing::AssertionSuccess();
450}
451
452::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
453                                              size_t Idx,
454                                              VerbatimBlockComment *&VBC,
455                                              StringRef Name,
456                                              StringRef CloseName) {
457  ::testing::AssertionResult AR = GetChildAt(C, Idx, VBC);
458  if (!AR)
459    return AR;
460
461  StringRef ActualName = VBC->getCommandName();
462  if (ActualName != Name)
463    return ::testing::AssertionFailure()
464        << "VerbatimBlockComment has name \"" << ActualName.str() << "\", "
465           "expected \"" << Name.str() << "\"";
466
467  StringRef ActualCloseName = VBC->getCloseName();
468  if (ActualCloseName != CloseName)
469    return ::testing::AssertionFailure()
470        << "VerbatimBlockComment has closing command name \""
471        << ActualCloseName.str() << "\", "
472           "expected \"" << CloseName.str() << "\"";
473
474  return ::testing::AssertionSuccess();
475}
476
477struct NoLines {};
478struct Lines {};
479
480::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
481                                              size_t Idx,
482                                              VerbatimBlockComment *&VBC,
483                                              StringRef Name,
484                                              StringRef CloseName,
485                                              NoLines) {
486  ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name,
487                                                     CloseName);
488  if (!AR)
489    return AR;
490
491  if (VBC->getNumLines() != 0)
492    return ::testing::AssertionFailure()
493        << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
494           "expected 0";
495
496  return ::testing::AssertionSuccess();
497}
498
499::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
500                                              size_t Idx,
501                                              VerbatimBlockComment *&VBC,
502                                              StringRef Name,
503                                              StringRef CloseName,
504                                              Lines,
505                                              StringRef Line0) {
506  ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name,
507                                                     CloseName);
508  if (!AR)
509    return AR;
510
511  if (VBC->getNumLines() != 1)
512    return ::testing::AssertionFailure()
513        << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
514           "expected 1";
515
516  StringRef ActualLine0 = VBC->getText(0);
517  if (ActualLine0 != Line0)
518    return ::testing::AssertionFailure()
519        << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
520           "expected \"" << Line0.str() << "\"";
521
522  return ::testing::AssertionSuccess();
523}
524
525::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
526                                              size_t Idx,
527                                              VerbatimBlockComment *&VBC,
528                                              StringRef Name,
529                                              StringRef CloseName,
530                                              Lines,
531                                              StringRef Line0,
532                                              StringRef Line1) {
533  ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name,
534                                                     CloseName);
535  if (!AR)
536    return AR;
537
538  if (VBC->getNumLines() != 2)
539    return ::testing::AssertionFailure()
540        << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
541           "expected 2";
542
543  StringRef ActualLine0 = VBC->getText(0);
544  if (ActualLine0 != Line0)
545    return ::testing::AssertionFailure()
546        << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
547           "expected \"" << Line0.str() << "\"";
548
549  StringRef ActualLine1 = VBC->getText(1);
550  if (ActualLine1 != Line1)
551    return ::testing::AssertionFailure()
552        << "VerbatimBlockComment has lines[1] \"" << ActualLine1.str() << "\", "
553           "expected \"" << Line1.str() << "\"";
554
555  return ::testing::AssertionSuccess();
556}
557
558::testing::AssertionResult HasVerbatimLineAt(const Comment *C,
559                                             size_t Idx,
560                                             VerbatimLineComment *&VLC,
561                                             StringRef Name,
562                                             StringRef Text) {
563  ::testing::AssertionResult AR = GetChildAt(C, Idx, VLC);
564  if (!AR)
565    return AR;
566
567  StringRef ActualName = VLC->getCommandName();
568  if (ActualName != Name)
569    return ::testing::AssertionFailure()
570        << "VerbatimLineComment has name \"" << ActualName.str() << "\", "
571           "expected \"" << Name.str() << "\"";
572
573  StringRef ActualText = VLC->getText();
574  if (ActualText != Text)
575    return ::testing::AssertionFailure()
576        << "VerbatimLineComment has text \"" << ActualText.str() << "\", "
577           "expected \"" << Text.str() << "\"";
578
579  return ::testing::AssertionSuccess();
580}
581
582
583TEST_F(CommentParserTest, Basic1) {
584  const char *Source = "//";
585
586  FullComment *FC = parseString(Source);
587  ASSERT_TRUE(HasChildCount(FC, 0));
588}
589
590TEST_F(CommentParserTest, Basic2) {
591  const char *Source = "// Meow";
592
593  FullComment *FC = parseString(Source);
594  ASSERT_TRUE(HasChildCount(FC, 1));
595
596  ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Meow"));
597}
598
599TEST_F(CommentParserTest, Basic3) {
600  const char *Source =
601    "// Aaa\n"
602    "// Bbb";
603
604  FullComment *FC = parseString(Source);
605  ASSERT_TRUE(HasChildCount(FC, 1));
606
607  {
608    ParagraphComment *PC;
609    ASSERT_TRUE(GetChildAt(FC, 0, PC));
610
611    ASSERT_TRUE(HasChildCount(PC, 2));
612      ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
613      ASSERT_TRUE(HasTextAt(PC, 1, " Bbb"));
614  }
615}
616
617TEST_F(CommentParserTest, Paragraph1) {
618  const char *Sources[] = {
619    "// Aaa\n"
620    "//\n"
621    "// Bbb",
622
623    "// Aaa\n"
624    "//\n"
625    "//\n"
626    "// Bbb",
627  };
628
629
630  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
631    FullComment *FC = parseString(Sources[i]);
632    ASSERT_TRUE(HasChildCount(FC, 2));
633
634    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Aaa"));
635    ASSERT_TRUE(HasParagraphCommentAt(FC, 1, " Bbb"));
636  }
637}
638
639TEST_F(CommentParserTest, Paragraph2) {
640  const char *Source =
641    "// \\brief Aaa\n"
642    "//\n"
643    "// Bbb";
644
645  FullComment *FC = parseString(Source);
646  ASSERT_TRUE(HasChildCount(FC, 3));
647
648  ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
649  {
650    BlockCommandComment *BCC;
651    ParagraphComment *PC;
652    ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC));
653
654    ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Aaa"));
655  }
656  ASSERT_TRUE(HasParagraphCommentAt(FC, 2, " Bbb"));
657}
658
659TEST_F(CommentParserTest, Paragraph3) {
660  const char *Source = "// \\brief \\author";
661
662  FullComment *FC = parseString(Source);
663  ASSERT_TRUE(HasChildCount(FC, 3));
664
665  ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
666  {
667    BlockCommandComment *BCC;
668    ParagraphComment *PC;
669    ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC));
670
671    ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " "));
672  }
673  {
674    BlockCommandComment *BCC;
675    ParagraphComment *PC;
676    ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "author", PC));
677
678    ASSERT_TRUE(GetChildAt(BCC, 0, PC));
679      ASSERT_TRUE(HasChildCount(PC, 0));
680  }
681}
682
683TEST_F(CommentParserTest, Paragraph4) {
684  const char *Source =
685    "// \\brief Aaa\n"
686    "// Bbb \\author\n"
687    "// Ccc";
688
689  FullComment *FC = parseString(Source);
690  ASSERT_TRUE(HasChildCount(FC, 3));
691
692  ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
693  {
694    BlockCommandComment *BCC;
695    ParagraphComment *PC;
696    ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC));
697
698    ASSERT_TRUE(GetChildAt(BCC, 0, PC));
699      ASSERT_TRUE(HasChildCount(PC, 2));
700      ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
701      ASSERT_TRUE(HasTextAt(PC, 1, " Bbb "));
702  }
703  {
704    BlockCommandComment *BCC;
705    ParagraphComment *PC;
706    ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "author", PC));
707
708    ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Ccc"));
709  }
710}
711
712TEST_F(CommentParserTest, ParamCommand1) {
713  const char *Source = "// \\param aaa";
714
715  FullComment *FC = parseString(Source);
716  ASSERT_TRUE(HasChildCount(FC, 2));
717
718  ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
719  {
720    ParamCommandComment *PCC;
721    ParagraphComment *PC;
722    ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
723                                  ParamCommandComment::In,
724                                  /* IsDirectionExplicit = */ false,
725                                  "aaa", PC));
726    ASSERT_TRUE(HasChildCount(PCC, 1));
727    ASSERT_TRUE(HasChildCount(PC, 0));
728  }
729}
730
731TEST_F(CommentParserTest, ParamCommand2) {
732  const char *Sources[] = {
733    "// \\param aaa Bbb\n",
734    "// \\param\n"
735    "//     aaa Bbb\n",
736    "// \\param \n"
737    "//     aaa Bbb\n",
738    "// \\param aaa\n"
739    "// Bbb\n"
740  };
741
742  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
743    FullComment *FC = parseString(Sources[i]);
744    ASSERT_TRUE(HasChildCount(FC, 2));
745
746    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
747    {
748      ParamCommandComment *PCC;
749      ParagraphComment *PC;
750      ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
751                                    ParamCommandComment::In,
752                                    /* IsDirectionExplicit = */ false,
753                                    "aaa", PC));
754      ASSERT_TRUE(HasChildCount(PCC, 1));
755      ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
756    }
757  }
758}
759
760TEST_F(CommentParserTest, ParamCommand3) {
761  const char *Sources[] = {
762    "// \\param [in] aaa Bbb\n",
763    "// \\param\n"
764    "//     [in] aaa Bbb\n",
765    "// \\param [in]\n"
766    "//     aaa Bbb\n",
767    "// \\param [in] aaa\n"
768    "// Bbb\n",
769  };
770
771  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
772    FullComment *FC = parseString(Sources[i]);
773    ASSERT_TRUE(HasChildCount(FC, 2));
774
775    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
776    {
777      ParamCommandComment *PCC;
778      ParagraphComment *PC;
779      ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
780                                    ParamCommandComment::In,
781                                    /* IsDirectionExplicit = */ true,
782                                    "aaa", PC));
783      ASSERT_TRUE(HasChildCount(PCC, 1));
784      ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
785    }
786  }
787}
788
789TEST_F(CommentParserTest, ParamCommand4) {
790  const char *Sources[] = {
791    "// \\param [out] aaa Bbb\n",
792    "// \\param\n"
793    "//     [out] aaa Bbb\n",
794    "// \\param [out]\n"
795    "//     aaa Bbb\n",
796    "// \\param [out] aaa\n"
797    "// Bbb\n",
798  };
799
800  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
801    FullComment *FC = parseString(Sources[i]);
802    ASSERT_TRUE(HasChildCount(FC, 2));
803
804    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
805    {
806      ParamCommandComment *PCC;
807      ParagraphComment *PC;
808      ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
809                                    ParamCommandComment::Out,
810                                    /* IsDirectionExplicit = */ true,
811                                    "aaa", PC));
812      ASSERT_TRUE(HasChildCount(PCC, 1));
813      ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
814    }
815  }
816}
817
818TEST_F(CommentParserTest, ParamCommand5) {
819  const char *Sources[] = {
820    "// \\param [in,out] aaa Bbb\n",
821    "// \\param [in, out] aaa Bbb\n",
822    "// \\param [in,\n"
823    "//     out] aaa Bbb\n",
824    "// \\param [in,out]\n"
825    "//     aaa Bbb\n",
826    "// \\param [in,out] aaa\n"
827    "// Bbb\n"
828  };
829
830  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
831    FullComment *FC = parseString(Sources[i]);
832    ASSERT_TRUE(HasChildCount(FC, 2));
833
834    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
835    {
836      ParamCommandComment *PCC;
837      ParagraphComment *PC;
838      ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
839                                    ParamCommandComment::InOut,
840                                    /* IsDirectionExplicit = */ true,
841                                    "aaa", PC));
842      ASSERT_TRUE(HasChildCount(PCC, 1));
843      ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
844    }
845  }
846}
847
848TEST_F(CommentParserTest, ParamCommand6) {
849  const char *Source =
850    "// \\param aaa \\% Bbb \\$ ccc\n";
851
852  FullComment *FC = parseString(Source);
853  ASSERT_TRUE(HasChildCount(FC, 2));
854
855  ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
856  {
857    ParamCommandComment *PCC;
858    ParagraphComment *PC;
859    ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
860                                  ParamCommandComment::In,
861                                  /* IsDirectionExplicit = */ false,
862                                  "aaa", PC));
863    ASSERT_TRUE(HasChildCount(PCC, 1));
864
865    ASSERT_TRUE(HasChildCount(PC, 5));
866      ASSERT_TRUE(HasTextAt(PC, 0, " "));
867      ASSERT_TRUE(HasTextAt(PC, 1, "%"));
868      ASSERT_TRUE(HasTextAt(PC, 2, " Bbb "));
869      ASSERT_TRUE(HasTextAt(PC, 3, "$"));
870      ASSERT_TRUE(HasTextAt(PC, 4, " ccc"));
871  }
872}
873
874TEST_F(CommentParserTest, TParamCommand1) {
875  const char *Sources[] = {
876    "// \\tparam aaa Bbb\n",
877    "// \\tparam\n"
878    "//     aaa Bbb\n",
879    "// \\tparam \n"
880    "//     aaa Bbb\n",
881    "// \\tparam aaa\n"
882    "// Bbb\n"
883  };
884
885  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
886    FullComment *FC = parseString(Sources[i]);
887    ASSERT_TRUE(HasChildCount(FC, 2));
888
889    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
890    {
891      TParamCommandComment *TPCC;
892      ParagraphComment *PC;
893      ASSERT_TRUE(HasTParamCommandAt(FC, 1, TPCC, "tparam",
894                                     "aaa", PC));
895      ASSERT_TRUE(HasChildCount(TPCC, 1));
896      ASSERT_TRUE(HasParagraphCommentAt(TPCC, 0, " Bbb"));
897    }
898  }
899}
900
901TEST_F(CommentParserTest, InlineCommand1) {
902  const char *Source = "// \\c";
903
904  FullComment *FC = parseString(Source);
905  ASSERT_TRUE(HasChildCount(FC, 1));
906
907  {
908    ParagraphComment *PC;
909    InlineCommandComment *ICC;
910    ASSERT_TRUE(GetChildAt(FC, 0, PC));
911
912    ASSERT_TRUE(HasChildCount(PC, 2));
913      ASSERT_TRUE(HasTextAt(PC, 0, " "));
914      ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", NoArgs()));
915  }
916}
917
918TEST_F(CommentParserTest, InlineCommand2) {
919  const char *Source = "// \\c ";
920
921  FullComment *FC = parseString(Source);
922  ASSERT_TRUE(HasChildCount(FC, 1));
923
924  {
925    ParagraphComment *PC;
926    InlineCommandComment *ICC;
927    ASSERT_TRUE(GetChildAt(FC, 0, PC));
928
929    ASSERT_TRUE(HasChildCount(PC, 3));
930      ASSERT_TRUE(HasTextAt(PC, 0, " "));
931      ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", NoArgs()));
932      ASSERT_TRUE(HasTextAt(PC, 2, " "));
933  }
934}
935
936TEST_F(CommentParserTest, InlineCommand3) {
937  const char *Source = "// \\c aaa\n";
938
939  FullComment *FC = parseString(Source);
940  ASSERT_TRUE(HasChildCount(FC, 1));
941
942  {
943    ParagraphComment *PC;
944    InlineCommandComment *ICC;
945    ASSERT_TRUE(GetChildAt(FC, 0, PC));
946
947    ASSERT_TRUE(HasChildCount(PC, 2));
948      ASSERT_TRUE(HasTextAt(PC, 0, " "));
949      ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", "aaa"));
950  }
951}
952
953TEST_F(CommentParserTest, InlineCommand4) {
954  const char *Source = "// \\c aaa bbb";
955
956  FullComment *FC = parseString(Source);
957  ASSERT_TRUE(HasChildCount(FC, 1));
958
959  {
960    ParagraphComment *PC;
961    InlineCommandComment *ICC;
962    ASSERT_TRUE(GetChildAt(FC, 0, PC));
963
964    ASSERT_TRUE(HasChildCount(PC, 3));
965      ASSERT_TRUE(HasTextAt(PC, 0, " "));
966      ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", "aaa"));
967      ASSERT_TRUE(HasTextAt(PC, 2, " bbb"));
968  }
969}
970
971TEST_F(CommentParserTest, InlineCommand5) {
972  const char *Source = "// \\unknown aaa\n";
973
974  FullComment *FC = parseString(Source);
975  ASSERT_TRUE(HasChildCount(FC, 1));
976
977  {
978    ParagraphComment *PC;
979    InlineCommandComment *ICC;
980    ASSERT_TRUE(GetChildAt(FC, 0, PC));
981
982    ASSERT_TRUE(HasChildCount(PC, 3));
983      ASSERT_TRUE(HasTextAt(PC, 0, " "));
984      ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "unknown", NoArgs()));
985      ASSERT_TRUE(HasTextAt(PC, 2, " aaa"));
986  }
987}
988
989TEST_F(CommentParserTest, HTML1) {
990  const char *Sources[] = {
991    "// <a",
992    "// <a>",
993    "// <a >"
994  };
995
996  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
997    FullComment *FC = parseString(Sources[i]);
998    ASSERT_TRUE(HasChildCount(FC, 1));
999
1000    {
1001      ParagraphComment *PC;
1002      HTMLStartTagComment *HST;
1003      ASSERT_TRUE(GetChildAt(FC, 0, PC));
1004
1005      ASSERT_TRUE(HasChildCount(PC, 2));
1006        ASSERT_TRUE(HasTextAt(PC, 0, " "));
1007        ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", NoAttrs()));
1008    }
1009  }
1010}
1011
1012TEST_F(CommentParserTest, HTML2) {
1013  const char *Sources[] = {
1014    "// <br/>",
1015    "// <br />"
1016  };
1017
1018  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1019    FullComment *FC = parseString(Sources[i]);
1020    ASSERT_TRUE(HasChildCount(FC, 1));
1021
1022    {
1023      ParagraphComment *PC;
1024      HTMLStartTagComment *HST;
1025      ASSERT_TRUE(GetChildAt(FC, 0, PC));
1026
1027      ASSERT_TRUE(HasChildCount(PC, 2));
1028        ASSERT_TRUE(HasTextAt(PC, 0, " "));
1029        ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "br", SelfClosing()));
1030    }
1031  }
1032}
1033
1034TEST_F(CommentParserTest, HTML3) {
1035  const char *Sources[] = {
1036    "// <a href",
1037    "// <a href ",
1038    "// <a href>",
1039    "// <a href >",
1040  };
1041
1042  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1043    FullComment *FC = parseString(Sources[i]);
1044    ASSERT_TRUE(HasChildCount(FC, 1));
1045
1046    {
1047      ParagraphComment *PC;
1048      HTMLStartTagComment *HST;
1049      ASSERT_TRUE(GetChildAt(FC, 0, PC));
1050
1051      ASSERT_TRUE(HasChildCount(PC, 2));
1052        ASSERT_TRUE(HasTextAt(PC, 0, " "));
1053        ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", ""));
1054    }
1055  }
1056}
1057
1058TEST_F(CommentParserTest, HTML4) {
1059  const char *Sources[] = {
1060    "// <a href=\"bbb\"",
1061    "// <a href=\"bbb\">",
1062  };
1063
1064  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1065    FullComment *FC = parseString(Sources[i]);
1066    ASSERT_TRUE(HasChildCount(FC, 1));
1067
1068    {
1069      ParagraphComment *PC;
1070      HTMLStartTagComment *HST;
1071      ASSERT_TRUE(GetChildAt(FC, 0, PC));
1072
1073      ASSERT_TRUE(HasChildCount(PC, 2));
1074        ASSERT_TRUE(HasTextAt(PC, 0, " "));
1075        ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", "bbb"));
1076    }
1077  }
1078}
1079
1080TEST_F(CommentParserTest, HTML5) {
1081  const char *Sources[] = {
1082    "// </a",
1083    "// </a>",
1084    "// </a >"
1085  };
1086
1087  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1088    FullComment *FC = parseString(Sources[i]);
1089    ASSERT_TRUE(HasChildCount(FC, 1));
1090
1091    {
1092      ParagraphComment *PC;
1093      HTMLEndTagComment *HET;
1094      ASSERT_TRUE(GetChildAt(FC, 0, PC));
1095
1096      ASSERT_TRUE(HasChildCount(PC, 2));
1097        ASSERT_TRUE(HasTextAt(PC, 0, " "));
1098        ASSERT_TRUE(HasHTMLEndTagAt(PC, 1, HET, "a"));
1099    }
1100  }
1101}
1102
1103TEST_F(CommentParserTest, HTML6) {
1104  const char *Source =
1105    "// <pre>\n"
1106    "// Aaa\n"
1107    "// Bbb\n"
1108    "// </pre>\n";
1109
1110  FullComment *FC = parseString(Source);
1111  ASSERT_TRUE(HasChildCount(FC, 1));
1112
1113  {
1114    ParagraphComment *PC;
1115    HTMLStartTagComment *HST;
1116    HTMLEndTagComment *HET;
1117    ASSERT_TRUE(GetChildAt(FC, 0, PC));
1118
1119    ASSERT_TRUE(HasChildCount(PC, 6));
1120      ASSERT_TRUE(HasTextAt(PC, 0, " "));
1121      ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "pre", NoAttrs()));
1122      ASSERT_TRUE(HasTextWithNewlineAt(PC, 2, " Aaa"));
1123      ASSERT_TRUE(HasTextWithNewlineAt(PC, 3, " Bbb"));
1124      ASSERT_TRUE(HasTextAt(PC, 4, " "));
1125      ASSERT_TRUE(HasHTMLEndTagAt(PC, 5, HET, "pre"));
1126  }
1127}
1128
1129TEST_F(CommentParserTest, VerbatimBlock1) {
1130  const char *Source = "// \\verbatim\\endverbatim\n";
1131
1132  FullComment *FC = parseString(Source);
1133  ASSERT_TRUE(HasChildCount(FC, 2));
1134
1135  ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1136  {
1137    VerbatimBlockComment *VCC;
1138    ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VCC, "verbatim", "endverbatim",
1139                                   NoLines()));
1140  }
1141}
1142
1143TEST_F(CommentParserTest, VerbatimBlock2) {
1144  const char *Source = "// \\verbatim Aaa \\endverbatim\n";
1145
1146  FullComment *FC = parseString(Source);
1147  ASSERT_TRUE(HasChildCount(FC, 2));
1148
1149  ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1150  {
1151    VerbatimBlockComment *VBC;
1152    ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim",
1153                                   Lines(), " Aaa "));
1154  }
1155}
1156
1157TEST_F(CommentParserTest, VerbatimBlock3) {
1158  const char *Source = "// \\verbatim Aaa\n";
1159
1160  FullComment *FC = parseString(Source);
1161  ASSERT_TRUE(HasChildCount(FC, 2));
1162
1163  ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1164  {
1165    VerbatimBlockComment *VBC;
1166    ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "",
1167                                   Lines(), " Aaa"));
1168  }
1169}
1170
1171TEST_F(CommentParserTest, VerbatimBlock4) {
1172  const char *Source =
1173    "//\\verbatim\n"
1174    "//\\endverbatim\n";
1175
1176  FullComment *FC = parseString(Source);
1177  ASSERT_TRUE(HasChildCount(FC, 1));
1178
1179  {
1180    VerbatimBlockComment *VBC;
1181    ASSERT_TRUE(HasVerbatimBlockAt(FC, 0, VBC, "verbatim", "endverbatim",
1182                                   NoLines()));
1183  }
1184}
1185
1186TEST_F(CommentParserTest, VerbatimBlock5) {
1187  const char *Sources[] = {
1188    "//\\verbatim\n"
1189    "// Aaa\n"
1190    "//\\endverbatim\n",
1191
1192    "/*\\verbatim\n"
1193    " * Aaa\n"
1194    " *\\endverbatim*/"
1195  };
1196
1197  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1198    FullComment *FC = parseString(Sources[i]);
1199    ASSERT_TRUE(HasChildCount(FC, 1));
1200
1201    {
1202      VerbatimBlockComment *VBC;
1203      ASSERT_TRUE(HasVerbatimBlockAt(FC, 0, VBC, "verbatim", "endverbatim",
1204                                     Lines(), " Aaa"));
1205    }
1206  }
1207}
1208
1209TEST_F(CommentParserTest, VerbatimBlock6) {
1210  const char *Sources[] = {
1211    "// \\verbatim\n"
1212    "// Aaa\n"
1213    "// \\endverbatim\n",
1214
1215    "/* \\verbatim\n"
1216    " * Aaa\n"
1217    " * \\endverbatim*/"
1218  };
1219
1220  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1221    FullComment *FC = parseString(Sources[i]);
1222    ASSERT_TRUE(HasChildCount(FC, 2));
1223
1224    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1225    {
1226      VerbatimBlockComment *VBC;
1227      ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim",
1228                                     Lines(), " Aaa"));
1229    }
1230  }
1231}
1232
1233TEST_F(CommentParserTest, VerbatimBlock7) {
1234  const char *Sources[] = {
1235    "// \\verbatim\n"
1236    "// Aaa\n"
1237    "// Bbb\n"
1238    "// \\endverbatim\n",
1239
1240    "/* \\verbatim\n"
1241    " * Aaa\n"
1242    " * Bbb\n"
1243    " * \\endverbatim*/"
1244  };
1245
1246  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1247    FullComment *FC = parseString(Sources[i]);
1248    ASSERT_TRUE(HasChildCount(FC, 2));
1249
1250    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1251    {
1252      VerbatimBlockComment *VBC;
1253      ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim",
1254                                     Lines(), " Aaa", " Bbb"));
1255    }
1256  }
1257}
1258
1259TEST_F(CommentParserTest, VerbatimBlock8) {
1260  const char *Sources[] = {
1261    "// \\verbatim\n"
1262    "// Aaa\n"
1263    "//\n"
1264    "// Bbb\n"
1265    "// \\endverbatim\n",
1266
1267    "/* \\verbatim\n"
1268    " * Aaa\n"
1269    " *\n"
1270    " * Bbb\n"
1271    " * \\endverbatim*/"
1272  };
1273  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1274    FullComment *FC = parseString(Sources[i]);
1275    ASSERT_TRUE(HasChildCount(FC, 2));
1276
1277    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1278    {
1279      VerbatimBlockComment *VBC;
1280      ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim"));
1281      ASSERT_EQ(3U, VBC->getNumLines());
1282      ASSERT_EQ(" Aaa", VBC->getText(0));
1283      ASSERT_EQ("",     VBC->getText(1));
1284      ASSERT_EQ(" Bbb", VBC->getText(2));
1285    }
1286  }
1287}
1288
1289TEST_F(CommentParserTest, VerbatimLine1) {
1290  const char *Sources[] = {
1291    "// \\fn",
1292    "// \\fn\n"
1293  };
1294
1295  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1296    FullComment *FC = parseString(Sources[i]);
1297    ASSERT_TRUE(HasChildCount(FC, 2));
1298
1299    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1300    {
1301      VerbatimLineComment *VLC;
1302      ASSERT_TRUE(HasVerbatimLineAt(FC, 1, VLC, "fn", ""));
1303    }
1304  }
1305}
1306
1307TEST_F(CommentParserTest, VerbatimLine2) {
1308  const char *Sources[] = {
1309    "/// \\fn void *foo(const char *zzz = \"\\$\");\n//",
1310    "/** \\fn void *foo(const char *zzz = \"\\$\");*/"
1311  };
1312
1313  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
1314    FullComment *FC = parseString(Sources[i]);
1315    ASSERT_TRUE(HasChildCount(FC, 2));
1316
1317    ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1318    {
1319      VerbatimLineComment *VLC;
1320      ASSERT_TRUE(HasVerbatimLineAt(FC, 1, VLC, "fn",
1321                  " void *foo(const char *zzz = \"\\$\");"));
1322    }
1323  }
1324}
1325
1326} // unnamed namespace
1327
1328} // end namespace comments
1329} // end namespace clang
1330
1331