xref: /llvm-project/clang/unittests/AST/CommentParser.cpp (revision af6acd7442646fde56de919964bd52d7bb7922b2)
1 //===- unittests/AST/CommentParser.cpp ------ Comment parser tests --------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/AST/CommentParser.h"
10 #include "clang/AST/Comment.h"
11 #include "clang/AST/CommentCommandTraits.h"
12 #include "clang/AST/CommentLexer.h"
13 #include "clang/AST/CommentSema.h"
14 #include "clang/Basic/CommentOptions.h"
15 #include "clang/Basic/Diagnostic.h"
16 #include "clang/Basic/DiagnosticOptions.h"
17 #include "clang/Basic/FileManager.h"
18 #include "clang/Basic/SourceManager.h"
19 #include "llvm/ADT/STLExtras.h"
20 #include "llvm/Support/Allocator.h"
21 #include "gtest/gtest.h"
22 
23 using namespace llvm;
24 using namespace clang;
25 
26 namespace clang {
27 namespace comments {
28 
29 namespace {
30 
31 const bool MY_DEBUG = true;
32 
33 class CommentParserTest : public ::testing::Test {
34 protected:
CommentParserTest()35   CommentParserTest()
36     : FileMgr(FileMgrOpts),
37       DiagID(new DiagnosticIDs()),
38       Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
39       SourceMgr(Diags, FileMgr),
40       Traits(Allocator, CommentOptions()) {
41   }
42 
43   FileSystemOptions FileMgrOpts;
44   FileManager FileMgr;
45   IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
46   DiagnosticsEngine Diags;
47   SourceManager SourceMgr;
48   llvm::BumpPtrAllocator Allocator;
49   CommandTraits Traits;
50 
51   FullComment *parseString(const char *Source);
52 };
53 
parseString(const char * Source)54 FullComment *CommentParserTest::parseString(const char *Source) {
55   std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(Source);
56   FileID File = SourceMgr.createFileID(std::move(Buf));
57   SourceLocation Begin = SourceMgr.getLocForStartOfFile(File);
58 
59   Lexer L(Allocator, Diags, Traits, Begin, Source, Source + strlen(Source));
60 
61   Sema S(Allocator, SourceMgr, Diags, Traits, /*PP=*/ nullptr);
62   Parser P(L, S, Allocator, SourceMgr, Diags, Traits);
63   FullComment *FC = P.parseFullComment();
64 
65   if (MY_DEBUG) {
66     llvm::errs() << "=== Source:\n" << Source << "\n=== AST:\n";
67     FC->dump();
68   }
69 
70   Token Tok;
71   L.lex(Tok);
72   if (Tok.is(tok::eof))
73     return FC;
74   else
75     return nullptr;
76 }
77 
HasChildCount(const Comment * C,size_t Count)78 ::testing::AssertionResult HasChildCount(const Comment *C, size_t Count) {
79   if (!C)
80     return ::testing::AssertionFailure() << "Comment is NULL";
81 
82   if (Count != C->child_count())
83     return ::testing::AssertionFailure()
84         << "Count = " << Count
85         << ", child_count = " << C->child_count();
86 
87   return ::testing::AssertionSuccess();
88 }
89 
90 template <typename T>
GetChildAt(const Comment * C,size_t Idx,T * & Child)91 ::testing::AssertionResult GetChildAt(const Comment *C,
92                                       size_t Idx,
93                                       T *&Child) {
94   if (!C)
95     return ::testing::AssertionFailure() << "Comment is NULL";
96 
97   if (Idx >= C->child_count())
98     return ::testing::AssertionFailure()
99         << "Idx out of range.  Idx = " << Idx
100         << ", child_count = " << C->child_count();
101 
102   Comment::child_iterator I = C->child_begin() + Idx;
103   Comment *CommentChild = *I;
104   if (!CommentChild)
105     return ::testing::AssertionFailure() << "Child is NULL";
106 
107   Child = dyn_cast<T>(CommentChild);
108   if (!Child)
109     return ::testing::AssertionFailure()
110         << "Child is not of requested type, but a "
111         << CommentChild->getCommentKindName();
112 
113   return ::testing::AssertionSuccess();
114 }
115 
HasTextAt(const Comment * C,size_t Idx,StringRef Text)116 ::testing::AssertionResult HasTextAt(const Comment *C,
117                                      size_t Idx,
118                                      StringRef Text) {
119   TextComment *TC;
120   ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
121   if (!AR)
122     return AR;
123 
124   StringRef ActualText = TC->getText();
125   if (ActualText != Text)
126     return ::testing::AssertionFailure()
127         << "TextComment has text \"" << ActualText.str() << "\", "
128            "expected \"" << Text.str() << "\"";
129 
130   if (TC->hasTrailingNewline())
131     return ::testing::AssertionFailure()
132         << "TextComment has a trailing newline";
133 
134   return ::testing::AssertionSuccess();
135 }
136 
HasTextWithNewlineAt(const Comment * C,size_t Idx,StringRef Text)137 ::testing::AssertionResult HasTextWithNewlineAt(const Comment *C,
138                                                 size_t Idx,
139                                                 StringRef Text) {
140   TextComment *TC;
141   ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
142   if (!AR)
143     return AR;
144 
145   StringRef ActualText = TC->getText();
146   if (ActualText != Text)
147     return ::testing::AssertionFailure()
148         << "TextComment has text \"" << ActualText.str() << "\", "
149            "expected \"" << Text.str() << "\"";
150 
151   if (!TC->hasTrailingNewline())
152     return ::testing::AssertionFailure()
153         << "TextComment has no trailing newline";
154 
155   return ::testing::AssertionSuccess();
156 }
157 
HasBlockCommandAt(const Comment * C,const CommandTraits & Traits,size_t Idx,BlockCommandComment * & BCC,StringRef Name,ParagraphComment * & Paragraph)158 ::testing::AssertionResult HasBlockCommandAt(const Comment *C,
159                                              const CommandTraits &Traits,
160                                              size_t Idx,
161                                              BlockCommandComment *&BCC,
162                                              StringRef Name,
163                                              ParagraphComment *&Paragraph) {
164   ::testing::AssertionResult AR = GetChildAt(C, Idx, BCC);
165   if (!AR)
166     return AR;
167 
168   StringRef ActualName = BCC->getCommandName(Traits);
169   if (ActualName != Name)
170     return ::testing::AssertionFailure()
171         << "BlockCommandComment has name \"" << ActualName.str() << "\", "
172            "expected \"" << Name.str() << "\"";
173 
174   Paragraph = BCC->getParagraph();
175 
176   return ::testing::AssertionSuccess();
177 }
178 
179 ::testing::AssertionResult
HasParamCommandAt(const Comment * C,const CommandTraits & Traits,size_t Idx,ParamCommandComment * & PCC,StringRef CommandName,ParamCommandPassDirection Direction,bool IsDirectionExplicit,StringRef ParamName,ParagraphComment * & Paragraph)180 HasParamCommandAt(const Comment *C, const CommandTraits &Traits, size_t Idx,
181                   ParamCommandComment *&PCC, StringRef CommandName,
182                   ParamCommandPassDirection Direction, bool IsDirectionExplicit,
183                   StringRef ParamName, ParagraphComment *&Paragraph) {
184   ::testing::AssertionResult AR = GetChildAt(C, Idx, PCC);
185   if (!AR)
186     return AR;
187 
188   StringRef ActualCommandName = PCC->getCommandName(Traits);
189   if (ActualCommandName != CommandName)
190     return ::testing::AssertionFailure()
191         << "ParamCommandComment has name \"" << ActualCommandName.str() << "\", "
192            "expected \"" << CommandName.str() << "\"";
193 
194   if (PCC->getDirection() != Direction)
195     return ::testing::AssertionFailure()
196            << "ParamCommandComment has direction "
197            << llvm::to_underlying(PCC->getDirection()) << ", expected "
198            << llvm::to_underlying(Direction);
199 
200   if (PCC->isDirectionExplicit() != IsDirectionExplicit)
201     return ::testing::AssertionFailure()
202         << "ParamCommandComment has "
203         << (PCC->isDirectionExplicit() ? "explicit" : "implicit")
204         << " direction, "
205            "expected " << (IsDirectionExplicit ? "explicit" : "implicit");
206 
207   if (!ParamName.empty() && !PCC->hasParamName())
208     return ::testing::AssertionFailure()
209         << "ParamCommandComment has no parameter name";
210 
211   StringRef ActualParamName = PCC->hasParamName() ? PCC->getParamNameAsWritten() : "";
212   if (ActualParamName != ParamName)
213     return ::testing::AssertionFailure()
214         << "ParamCommandComment has parameter name \"" << ActualParamName.str()
215         << "\", "
216            "expected \"" << ParamName.str() << "\"";
217 
218   Paragraph = PCC->getParagraph();
219 
220   return ::testing::AssertionSuccess();
221 }
222 
HasTParamCommandAt(const Comment * C,const CommandTraits & Traits,size_t Idx,TParamCommandComment * & TPCC,StringRef CommandName,StringRef ParamName,ParagraphComment * & Paragraph)223 ::testing::AssertionResult HasTParamCommandAt(
224                               const Comment *C,
225                               const CommandTraits &Traits,
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(Traits);
236   if (ActualCommandName != CommandName)
237     return ::testing::AssertionFailure()
238         << "TParamCommandComment has name \"" << ActualCommandName.str() << "\", "
239            "expected \"" << CommandName.str() << "\"";
240 
241   if (!ParamName.empty() && !TPCC->hasParamName())
242     return ::testing::AssertionFailure()
243         << "TParamCommandComment has no parameter name";
244 
245   StringRef ActualParamName = TPCC->hasParamName() ? TPCC->getParamNameAsWritten() : "";
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 
HasInlineCommandAt(const Comment * C,const CommandTraits & Traits,size_t Idx,InlineCommandComment * & ICC,StringRef Name)257 ::testing::AssertionResult HasInlineCommandAt(const Comment *C,
258                                               const CommandTraits &Traits,
259                                               size_t Idx,
260                                               InlineCommandComment *&ICC,
261                                               StringRef Name) {
262   ::testing::AssertionResult AR = GetChildAt(C, Idx, ICC);
263   if (!AR)
264     return AR;
265 
266   StringRef ActualName = ICC->getCommandName(Traits);
267   if (ActualName != Name)
268     return ::testing::AssertionFailure()
269         << "InlineCommandComment has name \"" << ActualName.str() << "\", "
270            "expected \"" << Name.str() << "\"";
271 
272   return ::testing::AssertionSuccess();
273 }
274 
275 struct NoArgs {};
276 
HasInlineCommandAt(const Comment * C,const CommandTraits & Traits,size_t Idx,InlineCommandComment * & ICC,StringRef Name,NoArgs)277 ::testing::AssertionResult HasInlineCommandAt(const Comment *C,
278                                               const CommandTraits &Traits,
279                                               size_t Idx,
280                                               InlineCommandComment *&ICC,
281                                               StringRef Name,
282                                               NoArgs) {
283   ::testing::AssertionResult AR = HasInlineCommandAt(C, Traits, Idx, ICC, Name);
284   if (!AR)
285     return AR;
286 
287   if (ICC->getNumArgs() != 0)
288     return ::testing::AssertionFailure()
289         << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
290            "expected 0";
291 
292   return ::testing::AssertionSuccess();
293 }
294 
HasInlineCommandAt(const Comment * C,const CommandTraits & Traits,size_t Idx,InlineCommandComment * & ICC,StringRef Name,StringRef Arg)295 ::testing::AssertionResult HasInlineCommandAt(const Comment *C,
296                                               const CommandTraits &Traits,
297                                               size_t Idx,
298                                               InlineCommandComment *&ICC,
299                                               StringRef Name,
300                                               StringRef Arg) {
301   ::testing::AssertionResult AR = HasInlineCommandAt(C, Traits, Idx, ICC, Name);
302   if (!AR)
303     return AR;
304 
305   if (ICC->getNumArgs() != 1)
306     return ::testing::AssertionFailure()
307         << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
308            "expected 1";
309 
310   StringRef ActualArg = ICC->getArgText(0);
311   if (ActualArg != Arg)
312     return ::testing::AssertionFailure()
313         << "InlineCommandComment has argument \"" << ActualArg.str() << "\", "
314            "expected \"" << Arg.str() << "\"";
315 
316   return ::testing::AssertionSuccess();
317 }
318 
HasHTMLStartTagAt(const Comment * C,size_t Idx,HTMLStartTagComment * & HST,StringRef TagName)319 ::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
320                                              size_t Idx,
321                                              HTMLStartTagComment *&HST,
322                                              StringRef TagName) {
323   ::testing::AssertionResult AR = GetChildAt(C, Idx, HST);
324   if (!AR)
325     return AR;
326 
327   StringRef ActualTagName = HST->getTagName();
328   if (ActualTagName != TagName)
329     return ::testing::AssertionFailure()
330         << "HTMLStartTagComment has name \"" << ActualTagName.str() << "\", "
331            "expected \"" << TagName.str() << "\"";
332 
333   return ::testing::AssertionSuccess();
334 }
335 
336 struct SelfClosing {};
337 
HasHTMLStartTagAt(const Comment * C,size_t Idx,HTMLStartTagComment * & HST,StringRef TagName,SelfClosing)338 ::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
339                                              size_t Idx,
340                                              HTMLStartTagComment *&HST,
341                                              StringRef TagName,
342                                              SelfClosing) {
343   ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
344   if (!AR)
345     return AR;
346 
347   if (!HST->isSelfClosing())
348     return ::testing::AssertionFailure()
349         << "HTMLStartTagComment is not self-closing";
350 
351   return ::testing::AssertionSuccess();
352 }
353 
354 
355 struct NoAttrs {};
356 
HasHTMLStartTagAt(const Comment * C,size_t Idx,HTMLStartTagComment * & HST,StringRef TagName,NoAttrs)357 ::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
358                                              size_t Idx,
359                                              HTMLStartTagComment *&HST,
360                                              StringRef TagName,
361                                              NoAttrs) {
362   ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
363   if (!AR)
364     return AR;
365 
366   if (HST->isSelfClosing())
367     return ::testing::AssertionFailure()
368         << "HTMLStartTagComment is self-closing";
369 
370   if (HST->getNumAttrs() != 0)
371     return ::testing::AssertionFailure()
372         << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
373            "expected 0";
374 
375   return ::testing::AssertionSuccess();
376 }
377 
HasHTMLStartTagAt(const Comment * C,size_t Idx,HTMLStartTagComment * & HST,StringRef TagName,StringRef AttrName,StringRef AttrValue)378 ::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
379                                              size_t Idx,
380                                              HTMLStartTagComment *&HST,
381                                              StringRef TagName,
382                                              StringRef AttrName,
383                                              StringRef AttrValue) {
384   ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
385   if (!AR)
386     return AR;
387 
388   if (HST->isSelfClosing())
389     return ::testing::AssertionFailure()
390         << "HTMLStartTagComment is self-closing";
391 
392   if (HST->getNumAttrs() != 1)
393     return ::testing::AssertionFailure()
394         << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
395            "expected 1";
396 
397   StringRef ActualName = HST->getAttr(0).Name;
398   if (ActualName != AttrName)
399     return ::testing::AssertionFailure()
400         << "HTMLStartTagComment has attr \"" << ActualName.str() << "\", "
401            "expected \"" << AttrName.str() << "\"";
402 
403   StringRef ActualValue = HST->getAttr(0).Value;
404   if (ActualValue != AttrValue)
405     return ::testing::AssertionFailure()
406         << "HTMLStartTagComment has attr value \"" << ActualValue.str() << "\", "
407            "expected \"" << AttrValue.str() << "\"";
408 
409   return ::testing::AssertionSuccess();
410 }
411 
HasHTMLEndTagAt(const Comment * C,size_t Idx,HTMLEndTagComment * & HET,StringRef TagName)412 ::testing::AssertionResult HasHTMLEndTagAt(const Comment *C,
413                                            size_t Idx,
414                                            HTMLEndTagComment *&HET,
415                                            StringRef TagName) {
416   ::testing::AssertionResult AR = GetChildAt(C, Idx, HET);
417   if (!AR)
418     return AR;
419 
420   StringRef ActualTagName = HET->getTagName();
421   if (ActualTagName != TagName)
422     return ::testing::AssertionFailure()
423         << "HTMLEndTagComment has name \"" << ActualTagName.str() << "\", "
424            "expected \"" << TagName.str() << "\"";
425 
426   return ::testing::AssertionSuccess();
427 }
428 
HasParagraphCommentAt(const Comment * C,size_t Idx,StringRef Text)429 ::testing::AssertionResult HasParagraphCommentAt(const Comment *C,
430                                                  size_t Idx,
431                                                  StringRef Text) {
432   ParagraphComment *PC;
433 
434   {
435     ::testing::AssertionResult AR = GetChildAt(C, Idx, PC);
436     if (!AR)
437       return AR;
438   }
439 
440   {
441     ::testing::AssertionResult AR = HasChildCount(PC, 1);
442     if (!AR)
443       return AR;
444   }
445 
446   {
447     ::testing::AssertionResult AR = HasTextAt(PC, 0, Text);
448     if (!AR)
449       return AR;
450   }
451 
452   return ::testing::AssertionSuccess();
453 }
454 
HasVerbatimBlockAt(const Comment * C,const CommandTraits & Traits,size_t Idx,VerbatimBlockComment * & VBC,StringRef Name,StringRef CloseName)455 ::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
456                                               const CommandTraits &Traits,
457                                               size_t Idx,
458                                               VerbatimBlockComment *&VBC,
459                                               StringRef Name,
460                                               StringRef CloseName) {
461   ::testing::AssertionResult AR = GetChildAt(C, Idx, VBC);
462   if (!AR)
463     return AR;
464 
465   StringRef ActualName = VBC->getCommandName(Traits);
466   if (ActualName != Name)
467     return ::testing::AssertionFailure()
468         << "VerbatimBlockComment has name \"" << ActualName.str() << "\", "
469            "expected \"" << Name.str() << "\"";
470 
471   StringRef ActualCloseName = VBC->getCloseName();
472   if (ActualCloseName != CloseName)
473     return ::testing::AssertionFailure()
474         << "VerbatimBlockComment has closing command name \""
475         << ActualCloseName.str() << "\", "
476            "expected \"" << CloseName.str() << "\"";
477 
478   return ::testing::AssertionSuccess();
479 }
480 
481 struct NoLines {};
482 struct Lines {};
483 
HasVerbatimBlockAt(const Comment * C,const CommandTraits & Traits,size_t Idx,VerbatimBlockComment * & VBC,StringRef Name,StringRef CloseName,NoLines)484 ::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
485                                               const CommandTraits &Traits,
486                                               size_t Idx,
487                                               VerbatimBlockComment *&VBC,
488                                               StringRef Name,
489                                               StringRef CloseName,
490                                               NoLines) {
491   ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Traits, Idx, VBC, Name,
492                                                      CloseName);
493   if (!AR)
494     return AR;
495 
496   if (VBC->getNumLines() != 0)
497     return ::testing::AssertionFailure()
498         << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
499            "expected 0";
500 
501   return ::testing::AssertionSuccess();
502 }
503 
HasVerbatimBlockAt(const Comment * C,const CommandTraits & Traits,size_t Idx,VerbatimBlockComment * & VBC,StringRef Name,StringRef CloseName,Lines,StringRef Line0)504 ::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
505                                               const CommandTraits &Traits,
506                                               size_t Idx,
507                                               VerbatimBlockComment *&VBC,
508                                               StringRef Name,
509                                               StringRef CloseName,
510                                               Lines,
511                                               StringRef Line0) {
512   ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Traits, Idx, VBC, Name,
513                                                      CloseName);
514   if (!AR)
515     return AR;
516 
517   if (VBC->getNumLines() != 1)
518     return ::testing::AssertionFailure()
519         << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
520            "expected 1";
521 
522   StringRef ActualLine0 = VBC->getText(0);
523   if (ActualLine0 != Line0)
524     return ::testing::AssertionFailure()
525         << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
526            "expected \"" << Line0.str() << "\"";
527 
528   return ::testing::AssertionSuccess();
529 }
530 
HasVerbatimBlockAt(const Comment * C,const CommandTraits & Traits,size_t Idx,VerbatimBlockComment * & VBC,StringRef Name,StringRef CloseName,Lines,StringRef Line0,StringRef Line1)531 ::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
532                                               const CommandTraits &Traits,
533                                               size_t Idx,
534                                               VerbatimBlockComment *&VBC,
535                                               StringRef Name,
536                                               StringRef CloseName,
537                                               Lines,
538                                               StringRef Line0,
539                                               StringRef Line1) {
540   ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Traits, Idx, VBC, Name,
541                                                      CloseName);
542   if (!AR)
543     return AR;
544 
545   if (VBC->getNumLines() != 2)
546     return ::testing::AssertionFailure()
547         << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
548            "expected 2";
549 
550   StringRef ActualLine0 = VBC->getText(0);
551   if (ActualLine0 != Line0)
552     return ::testing::AssertionFailure()
553         << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
554            "expected \"" << Line0.str() << "\"";
555 
556   StringRef ActualLine1 = VBC->getText(1);
557   if (ActualLine1 != Line1)
558     return ::testing::AssertionFailure()
559         << "VerbatimBlockComment has lines[1] \"" << ActualLine1.str() << "\", "
560            "expected \"" << Line1.str() << "\"";
561 
562   return ::testing::AssertionSuccess();
563 }
564 
HasVerbatimLineAt(const Comment * C,const CommandTraits & Traits,size_t Idx,VerbatimLineComment * & VLC,StringRef Name,StringRef Text)565 ::testing::AssertionResult HasVerbatimLineAt(const Comment *C,
566                                              const CommandTraits &Traits,
567                                              size_t Idx,
568                                              VerbatimLineComment *&VLC,
569                                              StringRef Name,
570                                              StringRef Text) {
571   ::testing::AssertionResult AR = GetChildAt(C, Idx, VLC);
572   if (!AR)
573     return AR;
574 
575   StringRef ActualName = VLC->getCommandName(Traits);
576   if (ActualName != Name)
577     return ::testing::AssertionFailure()
578         << "VerbatimLineComment has name \"" << ActualName.str() << "\", "
579            "expected \"" << Name.str() << "\"";
580 
581   StringRef ActualText = VLC->getText();
582   if (ActualText != Text)
583     return ::testing::AssertionFailure()
584         << "VerbatimLineComment has text \"" << ActualText.str() << "\", "
585            "expected \"" << Text.str() << "\"";
586 
587   return ::testing::AssertionSuccess();
588 }
589 
590 
TEST_F(CommentParserTest,Basic1)591 TEST_F(CommentParserTest, Basic1) {
592   const char *Source = "//";
593 
594   FullComment *FC = parseString(Source);
595   ASSERT_TRUE(HasChildCount(FC, 0));
596 }
597 
TEST_F(CommentParserTest,Basic2)598 TEST_F(CommentParserTest, Basic2) {
599   const char *Source = "// Meow";
600 
601   FullComment *FC = parseString(Source);
602   ASSERT_TRUE(HasChildCount(FC, 1));
603 
604   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Meow"));
605 }
606 
TEST_F(CommentParserTest,Basic3)607 TEST_F(CommentParserTest, Basic3) {
608   const char *Source =
609     "// Aaa\n"
610     "// Bbb";
611 
612   FullComment *FC = parseString(Source);
613   ASSERT_TRUE(HasChildCount(FC, 1));
614 
615   {
616     ParagraphComment *PC;
617     ASSERT_TRUE(GetChildAt(FC, 0, PC));
618 
619     ASSERT_TRUE(HasChildCount(PC, 2));
620       ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
621       ASSERT_TRUE(HasTextAt(PC, 1, " Bbb"));
622   }
623 }
624 
TEST_F(CommentParserTest,ParagraphSplitting1)625 TEST_F(CommentParserTest, ParagraphSplitting1) {
626   const char *Sources[] = {
627     ("// Aaa\n"
628     "//\n"
629     "// Bbb"),
630 
631     ("// Aaa\n"
632     "// \n"
633     "// Bbb"),
634 
635     ("// Aaa\n"
636     "//\t\n"
637     "// Bbb"),
638 
639     ("// Aaa\n"
640     "//\n"
641     "//\n"
642     "// Bbb"),
643 
644     ("/**\n"
645     " Aaa\n"
646     "\n"
647     " Bbb\n"
648     "*/"),
649 
650     ("/**\n"
651     " Aaa\n"
652     " \n"
653     " Bbb\n"
654     "*/"),
655 
656     ("/**\n"
657     " Aaa\n"
658     "\t \n"
659     " Bbb\n"
660     "*/"),
661   };
662 
663   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
664     FullComment *FC = parseString(Sources[i]);
665     ASSERT_TRUE(HasChildCount(FC, 2));
666 
667     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Aaa"));
668     ASSERT_TRUE(HasParagraphCommentAt(FC, 1, " Bbb"));
669   }
670 }
671 
TEST_F(CommentParserTest,Paragraph1)672 TEST_F(CommentParserTest, Paragraph1) {
673   const char *Source =
674     "// \\brief Aaa\n"
675     "//\n"
676     "// Bbb";
677 
678   FullComment *FC = parseString(Source);
679   ASSERT_TRUE(HasChildCount(FC, 3));
680 
681   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
682   {
683     BlockCommandComment *BCC;
684     ParagraphComment *PC;
685     ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "brief", PC));
686 
687     ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Aaa"));
688   }
689   ASSERT_TRUE(HasParagraphCommentAt(FC, 2, " Bbb"));
690 }
691 
TEST_F(CommentParserTest,Paragraph2)692 TEST_F(CommentParserTest, Paragraph2) {
693   const char *Source = "// \\brief \\author";
694 
695   FullComment *FC = parseString(Source);
696   ASSERT_TRUE(HasChildCount(FC, 3));
697 
698   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
699   {
700     BlockCommandComment *BCC;
701     ParagraphComment *PC;
702     ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "brief", PC));
703 
704     ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " "));
705   }
706   {
707     BlockCommandComment *BCC;
708     ParagraphComment *PC;
709     ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "author", PC));
710 
711     ASSERT_TRUE(GetChildAt(BCC, 0, PC));
712       ASSERT_TRUE(HasChildCount(PC, 0));
713   }
714 }
715 
TEST_F(CommentParserTest,Paragraph3)716 TEST_F(CommentParserTest, Paragraph3) {
717   const char *Source =
718     "// \\brief Aaa\n"
719     "// Bbb \\author\n"
720     "// Ccc";
721 
722   FullComment *FC = parseString(Source);
723   ASSERT_TRUE(HasChildCount(FC, 3));
724 
725   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
726   {
727     BlockCommandComment *BCC;
728     ParagraphComment *PC;
729     ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "brief", PC));
730 
731     ASSERT_TRUE(GetChildAt(BCC, 0, PC));
732       ASSERT_TRUE(HasChildCount(PC, 2));
733       ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
734       ASSERT_TRUE(HasTextAt(PC, 1, " Bbb "));
735   }
736   {
737     BlockCommandComment *BCC;
738     ParagraphComment *PC;
739     ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "author", PC));
740 
741     ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Ccc"));
742   }
743 }
744 
TEST_F(CommentParserTest,ParamCommand1)745 TEST_F(CommentParserTest, ParamCommand1) {
746   const char *Source = "// \\param aaa";
747 
748   FullComment *FC = parseString(Source);
749   ASSERT_TRUE(HasChildCount(FC, 2));
750 
751   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
752   {
753     ParamCommandComment *PCC;
754     ParagraphComment *PC;
755     ASSERT_TRUE(HasParamCommandAt(
756         FC, Traits, 1, PCC, "param", ParamCommandPassDirection::In,
757         /* IsDirectionExplicit = */ false, "aaa", PC));
758     ASSERT_TRUE(HasChildCount(PCC, 1));
759     ASSERT_TRUE(HasChildCount(PC, 0));
760   }
761 }
762 
TEST_F(CommentParserTest,ParamCommand2)763 TEST_F(CommentParserTest, ParamCommand2) {
764   const char *Source = "// \\param\\brief";
765 
766   FullComment *FC = parseString(Source);
767   ASSERT_TRUE(HasChildCount(FC, 3));
768 
769   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
770   {
771     ParamCommandComment *PCC;
772     ParagraphComment *PC;
773     ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param",
774                                   ParamCommandPassDirection::In,
775                                   /* IsDirectionExplicit = */ false, "", PC));
776     ASSERT_TRUE(HasChildCount(PCC, 1));
777     ASSERT_TRUE(HasChildCount(PC, 0));
778   }
779   {
780     BlockCommandComment *BCC;
781     ParagraphComment *PC;
782     ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "brief", PC));
783     ASSERT_TRUE(HasChildCount(PC, 0));
784   }
785 }
786 
TEST_F(CommentParserTest,ParamCommand3)787 TEST_F(CommentParserTest, ParamCommand3) {
788   const char *Sources[] = {
789     "// \\param aaa Bbb\n",
790     ("// \\param\n"
791     "//     aaa Bbb\n"),
792     ("// \\param \n"
793     "//     aaa Bbb\n"),
794     ("// \\param aaa\n"
795     "// Bbb\n")
796   };
797 
798   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
799     FullComment *FC = parseString(Sources[i]);
800     ASSERT_TRUE(HasChildCount(FC, 2));
801 
802     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
803     {
804       ParamCommandComment *PCC;
805       ParagraphComment *PC;
806       ASSERT_TRUE(HasParamCommandAt(
807           FC, Traits, 1, PCC, "param", ParamCommandPassDirection::In,
808           /* IsDirectionExplicit = */ false, "aaa", PC));
809       ASSERT_TRUE(HasChildCount(PCC, 1));
810       ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
811     }
812   }
813 }
814 
TEST_F(CommentParserTest,ParamCommand4)815 TEST_F(CommentParserTest, ParamCommand4) {
816   const char *Sources[] = {
817     "// \\param [in] aaa Bbb\n",
818     "// \\param[in] aaa Bbb\n",
819     ("// \\param\n"
820     "//     [in] aaa Bbb\n"),
821     ("// \\param [in]\n"
822     "//     aaa Bbb\n"),
823     ("// \\param [in] aaa\n"
824     "// Bbb\n"),
825   };
826 
827   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
828     FullComment *FC = parseString(Sources[i]);
829     ASSERT_TRUE(HasChildCount(FC, 2));
830 
831     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
832     {
833       ParamCommandComment *PCC;
834       ParagraphComment *PC;
835       ASSERT_TRUE(HasParamCommandAt(
836           FC, Traits, 1, PCC, "param", ParamCommandPassDirection::In,
837           /* IsDirectionExplicit = */ true, "aaa", PC));
838       ASSERT_TRUE(HasChildCount(PCC, 1));
839       ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
840     }
841   }
842 }
843 
TEST_F(CommentParserTest,ParamCommand5)844 TEST_F(CommentParserTest, ParamCommand5) {
845   const char *Sources[] = {
846     "// \\param [out] aaa Bbb\n",
847     "// \\param[out] aaa Bbb\n",
848     ("// \\param\n"
849     "//     [out] aaa Bbb\n"),
850     ("// \\param [out]\n"
851     "//     aaa Bbb\n"),
852     ("// \\param [out] aaa\n"
853     "// Bbb\n"),
854   };
855 
856   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
857     FullComment *FC = parseString(Sources[i]);
858     ASSERT_TRUE(HasChildCount(FC, 2));
859 
860     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
861     {
862       ParamCommandComment *PCC;
863       ParagraphComment *PC;
864       ASSERT_TRUE(HasParamCommandAt(
865           FC, Traits, 1, PCC, "param", ParamCommandPassDirection::Out,
866           /* IsDirectionExplicit = */ true, "aaa", PC));
867       ASSERT_TRUE(HasChildCount(PCC, 1));
868       ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
869     }
870   }
871 }
872 
TEST_F(CommentParserTest,ParamCommand6)873 TEST_F(CommentParserTest, ParamCommand6) {
874   const char *Sources[] = {
875     "// \\param [in,out] aaa Bbb\n",
876     "// \\param[in,out] aaa Bbb\n",
877     "// \\param [in, out] aaa Bbb\n",
878     "// \\param [in,\n"
879     "//     out] aaa Bbb\n",
880     "// \\param [in,out]\n"
881     "//     aaa Bbb\n",
882     "// \\param [in,out] aaa\n"
883     "// Bbb\n"
884   };
885 
886   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
887     FullComment *FC = parseString(Sources[i]);
888     ASSERT_TRUE(HasChildCount(FC, 2));
889 
890     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
891     {
892       ParamCommandComment *PCC;
893       ParagraphComment *PC;
894       ASSERT_TRUE(HasParamCommandAt(
895           FC, Traits, 1, PCC, "param", ParamCommandPassDirection::InOut,
896           /* IsDirectionExplicit = */ true, "aaa", PC));
897       ASSERT_TRUE(HasChildCount(PCC, 1));
898       ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
899     }
900   }
901 }
902 
TEST_F(CommentParserTest,ParamCommand7)903 TEST_F(CommentParserTest, ParamCommand7) {
904   const char *Source =
905     "// \\param aaa \\% Bbb \\$ ccc\n";
906 
907   FullComment *FC = parseString(Source);
908   ASSERT_TRUE(HasChildCount(FC, 2));
909 
910   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
911   {
912     ParamCommandComment *PCC;
913     ParagraphComment *PC;
914     ASSERT_TRUE(HasParamCommandAt(
915         FC, Traits, 1, PCC, "param", ParamCommandPassDirection::In,
916         /* IsDirectionExplicit = */ false, "aaa", PC));
917     ASSERT_TRUE(HasChildCount(PCC, 1));
918 
919     ASSERT_TRUE(HasChildCount(PC, 5));
920       ASSERT_TRUE(HasTextAt(PC, 0, " "));
921       ASSERT_TRUE(HasTextAt(PC, 1, "%"));
922       ASSERT_TRUE(HasTextAt(PC, 2, " Bbb "));
923       ASSERT_TRUE(HasTextAt(PC, 3, "$"));
924       ASSERT_TRUE(HasTextAt(PC, 4, " ccc"));
925   }
926 }
927 
TEST_F(CommentParserTest,TParamCommand1)928 TEST_F(CommentParserTest, TParamCommand1) {
929   const char *Sources[] = {
930     "// \\tparam aaa Bbb\n",
931     "// \\tparam\n"
932     "//     aaa Bbb\n",
933     "// \\tparam \n"
934     "//     aaa Bbb\n",
935     "// \\tparam aaa\n"
936     "// Bbb\n"
937   };
938 
939   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
940     FullComment *FC = parseString(Sources[i]);
941     ASSERT_TRUE(HasChildCount(FC, 2));
942 
943     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
944     {
945       TParamCommandComment *TPCC;
946       ParagraphComment *PC;
947       ASSERT_TRUE(HasTParamCommandAt(FC, Traits, 1, TPCC, "tparam",
948                                      "aaa", PC));
949       ASSERT_TRUE(HasChildCount(TPCC, 1));
950       ASSERT_TRUE(HasParagraphCommentAt(TPCC, 0, " Bbb"));
951     }
952   }
953 }
954 
TEST_F(CommentParserTest,TParamCommand2)955 TEST_F(CommentParserTest, TParamCommand2) {
956   const char *Source = "// \\tparam\\brief";
957 
958   FullComment *FC = parseString(Source);
959   ASSERT_TRUE(HasChildCount(FC, 3));
960 
961   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
962   {
963     TParamCommandComment *TPCC;
964     ParagraphComment *PC;
965     ASSERT_TRUE(HasTParamCommandAt(FC, Traits, 1, TPCC, "tparam", "", PC));
966     ASSERT_TRUE(HasChildCount(TPCC, 1));
967     ASSERT_TRUE(HasChildCount(PC, 0));
968   }
969   {
970     BlockCommandComment *BCC;
971     ParagraphComment *PC;
972     ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "brief", PC));
973     ASSERT_TRUE(HasChildCount(PC, 0));
974   }
975 }
976 
977 
TEST_F(CommentParserTest,InlineCommand1)978 TEST_F(CommentParserTest, InlineCommand1) {
979   const char *Source = "// \\c";
980 
981   FullComment *FC = parseString(Source);
982   ASSERT_TRUE(HasChildCount(FC, 1));
983 
984   {
985     ParagraphComment *PC;
986     InlineCommandComment *ICC;
987     ASSERT_TRUE(GetChildAt(FC, 0, PC));
988 
989     ASSERT_TRUE(HasChildCount(PC, 2));
990       ASSERT_TRUE(HasTextAt(PC, 0, " "));
991       ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", NoArgs()));
992   }
993 }
994 
TEST_F(CommentParserTest,InlineCommand2)995 TEST_F(CommentParserTest, InlineCommand2) {
996   const char *Source = "// \\c ";
997 
998   FullComment *FC = parseString(Source);
999   ASSERT_TRUE(HasChildCount(FC, 1));
1000 
1001   {
1002     ParagraphComment *PC;
1003     InlineCommandComment *ICC;
1004     ASSERT_TRUE(GetChildAt(FC, 0, PC));
1005 
1006     ASSERT_TRUE(HasChildCount(PC, 3));
1007       ASSERT_TRUE(HasTextAt(PC, 0, " "));
1008       ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", NoArgs()));
1009       ASSERT_TRUE(HasTextAt(PC, 2, " "));
1010   }
1011 }
1012 
TEST_F(CommentParserTest,InlineCommand3)1013 TEST_F(CommentParserTest, InlineCommand3) {
1014   const char *Source = "// \\c aaa\n";
1015 
1016   FullComment *FC = parseString(Source);
1017   ASSERT_TRUE(HasChildCount(FC, 1));
1018 
1019   {
1020     ParagraphComment *PC;
1021     InlineCommandComment *ICC;
1022     ASSERT_TRUE(GetChildAt(FC, 0, PC));
1023 
1024     ASSERT_TRUE(HasChildCount(PC, 2));
1025       ASSERT_TRUE(HasTextAt(PC, 0, " "));
1026       ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", "aaa"));
1027   }
1028 }
1029 
TEST_F(CommentParserTest,InlineCommand4)1030 TEST_F(CommentParserTest, InlineCommand4) {
1031   const char *Source = "// \\c aaa bbb";
1032 
1033   FullComment *FC = parseString(Source);
1034   ASSERT_TRUE(HasChildCount(FC, 1));
1035 
1036   {
1037     ParagraphComment *PC;
1038     InlineCommandComment *ICC;
1039     ASSERT_TRUE(GetChildAt(FC, 0, PC));
1040 
1041     ASSERT_TRUE(HasChildCount(PC, 3));
1042       ASSERT_TRUE(HasTextAt(PC, 0, " "));
1043       ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", "aaa"));
1044       ASSERT_TRUE(HasTextAt(PC, 2, " bbb"));
1045   }
1046 }
1047 
TEST_F(CommentParserTest,InlineCommand5)1048 TEST_F(CommentParserTest, InlineCommand5) {
1049   const char *Source = "// \\unknown aaa\n";
1050 
1051   FullComment *FC = parseString(Source);
1052   ASSERT_TRUE(HasChildCount(FC, 1));
1053 
1054   {
1055     ParagraphComment *PC;
1056     InlineCommandComment *ICC;
1057     ASSERT_TRUE(GetChildAt(FC, 0, PC));
1058 
1059     ASSERT_TRUE(HasChildCount(PC, 3));
1060       ASSERT_TRUE(HasTextAt(PC, 0, " "));
1061       ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "unknown", NoArgs()));
1062       ASSERT_TRUE(HasTextAt(PC, 2, " aaa"));
1063   }
1064 }
1065 
TEST_F(CommentParserTest,HTML1)1066 TEST_F(CommentParserTest, HTML1) {
1067   const char *Sources[] = {
1068     "// <a",
1069     "// <a>",
1070     "// <a >"
1071   };
1072 
1073   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1074     FullComment *FC = parseString(Sources[i]);
1075     ASSERT_TRUE(HasChildCount(FC, 1));
1076 
1077     {
1078       ParagraphComment *PC;
1079       HTMLStartTagComment *HST;
1080       ASSERT_TRUE(GetChildAt(FC, 0, PC));
1081 
1082       ASSERT_TRUE(HasChildCount(PC, 2));
1083         ASSERT_TRUE(HasTextAt(PC, 0, " "));
1084         ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", NoAttrs()));
1085     }
1086   }
1087 }
1088 
TEST_F(CommentParserTest,HTML2)1089 TEST_F(CommentParserTest, HTML2) {
1090   const char *Sources[] = {
1091     "// <br/>",
1092     "// <br />"
1093   };
1094 
1095   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1096     FullComment *FC = parseString(Sources[i]);
1097     ASSERT_TRUE(HasChildCount(FC, 1));
1098 
1099     {
1100       ParagraphComment *PC;
1101       HTMLStartTagComment *HST;
1102       ASSERT_TRUE(GetChildAt(FC, 0, PC));
1103 
1104       ASSERT_TRUE(HasChildCount(PC, 2));
1105         ASSERT_TRUE(HasTextAt(PC, 0, " "));
1106         ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "br", SelfClosing()));
1107     }
1108   }
1109 }
1110 
TEST_F(CommentParserTest,HTML3)1111 TEST_F(CommentParserTest, HTML3) {
1112   const char *Sources[] = {
1113     "// <a href",
1114     "// <a href ",
1115     "// <a href>",
1116     "// <a href >",
1117   };
1118 
1119   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1120     FullComment *FC = parseString(Sources[i]);
1121     ASSERT_TRUE(HasChildCount(FC, 1));
1122 
1123     {
1124       ParagraphComment *PC;
1125       HTMLStartTagComment *HST;
1126       ASSERT_TRUE(GetChildAt(FC, 0, PC));
1127 
1128       ASSERT_TRUE(HasChildCount(PC, 2));
1129         ASSERT_TRUE(HasTextAt(PC, 0, " "));
1130         ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", ""));
1131     }
1132   }
1133 }
1134 
TEST_F(CommentParserTest,HTML4)1135 TEST_F(CommentParserTest, HTML4) {
1136   const char *Sources[] = {
1137     "// <a href=\"bbb\"",
1138     "// <a href=\"bbb\">",
1139   };
1140 
1141   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1142     FullComment *FC = parseString(Sources[i]);
1143     ASSERT_TRUE(HasChildCount(FC, 1));
1144 
1145     {
1146       ParagraphComment *PC;
1147       HTMLStartTagComment *HST;
1148       ASSERT_TRUE(GetChildAt(FC, 0, PC));
1149 
1150       ASSERT_TRUE(HasChildCount(PC, 2));
1151         ASSERT_TRUE(HasTextAt(PC, 0, " "));
1152         ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", "bbb"));
1153     }
1154   }
1155 }
1156 
TEST_F(CommentParserTest,HTML5)1157 TEST_F(CommentParserTest, HTML5) {
1158   const char *Sources[] = {
1159     "// </a",
1160     "// </a>",
1161     "// </a >"
1162   };
1163 
1164   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1165     FullComment *FC = parseString(Sources[i]);
1166     ASSERT_TRUE(HasChildCount(FC, 1));
1167 
1168     {
1169       ParagraphComment *PC;
1170       HTMLEndTagComment *HET;
1171       ASSERT_TRUE(GetChildAt(FC, 0, PC));
1172 
1173       ASSERT_TRUE(HasChildCount(PC, 2));
1174         ASSERT_TRUE(HasTextAt(PC, 0, " "));
1175         ASSERT_TRUE(HasHTMLEndTagAt(PC, 1, HET, "a"));
1176     }
1177   }
1178 }
1179 
TEST_F(CommentParserTest,HTML6)1180 TEST_F(CommentParserTest, HTML6) {
1181   const char *Source =
1182     "// <pre>\n"
1183     "// Aaa\n"
1184     "// Bbb\n"
1185     "// </pre>\n";
1186 
1187   FullComment *FC = parseString(Source);
1188   ASSERT_TRUE(HasChildCount(FC, 1));
1189 
1190   {
1191     ParagraphComment *PC;
1192     HTMLStartTagComment *HST;
1193     HTMLEndTagComment *HET;
1194     ASSERT_TRUE(GetChildAt(FC, 0, PC));
1195 
1196     ASSERT_TRUE(HasChildCount(PC, 6));
1197       ASSERT_TRUE(HasTextAt(PC, 0, " "));
1198       ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "pre", NoAttrs()));
1199       ASSERT_TRUE(HasTextWithNewlineAt(PC, 2, " Aaa"));
1200       ASSERT_TRUE(HasTextWithNewlineAt(PC, 3, " Bbb"));
1201       ASSERT_TRUE(HasTextAt(PC, 4, " "));
1202       ASSERT_TRUE(HasHTMLEndTagAt(PC, 5, HET, "pre"));
1203   }
1204 }
1205 
TEST_F(CommentParserTest,VerbatimBlock1)1206 TEST_F(CommentParserTest, VerbatimBlock1) {
1207   const char *Source = "// \\verbatim\\endverbatim\n";
1208 
1209   FullComment *FC = parseString(Source);
1210   ASSERT_TRUE(HasChildCount(FC, 2));
1211 
1212   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1213   {
1214     VerbatimBlockComment *VCC;
1215     ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VCC,
1216                                    "verbatim", "endverbatim",
1217                                    NoLines()));
1218   }
1219 }
1220 
TEST_F(CommentParserTest,VerbatimBlock2)1221 TEST_F(CommentParserTest, VerbatimBlock2) {
1222   const char *Source = "// \\verbatim Aaa \\endverbatim\n";
1223 
1224   FullComment *FC = parseString(Source);
1225   ASSERT_TRUE(HasChildCount(FC, 2));
1226 
1227   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1228   {
1229     VerbatimBlockComment *VBC;
1230     ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC,
1231                                    "verbatim", "endverbatim",
1232                                    Lines(), " Aaa "));
1233   }
1234 }
1235 
TEST_F(CommentParserTest,VerbatimBlock3)1236 TEST_F(CommentParserTest, VerbatimBlock3) {
1237   const char *Source = "// \\verbatim Aaa\n";
1238 
1239   FullComment *FC = parseString(Source);
1240   ASSERT_TRUE(HasChildCount(FC, 2));
1241 
1242   ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1243   {
1244     VerbatimBlockComment *VBC;
1245     ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC, "verbatim", "",
1246                                    Lines(), " Aaa"));
1247   }
1248 }
1249 
TEST_F(CommentParserTest,VerbatimBlock4)1250 TEST_F(CommentParserTest, VerbatimBlock4) {
1251   const char *Source =
1252     "//\\verbatim\n"
1253     "//\\endverbatim\n";
1254 
1255   FullComment *FC = parseString(Source);
1256   ASSERT_TRUE(HasChildCount(FC, 1));
1257 
1258   {
1259     VerbatimBlockComment *VBC;
1260     ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 0, VBC,
1261                                    "verbatim", "endverbatim",
1262                                    NoLines()));
1263   }
1264 }
1265 
TEST_F(CommentParserTest,VerbatimBlock5)1266 TEST_F(CommentParserTest, VerbatimBlock5) {
1267   const char *Sources[] = {
1268     "//\\verbatim\n"
1269     "// Aaa\n"
1270     "//\\endverbatim\n",
1271 
1272     "/*\\verbatim\n"
1273     " * Aaa\n"
1274     " *\\endverbatim*/"
1275   };
1276 
1277   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1278     FullComment *FC = parseString(Sources[i]);
1279     ASSERT_TRUE(HasChildCount(FC, 1));
1280 
1281     {
1282       VerbatimBlockComment *VBC;
1283       ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 0, VBC,
1284                                      "verbatim", "endverbatim",
1285                                      Lines(), " Aaa"));
1286     }
1287   }
1288 }
1289 
TEST_F(CommentParserTest,VerbatimBlock6)1290 TEST_F(CommentParserTest, VerbatimBlock6) {
1291   const char *Sources[] = {
1292     "// \\verbatim\n"
1293     "// Aaa\n"
1294     "// \\endverbatim\n",
1295 
1296     "/* \\verbatim\n"
1297     " * Aaa\n"
1298     " * \\endverbatim*/"
1299   };
1300 
1301   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1302     FullComment *FC = parseString(Sources[i]);
1303     ASSERT_TRUE(HasChildCount(FC, 2));
1304 
1305     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1306     {
1307       VerbatimBlockComment *VBC;
1308       ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC,
1309                                      "verbatim", "endverbatim",
1310                                      Lines(), " Aaa"));
1311     }
1312   }
1313 }
1314 
TEST_F(CommentParserTest,VerbatimBlock7)1315 TEST_F(CommentParserTest, VerbatimBlock7) {
1316   const char *Sources[] = {
1317     "// \\verbatim\n"
1318     "// Aaa\n"
1319     "// Bbb\n"
1320     "// \\endverbatim\n",
1321 
1322     "/* \\verbatim\n"
1323     " * Aaa\n"
1324     " * Bbb\n"
1325     " * \\endverbatim*/"
1326   };
1327 
1328   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1329     FullComment *FC = parseString(Sources[i]);
1330     ASSERT_TRUE(HasChildCount(FC, 2));
1331 
1332     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1333     {
1334       VerbatimBlockComment *VBC;
1335       ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC,
1336                                      "verbatim", "endverbatim",
1337                                      Lines(), " Aaa", " Bbb"));
1338     }
1339   }
1340 }
1341 
TEST_F(CommentParserTest,VerbatimBlock8)1342 TEST_F(CommentParserTest, VerbatimBlock8) {
1343   const char *Sources[] = {
1344     "// \\verbatim\n"
1345     "// Aaa\n"
1346     "//\n"
1347     "// Bbb\n"
1348     "// \\endverbatim\n",
1349 
1350     "/* \\verbatim\n"
1351     " * Aaa\n"
1352     " *\n"
1353     " * Bbb\n"
1354     " * \\endverbatim*/"
1355   };
1356   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1357     FullComment *FC = parseString(Sources[i]);
1358     ASSERT_TRUE(HasChildCount(FC, 2));
1359 
1360     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1361     {
1362       VerbatimBlockComment *VBC;
1363       ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC,
1364                                      "verbatim", "endverbatim"));
1365       ASSERT_EQ(3U, VBC->getNumLines());
1366       ASSERT_EQ(" Aaa", VBC->getText(0));
1367       ASSERT_EQ("",     VBC->getText(1));
1368       ASSERT_EQ(" Bbb", VBC->getText(2));
1369     }
1370   }
1371 }
1372 
TEST_F(CommentParserTest,VerbatimLine1)1373 TEST_F(CommentParserTest, VerbatimLine1) {
1374   const char *Sources[] = {
1375     "// \\fn",
1376     "// \\fn\n"
1377   };
1378 
1379   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1380     FullComment *FC = parseString(Sources[i]);
1381     ASSERT_TRUE(HasChildCount(FC, 2));
1382 
1383     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1384     {
1385       VerbatimLineComment *VLC;
1386       ASSERT_TRUE(HasVerbatimLineAt(FC, Traits, 1, VLC, "fn", ""));
1387     }
1388   }
1389 }
1390 
TEST_F(CommentParserTest,VerbatimLine2)1391 TEST_F(CommentParserTest, VerbatimLine2) {
1392   const char *Sources[] = {
1393     "/// \\fn void *foo(const char *zzz = \"\\$\");\n//",
1394     "/** \\fn void *foo(const char *zzz = \"\\$\");*/"
1395   };
1396 
1397   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1398     FullComment *FC = parseString(Sources[i]);
1399     ASSERT_TRUE(HasChildCount(FC, 2));
1400 
1401     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1402     {
1403       VerbatimLineComment *VLC;
1404       ASSERT_TRUE(HasVerbatimLineAt(FC, Traits, 1, VLC, "fn",
1405                   " void *foo(const char *zzz = \"\\$\");"));
1406     }
1407   }
1408 }
1409 
TEST_F(CommentParserTest,Deprecated)1410 TEST_F(CommentParserTest, Deprecated) {
1411   const char *Sources[] = {
1412     "/** @deprecated*/",
1413     "/// @deprecated\n"
1414   };
1415 
1416   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1417     FullComment *FC = parseString(Sources[i]);
1418     ASSERT_TRUE(HasChildCount(FC, 2));
1419 
1420     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1421     {
1422       BlockCommandComment *BCC;
1423       ParagraphComment *PC;
1424       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "deprecated", PC));
1425       ASSERT_TRUE(HasChildCount(PC, 0));
1426     }
1427   }
1428 }
1429 
TEST_F(CommentParserTest,ThrowsCommandHasArg1)1430 TEST_F(CommentParserTest, ThrowsCommandHasArg1) {
1431   const char *Sources[] = {
1432       "/// @throws int This function throws an integer",
1433       ("/// @throws\n"
1434        "/// int This function throws an integer"),
1435       ("/// @throws \n"
1436        "/// int This function throws an integer"),
1437       ("/// @throws\n"
1438        "/// int\n"
1439        "/// This function throws an integer"),
1440       ("/// @throws \n"
1441        "/// int \n"
1442        "/// This function throws an integer"),
1443   };
1444 
1445   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1446     FullComment *FC = parseString(Sources[i]);
1447     ASSERT_TRUE(HasChildCount(FC, 2));
1448 
1449     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1450     {
1451       BlockCommandComment *BCC;
1452       ParagraphComment *PC;
1453       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1454       ASSERT_TRUE(HasChildCount(PC, 1));
1455       ASSERT_TRUE(BCC->getNumArgs() == 1);
1456       ASSERT_TRUE(BCC->getArgText(0) == "int");
1457     }
1458   }
1459 }
1460 
TEST_F(CommentParserTest,ThrowsCommandHasArg2)1461 TEST_F(CommentParserTest, ThrowsCommandHasArg2) {
1462   const char *Sources[] = {
1463       "/// @throws int** This function throws a double pointer to an integer",
1464   };
1465 
1466   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1467     FullComment *FC = parseString(Sources[i]);
1468     ASSERT_TRUE(HasChildCount(FC, 2));
1469 
1470     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1471     {
1472       BlockCommandComment *BCC;
1473       ParagraphComment *PC;
1474       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1475       ASSERT_TRUE(HasChildCount(PC, 1));
1476       ASSERT_TRUE(BCC->getNumArgs() == 1);
1477       ASSERT_TRUE(BCC->getArgText(0) == "int**");
1478     }
1479   }
1480 }
1481 
TEST_F(CommentParserTest,ThrowsCommandHasArg3)1482 TEST_F(CommentParserTest, ThrowsCommandHasArg3) {
1483   const char *Sources[] = {
1484       "/// @throws Error<T> error of type Error<T>",
1485   };
1486 
1487   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1488     FullComment *FC = parseString(Sources[i]);
1489     ASSERT_TRUE(HasChildCount(FC, 2));
1490 
1491     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1492     {
1493       BlockCommandComment *BCC;
1494       ParagraphComment *PC;
1495       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1496       ASSERT_TRUE(HasChildCount(PC, 3)); // Extra children because <T> is parsed
1497                                          // as a series of TextComments
1498       ASSERT_TRUE(BCC->getNumArgs() == 1);
1499       ASSERT_TRUE(BCC->getArgText(0) == "Error<T>");
1500     }
1501   }
1502 }
1503 
TEST_F(CommentParserTest,ThrowsCommandHasArg4)1504 TEST_F(CommentParserTest, ThrowsCommandHasArg4) {
1505   const char *Sources[] = {
1506       "/// @throws Error<Container<T>> nested templates",
1507   };
1508 
1509   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1510     FullComment *FC = parseString(Sources[i]);
1511     ASSERT_TRUE(HasChildCount(FC, 2));
1512 
1513     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1514     {
1515       BlockCommandComment *BCC;
1516       ParagraphComment *PC;
1517       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1518       ASSERT_TRUE(HasChildCount(PC, 1));
1519       ASSERT_TRUE(BCC->getNumArgs() == 1);
1520       ASSERT_TRUE(BCC->getArgText(0) == "Error<Container<T>>");
1521     }
1522   }
1523 }
1524 
TEST_F(CommentParserTest,ThrowsCommandHasArg5)1525 TEST_F(CommentParserTest, ThrowsCommandHasArg5) {
1526   const char *Sources[] = {
1527       "/// @throws Error<Ts...> variadic templates",
1528   };
1529 
1530   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1531     FullComment *FC = parseString(Sources[i]);
1532     ASSERT_TRUE(HasChildCount(FC, 2));
1533 
1534     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1535     {
1536       BlockCommandComment *BCC;
1537       ParagraphComment *PC;
1538       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1539       ASSERT_TRUE(HasChildCount(PC, 1));
1540       ASSERT_TRUE(BCC->getNumArgs() == 1);
1541       ASSERT_TRUE(BCC->getArgText(0) == "Error<Ts...>");
1542     }
1543   }
1544 }
1545 
TEST_F(CommentParserTest,ThrowsCommandHasArg6)1546 TEST_F(CommentParserTest, ThrowsCommandHasArg6) {
1547   const char *Sources[] = {
1548       "/// @throws Foo<(1 > 0)> typo1",
1549   };
1550 
1551   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1552     FullComment *FC = parseString(Sources[i]);
1553     ASSERT_TRUE(HasChildCount(FC, 2));
1554 
1555     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1556     {
1557       BlockCommandComment *BCC;
1558       ParagraphComment *PC;
1559       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1560       ASSERT_TRUE(HasChildCount(PC, 1));
1561       ASSERT_TRUE(BCC->getNumArgs() == 1);
1562       ASSERT_TRUE(BCC->getArgText(0) == "Foo<(1 >");
1563     }
1564   }
1565 }
1566 
1567 // No matter the number of (unmatched) opening brackets, no type is parsed.
TEST_F(CommentParserTest,ThrowsCommandHasArg7)1568 TEST_F(CommentParserTest, ThrowsCommandHasArg7) {
1569   const char *Sources[] = {
1570     "/// @throws Foo<",
1571     "/// @throws Foo<<<",
1572     "/// @throws Foo<<<<<<<",
1573     "/// @throws Foo<\n",
1574     "/// @throws Foo<<<\n",
1575     "/// @throws Foo<<<<<<<\n",
1576   };
1577 
1578   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1579     FullComment *FC = parseString(Sources[i]);
1580     ASSERT_TRUE(HasChildCount(FC, 2));
1581 
1582     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1583     {
1584       BlockCommandComment *BCC;
1585       ParagraphComment *PC;
1586       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1587       ASSERT_TRUE(HasChildCount(PC, 0));
1588       ASSERT_TRUE(BCC->getNumArgs() == 0);
1589     }
1590   }
1591 }
1592 
1593 // Types with a non-matching closing bracket are parsed as if they are a type
TEST_F(CommentParserTest,ThrowsCommandHasArg8)1594 TEST_F(CommentParserTest, ThrowsCommandHasArg8) {
1595   const char *Sources[] = {
1596     "/// @throws Foo>",
1597     "/// @throws Foo>\n",
1598   };
1599 
1600   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1601     FullComment *FC = parseString(Sources[i]);
1602     ASSERT_TRUE(HasChildCount(FC, 2));
1603 
1604     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1605     {
1606       BlockCommandComment *BCC;
1607       ParagraphComment *PC;
1608       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1609       ASSERT_TRUE(HasChildCount(PC, 0));
1610       ASSERT_TRUE(BCC->getNumArgs() == 1);
1611       ASSERT_TRUE(BCC->getArgText(0) == "Foo>");
1612     }
1613   }
1614 }
1615 
1616 // Everying up until the end of the paragraph comment will be
1617 // eaten up if the template sequence is unterminated (i.e. number of
1618 // opening and closing brackets is not equal).
TEST_F(CommentParserTest,ThrowsCommandHasArg9)1619 TEST_F(CommentParserTest, ThrowsCommandHasArg9) {
1620   const char *Sources[] = {
1621     "/// @throws Foo<Bar<t>\n"
1622     "/// Aaa\n"
1623     "///\n"
1624     "/// Bbb\n"
1625   };
1626 
1627   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1628     FullComment *FC = parseString(Sources[i]);
1629     ASSERT_TRUE(HasChildCount(FC, 3));
1630 
1631     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1632     {
1633       BlockCommandComment *BCC;
1634       ParagraphComment *PC;
1635       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "throws", PC));
1636       ASSERT_TRUE(HasChildCount(PC, 0));
1637       ASSERT_TRUE(BCC->getNumArgs() == 0);
1638     }
1639   }
1640 }
1641 
TEST_F(CommentParserTest,ParCommandHasArg1)1642 TEST_F(CommentParserTest, ParCommandHasArg1) {
1643   const char *Sources[] = {
1644       "/// @par Paragraph header:",     "/// @par Paragraph header:\n",
1645       "/// @par Paragraph header:\r\n", "/// @par Paragraph header:\n\r",
1646       "/** @par Paragraph header:*/",
1647   };
1648 
1649   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1650     FullComment *FC = parseString(Sources[i]);
1651     ASSERT_TRUE(HasChildCount(FC, 2));
1652 
1653     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1654     {
1655       BlockCommandComment *BCC;
1656       ParagraphComment *PC;
1657       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "par", PC));
1658       ASSERT_TRUE(HasChildCount(PC, 0));
1659       ASSERT_TRUE(BCC->getNumArgs() == 1);
1660       ASSERT_TRUE(BCC->getArgText(0) == "Paragraph header:");
1661     }
1662   }
1663 }
1664 
TEST_F(CommentParserTest,ParCommandHasArg2)1665 TEST_F(CommentParserTest, ParCommandHasArg2) {
1666   const char *Sources[] = {
1667       "/// @par Paragraph header: ",     "/// @par Paragraph header: \n",
1668       "/// @par Paragraph header: \r\n", "/// @par Paragraph header: \n\r",
1669       "/** @par Paragraph header: */",
1670   };
1671 
1672   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1673     FullComment *FC = parseString(Sources[i]);
1674     ASSERT_TRUE(HasChildCount(FC, 2));
1675 
1676     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1677     {
1678       BlockCommandComment *BCC;
1679       ParagraphComment *PC;
1680       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "par", PC));
1681       ASSERT_TRUE(HasChildCount(PC, 0));
1682       ASSERT_TRUE(BCC->getNumArgs() == 1);
1683       ASSERT_TRUE(BCC->getArgText(0) == "Paragraph header: ");
1684     }
1685   }
1686 }
1687 
TEST_F(CommentParserTest,ParCommandHasArg3)1688 TEST_F(CommentParserTest, ParCommandHasArg3) {
1689   const char *Sources[] = {
1690       ("/// @par Paragraph header:\n"
1691        "/// Paragraph body"),
1692       ("/// @par Paragraph header:\r\n"
1693        "/// Paragraph body"),
1694       ("/// @par Paragraph header:\n\r"
1695        "/// Paragraph body"),
1696   };
1697 
1698   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1699     FullComment *FC = parseString(Sources[i]);
1700     ASSERT_TRUE(HasChildCount(FC, 2));
1701 
1702     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1703     {
1704       BlockCommandComment *BCC;
1705       ParagraphComment *PC;
1706       TextComment *TC;
1707       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "par", PC));
1708       ASSERT_TRUE(HasChildCount(PC, 1));
1709       ASSERT_TRUE(BCC->getNumArgs() == 1);
1710       ASSERT_TRUE(BCC->getArgText(0) == "Paragraph header:");
1711       ASSERT_TRUE(GetChildAt(PC, 0, TC));
1712       ASSERT_TRUE(TC->getText() == " Paragraph body");
1713     }
1714   }
1715 }
1716 
TEST_F(CommentParserTest,ParCommandHasArg4)1717 TEST_F(CommentParserTest, ParCommandHasArg4) {
1718   const char *Sources[] = {
1719       ("/// @par Paragraph header:\n"
1720        "/// Paragraph body1\n"
1721        "/// Paragraph body2"),
1722       ("/// @par Paragraph header:\r\n"
1723        "/// Paragraph body1\n"
1724        "/// Paragraph body2"),
1725       ("/// @par Paragraph header:\n\r"
1726        "/// Paragraph body1\n"
1727        "/// Paragraph body2"),
1728   };
1729 
1730   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1731     FullComment *FC = parseString(Sources[i]);
1732     ASSERT_TRUE(HasChildCount(FC, 2));
1733 
1734     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1735     {
1736       BlockCommandComment *BCC;
1737       ParagraphComment *PC;
1738       TextComment *TC;
1739       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "par", PC));
1740       ASSERT_TRUE(HasChildCount(PC, 2));
1741       ASSERT_TRUE(BCC->getNumArgs() == 1);
1742       ASSERT_TRUE(BCC->getArgText(0) == "Paragraph header:");
1743       ASSERT_TRUE(GetChildAt(PC, 0, TC));
1744       ASSERT_TRUE(TC->getText() == " Paragraph body1");
1745       ASSERT_TRUE(GetChildAt(PC, 1, TC));
1746       ASSERT_TRUE(TC->getText() == " Paragraph body2");
1747     }
1748   }
1749 }
1750 
TEST_F(CommentParserTest,ParCommandHasArg5)1751 TEST_F(CommentParserTest, ParCommandHasArg5) {
1752   const char *Sources[] = {
1753       ("/// @par \n"
1754        "/// Paragraphs with no text before newline have no heading"),
1755       ("/// @par \r\n"
1756        "/// Paragraphs with no text before newline have no heading"),
1757       ("/// @par \n\r"
1758        "/// Paragraphs with no text before newline have no heading"),
1759   };
1760 
1761   for (size_t i = 0, e = std::size(Sources); i != e; i++) {
1762     FullComment *FC = parseString(Sources[i]);
1763     ASSERT_TRUE(HasChildCount(FC, 2));
1764 
1765     ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
1766     {
1767       BlockCommandComment *BCC;
1768       ParagraphComment *PC;
1769       TextComment *TC;
1770       ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "par", PC));
1771       ASSERT_TRUE(HasChildCount(PC, 1));
1772       ASSERT_TRUE(BCC->getNumArgs() == 0);
1773       ASSERT_TRUE(GetChildAt(PC, 0, TC));
1774       ASSERT_TRUE(TC->getText() ==
1775                   "Paragraphs with no text before newline have no heading");
1776     }
1777   }
1778 }
1779 
1780 } // unnamed namespace
1781 
1782 } // end namespace comments
1783 } // end namespace clang
1784