clang-tools 22.0.0git
SymbolDocumentationTests.cpp
Go to the documentation of this file.
1//===-- SymbolDocumentationTests.cpp --------------------------------------===//
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//===----------------------------------------------------------------------===//
9
10#include "support/Markup.h"
11#include "clang/Basic/CommentOptions.h"
12#include "llvm/ADT/StringRef.h"
13#include "gtest/gtest.h"
14
15namespace clang {
16namespace clangd {
17
18TEST(SymbolDocumentation, DetailedDocToMarkup) {
19
20 CommentOptions CommentOpts;
21
22 struct Case {
23 llvm::StringRef Documentation;
24 llvm::StringRef ExpectedRenderEscapedMarkdown;
25 llvm::StringRef ExpectedRenderMarkdown;
26 llvm::StringRef ExpectedRenderPlainText;
27 } Cases[] = {
28 {
29 "brief\n\nfoo bar",
30 "foo bar",
31 "foo bar",
32 "foo bar",
33 },
34 {
35 "brief\n\nfoo\nbar\n",
36 "foo\nbar",
37 "foo\nbar",
38 "foo bar",
39 },
40 {
41 "brief\n\nfoo\n\nbar\n",
42 "foo\n\nbar",
43 "foo\n\nbar",
44 "foo\n\nbar",
45 },
46 {
47 "brief\n\nfoo \\p bar baz",
48 "foo `bar` baz",
49 "foo `bar` baz",
50 "foo bar baz",
51 },
52 {
53 "brief\n\nfoo \\e bar baz",
54 "foo \\*bar\\* baz",
55 "foo *bar* baz",
56 "foo *bar* baz",
57 },
58 {
59 "brief\n\nfoo \\b bar baz",
60 "foo \\*\\*bar\\*\\* baz",
61 "foo **bar** baz",
62 "foo **bar** baz",
63 },
64 {
65 "brief\n\nfoo \\ref bar baz",
66 "foo \\*\\*\\\\ref\\*\\* `bar` baz",
67 "foo **\\ref** `bar` baz",
68 "foo **\\ref** bar baz",
69 },
70 {
71 "brief\n\nfoo @ref bar baz",
72 "foo \\*\\*@ref\\*\\* `bar` baz",
73 "foo **@ref** `bar` baz",
74 "foo **@ref** bar baz",
75 },
76 {
77 "\\brief this is a \\n\nbrief description",
78 "",
79 "",
80 "",
81 },
82 {
83 "brief\n\n\\throw exception foo",
84 "\\*\\*\\\\throw\\*\\* `exception` foo",
85 "**\\throw** `exception` foo",
86 "**\\throw** exception foo",
87 },
88 {
89 R"(\brief this is a brief description
90
91\li item 1
92\li item 2
93\arg item 3)",
94 R"(- item 1
95
96- item 2
97
98- item 3)",
99 R"(- item 1
100
101- item 2
102
103- item 3)",
104 R"(- item 1
105
106- item 2
107
108- item 3)",
109 },
110 {
111 "brief\n\n\\defgroup mygroup this is a group\nthis is not a group "
112 "description",
113 "\\*\\*@defgroup\\*\\* `mygroup this is a group`\n\nthis is not a "
114 "group "
115 "description",
116 "**@defgroup** `mygroup this is a group`\n\nthis is not a group "
117 "description",
118 "**@defgroup** `mygroup this is a group`\n\nthis is not a group "
119 "description",
120 },
121 {
122 R"(brief
123
124\verbatim
125this is a
126verbatim block containing
127some verbatim text
128\endverbatim)",
129 R"(\*\*@verbatim\*\*
130
131```
132this is a
133verbatim block containing
134some verbatim text
135```
136
137\*\*@endverbatim\*\*)",
138 R"(**@verbatim**
139
140```
141this is a
142verbatim block containing
143some verbatim text
144```
145
146**@endverbatim**)",
147 R"(**@verbatim**
148
149this is a
150verbatim block containing
151some verbatim text
152
153**@endverbatim**)",
154 },
155 {
156 "brief\n\n@param foo this is a parameter\n@param bar this is another "
157 "parameter",
158 "",
159 "",
160 "",
161 },
162 {
163 R"(@brief brief docs
164
165@param foo this is a parameter
166
167\brief another brief?
169\details these are details
170
171More description
172documentation)",
173 R"(\*\*\\brief\*\* another brief?
174
175these are details
176
177More description
178documentation)",
179 R"(**\brief** another brief?
180
181these are details
182
183More description
184documentation)",
185 R"(**\brief** another brief?
186
187these are details
188
189More description documentation)",
190 },
191 {
192 R"(brief
193
194<b>this is a bold text</b>
195normal text<i>this is an italic text</i>
196<code>this is a code block</code>)",
197 R"(<b>this is a bold text</b>
198normal text<i>this is an italic text</i>
199<code>this is a code block</code>)",
200 R"(<b>this is a bold text</b>
201normal text<i>this is an italic text</i>
202<code>this is a code block</code>)",
203 "<b>this is a bold text</b> normal text<i>this is an italic text</i> "
204 "<code>this is a code block</code>",
205 },
206 {"brief\n\n@note This is a note",
207 R"(\*\*Note:\*\*
208This is a note)",
209 R"(**Note:**
210This is a note)",
211 R"(**Note:**
212This is a note)"},
213 {R"(brief
214
215Paragraph 1
216@note This is a note
217
218Paragraph 2)",
219 R"(Paragraph 1
220
221\*\*Note:\*\*
222This is a note
223
224Paragraph 2)",
225 R"(Paragraph 1
227**Note:**
228This is a note
229
230Paragraph 2)",
231 R"(Paragraph 1
232
233**Note:**
234This is a note
235
236Paragraph 2)"},
237 {"brief\n\n@warning This is a warning",
238 R"(\*\*Warning:\*\*
239This is a warning)",
240 R"(**Warning:**
241This is a warning)",
242 R"(**Warning:**
243This is a warning)"},
244 {R"(brief
245
246Paragraph 1
247@warning This is a warning
248
249Paragraph 2)",
250 R"(Paragraph 1
251
252\*\*Warning:\*\*
253This is a warning
254
255Paragraph 2)",
256 R"(Paragraph 1
257
258**Warning:**
259This is a warning
260
261Paragraph 2)",
262 R"(Paragraph 1
263
264**Warning:**
265This is a warning
266
267Paragraph 2)"},
268 {R"(@note this is not treated as brief
269
270@brief this is the brief
271
272Another paragraph)",
273 R"(\*\*Note:\*\*
274this is not treated as brief
275
276Another paragraph)",
277 R"(**Note:**
278this is not treated as brief
279
280Another paragraph)",
281 R"(**Note:**
282this is not treated as brief
283
284Another paragraph)"},
285 {R"(
286@brief Some brief
287)",
288 "", "", ""},
289 {R"(
290Some brief
291)",
292 "", "", ""},
293 };
294 for (const auto &C : Cases) {
295 markup::Document Doc;
296 SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts);
297
298 SymbolDoc.detailedDocToMarkup(Doc);
299
300 EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText);
301 EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown);
302 EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown);
303 }
304}
305
306TEST(SymbolDocumentation, RetvalCommand) {
307
308 CommentOptions CommentOpts;
309
310 struct Case {
311 llvm::StringRef Documentation;
312 llvm::StringRef ExpectedRenderEscapedMarkdown;
313 llvm::StringRef ExpectedRenderMarkdown;
314 llvm::StringRef ExpectedRenderPlainText;
315 } Cases[] = {
316 {"@retval", "", "", ""},
317 {R"(@retval MyReturnVal
318@retval MyOtherReturnVal)",
319 R"(- `MyReturnVal`
320- `MyOtherReturnVal`)",
321 R"(- `MyReturnVal`
322- `MyOtherReturnVal`)",
323 R"(- MyReturnVal
324- MyOtherReturnVal)"},
325 {R"(@retval MyReturnVal if foo
326@retval MyOtherReturnVal if bar)",
327 R"(- `MyReturnVal` - if foo
328- `MyOtherReturnVal` - if bar)",
329 R"(- `MyReturnVal` - if foo
330- `MyOtherReturnVal` - if bar)",
331 R"(- MyReturnVal - if foo
332- MyOtherReturnVal - if bar)"},
333 };
334 for (const auto &C : Cases) {
335 markup::Document Doc;
336 SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts);
337
338 SymbolDoc.retvalsToMarkup(Doc);
339
340 EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText);
341 EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown);
342 EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown);
343 }
344}
345
346TEST(SymbolDocumentation, DoxygenCodeBlocks) {
347 CommentOptions CommentOpts;
348
349 struct Case {
350 llvm::StringRef Documentation;
351 llvm::StringRef ExpectedRenderEscapedMarkdown;
352 llvm::StringRef ExpectedRenderMarkdown;
353 llvm::StringRef ExpectedRenderPlainText;
354 } Cases[] = {
355 {R"(@code
356int code() { return 0; }
357@endcode
358@code{.cpp}
359int code_lang() { return 0; }
360@endcode
361@code{.c++}
362int code_lang_plus() { return 0; }
363@endcode
364@code{.py}
365class A:
366 pass
367@endcode
368@code{nolang}
369class B:
370 pass
371@endcode)",
372 R"(```
373int code() { return 0; }
374```
375
376```cpp
377int code_lang() { return 0; }
378```
379
380```c++
381int code_lang_plus() { return 0; }
382```
383
384```py
385class A:
386 pass
387```
388
389```nolang
390class B:
391 pass
392```)",
393 R"(```
394int code() { return 0; }
395```
396
397```cpp
398int code_lang() { return 0; }
399```
400
401```c++
402int code_lang_plus() { return 0; }
403```
404
405```py
406class A:
407 pass
408```
409
410```nolang
411class B:
412 pass
413```)",
414 R"(int code() { return 0; }
415
416int code_lang() { return 0; }
417
418int code_lang_plus() { return 0; }
419
420class A:
421 pass
422
423class B:
424 pass)"},
425 };
426 for (const auto &C : Cases) {
427 markup::Document Doc;
428 SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts);
429
430 SymbolDoc.detailedDocToMarkup(Doc);
431
432 EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText);
433 EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown);
434 EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown);
435 }
436}
437
438TEST(SymbolDocumentation, MarkdownCodeBlocks) {
439 CommentOptions CommentOpts;
440
441 struct Case {
442 llvm::StringRef Documentation;
443 llvm::StringRef ExpectedRenderEscapedMarkdown;
444 llvm::StringRef ExpectedRenderMarkdown;
445 llvm::StringRef ExpectedRenderPlainText;
446 } Cases[] = {
447 {R"(```
448int backticks() { return 0; }
449```
450```cpp
451int backticks_lang() { return 0; }
452```
453```c++
454int backticks_lang_plus() { return 0; }
455```
456~~~
457int tilde() { return 0; }
458~~~
459~~~~~~~~~~~~~~~~~~~~~~~~
460int tilde_many() { return 0; }
461~~~~~~~~~~~~~~~~~~~~~~~~
462~~~~~~~~~~~~~~~~~~~~~~~~{.c++}
463int tilde_many_lang() { return 0; }
464~~~~~~~~~~~~~~~~~~~~~~~~
465```py
466class A:
467 pass
468```
469```python
470class B:
471 pass
472```
473~~~{.python}
474class C:
475 pass
476~~~
477)",
478 R"(```
479int backticks() { return 0; }
480```
481
482```cpp
483int backticks_lang() { return 0; }
484```
485
486```c++
487int backticks_lang_plus() { return 0; }
488```
489
490```
491int tilde() { return 0; }
492```
493
494```
495int tilde_many() { return 0; }
496```
497
498```c++
499int tilde_many_lang() { return 0; }
500```
501
502```py
503class A:
504 pass
505```
506
507```python
508class B:
509 pass
510```
511
512```python
513class C:
514 pass
515```)",
516 R"(```
517int backticks() { return 0; }
518```
519
520```cpp
521int backticks_lang() { return 0; }
522```
523
524```c++
525int backticks_lang_plus() { return 0; }
526```
527
528```
529int tilde() { return 0; }
530```
531
532```
533int tilde_many() { return 0; }
534```
535
536```c++
537int tilde_many_lang() { return 0; }
538```
539
540```py
541class A:
542 pass
543```
544
545```python
546class B:
547 pass
548```
549
550```python
551class C:
552 pass
553```)",
554 R"(int backticks() { return 0; }
555
556int backticks_lang() { return 0; }
557
558int backticks_lang_plus() { return 0; }
559
560int tilde() { return 0; }
561
562int tilde_many() { return 0; }
563
564int tilde_many_lang() { return 0; }
565
566class A:
567 pass
568
569class B:
570 pass
571
572class C:
573 pass)"},
574 {R"(```
575// this code block is missing end backticks
576
577)",
578 R"(```
579// this code block is missing end backticks
580```)",
581 R"(```
582// this code block is missing end backticks
583```)",
584 R"(// this code block is missing end backticks)"},
585 };
586 for (const auto &C : Cases) {
587 markup::Document Doc;
588 SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts);
589
590 SymbolDoc.detailedDocToMarkup(Doc);
591
592 EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText);
593 EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown);
594 EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown);
595 }
596}
597
598TEST(SymbolDocumentation, MarkdownCodeBlocksSeparation) {
599 CommentOptions CommentOpts;
600
601 struct Case {
602 llvm::StringRef Documentation;
603 llvm::StringRef ExpectedRenderEscapedMarkdown;
604 llvm::StringRef ExpectedRenderMarkdown;
605 llvm::StringRef ExpectedRenderPlainText;
606 } Cases[] = {
607 {R"(@note Show that code blocks are correctly separated
608```
609/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph.
610
611/// With preprocessing, the code block is correctly separated from the @note paragraph.
612/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted.
613int function() { return 0; }
614```)",
615 R"(\*\*Note:\*\*
616Show that code blocks are correctly separated
617
618```
619/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph.
620
621/// With preprocessing, the code block is correctly separated from the @note paragraph.
622/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted.
623int function() { return 0; }
624```)",
625 R"(**Note:**
626Show that code blocks are correctly separated
627
628```
629/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph.
630
631/// With preprocessing, the code block is correctly separated from the @note paragraph.
632/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted.
633int function() { return 0; }
634```)",
635 R"(**Note:**
636Show that code blocks are correctly separated
637
638/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph.
639
640/// With preprocessing, the code block is correctly separated from the @note paragraph.
641/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted.
642int function() { return 0; })"},
643 {R"(@note Show that code blocks are correctly separated
644~~~~~~~~~
645/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph.
646
647/// With preprocessing, the code block is correctly separated from the @note paragraph.
648/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted.
649int function() { return 0; }
650~~~~~~~~~)",
651 R"(\*\*Note:\*\*
652Show that code blocks are correctly separated
653
654```
655/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph.
656
657/// With preprocessing, the code block is correctly separated from the @note paragraph.
658/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted.
659int function() { return 0; }
660```)",
661 R"(**Note:**
662Show that code blocks are correctly separated
663
664```
665/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph.
666
667/// With preprocessing, the code block is correctly separated from the @note paragraph.
668/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted.
669int function() { return 0; }
670```)",
671 R"(**Note:**
672Show that code blocks are correctly separated
673
674/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph.
675
676/// With preprocessing, the code block is correctly separated from the @note paragraph.
677/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted.
678int function() { return 0; })"},
679 };
680 for (const auto &C : Cases) {
681 markup::Document Doc;
682 SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts);
683
684 SymbolDoc.detailedDocToMarkup(Doc);
685
686 EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText);
687 EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown);
688 EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown);
689 }
690}
691
692TEST(SymbolDocumentation, MarkdownCodeSpans) {
693 CommentOptions CommentOpts;
694
695 struct Case {
696 llvm::StringRef Documentation;
697 llvm::StringRef ExpectedRenderEscapedMarkdown;
698 llvm::StringRef ExpectedRenderMarkdown;
699 llvm::StringRef ExpectedRenderPlainText;
700 } Cases[] = {
701 {R"(`this is a code span with @p and \c inside`)",
702 R"(\`this is a code span with @p and \\c inside\`)",
703 R"(`this is a code span with @p and \c inside`)",
704 R"(`this is a code span with @p and \c inside`)"},
705 {R"(<escaped> `<not-escaped>`)", R"(<escaped> \`<not-escaped>\`)",
706 R"(<escaped> `<not-escaped>`)", R"(<escaped> `<not-escaped>`)"},
707 {R"(<escaped> \`<escaped> doxygen commands not parsed @p, \c, @note, \warning \`)",
708 R"(<escaped> \\\`<escaped> doxygen commands not parsed @p, \\c, @note, \\warning \\\`)",
709 R"(<escaped> \`<escaped> doxygen commands not parsed @p, \c, @note, \warning \`)",
710 R"(<escaped> \`<escaped> doxygen commands not parsed @p, \c, @note, \warning \`)"},
711 {R"(`multi
712line
713\c span`)",
714 R"(\`multi
715line
716\\c span\`)",
717 R"(`multi
718line
719\c span`)",
720 R"(`multi line
721\c span`)"},
722 };
723 for (const auto &C : Cases) {
724 markup::Document Doc;
725 SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts);
726
727 SymbolDoc.briefToMarkup(Doc.addParagraph());
728
729 EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText);
730 EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown);
731 EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown);
732 }
733}
734
735} // namespace clangd
736} // namespace clang
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:45
TEST(BackgroundQueueTest, Priority)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//