summaryrefslogtreecommitdiffstats
path: root/unittests/AST
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/AST')
-rw-r--r--unittests/AST/CMakeLists.txt8
-rw-r--r--unittests/AST/CommentLexer.cpp1636
-rw-r--r--unittests/AST/CommentParser.cpp1383
-rw-r--r--unittests/AST/Makefile15
4 files changed, 3042 insertions, 0 deletions
diff --git a/unittests/AST/CMakeLists.txt b/unittests/AST/CMakeLists.txt
new file mode 100644
index 0000000..63418a2
--- /dev/null
+++ b/unittests/AST/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_clang_unittest(ASTTests
+ CommentLexer.cpp
+ CommentParser.cpp
+ )
+
+target_link_libraries(ASTTests
+ clangAST
+ )
diff --git a/unittests/AST/CommentLexer.cpp b/unittests/AST/CommentLexer.cpp
new file mode 100644
index 0000000..cab0fdd
--- /dev/null
+++ b/unittests/AST/CommentLexer.cpp
@@ -0,0 +1,1636 @@
+//===- unittests/AST/CommentLexer.cpp ------ Comment lexer tests ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/AST/CommentLexer.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "llvm/ADT/STLExtras.h"
+#include <vector>
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace clang {
+namespace comments {
+
+namespace {
+class CommentLexerTest : public ::testing::Test {
+protected:
+ CommentLexerTest()
+ : FileMgr(FileMgrOpts),
+ DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr) {
+ }
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ llvm::BumpPtrAllocator Allocator;
+
+ void lexString(const char *Source, std::vector<Token> &Toks);
+};
+
+void CommentLexerTest::lexString(const char *Source,
+ std::vector<Token> &Toks) {
+ MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(Source);
+ FileID File = SourceMgr.createFileIDForMemBuffer(Buf);
+ SourceLocation Begin = SourceMgr.getLocForStartOfFile(File);
+
+ comments::CommandTraits Traits;
+ comments::Lexer L(Allocator, Traits, Begin, CommentOptions(),
+ Source, Source + strlen(Source));
+
+ while (1) {
+ Token Tok;
+ L.lex(Tok);
+ if (Tok.is(tok::eof))
+ break;
+ Toks.push_back(Tok);
+ }
+}
+
+} // unnamed namespace
+
+// Empty source range should be handled.
+TEST_F(CommentLexerTest, Basic1) {
+ const char *Source = "";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(0U, Toks.size());
+}
+
+// Empty comments should be handled.
+TEST_F(CommentLexerTest, Basic2) {
+ const char *Sources[] = {
+ "//", "///", "//!", "///<", "//!<"
+ };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(1U, Toks.size());
+
+ ASSERT_EQ(tok::newline, Toks[0].getKind());
+ }
+}
+
+// Empty comments should be handled.
+TEST_F(CommentLexerTest, Basic3) {
+ const char *Sources[] = {
+ "/**/", "/***/", "/*!*/", "/**<*/", "/*!<*/"
+ };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(2U, Toks.size());
+
+ ASSERT_EQ(tok::newline, Toks[0].getKind());
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ }
+}
+
+// Single comment with plain text.
+TEST_F(CommentLexerTest, Basic4) {
+ const char *Sources[] = {
+ "// Meow", "/// Meow", "//! Meow",
+ "// Meow\n", "// Meow\r\n", "//! Meow\r",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(2U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ }
+}
+
+// Single comment with plain text.
+TEST_F(CommentLexerTest, Basic5) {
+ const char *Sources[] = {
+ "/* Meow*/", "/** Meow*/", "/*! Meow*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+// Test newline escaping.
+TEST_F(CommentLexerTest, Basic6) {
+ const char *Sources[] = {
+ "// Aaa\\\n" " Bbb\\ \n" " Ccc?" "?/\n",
+ "// Aaa\\\r\n" " Bbb\\ \r\n" " Ccc?" "?/\r\n",
+ "// Aaa\\\r" " Bbb\\ \r" " Ccc?" "?/\r"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(10U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[0].getText());
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[1].getText());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[3].getText());
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[4].getText());
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[5].getText());
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" Ccc?" "?/"), Toks[7].getText());
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+ }
+}
+
+// Check that we skip C-style aligned stars correctly.
+TEST_F(CommentLexerTest, Basic7) {
+ const char *Source =
+ "/* Aaa\n"
+ " * Bbb\r\n"
+ "\t* Ccc\n"
+ " ! Ddd\n"
+ " * Eee\n"
+ " ** Fff\n"
+ " */";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(15U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[0].getText());
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[2].getText());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Ccc"), Toks[4].getText());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" ! Ddd"), Toks[6].getText());
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+
+ ASSERT_EQ(tok::text, Toks[8].getKind());
+ ASSERT_EQ(StringRef(" Eee"), Toks[8].getText());
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+
+ ASSERT_EQ(tok::text, Toks[10].getKind());
+ ASSERT_EQ(StringRef("* Fff"), Toks[10].getText());
+ ASSERT_EQ(tok::newline, Toks[11].getKind());
+
+ ASSERT_EQ(tok::text, Toks[12].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[12].getText());
+
+ ASSERT_EQ(tok::newline, Toks[13].getKind());
+ ASSERT_EQ(tok::newline, Toks[14].getKind());
+}
+
+// A command marker followed by comment end.
+TEST_F(CommentLexerTest, DoxygenCommand1) {
+ const char *Sources[] = { "//@", "///@", "//!@" };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(2U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ }
+}
+
+// A command marker followed by comment end.
+TEST_F(CommentLexerTest, DoxygenCommand2) {
+ const char *Sources[] = { "/*@*/", "/**@*/", "/*!@*/"};
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+// A command marker followed by comment end.
+TEST_F(CommentLexerTest, DoxygenCommand3) {
+ const char *Sources[] = { "/*\\*/", "/**\\*/" };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef("\\"), Toks[0].getText());
+
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+// Doxygen escape sequences.
+TEST_F(CommentLexerTest, DoxygenCommand4) {
+ const char *Source =
+ "/// \\\\ \\@ \\& \\$ \\# \\< \\> \\% \\\" \\. \\::";
+ const char *Text[] = {
+ " ",
+ "\\", " ", "@", " ", "&", " ", "$", " ", "#", " ",
+ "<", " ", ">", " ", "%", " ", "\"", " ", ".", " ",
+ "::", ""
+ };
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(array_lengthof(Text), Toks.size());
+
+ for (size_t i = 0, e = Toks.size(); i != e; i++) {
+ if(Toks[i].is(tok::text))
+ ASSERT_EQ(StringRef(Text[i]), Toks[i].getText())
+ << "index " << i;
+ }
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand5) {
+ const char *Source = "/// \\brief Aaa.";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("brief"), Toks[1].getCommandName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa."), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand6) {
+ const char *Source = "/// \\aaa\\bbb \\ccc\t\\ddd\n";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(8U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("aaa"), Toks[1].getCommandName());
+
+ ASSERT_EQ(tok::command, Toks[2].getKind());
+ ASSERT_EQ(StringRef("bbb"), Toks[2].getCommandName());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[3].getText());
+
+ ASSERT_EQ(tok::command, Toks[4].getKind());
+ ASSERT_EQ(StringRef("ccc"), Toks[4].getCommandName());
+
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef("\t"), Toks[5].getText());
+
+ ASSERT_EQ(tok::command, Toks[6].getKind());
+ ASSERT_EQ(StringRef("ddd"), Toks[6].getCommandName());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+}
+
+TEST_F(CommentLexerTest, DoxygenCommand7) {
+ const char *Source = "// \\c\n";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::command, Toks[1].getKind());
+ ASSERT_EQ(StringRef("c"), Toks[1].getCommandName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+// Empty verbatim block.
+TEST_F(CommentLexerTest, VerbatimBlock1) {
+ const char *Sources[] = {
+ "/// \\verbatim\\endverbatim\n//",
+ "/** \\verbatim\\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[2].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[2].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+// Empty verbatim block without an end command.
+TEST_F(CommentLexerTest, VerbatimBlock2) {
+ const char *Source = "/// \\verbatim";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+// Empty verbatim block without an end command.
+TEST_F(CommentLexerTest, VerbatimBlock3) {
+ const char *Source = "/** \\verbatim*/";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+// Single-line verbatim block.
+TEST_F(CommentLexerTest, VerbatimBlock4) {
+ const char *Sources[] = {
+ "/// Meow \\verbatim aaa \\endverbatim\n//",
+ "/** Meow \\verbatim aaa \\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" aaa "), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[3].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[3].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+ }
+}
+
+// Single-line verbatim block without an end command.
+TEST_F(CommentLexerTest, VerbatimBlock5) {
+ const char *Sources[] = {
+ "/// Meow \\verbatim aaa \n//",
+ "/** Meow \\verbatim aaa */"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" aaa "), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, VerbatimBlock6) {
+ const char *Source =
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "//\n"
+ "// Bbb\n"
+ "// \\endverbatim\n";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(10U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[3].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[6].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[8].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[8].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+}
+
+TEST_F(CommentLexerTest, VerbatimBlock7) {
+ const char *Source =
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " *\n"
+ " * Bbb\n"
+ " * \\endverbatim\n"
+ " */";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(10U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[3].getKind());
+ ASSERT_EQ(StringRef(""), Toks[3].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[4].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[5].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[5].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[7].getText());
+
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+ ASSERT_EQ(tok::newline, Toks[9].getKind());
+}
+
+// Complex test for verbatim blocks.
+TEST_F(CommentLexerTest, VerbatimBlock8) {
+ const char *Source =
+ "/* Meow \\verbatim aaa\\$\\@\n"
+ "bbb \\endverbati\r"
+ "ccc\r\n"
+ "ddd \\endverbatim Blah \\verbatim eee\n"
+ "\\endverbatim BlahBlah*/";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(14U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Meow "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" aaa\\$\\@"), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[3].getKind());
+ ASSERT_EQ(StringRef("bbb \\endverbati"), Toks[3].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[4].getKind());
+ ASSERT_EQ(StringRef("ccc"), Toks[4].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[5].getKind());
+ ASSERT_EQ(StringRef("ddd "), Toks[5].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[6].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[6].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::text, Toks[7].getKind());
+ ASSERT_EQ(StringRef(" Blah "), Toks[7].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[8].getKind());
+ ASSERT_EQ(StringRef("verbatim"), Toks[8].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[9].getKind());
+ ASSERT_EQ(StringRef(" eee"), Toks[9].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[10].getKind());
+ ASSERT_EQ(StringRef("endverbatim"), Toks[10].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::text, Toks[11].getKind());
+ ASSERT_EQ(StringRef(" BlahBlah"), Toks[11].getText());
+
+ ASSERT_EQ(tok::newline, Toks[12].getKind());
+ ASSERT_EQ(tok::newline, Toks[13].getKind());
+}
+
+// LaTeX verbatim blocks.
+TEST_F(CommentLexerTest, VerbatimBlock9) {
+ const char *Source =
+ "/// \\f$ Aaa \\f$ \\f[ Bbb \\f] \\f{ Ccc \\f}";
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(13U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind());
+ ASSERT_EQ(StringRef("f$"), Toks[1].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Aaa "), Toks[2].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[3].getKind());
+ ASSERT_EQ(StringRef("f$"), Toks[3].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[4].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[5].getKind());
+ ASSERT_EQ(StringRef("f["), Toks[5].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Bbb "), Toks[6].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[7].getKind());
+ ASSERT_EQ(StringRef("f]"), Toks[7].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::text, Toks[8].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[8].getText());
+
+ ASSERT_EQ(tok::verbatim_block_begin, Toks[9].getKind());
+ ASSERT_EQ(StringRef("f{"), Toks[9].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::verbatim_block_line, Toks[10].getKind());
+ ASSERT_EQ(StringRef(" Ccc "), Toks[10].getVerbatimBlockText());
+
+ ASSERT_EQ(tok::verbatim_block_end, Toks[11].getKind());
+ ASSERT_EQ(StringRef("f}"), Toks[11].getVerbatimBlockName());
+
+ ASSERT_EQ(tok::newline, Toks[12].getKind());
+}
+
+// Empty verbatim line.
+TEST_F(CommentLexerTest, VerbatimLine1) {
+ const char *Sources[] = {
+ "/// \\fn\n//",
+ "/** \\fn*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind());
+ ASSERT_EQ(StringRef("fn"), Toks[1].getVerbatimLineName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+// Verbatim line with Doxygen escape sequences, which should not be expanded.
+TEST_F(CommentLexerTest, VerbatimLine2) {
+ const char *Sources[] = {
+ "/// \\fn void *foo(const char *zzz = \"\\$\");\n//",
+ "/** \\fn void *foo(const char *zzz = \"\\$\");*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind());
+ ASSERT_EQ(StringRef("fn"), Toks[1].getVerbatimLineName());
+
+ ASSERT_EQ(tok::verbatim_line_text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" void *foo(const char *zzz = \"\\$\");"),
+ Toks[2].getVerbatimLineText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+// Verbatim line should not eat anything from next source line.
+TEST_F(CommentLexerTest, VerbatimLine3) {
+ const char *Source =
+ "/** \\fn void *foo(const char *zzz = \"\\$\");\n"
+ " * Meow\n"
+ " */";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(9U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind());
+ ASSERT_EQ(StringRef("fn"), Toks[1].getVerbatimLineName());
+
+ ASSERT_EQ(tok::verbatim_line_text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" void *foo(const char *zzz = \"\\$\");"),
+ Toks[2].getVerbatimLineText());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[4].getText());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[6].getText());
+
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML1) {
+ const char *Source =
+ "// <";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML2) {
+ const char *Source =
+ "// a<2";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" a"), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("2"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML3) {
+ const char *Source =
+ "// < tag";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" tag"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML4) {
+ const char *Sources[] = {
+ "// <tag",
+ "// <tag "
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML5) {
+ const char *Source =
+ "// <tag 42";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("42"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML6) {
+ const char *Source = "// <tag> Meow";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_greater, Toks[2].getKind());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[3].getText());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML7) {
+ const char *Source = "// <tag=";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("="), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML8) {
+ const char *Source = "// <tag attr=> Meow";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(7U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_greater, Toks[4].getKind());
+
+ ASSERT_EQ(tok::text, Toks[5].getKind());
+ ASSERT_EQ(StringRef(" Meow"), Toks[5].getText());
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML9) {
+ const char *Sources[] = {
+ "// <tag attr",
+ "// <tag attr "
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML10) {
+ const char *Sources[] = {
+ "// <tag attr=",
+ "// <tag attr ="
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML11) {
+ const char *Sources[] = {
+ "// <tag attr=\"",
+ "// <tag attr = \"",
+ "// <tag attr=\'",
+ "// <tag attr = \'"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_quoted_string, Toks[4].getKind());
+ ASSERT_EQ(StringRef(""), Toks[4].getHTMLQuotedString());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML12) {
+ const char *Source = "// <tag attr=@";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[4].getText());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML13) {
+ const char *Sources[] = {
+ "// <tag attr=\"val\\\"\\'val",
+ "// <tag attr=\"val\\\"\\'val\"",
+ "// <tag attr=\'val\\\"\\'val",
+ "// <tag attr=\'val\\\"\\'val\'"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(6U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_quoted_string, Toks[4].getKind());
+ ASSERT_EQ(StringRef("val\\\"\\'val"), Toks[4].getHTMLQuotedString());
+
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML14) {
+ const char *Sources[] = {
+ "// <tag attr=\"val\\\"\\'val\">",
+ "// <tag attr=\'val\\\"\\'val\'>"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(7U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_ident, Toks[2].getKind());
+ ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent());
+
+ ASSERT_EQ(tok::html_equals, Toks[3].getKind());
+
+ ASSERT_EQ(tok::html_quoted_string, Toks[4].getKind());
+ ASSERT_EQ(StringRef("val\\\"\\'val"), Toks[4].getHTMLQuotedString());
+
+ ASSERT_EQ(tok::html_greater, Toks[5].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[6].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML15) {
+ const char *Sources[] = {
+ "// <tag/>",
+ "// <tag />"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::html_slash_greater, Toks[2].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML16) {
+ const char *Sources[] = {
+ "// <tag/ Aaa",
+ "// <tag / Aaa"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(5U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_start_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("/"), Toks[2].getText());
+
+ ASSERT_EQ(tok::text, Toks[3].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[3].getText());
+
+ ASSERT_EQ(tok::newline, Toks[4].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTML17) {
+ const char *Source = "// </";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_end_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef(""), Toks[1].getHTMLTagEndName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML18) {
+ const char *Source = "// </@";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_end_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef(""), Toks[1].getHTMLTagEndName());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("@"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML19) {
+ const char *Source = "// </tag";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_end_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagEndName());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTML20) {
+ const char *Sources[] = {
+ "// </tag>",
+ "// </ tag>",
+ "// </ tag >"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::html_end_tag, Toks[1].getKind());
+ ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagEndName());
+
+ ASSERT_EQ(tok::html_greater, Toks[2].getKind());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences1) {
+ const char *Source = "// &";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences2) {
+ const char *Source = "// &!";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("!"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences3) {
+ const char *Source = "// &amp";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&amp"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences4) {
+ const char *Source = "// &amp!";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&amp"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("!"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences5) {
+ const char *Source = "// &#";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences6) {
+ const char *Source = "// &#a";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("a"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences7) {
+ const char *Source = "// &#42";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#42"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences8) {
+ const char *Source = "// &#42a";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#42"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("a"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences9) {
+ const char *Source = "// &#x";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#x"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences10) {
+ const char *Source = "// &#xz";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#x"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("z"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences11) {
+ const char *Source = "// &#xab";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#xab"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences12) {
+ const char *Source = "// &#xaBz";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&#xaB"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("z"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences13) {
+ const char *Source = "// &amp;";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences14) {
+ const char *Source = "// &amp;&lt;";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef("<"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences15) {
+ const char *Source = "// &amp; meow";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(4U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("&"), Toks[1].getText());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" meow"), Toks[2].getText());
+
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+}
+
+TEST_F(CommentLexerTest, HTMLCharacterReferences16) {
+ const char *Sources[] = {
+ "// &#61;",
+ "// &#x3d;",
+ "// &#X3d;"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ std::vector<Token> Toks;
+
+ lexString(Sources[i], Toks);
+
+ ASSERT_EQ(3U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" "), Toks[0].getText());
+
+ ASSERT_EQ(tok::text, Toks[1].getKind());
+ ASSERT_EQ(StringRef("="), Toks[1].getText());
+
+ ASSERT_EQ(tok::newline, Toks[2].getKind());
+ }
+}
+
+TEST_F(CommentLexerTest, MultipleComments) {
+ const char *Source =
+ "// Aaa\n"
+ "/// Bbb\n"
+ "/* Ccc\n"
+ " * Ddd*/\n"
+ "/** Eee*/";
+
+ std::vector<Token> Toks;
+
+ lexString(Source, Toks);
+
+ ASSERT_EQ(12U, Toks.size());
+
+ ASSERT_EQ(tok::text, Toks[0].getKind());
+ ASSERT_EQ(StringRef(" Aaa"), Toks[0].getText());
+ ASSERT_EQ(tok::newline, Toks[1].getKind());
+
+ ASSERT_EQ(tok::text, Toks[2].getKind());
+ ASSERT_EQ(StringRef(" Bbb"), Toks[2].getText());
+ ASSERT_EQ(tok::newline, Toks[3].getKind());
+
+ ASSERT_EQ(tok::text, Toks[4].getKind());
+ ASSERT_EQ(StringRef(" Ccc"), Toks[4].getText());
+ ASSERT_EQ(tok::newline, Toks[5].getKind());
+
+ ASSERT_EQ(tok::text, Toks[6].getKind());
+ ASSERT_EQ(StringRef(" Ddd"), Toks[6].getText());
+ ASSERT_EQ(tok::newline, Toks[7].getKind());
+ ASSERT_EQ(tok::newline, Toks[8].getKind());
+
+ ASSERT_EQ(tok::text, Toks[9].getKind());
+ ASSERT_EQ(StringRef(" Eee"), Toks[9].getText());
+
+ ASSERT_EQ(tok::newline, Toks[10].getKind());
+ ASSERT_EQ(tok::newline, Toks[11].getKind());
+}
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/unittests/AST/CommentParser.cpp b/unittests/AST/CommentParser.cpp
new file mode 100644
index 0000000..7258a7e
--- /dev/null
+++ b/unittests/AST/CommentParser.cpp
@@ -0,0 +1,1383 @@
+//===- unittests/AST/CommentParser.cpp ------ Comment parser tests --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/AST/Comment.h"
+#include "clang/AST/CommentLexer.h"
+#include "clang/AST/CommentParser.h"
+#include "clang/AST/CommentSema.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Allocator.h"
+#include <vector>
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace clang {
+namespace comments {
+
+namespace {
+
+const bool DEBUG = true;
+
+class CommentParserTest : public ::testing::Test {
+protected:
+ CommentParserTest()
+ : FileMgr(FileMgrOpts),
+ DiagID(new DiagnosticIDs()),
+ Diags(DiagID, new IgnoringDiagConsumer()),
+ SourceMgr(Diags, FileMgr) {
+ }
+
+ FileSystemOptions FileMgrOpts;
+ FileManager FileMgr;
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ llvm::BumpPtrAllocator Allocator;
+
+ FullComment *parseString(const char *Source);
+};
+
+FullComment *CommentParserTest::parseString(const char *Source) {
+ MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(Source);
+ FileID File = SourceMgr.createFileIDForMemBuffer(Buf);
+ SourceLocation Begin = SourceMgr.getLocForStartOfFile(File);
+
+ comments::CommandTraits Traits;
+ comments::Lexer L(Allocator, Traits, Begin, CommentOptions(),
+ Source, Source + strlen(Source));
+
+ comments::Sema S(Allocator, SourceMgr, Diags, Traits);
+ comments::Parser P(L, S, Allocator, SourceMgr, Diags, Traits);
+ comments::FullComment *FC = P.parseFullComment();
+
+ if (DEBUG) {
+ llvm::errs() << "=== Source:\n" << Source << "\n=== AST:\n";
+ FC->dump(SourceMgr);
+ }
+
+ Token Tok;
+ L.lex(Tok);
+ if (Tok.is(tok::eof))
+ return FC;
+ else
+ return NULL;
+}
+
+::testing::AssertionResult HasChildCount(const Comment *C, size_t Count) {
+ if (!C)
+ return ::testing::AssertionFailure() << "Comment is NULL";
+
+ if (Count != C->child_count())
+ return ::testing::AssertionFailure()
+ << "Count = " << Count
+ << ", child_count = " << C->child_count();
+
+ return ::testing::AssertionSuccess();
+}
+
+template <typename T>
+::testing::AssertionResult GetChildAt(const Comment *C,
+ size_t Idx,
+ T *&Child) {
+ if (!C)
+ return ::testing::AssertionFailure() << "Comment is NULL";
+
+ if (Idx >= C->child_count())
+ return ::testing::AssertionFailure()
+ << "Idx out of range. Idx = " << Idx
+ << ", child_count = " << C->child_count();
+
+ Comment::child_iterator I = C->child_begin() + Idx;
+ Comment *CommentChild = *I;
+ if (!CommentChild)
+ return ::testing::AssertionFailure() << "Child is NULL";
+
+ Child = dyn_cast<T>(CommentChild);
+ if (!Child)
+ return ::testing::AssertionFailure()
+ << "Child is not of requested type, but a "
+ << CommentChild->getCommentKindName();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasTextAt(const Comment *C,
+ size_t Idx,
+ StringRef Text) {
+ TextComment *TC;
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualText = TC->getText();
+ if (ActualText != Text)
+ return ::testing::AssertionFailure()
+ << "TextComment has text \"" << ActualText.str() << "\", "
+ "expected \"" << Text.str() << "\"";
+
+ if (TC->hasTrailingNewline())
+ return ::testing::AssertionFailure()
+ << "TextComment has a trailing newline";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasTextWithNewlineAt(const Comment *C,
+ size_t Idx,
+ StringRef Text) {
+ TextComment *TC;
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, TC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualText = TC->getText();
+ if (ActualText != Text)
+ return ::testing::AssertionFailure()
+ << "TextComment has text \"" << ActualText.str() << "\", "
+ "expected \"" << Text.str() << "\"";
+
+ if (!TC->hasTrailingNewline())
+ return ::testing::AssertionFailure()
+ << "TextComment has no trailing newline";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasBlockCommandAt(const Comment *C,
+ size_t Idx,
+ BlockCommandComment *&BCC,
+ StringRef Name,
+ ParagraphComment *&Paragraph) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, BCC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = BCC->getCommandName();
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "BlockCommandComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ Paragraph = BCC->getParagraph();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasParamCommandAt(
+ const Comment *C,
+ size_t Idx,
+ ParamCommandComment *&PCC,
+ StringRef CommandName,
+ ParamCommandComment::PassDirection Direction,
+ bool IsDirectionExplicit,
+ StringRef ParamName,
+ ParagraphComment *&Paragraph) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, PCC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualCommandName = PCC->getCommandName();
+ if (ActualCommandName != CommandName)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has name \"" << ActualCommandName.str() << "\", "
+ "expected \"" << CommandName.str() << "\"";
+
+ if (PCC->getDirection() != Direction)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has direction " << PCC->getDirection() << ", "
+ "expected " << Direction;
+
+ if (PCC->isDirectionExplicit() != IsDirectionExplicit)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has "
+ << (PCC->isDirectionExplicit() ? "explicit" : "implicit")
+ << " direction, "
+ "expected " << (IsDirectionExplicit ? "explicit" : "implicit");
+
+ if (!ParamName.empty() && !PCC->hasParamName())
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has no parameter name";
+
+ StringRef ActualParamName = PCC->hasParamName() ? PCC->getParamName() : "";
+ if (ActualParamName != ParamName)
+ return ::testing::AssertionFailure()
+ << "ParamCommandComment has parameter name \"" << ActualParamName.str()
+ << "\", "
+ "expected \"" << ParamName.str() << "\"";
+
+ Paragraph = PCC->getParagraph();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasTParamCommandAt(
+ const Comment *C,
+ size_t Idx,
+ TParamCommandComment *&TPCC,
+ StringRef CommandName,
+ StringRef ParamName,
+ ParagraphComment *&Paragraph) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, TPCC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualCommandName = TPCC->getCommandName();
+ if (ActualCommandName != CommandName)
+ return ::testing::AssertionFailure()
+ << "TParamCommandComment has name \"" << ActualCommandName.str() << "\", "
+ "expected \"" << CommandName.str() << "\"";
+
+ if (!ParamName.empty() && !TPCC->hasParamName())
+ return ::testing::AssertionFailure()
+ << "TParamCommandComment has no parameter name";
+
+ StringRef ActualParamName = TPCC->hasParamName() ? TPCC->getParamName() : "";
+ if (ActualParamName != ParamName)
+ return ::testing::AssertionFailure()
+ << "TParamCommandComment has parameter name \"" << ActualParamName.str()
+ << "\", "
+ "expected \"" << ParamName.str() << "\"";
+
+ Paragraph = TPCC->getParagraph();
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasInlineCommandAt(const Comment *C,
+ size_t Idx,
+ InlineCommandComment *&ICC,
+ StringRef Name) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, ICC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = ICC->getCommandName();
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+struct NoArgs {};
+
+::testing::AssertionResult HasInlineCommandAt(const Comment *C,
+ size_t Idx,
+ InlineCommandComment *&ICC,
+ StringRef Name,
+ NoArgs) {
+ ::testing::AssertionResult AR = HasInlineCommandAt(C, Idx, ICC, Name);
+ if (!AR)
+ return AR;
+
+ if (ICC->getNumArgs() != 0)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
+ "expected 0";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasInlineCommandAt(const Comment *C,
+ size_t Idx,
+ InlineCommandComment *&ICC,
+ StringRef Name,
+ StringRef Arg) {
+ ::testing::AssertionResult AR = HasInlineCommandAt(C, Idx, ICC, Name);
+ if (!AR)
+ return AR;
+
+ if (ICC->getNumArgs() != 1)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has " << ICC->getNumArgs() << " arg(s), "
+ "expected 1";
+
+ StringRef ActualArg = ICC->getArgText(0);
+ if (ActualArg != Arg)
+ return ::testing::AssertionFailure()
+ << "InlineCommandComment has argument \"" << ActualArg.str() << "\", "
+ "expected \"" << Arg.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, HST);
+ if (!AR)
+ return AR;
+
+ StringRef ActualTagName = HST->getTagName();
+ if (ActualTagName != TagName)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has name \"" << ActualTagName.str() << "\", "
+ "expected \"" << TagName.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+struct SelfClosing {};
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName,
+ SelfClosing) {
+ ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
+ if (!AR)
+ return AR;
+
+ if (!HST->isSelfClosing())
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment is not self-closing";
+
+ return ::testing::AssertionSuccess();
+}
+
+
+struct NoAttrs {};
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName,
+ NoAttrs) {
+ ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
+ if (!AR)
+ return AR;
+
+ if (HST->isSelfClosing())
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment is self-closing";
+
+ if (HST->getNumAttrs() != 0)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
+ "expected 0";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasHTMLStartTagAt(const Comment *C,
+ size_t Idx,
+ HTMLStartTagComment *&HST,
+ StringRef TagName,
+ StringRef AttrName,
+ StringRef AttrValue) {
+ ::testing::AssertionResult AR = HasHTMLStartTagAt(C, Idx, HST, TagName);
+ if (!AR)
+ return AR;
+
+ if (HST->isSelfClosing())
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment is self-closing";
+
+ if (HST->getNumAttrs() != 1)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has " << HST->getNumAttrs() << " attr(s), "
+ "expected 1";
+
+ StringRef ActualName = HST->getAttr(0).Name;
+ if (ActualName != AttrName)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has attr \"" << ActualName.str() << "\", "
+ "expected \"" << AttrName.str() << "\"";
+
+ StringRef ActualValue = HST->getAttr(0).Value;
+ if (ActualValue != AttrValue)
+ return ::testing::AssertionFailure()
+ << "HTMLStartTagComment has attr value \"" << ActualValue.str() << "\", "
+ "expected \"" << AttrValue.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasHTMLEndTagAt(const Comment *C,
+ size_t Idx,
+ HTMLEndTagComment *&HET,
+ StringRef TagName) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, HET);
+ if (!AR)
+ return AR;
+
+ StringRef ActualTagName = HET->getTagName();
+ if (ActualTagName != TagName)
+ return ::testing::AssertionFailure()
+ << "HTMLEndTagComment has name \"" << ActualTagName.str() << "\", "
+ "expected \"" << TagName.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasParagraphCommentAt(const Comment *C,
+ size_t Idx,
+ StringRef Text) {
+ ParagraphComment *PC;
+
+ {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, PC);
+ if (!AR)
+ return AR;
+ }
+
+ {
+ ::testing::AssertionResult AR = HasChildCount(PC, 1);
+ if (!AR)
+ return AR;
+ }
+
+ {
+ ::testing::AssertionResult AR = HasTextAt(PC, 0, Text);
+ if (!AR)
+ return AR;
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, VBC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = VBC->getCommandName();
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ StringRef ActualCloseName = VBC->getCloseName();
+ if (ActualCloseName != CloseName)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has closing command name \""
+ << ActualCloseName.str() << "\", "
+ "expected \"" << CloseName.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+struct NoLines {};
+struct Lines {};
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName,
+ NoLines) {
+ ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name,
+ CloseName);
+ if (!AR)
+ return AR;
+
+ if (VBC->getNumLines() != 0)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
+ "expected 0";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName,
+ Lines,
+ StringRef Line0) {
+ ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name,
+ CloseName);
+ if (!AR)
+ return AR;
+
+ if (VBC->getNumLines() != 1)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
+ "expected 1";
+
+ StringRef ActualLine0 = VBC->getText(0);
+ if (ActualLine0 != Line0)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
+ "expected \"" << Line0.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimBlockAt(const Comment *C,
+ size_t Idx,
+ VerbatimBlockComment *&VBC,
+ StringRef Name,
+ StringRef CloseName,
+ Lines,
+ StringRef Line0,
+ StringRef Line1) {
+ ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name,
+ CloseName);
+ if (!AR)
+ return AR;
+
+ if (VBC->getNumLines() != 2)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has " << VBC->getNumLines() << " lines(s), "
+ "expected 2";
+
+ StringRef ActualLine0 = VBC->getText(0);
+ if (ActualLine0 != Line0)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has lines[0] \"" << ActualLine0.str() << "\", "
+ "expected \"" << Line0.str() << "\"";
+
+ StringRef ActualLine1 = VBC->getText(1);
+ if (ActualLine1 != Line1)
+ return ::testing::AssertionFailure()
+ << "VerbatimBlockComment has lines[1] \"" << ActualLine1.str() << "\", "
+ "expected \"" << Line1.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult HasVerbatimLineAt(const Comment *C,
+ size_t Idx,
+ VerbatimLineComment *&VLC,
+ StringRef Name,
+ StringRef Text) {
+ ::testing::AssertionResult AR = GetChildAt(C, Idx, VLC);
+ if (!AR)
+ return AR;
+
+ StringRef ActualName = VLC->getCommandName();
+ if (ActualName != Name)
+ return ::testing::AssertionFailure()
+ << "VerbatimLineComment has name \"" << ActualName.str() << "\", "
+ "expected \"" << Name.str() << "\"";
+
+ StringRef ActualText = VLC->getText();
+ if (ActualText != Text)
+ return ::testing::AssertionFailure()
+ << "VerbatimLineComment has text \"" << ActualText.str() << "\", "
+ "expected \"" << Text.str() << "\"";
+
+ return ::testing::AssertionSuccess();
+}
+
+
+TEST_F(CommentParserTest, Basic1) {
+ const char *Source = "//";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 0));
+}
+
+TEST_F(CommentParserTest, Basic2) {
+ const char *Source = "// Meow";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Meow"));
+}
+
+TEST_F(CommentParserTest, Basic3) {
+ const char *Source =
+ "// Aaa\n"
+ "// Bbb";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
+ ASSERT_TRUE(HasTextAt(PC, 1, " Bbb"));
+ }
+}
+
+TEST_F(CommentParserTest, Paragraph1) {
+ const char *Sources[] = {
+ "// Aaa\n"
+ "//\n"
+ "// Bbb",
+
+ "// Aaa\n"
+ "//\n"
+ "//\n"
+ "// Bbb",
+ };
+
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " Aaa"));
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 1, " Bbb"));
+ }
+}
+
+TEST_F(CommentParserTest, Paragraph2) {
+ const char *Source =
+ "// \\brief Aaa\n"
+ "//\n"
+ "// Bbb";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC));
+
+ ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Aaa"));
+ }
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 2, " Bbb"));
+}
+
+TEST_F(CommentParserTest, Paragraph3) {
+ const char *Source = "// \\brief \\author";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC));
+
+ ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " "));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "author", PC));
+
+ ASSERT_TRUE(GetChildAt(BCC, 0, PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+TEST_F(CommentParserTest, Paragraph4) {
+ const char *Source =
+ "// \\brief Aaa\n"
+ "// Bbb \\author\n"
+ "// Ccc";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC));
+
+ ASSERT_TRUE(GetChildAt(BCC, 0, PC));
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 0, " Aaa"));
+ ASSERT_TRUE(HasTextAt(PC, 1, " Bbb "));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "author", PC));
+
+ ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Ccc"));
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand1) {
+ const char *Source = "// \\param aaa";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand2) {
+ const char *Source = "// \\param\\brief";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "brief", PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand3) {
+ const char *Sources[] = {
+ "// \\param aaa Bbb\n",
+ "// \\param\n"
+ "// aaa Bbb\n",
+ "// \\param \n"
+ "// aaa Bbb\n",
+ "// \\param aaa\n"
+ "// Bbb\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand4) {
+ const char *Sources[] = {
+ "// \\param [in] aaa Bbb\n",
+ "// \\param[in] aaa Bbb\n",
+ "// \\param\n"
+ "// [in] aaa Bbb\n",
+ "// \\param [in]\n"
+ "// aaa Bbb\n",
+ "// \\param [in] aaa\n"
+ "// Bbb\n",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ true,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand5) {
+ const char *Sources[] = {
+ "// \\param [out] aaa Bbb\n",
+ "// \\param[out] aaa Bbb\n",
+ "// \\param\n"
+ "// [out] aaa Bbb\n",
+ "// \\param [out]\n"
+ "// aaa Bbb\n",
+ "// \\param [out] aaa\n"
+ "// Bbb\n",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::Out,
+ /* IsDirectionExplicit = */ true,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand6) {
+ const char *Sources[] = {
+ "// \\param [in,out] aaa Bbb\n",
+ "// \\param[in,out] aaa Bbb\n",
+ "// \\param [in, out] aaa Bbb\n",
+ "// \\param [in,\n"
+ "// out] aaa Bbb\n",
+ "// \\param [in,out]\n"
+ "// aaa Bbb\n",
+ "// \\param [in,out] aaa\n"
+ "// Bbb\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::InOut,
+ /* IsDirectionExplicit = */ true,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(PCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, ParamCommand7) {
+ const char *Source =
+ "// \\param aaa \\% Bbb \\$ ccc\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ ParamCommandComment *PCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param",
+ ParamCommandComment::In,
+ /* IsDirectionExplicit = */ false,
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(PCC, 1));
+
+ ASSERT_TRUE(HasChildCount(PC, 5));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasTextAt(PC, 1, "%"));
+ ASSERT_TRUE(HasTextAt(PC, 2, " Bbb "));
+ ASSERT_TRUE(HasTextAt(PC, 3, "$"));
+ ASSERT_TRUE(HasTextAt(PC, 4, " ccc"));
+ }
+}
+
+TEST_F(CommentParserTest, TParamCommand1) {
+ const char *Sources[] = {
+ "// \\tparam aaa Bbb\n",
+ "// \\tparam\n"
+ "// aaa Bbb\n",
+ "// \\tparam \n"
+ "// aaa Bbb\n",
+ "// \\tparam aaa\n"
+ "// Bbb\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ TParamCommandComment *TPCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasTParamCommandAt(FC, 1, TPCC, "tparam",
+ "aaa", PC));
+ ASSERT_TRUE(HasChildCount(TPCC, 1));
+ ASSERT_TRUE(HasParagraphCommentAt(TPCC, 0, " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, TParamCommand2) {
+ const char *Source = "// \\tparam\\brief";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 3));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ TParamCommandComment *TPCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasTParamCommandAt(FC, 1, TPCC, "tparam", "", PC));
+ ASSERT_TRUE(HasChildCount(TPCC, 1));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+ {
+ BlockCommandComment *BCC;
+ ParagraphComment *PC;
+ ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "brief", PC));
+ ASSERT_TRUE(HasChildCount(PC, 0));
+ }
+}
+
+
+TEST_F(CommentParserTest, InlineCommand1) {
+ const char *Source = "// \\c";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", NoArgs()));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand2) {
+ const char *Source = "// \\c ";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 3));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", NoArgs()));
+ ASSERT_TRUE(HasTextAt(PC, 2, " "));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand3) {
+ const char *Source = "// \\c aaa\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", "aaa"));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand4) {
+ const char *Source = "// \\c aaa bbb";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 3));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", "aaa"));
+ ASSERT_TRUE(HasTextAt(PC, 2, " bbb"));
+ }
+}
+
+TEST_F(CommentParserTest, InlineCommand5) {
+ const char *Source = "// \\unknown aaa\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ InlineCommandComment *ICC;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 3));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "unknown", NoArgs()));
+ ASSERT_TRUE(HasTextAt(PC, 2, " aaa"));
+ }
+}
+
+TEST_F(CommentParserTest, HTML1) {
+ const char *Sources[] = {
+ "// <a",
+ "// <a>",
+ "// <a >"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", NoAttrs()));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML2) {
+ const char *Sources[] = {
+ "// <br/>",
+ "// <br />"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "br", SelfClosing()));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML3) {
+ const char *Sources[] = {
+ "// <a href",
+ "// <a href ",
+ "// <a href>",
+ "// <a href >",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", ""));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML4) {
+ const char *Sources[] = {
+ "// <a href=\"bbb\"",
+ "// <a href=\"bbb\">",
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "a", "href", "bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML5) {
+ const char *Sources[] = {
+ "// </a",
+ "// </a>",
+ "// </a >"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLEndTagComment *HET;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 2));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLEndTagAt(PC, 1, HET, "a"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, HTML6) {
+ const char *Source =
+ "// <pre>\n"
+ "// Aaa\n"
+ "// Bbb\n"
+ "// </pre>\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ ParagraphComment *PC;
+ HTMLStartTagComment *HST;
+ HTMLEndTagComment *HET;
+ ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+ ASSERT_TRUE(HasChildCount(PC, 6));
+ ASSERT_TRUE(HasTextAt(PC, 0, " "));
+ ASSERT_TRUE(HasHTMLStartTagAt(PC, 1, HST, "pre", NoAttrs()));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 2, " Aaa"));
+ ASSERT_TRUE(HasTextWithNewlineAt(PC, 3, " Bbb"));
+ ASSERT_TRUE(HasTextAt(PC, 4, " "));
+ ASSERT_TRUE(HasHTMLEndTagAt(PC, 5, HET, "pre"));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock1) {
+ const char *Source = "// \\verbatim\\endverbatim\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VCC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VCC, "verbatim", "endverbatim",
+ NoLines()));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock2) {
+ const char *Source = "// \\verbatim Aaa \\endverbatim\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim",
+ Lines(), " Aaa "));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock3) {
+ const char *Source = "// \\verbatim Aaa\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "",
+ Lines(), " Aaa"));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock4) {
+ const char *Source =
+ "//\\verbatim\n"
+ "//\\endverbatim\n";
+
+ FullComment *FC = parseString(Source);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 0, VBC, "verbatim", "endverbatim",
+ NoLines()));
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock5) {
+ const char *Sources[] = {
+ "//\\verbatim\n"
+ "// Aaa\n"
+ "//\\endverbatim\n",
+
+ "/*\\verbatim\n"
+ " * Aaa\n"
+ " *\\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 1));
+
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 0, VBC, "verbatim", "endverbatim",
+ Lines(), " Aaa"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock6) {
+ const char *Sources[] = {
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "// \\endverbatim\n",
+
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " * \\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim",
+ Lines(), " Aaa"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock7) {
+ const char *Sources[] = {
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "// Bbb\n"
+ "// \\endverbatim\n",
+
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " * Bbb\n"
+ " * \\endverbatim*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim",
+ Lines(), " Aaa", " Bbb"));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimBlock8) {
+ const char *Sources[] = {
+ "// \\verbatim\n"
+ "// Aaa\n"
+ "//\n"
+ "// Bbb\n"
+ "// \\endverbatim\n",
+
+ "/* \\verbatim\n"
+ " * Aaa\n"
+ " *\n"
+ " * Bbb\n"
+ " * \\endverbatim*/"
+ };
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimBlockComment *VBC;
+ ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim"));
+ ASSERT_EQ(3U, VBC->getNumLines());
+ ASSERT_EQ(" Aaa", VBC->getText(0));
+ ASSERT_EQ("", VBC->getText(1));
+ ASSERT_EQ(" Bbb", VBC->getText(2));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimLine1) {
+ const char *Sources[] = {
+ "// \\fn",
+ "// \\fn\n"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimLineComment *VLC;
+ ASSERT_TRUE(HasVerbatimLineAt(FC, 1, VLC, "fn", ""));
+ }
+ }
+}
+
+TEST_F(CommentParserTest, VerbatimLine2) {
+ const char *Sources[] = {
+ "/// \\fn void *foo(const char *zzz = \"\\$\");\n//",
+ "/** \\fn void *foo(const char *zzz = \"\\$\");*/"
+ };
+
+ for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+ FullComment *FC = parseString(Sources[i]);
+ ASSERT_TRUE(HasChildCount(FC, 2));
+
+ ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " "));
+ {
+ VerbatimLineComment *VLC;
+ ASSERT_TRUE(HasVerbatimLineAt(FC, 1, VLC, "fn",
+ " void *foo(const char *zzz = \"\\$\");"));
+ }
+ }
+}
+
+} // unnamed namespace
+
+} // end namespace comments
+} // end namespace clang
+
diff --git a/unittests/AST/Makefile b/unittests/AST/Makefile
new file mode 100644
index 0000000..31cd5de
--- /dev/null
+++ b/unittests/AST/Makefile
@@ -0,0 +1,15 @@
+##===- unittests/AST/Makefile ------------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL = ../..
+TESTNAME = AST
+LINK_COMPONENTS := support mc
+USEDLIBS = clangAST.a clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/unittests/Makefile
OpenPOWER on IntegriCloud