diff options
Diffstat (limited to 'clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp')
-rw-r--r-- | clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp | 575 |
1 files changed, 548 insertions, 27 deletions
diff --git a/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp b/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp index 31a6a14..b3185cc 100644 --- a/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp +++ b/clang-tools-extra/clangd/unittests/SymbolDocumentationTests.cpp @@ -15,7 +15,7 @@ namespace clang { namespace clangd { -TEST(SymbolDocumentation, UnhandledDocs) { +TEST(SymbolDocumentation, DetailedDocToMarkup) { CommentOptions CommentOpts; @@ -26,52 +26,52 @@ TEST(SymbolDocumentation, UnhandledDocs) { llvm::StringRef ExpectedRenderPlainText; } Cases[] = { { - "foo bar", + "brief\n\nfoo bar", "foo bar", "foo bar", "foo bar", }, { - "foo\nbar\n", + "brief\n\nfoo\nbar\n", "foo\nbar", "foo\nbar", "foo bar", }, { - "foo\n\nbar\n", + "brief\n\nfoo\n\nbar\n", "foo\n\nbar", "foo\n\nbar", "foo\n\nbar", }, { - "foo \\p bar baz", + "brief\n\nfoo \\p bar baz", "foo `bar` baz", "foo `bar` baz", "foo bar baz", }, { - "foo \\e bar baz", + "brief\n\nfoo \\e bar baz", "foo \\*bar\\* baz", "foo *bar* baz", "foo *bar* baz", }, { - "foo \\b bar baz", + "brief\n\nfoo \\b bar baz", "foo \\*\\*bar\\*\\* baz", "foo **bar** baz", "foo **bar** baz", }, { - "foo \\ref bar baz", - "foo \\*\\*\\\\ref\\*\\* \\*bar\\* baz", - "foo **\\ref** *bar* baz", - "foo **\\ref** *bar* baz", + "brief\n\nfoo \\ref bar baz", + "foo \\*\\*\\\\ref\\*\\* `bar` baz", + "foo **\\ref** `bar` baz", + "foo **\\ref** bar baz", }, { - "foo @ref bar baz", - "foo \\*\\*@ref\\*\\* \\*bar\\* baz", - "foo **@ref** *bar* baz", - "foo **@ref** *bar* baz", + "brief\n\nfoo @ref bar baz", + "foo \\*\\*@ref\\*\\* `bar` baz", + "foo **@ref** `bar` baz", + "foo **@ref** bar baz", }, { "\\brief this is a \\n\nbrief description", @@ -80,10 +80,10 @@ TEST(SymbolDocumentation, UnhandledDocs) { "", }, { - "\\throw exception foo", - "\\*\\*\\\\throw\\*\\* \\*exception\\* foo", - "**\\throw** *exception* foo", - "**\\throw** *exception* foo", + "brief\n\n\\throw exception foo", + "\\*\\*\\\\throw\\*\\* `exception` foo", + "**\\throw** `exception` foo", + "**\\throw** exception foo", }, { R"(\brief this is a brief description @@ -108,7 +108,8 @@ TEST(SymbolDocumentation, UnhandledDocs) { - item 3)", }, { - "\\defgroup mygroup this is a group\nthis is not a group description", + "brief\n\n\\defgroup mygroup this is a group\nthis is not a group " + "description", "\\*\\*@defgroup\\*\\* `mygroup this is a group`\n\nthis is not a " "group " "description", @@ -118,7 +119,9 @@ TEST(SymbolDocumentation, UnhandledDocs) { "description", }, { - R"(\verbatim + R"(brief + +\verbatim this is a verbatim block containing some verbatim text @@ -150,7 +153,7 @@ some verbatim text **@endverbatim**)", }, { - "@param foo this is a parameter\n@param bar this is another " + "brief\n\n@param foo this is a parameter\n@param bar this is another " "parameter", "", "", @@ -169,24 +172,26 @@ More description documentation)", R"(\*\*\\brief\*\* another brief? -\*\*\\details\*\* these are details +these are details More description documentation)", R"(**\brief** another brief? -**\details** these are details +these are details More description documentation)", R"(**\brief** another brief? -**\details** these are details +these are details More description documentation)", }, { - R"(<b>this is a bold text</b> + R"(brief + +<b>this is a bold text</b> normal text<i>this is an italic text</i> <code>this is a code block</code>)", R"(\<b>this is a bold text\</b> @@ -198,12 +203,528 @@ normal text\<i>this is an italic text\</i> "<b>this is a bold text</b> normal text<i>this is an italic text</i> " "<code>this is a code block</code>", }, + {"brief\n\n@note This is a note", + R"(\*\*Note:\*\* +This is a note)", + R"(**Note:** +This is a note)", + R"(**Note:** +This is a note)"}, + {R"(brief + +Paragraph 1 +@note This is a note + +Paragraph 2)", + R"(Paragraph 1 + +\*\*Note:\*\* +This is a note + +Paragraph 2)", + R"(Paragraph 1 + +**Note:** +This is a note + +Paragraph 2)", + R"(Paragraph 1 + +**Note:** +This is a note + +Paragraph 2)"}, + {"brief\n\n@warning This is a warning", + R"(\*\*Warning:\*\* +This is a warning)", + R"(**Warning:** +This is a warning)", + R"(**Warning:** +This is a warning)"}, + {R"(brief + +Paragraph 1 +@warning This is a warning + +Paragraph 2)", + R"(Paragraph 1 + +\*\*Warning:\*\* +This is a warning + +Paragraph 2)", + R"(Paragraph 1 + +**Warning:** +This is a warning + +Paragraph 2)", + R"(Paragraph 1 + +**Warning:** +This is a warning + +Paragraph 2)"}, + {R"(@note this is not treated as brief + +@brief this is the brief + +Another paragraph)", + R"(\*\*Note:\*\* +this is not treated as brief + +Another paragraph)", + R"(**Note:** +this is not treated as brief + +Another paragraph)", + R"(**Note:** +this is not treated as brief + +Another paragraph)"}, + {R"( +@brief Some brief +)", + "", "", ""}, + {R"( +Some brief +)", + "", "", ""}, + }; + for (const auto &C : Cases) { + markup::Document Doc; + SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts); + + SymbolDoc.detailedDocToMarkup(Doc); + + EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText); + EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown); + EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown); + } +} + +TEST(SymbolDocumentation, RetvalCommand) { + + CommentOptions CommentOpts; + + struct Case { + llvm::StringRef Documentation; + llvm::StringRef ExpectedRenderEscapedMarkdown; + llvm::StringRef ExpectedRenderMarkdown; + llvm::StringRef ExpectedRenderPlainText; + } Cases[] = { + {"@retval", "", "", ""}, + {R"(@retval MyReturnVal +@retval MyOtherReturnVal)", + R"(- `MyReturnVal` +- `MyOtherReturnVal`)", + R"(- `MyReturnVal` +- `MyOtherReturnVal`)", + R"(- MyReturnVal +- MyOtherReturnVal)"}, + {R"(@retval MyReturnVal if foo +@retval MyOtherReturnVal if bar)", + R"(- `MyReturnVal` - if foo +- `MyOtherReturnVal` - if bar)", + R"(- `MyReturnVal` - if foo +- `MyOtherReturnVal` - if bar)", + R"(- MyReturnVal - if foo +- MyOtherReturnVal - if bar)"}, + }; + for (const auto &C : Cases) { + markup::Document Doc; + SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts); + + SymbolDoc.retvalsToMarkup(Doc); + + EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText); + EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown); + EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown); + } +} + +TEST(SymbolDocumentation, DoxygenCodeBlocks) { + CommentOptions CommentOpts; + + struct Case { + llvm::StringRef Documentation; + llvm::StringRef ExpectedRenderEscapedMarkdown; + llvm::StringRef ExpectedRenderMarkdown; + llvm::StringRef ExpectedRenderPlainText; + } Cases[] = { + {R"(@code +int code() { return 0; } +@endcode +@code{.cpp} +int code_lang() { return 0; } +@endcode +@code{.c++} +int code_lang_plus() { return 0; } +@endcode +@code{.py} +class A: + pass +@endcode +@code{nolang} +class B: + pass +@endcode)", + R"(``` +int code() { return 0; } +``` + +```cpp +int code_lang() { return 0; } +``` + +```c++ +int code_lang_plus() { return 0; } +``` + +```py +class A: + pass +``` + +```nolang +class B: + pass +```)", + R"(``` +int code() { return 0; } +``` + +```cpp +int code_lang() { return 0; } +``` + +```c++ +int code_lang_plus() { return 0; } +``` + +```py +class A: + pass +``` + +```nolang +class B: + pass +```)", + R"(int code() { return 0; } + +int code_lang() { return 0; } + +int code_lang_plus() { return 0; } + +class A: + pass + +class B: + pass)"}, + }; + for (const auto &C : Cases) { + markup::Document Doc; + SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts); + + SymbolDoc.detailedDocToMarkup(Doc); + + EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText); + EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown); + EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown); + } +} + +TEST(SymbolDocumentation, MarkdownCodeBlocks) { + CommentOptions CommentOpts; + + struct Case { + llvm::StringRef Documentation; + llvm::StringRef ExpectedRenderEscapedMarkdown; + llvm::StringRef ExpectedRenderMarkdown; + llvm::StringRef ExpectedRenderPlainText; + } Cases[] = { + {R"(``` +int backticks() { return 0; } +``` +```cpp +int backticks_lang() { return 0; } +``` +```c++ +int backticks_lang_plus() { return 0; } +``` +~~~ +int tilde() { return 0; } +~~~ +~~~~~~~~~~~~~~~~~~~~~~~~ +int tilde_many() { return 0; } +~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~{.c++} +int tilde_many_lang() { return 0; } +~~~~~~~~~~~~~~~~~~~~~~~~ +```py +class A: + pass +``` +```python +class B: + pass +``` +~~~{.python} +class C: + pass +~~~ +)", + R"(``` +int backticks() { return 0; } +``` + +```cpp +int backticks_lang() { return 0; } +``` + +```c++ +int backticks_lang_plus() { return 0; } +``` + +``` +int tilde() { return 0; } +``` + +``` +int tilde_many() { return 0; } +``` + +```c++ +int tilde_many_lang() { return 0; } +``` + +```py +class A: + pass +``` + +```python +class B: + pass +``` + +```python +class C: + pass +```)", + R"(``` +int backticks() { return 0; } +``` + +```cpp +int backticks_lang() { return 0; } +``` + +```c++ +int backticks_lang_plus() { return 0; } +``` + +``` +int tilde() { return 0; } +``` + +``` +int tilde_many() { return 0; } +``` + +```c++ +int tilde_many_lang() { return 0; } +``` + +```py +class A: + pass +``` + +```python +class B: + pass +``` + +```python +class C: + pass +```)", + R"(int backticks() { return 0; } + +int backticks_lang() { return 0; } + +int backticks_lang_plus() { return 0; } + +int tilde() { return 0; } + +int tilde_many() { return 0; } + +int tilde_many_lang() { return 0; } + +class A: + pass + +class B: + pass + +class C: + pass)"}, + {R"(``` +// this code block is missing end backticks + +)", + R"(``` +// this code block is missing end backticks +```)", + R"(``` +// this code block is missing end backticks +```)", + R"(// this code block is missing end backticks)"}, + }; + for (const auto &C : Cases) { + markup::Document Doc; + SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts); + + SymbolDoc.detailedDocToMarkup(Doc); + + EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText); + EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown); + EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown); + } +} + +TEST(SymbolDocumentation, MarkdownCodeBlocksSeparation) { + CommentOptions CommentOpts; + + struct Case { + llvm::StringRef Documentation; + llvm::StringRef ExpectedRenderEscapedMarkdown; + llvm::StringRef ExpectedRenderMarkdown; + llvm::StringRef ExpectedRenderPlainText; + } Cases[] = { + {R"(@note Show that code blocks are correctly separated +``` +/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph. + +/// With preprocessing, the code block is correctly separated from the @note paragraph. +/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted. +int function() { return 0; } +```)", + R"(\*\*Note:\*\* +Show that code blocks are correctly separated + +``` +/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph. + +/// With preprocessing, the code block is correctly separated from the @note paragraph. +/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted. +int function() { return 0; } +```)", + R"(**Note:** +Show that code blocks are correctly separated + +``` +/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph. + +/// With preprocessing, the code block is correctly separated from the @note paragraph. +/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted. +int function() { return 0; } +```)", + R"(**Note:** +Show that code blocks are correctly separated + +/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph. + +/// With preprocessing, the code block is correctly separated from the @note paragraph. +/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted. +int function() { return 0; })"}, + {R"(@note Show that code blocks are correctly separated +~~~~~~~~~ +/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph. + +/// With preprocessing, the code block is correctly separated from the @note paragraph. +/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted. +int function() { return 0; } +~~~~~~~~~)", + R"(\*\*Note:\*\* +Show that code blocks are correctly separated + +``` +/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph. + +/// With preprocessing, the code block is correctly separated from the @note paragraph. +/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted. +int function() { return 0; } +```)", + R"(**Note:** +Show that code blocks are correctly separated + +``` +/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph. + +/// With preprocessing, the code block is correctly separated from the @note paragraph. +/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted. +int function() { return 0; } +```)", + R"(**Note:** +Show that code blocks are correctly separated + +/// Without the markdown preprocessing, this line and the line above would be part of the @note paragraph. + +/// With preprocessing, the code block is correctly separated from the @note paragraph. +/// Also note that without preprocessing, all doxygen commands inside code blocks, like @p would be incorrectly interpreted. +int function() { return 0; })"}, + }; + for (const auto &C : Cases) { + markup::Document Doc; + SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts); + + SymbolDoc.detailedDocToMarkup(Doc); + + EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText); + EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown); + EXPECT_EQ(Doc.asEscapedMarkdown(), C.ExpectedRenderEscapedMarkdown); + } +} + +TEST(SymbolDocumentation, MarkdownCodeSpans) { + CommentOptions CommentOpts; + + struct Case { + llvm::StringRef Documentation; + llvm::StringRef ExpectedRenderEscapedMarkdown; + llvm::StringRef ExpectedRenderMarkdown; + llvm::StringRef ExpectedRenderPlainText; + } Cases[] = { + {R"(`this is a code span with @p and \c inside`)", + R"(\`this is a code span with @p and \\c inside\`)", + R"(`this is a code span with @p and \c inside`)", + R"(`this is a code span with @p and \c inside`)"}, + {R"(<escaped> `<not-escaped>`)", R"(\<escaped> \`\<not-escaped>\`)", + R"(\<escaped> `<not-escaped>`)", R"(<escaped> `<not-escaped>`)"}, + {R"(<escaped> \`<escaped> doxygen commands not parsed @p, \c, @note, \warning \`)", + R"(\<escaped> \\\`\<escaped> doxygen commands not parsed @p, \\c, @note, \\warning \\\`)", + R"(\<escaped> \`\<escaped> doxygen commands not parsed @p, \c, @note, \warning \`)", + R"(<escaped> \`<escaped> doxygen commands not parsed @p, \c, @note, \warning \`)"}, + {R"(`multi +line +\c span`)", + R"(\`multi +line +\\c span\`)", + R"(`multi +line +\c span`)", + R"(`multi line +\c span`)"}, }; for (const auto &C : Cases) { markup::Document Doc; SymbolDocCommentVisitor SymbolDoc(C.Documentation, CommentOpts); - SymbolDoc.docToMarkup(Doc); + SymbolDoc.briefToMarkup(Doc.addParagraph()); EXPECT_EQ(Doc.asPlainText(), C.ExpectedRenderPlainText); EXPECT_EQ(Doc.asMarkdown(), C.ExpectedRenderMarkdown); |