summaryrefslogtreecommitdiffstats
path: root/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'unittests')
-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
-rw-r--r--unittests/ASTMatchers/ASTMatchersTest.cpp2312
-rw-r--r--unittests/ASTMatchers/ASTMatchersTest.h128
-rw-r--r--unittests/ASTMatchers/CMakeLists.txt12
-rw-r--r--unittests/ASTMatchers/Makefile19
-rw-r--r--unittests/Basic/CMakeLists.txt9
-rw-r--r--unittests/Basic/SourceManagerTest.cpp48
-rw-r--r--unittests/CMakeLists.txt80
-rw-r--r--unittests/Frontend/CMakeLists.txt13
-rw-r--r--unittests/Frontend/FrontendActionTest.cpp1
-rw-r--r--unittests/Frontend/Makefile3
-rw-r--r--unittests/Lex/CMakeLists.txt8
-rw-r--r--unittests/Makefile2
-rw-r--r--unittests/Tooling/CMakeLists.txt22
-rw-r--r--unittests/Tooling/CommentHandlerTest.cpp221
-rw-r--r--unittests/Tooling/CompilationDatabaseTest.cpp58
-rw-r--r--unittests/Tooling/Makefile7
-rw-r--r--unittests/Tooling/RecursiveASTVisitorTest.cpp388
-rw-r--r--unittests/Tooling/RefactoringCallbacksTest.cpp100
-rw-r--r--unittests/Tooling/RefactoringTest.cpp305
-rw-r--r--unittests/Tooling/RewriterTest.cpp37
-rw-r--r--unittests/Tooling/RewriterTestContext.h125
-rw-r--r--unittests/Tooling/TestVisitor.h144
-rw-r--r--unittests/Tooling/ToolingTest.cpp25
27 files changed, 7034 insertions, 75 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
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp
new file mode 100644
index 0000000..cf37c7d
--- /dev/null
+++ b/unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -0,0 +1,2312 @@
+//===- unittest/Tooling/ASTMatchersTest.cpp - AST matcher unit tests ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTMatchersTest.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+
+#if GTEST_HAS_DEATH_TEST
+TEST(HasNameDeathTest, DiesOnEmptyName) {
+ ASSERT_DEBUG_DEATH({
+ DeclarationMatcher HasEmptyName = record(hasName(""));
+ EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
+ }, "");
+}
+
+TEST(HasNameDeathTest, DiesOnEmptyPattern) {
+ ASSERT_DEBUG_DEATH({
+ DeclarationMatcher HasEmptyName = record(matchesName(""));
+ EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
+ }, "");
+}
+
+TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) {
+ ASSERT_DEBUG_DEATH({
+ DeclarationMatcher IsDerivedFromEmpty = record(isDerivedFrom(""));
+ EXPECT_TRUE(notMatches("class X {};", IsDerivedFromEmpty));
+ }, "");
+}
+#endif
+
+TEST(Decl, MatchesDeclarations) {
+ EXPECT_TRUE(notMatches("", decl(usingDecl())));
+ EXPECT_TRUE(matches("namespace x { class X {}; } using x::X;",
+ decl(usingDecl())));
+}
+
+TEST(NameableDeclaration, MatchesVariousDecls) {
+ DeclarationMatcher NamedX = nameableDeclaration(hasName("X"));
+ EXPECT_TRUE(matches("typedef int X;", NamedX));
+ EXPECT_TRUE(matches("int X;", NamedX));
+ EXPECT_TRUE(matches("class foo { virtual void X(); };", NamedX));
+ EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", NamedX));
+ EXPECT_TRUE(matches("void foo() { int X; }", NamedX));
+ EXPECT_TRUE(matches("namespace X { }", NamedX));
+ EXPECT_TRUE(matches("enum X { A, B, C };", NamedX));
+
+ EXPECT_TRUE(notMatches("#define X 1", NamedX));
+}
+
+TEST(NameableDeclaration, REMatchesVariousDecls) {
+ DeclarationMatcher NamedX = nameableDeclaration(matchesName("::X"));
+ EXPECT_TRUE(matches("typedef int Xa;", NamedX));
+ EXPECT_TRUE(matches("int Xb;", NamedX));
+ EXPECT_TRUE(matches("class foo { virtual void Xc(); };", NamedX));
+ EXPECT_TRUE(matches("void foo() try { } catch(int Xdef) { }", NamedX));
+ EXPECT_TRUE(matches("void foo() { int Xgh; }", NamedX));
+ EXPECT_TRUE(matches("namespace Xij { }", NamedX));
+ EXPECT_TRUE(matches("enum X { A, B, C };", NamedX));
+
+ EXPECT_TRUE(notMatches("#define Xkl 1", NamedX));
+
+ DeclarationMatcher StartsWithNo = nameableDeclaration(matchesName("::no"));
+ EXPECT_TRUE(matches("int no_foo;", StartsWithNo));
+ EXPECT_TRUE(matches("class foo { virtual void nobody(); };", StartsWithNo));
+
+ DeclarationMatcher Abc = nameableDeclaration(matchesName("a.*b.*c"));
+ EXPECT_TRUE(matches("int abc;", Abc));
+ EXPECT_TRUE(matches("int aFOObBARc;", Abc));
+ EXPECT_TRUE(notMatches("int cab;", Abc));
+ EXPECT_TRUE(matches("int cabc;", Abc));
+}
+
+TEST(DeclarationMatcher, MatchClass) {
+ DeclarationMatcher ClassMatcher(record());
+#if !defined(_MSC_VER)
+ EXPECT_FALSE(matches("", ClassMatcher));
+#else
+ // Matches class type_info.
+ EXPECT_TRUE(matches("", ClassMatcher));
+#endif
+
+ DeclarationMatcher ClassX = record(record(hasName("X")));
+ EXPECT_TRUE(matches("class X;", ClassX));
+ EXPECT_TRUE(matches("class X {};", ClassX));
+ EXPECT_TRUE(matches("template<class T> class X {};", ClassX));
+ EXPECT_TRUE(notMatches("", ClassX));
+}
+
+TEST(DeclarationMatcher, ClassIsDerived) {
+ DeclarationMatcher IsDerivedFromX = record(isDerivedFrom("X"));
+
+ EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX));
+ EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX));
+ EXPECT_TRUE(matches("class X {};", IsDerivedFromX));
+ EXPECT_TRUE(matches("class X;", IsDerivedFromX));
+ EXPECT_TRUE(notMatches("class Y;", IsDerivedFromX));
+ EXPECT_TRUE(notMatches("", IsDerivedFromX));
+
+ DeclarationMatcher ZIsDerivedFromX =
+ record(hasName("Z"), isDerivedFrom("X"));
+ EXPECT_TRUE(
+ matches("class X {}; class Y : public X {}; class Z : public Y {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {};"
+ "template<class T> class Y : public X {};"
+ "class Z : public Y<int> {};", ZIsDerivedFromX));
+ EXPECT_TRUE(matches("class X {}; template<class T> class Z : public X {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T> class X {}; "
+ "template<class T> class Z : public X<T> {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T, class U=T> class X {}; "
+ "template<class T> class Z : public X<T> {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class X> class A { class Z : public X {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class X> class A { public: class Z : public X {}; }; "
+ "class X{}; void y() { A<X>::Z z; }", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template <class T> class X {}; "
+ "template<class Y> class A { class Z : public X<Y> {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<template<class T> class X> class A { "
+ " class Z : public X<int> {}; };", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<template<class T> class X> class A { "
+ " public: class Z : public X<int> {}; }; "
+ "template<class T> class X {}; void y() { A<X>::Z z; }",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class X> class A { class Z : public X::D {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class X> class A { public: "
+ " class Z : public X::D {}; }; "
+ "class Y { public: class X {}; typedef X D; }; "
+ "void y() { A<Y>::Z z; }", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; typedef X Y; class Z : public Y {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T> class Y { typedef typename T::U X; "
+ " class Z : public X {}; };", ZIsDerivedFromX));
+ EXPECT_TRUE(matches("class X {}; class Z : public ::X {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class T> class X {}; "
+ "template<class T> class A { class Z : public X<T>::D {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T> class X { public: typedef X<T> D; }; "
+ "template<class T> class A { public: "
+ " class Z : public X<T>::D {}; }; void y() { A<int>::Z z; }",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class X> class A { class Z : public X::D::E {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; typedef X V; typedef V W; class Z : public W {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; class Y : public X {}; "
+ "typedef Y V; typedef V W; class Z : public W {};",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template<class T, class U> class X {}; "
+ "template<class T> class A { class Z : public X<T, int> {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template<class X> class D { typedef X A; typedef A B; "
+ " typedef B C; class Z : public C {}; };",
+ ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; typedef X A; typedef A B; "
+ "class Z : public B {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class X {}; typedef X A; typedef A B; typedef B C; "
+ "class Z : public C {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class U {}; typedef U X; typedef X V; "
+ "class Z : public V {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class Base {}; typedef Base X; "
+ "class Z : public Base {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class Base {}; typedef Base Base2; typedef Base2 X; "
+ "class Z : public Base {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("class Base {}; class Base2 {}; typedef Base2 X; "
+ "class Z : public Base {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ matches("class A {}; typedef A X; typedef A Y; "
+ "class Z : public Y {};", ZIsDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("template <typename T> class Z;"
+ "template <> class Z<void> {};"
+ "template <typename T> class Z : public Z<void> {};",
+ IsDerivedFromX));
+ EXPECT_TRUE(
+ matches("template <typename T> class X;"
+ "template <> class X<void> {};"
+ "template <typename T> class X : public X<void> {};",
+ IsDerivedFromX));
+ EXPECT_TRUE(matches(
+ "class X {};"
+ "template <typename T> class Z;"
+ "template <> class Z<void> {};"
+ "template <typename T> class Z : public Z<void>, public X {};",
+ ZIsDerivedFromX));
+
+ // FIXME: Once we have better matchers for template type matching,
+ // get rid of the Variable(...) matching and match the right template
+ // declarations directly.
+ const char *RecursiveTemplateOneParameter =
+ "class Base1 {}; class Base2 {};"
+ "template <typename T> class Z;"
+ "template <> class Z<void> : public Base1 {};"
+ "template <> class Z<int> : public Base2 {};"
+ "template <> class Z<float> : public Z<void> {};"
+ "template <> class Z<double> : public Z<int> {};"
+ "template <typename T> class Z : public Z<float>, public Z<double> {};"
+ "void f() { Z<float> z_float; Z<double> z_double; Z<char> z_char; }";
+ EXPECT_TRUE(matches(
+ RecursiveTemplateOneParameter,
+ variable(hasName("z_float"),
+ hasInitializer(hasType(record(isDerivedFrom("Base1")))))));
+ EXPECT_TRUE(notMatches(
+ RecursiveTemplateOneParameter,
+ variable(
+ hasName("z_float"),
+ hasInitializer(hasType(record(isDerivedFrom("Base2")))))));
+ EXPECT_TRUE(matches(
+ RecursiveTemplateOneParameter,
+ variable(
+ hasName("z_char"),
+ hasInitializer(hasType(record(isDerivedFrom("Base1"),
+ isDerivedFrom("Base2")))))));
+
+ const char *RecursiveTemplateTwoParameters =
+ "class Base1 {}; class Base2 {};"
+ "template <typename T1, typename T2> class Z;"
+ "template <typename T> class Z<void, T> : public Base1 {};"
+ "template <typename T> class Z<int, T> : public Base2 {};"
+ "template <typename T> class Z<float, T> : public Z<void, T> {};"
+ "template <typename T> class Z<double, T> : public Z<int, T> {};"
+ "template <typename T1, typename T2> class Z : "
+ " public Z<float, T2>, public Z<double, T2> {};"
+ "void f() { Z<float, void> z_float; Z<double, void> z_double; "
+ " Z<char, void> z_char; }";
+ EXPECT_TRUE(matches(
+ RecursiveTemplateTwoParameters,
+ variable(
+ hasName("z_float"),
+ hasInitializer(hasType(record(isDerivedFrom("Base1")))))));
+ EXPECT_TRUE(notMatches(
+ RecursiveTemplateTwoParameters,
+ variable(
+ hasName("z_float"),
+ hasInitializer(hasType(record(isDerivedFrom("Base2")))))));
+ EXPECT_TRUE(matches(
+ RecursiveTemplateTwoParameters,
+ variable(
+ hasName("z_char"),
+ hasInitializer(hasType(record(isDerivedFrom("Base1"),
+ isDerivedFrom("Base2")))))));
+ EXPECT_TRUE(matches(
+ "namespace ns { class X {}; class Y : public X {}; }",
+ record(isDerivedFrom("::ns::X"))));
+ EXPECT_TRUE(notMatches(
+ "class X {}; class Y : public X {};",
+ record(isDerivedFrom("::ns::X"))));
+
+ EXPECT_TRUE(matches(
+ "class X {}; class Y : public X {};",
+ record(isDerivedFrom(record(hasName("X")).bind("test")))));
+}
+
+TEST(AllOf, AllOverloadsWork) {
+ const char Program[] =
+ "struct T { }; int f(int, T*); void g(int x) { T t; f(x, &t); }";
+ EXPECT_TRUE(matches(Program,
+ call(allOf(callee(function(hasName("f"))),
+ hasArgument(0, declarationReference(to(variable())))))));
+ EXPECT_TRUE(matches(Program,
+ call(allOf(callee(function(hasName("f"))),
+ hasArgument(0, declarationReference(to(variable()))),
+ hasArgument(1, hasType(pointsTo(record(hasName("T")))))))));
+}
+
+TEST(DeclarationMatcher, MatchAnyOf) {
+ DeclarationMatcher YOrZDerivedFromX =
+ record(anyOf(hasName("Y"), allOf(isDerivedFrom("X"), hasName("Z"))));
+ EXPECT_TRUE(
+ matches("class X {}; class Z : public X {};", YOrZDerivedFromX));
+ EXPECT_TRUE(matches("class Y {};", YOrZDerivedFromX));
+ EXPECT_TRUE(
+ notMatches("class X {}; class W : public X {};", YOrZDerivedFromX));
+ EXPECT_TRUE(notMatches("class Z {};", YOrZDerivedFromX));
+
+ DeclarationMatcher XOrYOrZOrU =
+ record(anyOf(hasName("X"), hasName("Y"), hasName("Z"), hasName("U")));
+ EXPECT_TRUE(matches("class X {};", XOrYOrZOrU));
+ EXPECT_TRUE(notMatches("class V {};", XOrYOrZOrU));
+
+ DeclarationMatcher XOrYOrZOrUOrV =
+ record(anyOf(hasName("X"), hasName("Y"), hasName("Z"), hasName("U"),
+ hasName("V")));
+ EXPECT_TRUE(matches("class X {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(matches("class Y {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(matches("class Z {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(matches("class U {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(matches("class V {};", XOrYOrZOrUOrV));
+ EXPECT_TRUE(notMatches("class A {};", XOrYOrZOrUOrV));
+}
+
+TEST(DeclarationMatcher, MatchHas) {
+ DeclarationMatcher HasClassX = record(has(record(hasName("X"))));
+
+ EXPECT_TRUE(matches("class Y { class X {}; };", HasClassX));
+ EXPECT_TRUE(matches("class X {};", HasClassX));
+
+ DeclarationMatcher YHasClassX =
+ record(hasName("Y"), has(record(hasName("X"))));
+ EXPECT_TRUE(matches("class Y { class X {}; };", YHasClassX));
+ EXPECT_TRUE(notMatches("class X {};", YHasClassX));
+ EXPECT_TRUE(
+ notMatches("class Y { class Z { class X {}; }; };", YHasClassX));
+}
+
+TEST(DeclarationMatcher, MatchHasRecursiveAllOf) {
+ DeclarationMatcher Recursive =
+ record(
+ has(record(
+ has(record(hasName("X"))),
+ has(record(hasName("Y"))),
+ hasName("Z"))),
+ has(record(
+ has(record(hasName("A"))),
+ has(record(hasName("B"))),
+ hasName("C"))),
+ hasName("F"));
+
+ EXPECT_TRUE(matches(
+ "class F {"
+ " class Z {"
+ " class X {};"
+ " class Y {};"
+ " };"
+ " class C {"
+ " class A {};"
+ " class B {};"
+ " };"
+ "};", Recursive));
+
+ EXPECT_TRUE(matches(
+ "class F {"
+ " class Z {"
+ " class A {};"
+ " class X {};"
+ " class Y {};"
+ " };"
+ " class C {"
+ " class X {};"
+ " class A {};"
+ " class B {};"
+ " };"
+ "};", Recursive));
+
+ EXPECT_TRUE(matches(
+ "class O1 {"
+ " class O2 {"
+ " class F {"
+ " class Z {"
+ " class A {};"
+ " class X {};"
+ " class Y {};"
+ " };"
+ " class C {"
+ " class X {};"
+ " class A {};"
+ " class B {};"
+ " };"
+ " };"
+ " };"
+ "};", Recursive));
+}
+
+TEST(DeclarationMatcher, MatchHasRecursiveAnyOf) {
+ DeclarationMatcher Recursive =
+ record(
+ anyOf(
+ has(record(
+ anyOf(
+ has(record(
+ hasName("X"))),
+ has(record(
+ hasName("Y"))),
+ hasName("Z")))),
+ has(record(
+ anyOf(
+ hasName("C"),
+ has(record(
+ hasName("A"))),
+ has(record(
+ hasName("B")))))),
+ hasName("F")));
+
+ EXPECT_TRUE(matches("class F {};", Recursive));
+ EXPECT_TRUE(matches("class Z {};", Recursive));
+ EXPECT_TRUE(matches("class C {};", Recursive));
+ EXPECT_TRUE(matches("class M { class N { class X {}; }; };", Recursive));
+ EXPECT_TRUE(matches("class M { class N { class B {}; }; };", Recursive));
+ EXPECT_TRUE(
+ matches("class O1 { class O2 {"
+ " class M { class N { class B {}; }; }; "
+ "}; };", Recursive));
+}
+
+TEST(DeclarationMatcher, MatchNot) {
+ DeclarationMatcher NotClassX =
+ record(
+ isDerivedFrom("Y"),
+ unless(hasName("Y")),
+ unless(hasName("X")));
+ EXPECT_TRUE(notMatches("", NotClassX));
+ EXPECT_TRUE(notMatches("class Y {};", NotClassX));
+ EXPECT_TRUE(matches("class Y {}; class Z : public Y {};", NotClassX));
+ EXPECT_TRUE(notMatches("class Y {}; class X : public Y {};", NotClassX));
+ EXPECT_TRUE(
+ notMatches("class Y {}; class Z {}; class X : public Y {};",
+ NotClassX));
+
+ DeclarationMatcher ClassXHasNotClassY =
+ record(
+ hasName("X"),
+ has(record(hasName("Z"))),
+ unless(
+ has(record(hasName("Y")))));
+ EXPECT_TRUE(matches("class X { class Z {}; };", ClassXHasNotClassY));
+ EXPECT_TRUE(notMatches("class X { class Y {}; class Z {}; };",
+ ClassXHasNotClassY));
+}
+
+TEST(DeclarationMatcher, HasDescendant) {
+ DeclarationMatcher ZDescendantClassX =
+ record(
+ hasDescendant(record(hasName("X"))),
+ hasName("Z"));
+ EXPECT_TRUE(matches("class Z { class X {}; };", ZDescendantClassX));
+ EXPECT_TRUE(
+ matches("class Z { class Y { class X {}; }; };", ZDescendantClassX));
+ EXPECT_TRUE(
+ matches("class Z { class A { class Y { class X {}; }; }; };",
+ ZDescendantClassX));
+ EXPECT_TRUE(
+ matches("class Z { class A { class B { class Y { class X {}; }; }; }; };",
+ ZDescendantClassX));
+ EXPECT_TRUE(notMatches("class Z {};", ZDescendantClassX));
+
+ DeclarationMatcher ZDescendantClassXHasClassY =
+ record(
+ hasDescendant(record(has(record(hasName("Y"))),
+ hasName("X"))),
+ hasName("Z"));
+ EXPECT_TRUE(matches("class Z { class X { class Y {}; }; };",
+ ZDescendantClassXHasClassY));
+ EXPECT_TRUE(
+ matches("class Z { class A { class B { class X { class Y {}; }; }; }; };",
+ ZDescendantClassXHasClassY));
+ EXPECT_TRUE(notMatches(
+ "class Z {"
+ " class A {"
+ " class B {"
+ " class X {"
+ " class C {"
+ " class Y {};"
+ " };"
+ " };"
+ " }; "
+ " };"
+ "};", ZDescendantClassXHasClassY));
+
+ DeclarationMatcher ZDescendantClassXDescendantClassY =
+ record(
+ hasDescendant(record(hasDescendant(record(hasName("Y"))),
+ hasName("X"))),
+ hasName("Z"));
+ EXPECT_TRUE(
+ matches("class Z { class A { class X { class B { class Y {}; }; }; }; };",
+ ZDescendantClassXDescendantClassY));
+ EXPECT_TRUE(matches(
+ "class Z {"
+ " class A {"
+ " class X {"
+ " class B {"
+ " class Y {};"
+ " };"
+ " class Y {};"
+ " };"
+ " };"
+ "};", ZDescendantClassXDescendantClassY));
+}
+
+TEST(Enum, DoesNotMatchClasses) {
+ EXPECT_TRUE(notMatches("class X {};", enumDecl(hasName("X"))));
+}
+
+TEST(Enum, MatchesEnums) {
+ EXPECT_TRUE(matches("enum X {};", enumDecl(hasName("X"))));
+}
+
+TEST(EnumConstant, Matches) {
+ DeclarationMatcher Matcher = enumConstant(hasName("A"));
+ EXPECT_TRUE(matches("enum X{ A };", Matcher));
+ EXPECT_TRUE(notMatches("enum X{ B };", Matcher));
+ EXPECT_TRUE(notMatches("enum X {};", Matcher));
+}
+
+TEST(StatementMatcher, Has) {
+ StatementMatcher HasVariableI =
+ expression(
+ hasType(pointsTo(record(hasName("X")))),
+ has(declarationReference(to(variable(hasName("i"))))));
+
+ EXPECT_TRUE(matches(
+ "class X; X *x(int); void c() { int i; x(i); }", HasVariableI));
+ EXPECT_TRUE(notMatches(
+ "class X; X *x(int); void c() { int i; x(42); }", HasVariableI));
+}
+
+TEST(StatementMatcher, HasDescendant) {
+ StatementMatcher HasDescendantVariableI =
+ expression(
+ hasType(pointsTo(record(hasName("X")))),
+ hasDescendant(declarationReference(to(variable(hasName("i"))))));
+
+ EXPECT_TRUE(matches(
+ "class X; X *x(bool); bool b(int); void c() { int i; x(b(i)); }",
+ HasDescendantVariableI));
+ EXPECT_TRUE(notMatches(
+ "class X; X *x(bool); bool b(int); void c() { int i; x(b(42)); }",
+ HasDescendantVariableI));
+}
+
+TEST(TypeMatcher, MatchesClassType) {
+ TypeMatcher TypeA = hasDeclaration(record(hasName("A")));
+
+ EXPECT_TRUE(matches("class A { public: A *a; };", TypeA));
+ EXPECT_TRUE(notMatches("class A {};", TypeA));
+
+ TypeMatcher TypeDerivedFromA = hasDeclaration(record(isDerivedFrom("A")));
+
+ EXPECT_TRUE(matches("class A {}; class B : public A { public: B *b; };",
+ TypeDerivedFromA));
+ EXPECT_TRUE(notMatches("class A {};", TypeA));
+
+ TypeMatcher TypeAHasClassB = hasDeclaration(
+ record(hasName("A"), has(record(hasName("B")))));
+
+ EXPECT_TRUE(
+ matches("class A { public: A *a; class B {}; };", TypeAHasClassB));
+}
+
+// Returns from Run whether 'bound_nodes' contain a Decl bound to 'Id', which
+// can be dynamically casted to T.
+// Optionally checks that the check succeeded a specific number of times.
+template <typename T>
+class VerifyIdIsBoundToDecl : public BoundNodesCallback {
+public:
+ // Create an object that checks that a node of type 'T' was bound to 'Id'.
+ // Does not check for a certain number of matches.
+ explicit VerifyIdIsBoundToDecl(const std::string& Id)
+ : Id(Id), ExpectedCount(-1), Count(0) {}
+
+ // Create an object that checks that a node of type 'T' was bound to 'Id'.
+ // Checks that there were exactly 'ExpectedCount' matches.
+ explicit VerifyIdIsBoundToDecl(const std::string& Id, int ExpectedCount)
+ : Id(Id), ExpectedCount(ExpectedCount), Count(0) {}
+
+ ~VerifyIdIsBoundToDecl() {
+ if (ExpectedCount != -1) {
+ EXPECT_EQ(ExpectedCount, Count);
+ }
+ }
+
+ virtual bool run(const BoundNodes *Nodes) {
+ if (Nodes->getDeclAs<T>(Id) != NULL) {
+ ++Count;
+ return true;
+ }
+ return false;
+ }
+
+private:
+ const std::string Id;
+ const int ExpectedCount;
+ int Count;
+};
+template <typename T>
+class VerifyIdIsBoundToStmt : public BoundNodesCallback {
+public:
+ explicit VerifyIdIsBoundToStmt(const std::string &Id) : Id(Id) {}
+ virtual bool run(const BoundNodes *Nodes) {
+ const T *Node = Nodes->getStmtAs<T>(Id);
+ return Node != NULL;
+ }
+private:
+ const std::string Id;
+};
+
+TEST(Matcher, BindMatchedNodes) {
+ DeclarationMatcher ClassX = has(record(hasName("::X")).bind("x"));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class X {};",
+ ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("x")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class X {};",
+ ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("other-id")));
+
+ TypeMatcher TypeAHasClassB = hasDeclaration(
+ record(hasName("A"), has(record(hasName("B")).bind("b"))));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class A { public: A *a; class B {}; };",
+ TypeAHasClassB,
+ new VerifyIdIsBoundToDecl<Decl>("b")));
+
+ StatementMatcher MethodX = call(callee(method(hasName("x")))).bind("x");
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class A { void x() { x(); } };",
+ MethodX,
+ new VerifyIdIsBoundToStmt<CXXMemberCallExpr>("x")));
+}
+
+TEST(Matcher, BindTheSameNameInAlternatives) {
+ StatementMatcher matcher = anyOf(
+ binaryOperator(hasOperatorName("+"),
+ hasLHS(expression().bind("x")),
+ hasRHS(integerLiteral(equals(0)))),
+ binaryOperator(hasOperatorName("+"),
+ hasLHS(integerLiteral(equals(0))),
+ hasRHS(expression().bind("x"))));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ // The first branch of the matcher binds x to 0 but then fails.
+ // The second branch binds x to f() and succeeds.
+ "int f() { return 0 + f(); }",
+ matcher,
+ new VerifyIdIsBoundToStmt<CallExpr>("x")));
+}
+
+TEST(HasType, TakesQualTypeMatcherAndMatchesExpr) {
+ TypeMatcher ClassX = hasDeclaration(record(hasName("X")));
+ EXPECT_TRUE(
+ matches("class X {}; void y(X &x) { x; }", expression(hasType(ClassX))));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y(X *x) { x; }",
+ expression(hasType(ClassX))));
+ EXPECT_TRUE(
+ matches("class X {}; void y(X *x) { x; }",
+ expression(hasType(pointsTo(ClassX)))));
+}
+
+TEST(HasType, TakesQualTypeMatcherAndMatchesValueDecl) {
+ TypeMatcher ClassX = hasDeclaration(record(hasName("X")));
+ EXPECT_TRUE(
+ matches("class X {}; void y() { X x; }", variable(hasType(ClassX))));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y() { X *x; }", variable(hasType(ClassX))));
+ EXPECT_TRUE(
+ matches("class X {}; void y() { X *x; }",
+ variable(hasType(pointsTo(ClassX)))));
+}
+
+TEST(HasType, TakesDeclMatcherAndMatchesExpr) {
+ DeclarationMatcher ClassX = record(hasName("X"));
+ EXPECT_TRUE(
+ matches("class X {}; void y(X &x) { x; }", expression(hasType(ClassX))));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y(X *x) { x; }",
+ expression(hasType(ClassX))));
+}
+
+TEST(HasType, TakesDeclMatcherAndMatchesValueDecl) {
+ DeclarationMatcher ClassX = record(hasName("X"));
+ EXPECT_TRUE(
+ matches("class X {}; void y() { X x; }", variable(hasType(ClassX))));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y() { X *x; }", variable(hasType(ClassX))));
+}
+
+TEST(Matcher, Call) {
+ // FIXME: Do we want to overload Call() to directly take
+ // Matcher<Decl>, too?
+ StatementMatcher MethodX = call(hasDeclaration(method(hasName("x"))));
+
+ EXPECT_TRUE(matches("class Y { void x() { x(); } };", MethodX));
+ EXPECT_TRUE(notMatches("class Y { void x() {} };", MethodX));
+
+ StatementMatcher MethodOnY = memberCall(on(hasType(record(hasName("Y")))));
+
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() { Y y; y.x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y &y) { y.x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z(Y *&y) { y->x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z(Y y[]) { y->x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z() { Y *y; y->x(); }",
+ MethodOnY));
+
+ StatementMatcher MethodOnYPointer =
+ memberCall(on(hasType(pointsTo(record(hasName("Y"))))));
+
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() { Y *y; y->x(); }",
+ MethodOnYPointer));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y *&y) { y->x(); }",
+ MethodOnYPointer));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y y[]) { y->x(); }",
+ MethodOnYPointer));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z() { Y y; y.x(); }",
+ MethodOnYPointer));
+ EXPECT_TRUE(
+ notMatches("class Y { public: void x(); }; void z(Y &y) { y.x(); }",
+ MethodOnYPointer));
+}
+
+TEST(HasType, MatchesAsString) {
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() {Y* y; y->x(); }",
+ memberCall(on(hasType(asString("class Y *"))))));
+ EXPECT_TRUE(matches("class X { void x(int x) {} };",
+ method(hasParameter(0, hasType(asString("int"))))));
+ EXPECT_TRUE(matches("namespace ns { struct A {}; } struct B { ns::A a; };",
+ field(hasType(asString("ns::A")))));
+ EXPECT_TRUE(matches("namespace { struct A {}; } struct B { A a; };",
+ field(hasType(asString("struct <anonymous>::A")))));
+}
+
+TEST(Matcher, OverloadedOperatorCall) {
+ StatementMatcher OpCall = overloadedOperatorCall();
+ // Unary operator
+ EXPECT_TRUE(matches("class Y { }; "
+ "bool operator!(Y x) { return false; }; "
+ "Y y; bool c = !y;", OpCall));
+ // No match -- special operators like "new", "delete"
+ // FIXME: operator new takes size_t, for which we need stddef.h, for which
+ // we need to figure out include paths in the test.
+ // EXPECT_TRUE(NotMatches("#include <stddef.h>\n"
+ // "class Y { }; "
+ // "void *operator new(size_t size) { return 0; } "
+ // "Y *y = new Y;", OpCall));
+ EXPECT_TRUE(notMatches("class Y { }; "
+ "void operator delete(void *p) { } "
+ "void a() {Y *y = new Y; delete y;}", OpCall));
+ // Binary operator
+ EXPECT_TRUE(matches("class Y { }; "
+ "bool operator&&(Y x, Y y) { return true; }; "
+ "Y a; Y b; bool c = a && b;",
+ OpCall));
+ // No match -- normal operator, not an overloaded one.
+ EXPECT_TRUE(notMatches("bool x = true, y = true; bool t = x && y;", OpCall));
+ EXPECT_TRUE(notMatches("int t = 5 << 2;", OpCall));
+}
+
+TEST(Matcher, HasOperatorNameForOverloadedOperatorCall) {
+ StatementMatcher OpCallAndAnd =
+ overloadedOperatorCall(hasOverloadedOperatorName("&&"));
+ EXPECT_TRUE(matches("class Y { }; "
+ "bool operator&&(Y x, Y y) { return true; }; "
+ "Y a; Y b; bool c = a && b;", OpCallAndAnd));
+ StatementMatcher OpCallLessLess =
+ overloadedOperatorCall(hasOverloadedOperatorName("<<"));
+ EXPECT_TRUE(notMatches("class Y { }; "
+ "bool operator&&(Y x, Y y) { return true; }; "
+ "Y a; Y b; bool c = a && b;",
+ OpCallLessLess));
+}
+
+TEST(Matcher, ThisPointerType) {
+ StatementMatcher MethodOnY =
+ memberCall(thisPointerType(record(hasName("Y"))));
+
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() { Y y; y.x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y &y) { y.x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y *&y) { y->x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z(Y y[]) { y->x(); }",
+ MethodOnY));
+ EXPECT_TRUE(
+ matches("class Y { public: void x(); }; void z() { Y *y; y->x(); }",
+ MethodOnY));
+
+ EXPECT_TRUE(matches(
+ "class Y {"
+ " public: virtual void x();"
+ "};"
+ "class X : public Y {"
+ " public: virtual void x();"
+ "};"
+ "void z() { X *x; x->Y::x(); }", MethodOnY));
+}
+
+TEST(Matcher, VariableUsage) {
+ StatementMatcher Reference =
+ declarationReference(to(
+ variable(hasInitializer(
+ memberCall(thisPointerType(record(hasName("Y"))))))));
+
+ EXPECT_TRUE(matches(
+ "class Y {"
+ " public:"
+ " bool x() const;"
+ "};"
+ "void z(const Y &y) {"
+ " bool b = y.x();"
+ " if (b) {}"
+ "}", Reference));
+
+ EXPECT_TRUE(notMatches(
+ "class Y {"
+ " public:"
+ " bool x() const;"
+ "};"
+ "void z(const Y &y) {"
+ " bool b = y.x();"
+ "}", Reference));
+}
+
+TEST(Matcher, FindsVarDeclInFuncitonParameter) {
+ EXPECT_TRUE(matches(
+ "void f(int i) {}",
+ variable(hasName("i"))));
+}
+
+TEST(Matcher, CalledVariable) {
+ StatementMatcher CallOnVariableY = expression(
+ memberCall(on(declarationReference(to(variable(hasName("y")))))));
+
+ EXPECT_TRUE(matches(
+ "class Y { public: void x() { Y y; y.x(); } };", CallOnVariableY));
+ EXPECT_TRUE(matches(
+ "class Y { public: void x() const { Y y; y.x(); } };", CallOnVariableY));
+ EXPECT_TRUE(matches(
+ "class Y { public: void x(); };"
+ "class X : public Y { void z() { X y; y.x(); } };", CallOnVariableY));
+ EXPECT_TRUE(matches(
+ "class Y { public: void x(); };"
+ "class X : public Y { void z() { X *y; y->x(); } };", CallOnVariableY));
+ EXPECT_TRUE(notMatches(
+ "class Y { public: void x(); };"
+ "class X : public Y { void z() { unsigned long y; ((X*)y)->x(); } };",
+ CallOnVariableY));
+}
+
+TEST(UnaryExprOrTypeTraitExpr, MatchesSizeOfAndAlignOf) {
+ EXPECT_TRUE(matches("void x() { int a = sizeof(a); }",
+ unaryExprOrTypeTraitExpr()));
+ EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }",
+ alignOfExpr(anything())));
+ // FIXME: Uncomment once alignof is enabled.
+ // EXPECT_TRUE(matches("void x() { int a = alignof(a); }",
+ // unaryExprOrTypeTraitExpr()));
+ // EXPECT_TRUE(notMatches("void x() { int a = alignof(a); }",
+ // sizeOfExpr()));
+}
+
+TEST(UnaryExpressionOrTypeTraitExpression, MatchesCorrectType) {
+ EXPECT_TRUE(matches("void x() { int a = sizeof(a); }", sizeOfExpr(
+ hasArgumentOfType(asString("int")))));
+ EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr(
+ hasArgumentOfType(asString("float")))));
+ EXPECT_TRUE(matches(
+ "struct A {}; void x() { A a; int b = sizeof(a); }",
+ sizeOfExpr(hasArgumentOfType(hasDeclaration(record(hasName("A")))))));
+ EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr(
+ hasArgumentOfType(hasDeclaration(record(hasName("string")))))));
+}
+
+TEST(MemberExpression, DoesNotMatchClasses) {
+ EXPECT_TRUE(notMatches("class Y { void x() {} };", memberExpression()));
+}
+
+TEST(MemberExpression, MatchesMemberFunctionCall) {
+ EXPECT_TRUE(matches("class Y { void x() { x(); } };", memberExpression()));
+}
+
+TEST(MemberExpression, MatchesVariable) {
+ EXPECT_TRUE(
+ matches("class Y { void x() { this->y; } int y; };", memberExpression()));
+ EXPECT_TRUE(
+ matches("class Y { void x() { y; } int y; };", memberExpression()));
+ EXPECT_TRUE(
+ matches("class Y { void x() { Y y; y.y; } int y; };",
+ memberExpression()));
+}
+
+TEST(MemberExpression, MatchesStaticVariable) {
+ EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };",
+ memberExpression()));
+ EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };",
+ memberExpression()));
+ EXPECT_TRUE(notMatches("class Y { void x() { Y::y; } static int y; };",
+ memberExpression()));
+}
+
+TEST(IsInteger, MatchesIntegers) {
+ EXPECT_TRUE(matches("int i = 0;", variable(hasType(isInteger()))));
+ EXPECT_TRUE(matches("long long i = 0; void f(long long) { }; void g() {f(i);}",
+ call(hasArgument(0, declarationReference(
+ to(variable(hasType(isInteger()))))))));
+}
+
+TEST(IsInteger, ReportsNoFalsePositives) {
+ EXPECT_TRUE(notMatches("int *i;", variable(hasType(isInteger()))));
+ EXPECT_TRUE(notMatches("struct T {}; T t; void f(T *) { }; void g() {f(&t);}",
+ call(hasArgument(0, declarationReference(
+ to(variable(hasType(isInteger()))))))));
+}
+
+TEST(IsArrow, MatchesMemberVariablesViaArrow) {
+ EXPECT_TRUE(matches("class Y { void x() { this->y; } int y; };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(matches("class Y { void x() { y; } int y; };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };",
+ memberExpression(isArrow())));
+}
+
+TEST(IsArrow, MatchesStaticMemberVariablesViaArrow) {
+ EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } static int y; };",
+ memberExpression(isArrow())));
+}
+
+TEST(IsArrow, MatchesMemberCallsViaArrow) {
+ EXPECT_TRUE(matches("class Y { void x() { this->x(); } };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(matches("class Y { void x() { x(); } };",
+ memberExpression(isArrow())));
+ EXPECT_TRUE(notMatches("class Y { void x() { Y y; y.x(); } };",
+ memberExpression(isArrow())));
+}
+
+TEST(Callee, MatchesDeclarations) {
+ StatementMatcher CallMethodX = call(callee(method(hasName("x"))));
+
+ EXPECT_TRUE(matches("class Y { void x() { x(); } };", CallMethodX));
+ EXPECT_TRUE(notMatches("class Y { void x() {} };", CallMethodX));
+}
+
+TEST(Callee, MatchesMemberExpressions) {
+ EXPECT_TRUE(matches("class Y { void x() { this->x(); } };",
+ call(callee(memberExpression()))));
+ EXPECT_TRUE(
+ notMatches("class Y { void x() { this->x(); } };", call(callee(call()))));
+}
+
+TEST(Function, MatchesFunctionDeclarations) {
+ StatementMatcher CallFunctionF = call(callee(function(hasName("f"))));
+
+ EXPECT_TRUE(matches("void f() { f(); }", CallFunctionF));
+ EXPECT_TRUE(notMatches("void f() { }", CallFunctionF));
+
+#if !defined(_MSC_VER)
+ // FIXME: Make this work for MSVC.
+ // Dependent contexts, but a non-dependent call.
+ EXPECT_TRUE(matches("void f(); template <int N> void g() { f(); }",
+ CallFunctionF));
+ EXPECT_TRUE(
+ matches("void f(); template <int N> struct S { void g() { f(); } };",
+ CallFunctionF));
+#endif
+
+ // Depedent calls don't match.
+ EXPECT_TRUE(
+ notMatches("void f(int); template <typename T> void g(T t) { f(t); }",
+ CallFunctionF));
+ EXPECT_TRUE(
+ notMatches("void f(int);"
+ "template <typename T> struct S { void g(T t) { f(t); } };",
+ CallFunctionF));
+}
+
+TEST(Matcher, Argument) {
+ StatementMatcher CallArgumentY = expression(call(
+ hasArgument(0, declarationReference(to(variable(hasName("y")))))));
+
+ EXPECT_TRUE(matches("void x(int) { int y; x(y); }", CallArgumentY));
+ EXPECT_TRUE(
+ matches("class X { void x(int) { int y; x(y); } };", CallArgumentY));
+ EXPECT_TRUE(notMatches("void x(int) { int z; x(z); }", CallArgumentY));
+
+ StatementMatcher WrongIndex = expression(call(
+ hasArgument(42, declarationReference(to(variable(hasName("y")))))));
+ EXPECT_TRUE(notMatches("void x(int) { int y; x(y); }", WrongIndex));
+}
+
+TEST(Matcher, AnyArgument) {
+ StatementMatcher CallArgumentY = expression(call(
+ hasAnyArgument(declarationReference(to(variable(hasName("y")))))));
+ EXPECT_TRUE(matches("void x(int, int) { int y; x(1, y); }", CallArgumentY));
+ EXPECT_TRUE(matches("void x(int, int) { int y; x(y, 42); }", CallArgumentY));
+ EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY));
+}
+
+TEST(Matcher, ArgumentCount) {
+ StatementMatcher Call1Arg = expression(call(argumentCountIs(1)));
+
+ EXPECT_TRUE(matches("void x(int) { x(0); }", Call1Arg));
+ EXPECT_TRUE(matches("class X { void x(int) { x(0); } };", Call1Arg));
+ EXPECT_TRUE(notMatches("void x(int, int) { x(0, 0); }", Call1Arg));
+}
+
+TEST(Matcher, References) {
+ DeclarationMatcher ReferenceClassX = variable(
+ hasType(references(record(hasName("X")))));
+ EXPECT_TRUE(matches("class X {}; void y(X y) { X &x = y; }",
+ ReferenceClassX));
+ EXPECT_TRUE(
+ matches("class X {}; void y(X y) { const X &x = y; }", ReferenceClassX));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y(X y) { X x = y; }", ReferenceClassX));
+ EXPECT_TRUE(
+ notMatches("class X {}; void y(X *y) { X *&x = y; }", ReferenceClassX));
+}
+
+TEST(HasParameter, CallsInnerMatcher) {
+ EXPECT_TRUE(matches("class X { void x(int) {} };",
+ method(hasParameter(0, variable()))));
+ EXPECT_TRUE(notMatches("class X { void x(int) {} };",
+ method(hasParameter(0, hasName("x")))));
+}
+
+TEST(HasParameter, DoesNotMatchIfIndexOutOfBounds) {
+ EXPECT_TRUE(notMatches("class X { void x(int) {} };",
+ method(hasParameter(42, variable()))));
+}
+
+TEST(HasType, MatchesParameterVariableTypesStrictly) {
+ EXPECT_TRUE(matches("class X { void x(X x) {} };",
+ method(hasParameter(0, hasType(record(hasName("X")))))));
+ EXPECT_TRUE(notMatches("class X { void x(const X &x) {} };",
+ method(hasParameter(0, hasType(record(hasName("X")))))));
+ EXPECT_TRUE(matches("class X { void x(const X *x) {} };",
+ method(hasParameter(0, hasType(pointsTo(record(hasName("X"))))))));
+ EXPECT_TRUE(matches("class X { void x(const X &x) {} };",
+ method(hasParameter(0, hasType(references(record(hasName("X"))))))));
+}
+
+TEST(HasAnyParameter, MatchesIndependentlyOfPosition) {
+ EXPECT_TRUE(matches("class Y {}; class X { void x(X x, Y y) {} };",
+ method(hasAnyParameter(hasType(record(hasName("X")))))));
+ EXPECT_TRUE(matches("class Y {}; class X { void x(Y y, X x) {} };",
+ method(hasAnyParameter(hasType(record(hasName("X")))))));
+}
+
+TEST(Returns, MatchesReturnTypes) {
+ EXPECT_TRUE(matches("class Y { int f() { return 1; } };",
+ function(returns(asString("int")))));
+ EXPECT_TRUE(notMatches("class Y { int f() { return 1; } };",
+ function(returns(asString("float")))));
+ EXPECT_TRUE(matches("class Y { Y getMe() { return *this; } };",
+ function(returns(hasDeclaration(record(hasName("Y")))))));
+}
+
+TEST(HasAnyParameter, DoesntMatchIfInnerMatcherDoesntMatch) {
+ EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };",
+ method(hasAnyParameter(hasType(record(hasName("X")))))));
+}
+
+TEST(HasAnyParameter, DoesNotMatchThisPointer) {
+ EXPECT_TRUE(notMatches("class Y {}; class X { void x() {} };",
+ method(hasAnyParameter(hasType(pointsTo(record(hasName("X"))))))));
+}
+
+TEST(HasName, MatchesParameterVariableDeclartions) {
+ EXPECT_TRUE(matches("class Y {}; class X { void x(int x) {} };",
+ method(hasAnyParameter(hasName("x")))));
+ EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };",
+ method(hasAnyParameter(hasName("x")))));
+}
+
+TEST(Matcher, MatchesClassTemplateSpecialization) {
+ EXPECT_TRUE(matches("template<typename T> struct A {};"
+ "template<> struct A<int> {};",
+ classTemplateSpecialization()));
+ EXPECT_TRUE(matches("template<typename T> struct A {}; A<int> a;",
+ classTemplateSpecialization()));
+ EXPECT_TRUE(notMatches("template<typename T> struct A {};",
+ classTemplateSpecialization()));
+}
+
+TEST(Matcher, MatchesTypeTemplateArgument) {
+ EXPECT_TRUE(matches(
+ "template<typename T> struct B {};"
+ "B<int> b;",
+ classTemplateSpecialization(hasAnyTemplateArgument(refersToType(
+ asString("int"))))));
+}
+
+TEST(Matcher, MatchesDeclarationReferenceTemplateArgument) {
+ EXPECT_TRUE(matches(
+ "struct B { int next; };"
+ "template<int(B::*next_ptr)> struct A {};"
+ "A<&B::next> a;",
+ classTemplateSpecialization(hasAnyTemplateArgument(
+ refersToDeclaration(field(hasName("next")))))));
+}
+
+TEST(Matcher, MatchesSpecificArgument) {
+ EXPECT_TRUE(matches(
+ "template<typename T, typename U> class A {};"
+ "A<bool, int> a;",
+ classTemplateSpecialization(hasTemplateArgument(
+ 1, refersToType(asString("int"))))));
+ EXPECT_TRUE(notMatches(
+ "template<typename T, typename U> class A {};"
+ "A<int, bool> a;",
+ classTemplateSpecialization(hasTemplateArgument(
+ 1, refersToType(asString("int"))))));
+}
+
+TEST(Matcher, ConstructorCall) {
+ StatementMatcher Constructor = expression(constructorCall());
+
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x() { X x; }", Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x() { X x = X(); }",
+ Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { X x = 0; }",
+ Constructor));
+ EXPECT_TRUE(matches("class X {}; void x(int) { X x; }", Constructor));
+}
+
+TEST(Matcher, ConstructorArgument) {
+ StatementMatcher Constructor = expression(constructorCall(
+ hasArgument(0, declarationReference(to(variable(hasName("y")))))));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; X x(y); }",
+ Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; X x = X(y); }",
+ Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; X x = y; }",
+ Constructor));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int); }; void x() { int z; X x(z); }",
+ Constructor));
+
+ StatementMatcher WrongIndex = expression(constructorCall(
+ hasArgument(42, declarationReference(to(variable(hasName("y")))))));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int); }; void x() { int y; X x(y); }",
+ WrongIndex));
+}
+
+TEST(Matcher, ConstructorArgumentCount) {
+ StatementMatcher Constructor1Arg =
+ expression(constructorCall(argumentCountIs(1)));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { X x(0); }",
+ Constructor1Arg));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { X x = X(0); }",
+ Constructor1Arg));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { X x = 0; }",
+ Constructor1Arg));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int, int); }; void x() { X x(0, 0); }",
+ Constructor1Arg));
+}
+
+TEST(Matcher, BindTemporaryExpression) {
+ StatementMatcher TempExpression = expression(bindTemporaryExpression());
+
+ std::string ClassString = "class string { public: string(); ~string(); }; ";
+
+ EXPECT_TRUE(
+ matches(ClassString +
+ "string GetStringByValue();"
+ "void FunctionTakesString(string s);"
+ "void run() { FunctionTakesString(GetStringByValue()); }",
+ TempExpression));
+
+ EXPECT_TRUE(
+ notMatches(ClassString +
+ "string* GetStringPointer(); "
+ "void FunctionTakesStringPtr(string* s);"
+ "void run() {"
+ " string* s = GetStringPointer();"
+ " FunctionTakesStringPtr(GetStringPointer());"
+ " FunctionTakesStringPtr(s);"
+ "}",
+ TempExpression));
+
+ EXPECT_TRUE(
+ notMatches("class no_dtor {};"
+ "no_dtor GetObjByValue();"
+ "void ConsumeObj(no_dtor param);"
+ "void run() { ConsumeObj(GetObjByValue()); }",
+ TempExpression));
+}
+
+TEST(ConstructorDeclaration, SimpleCase) {
+ EXPECT_TRUE(matches("class Foo { Foo(int i); };",
+ constructor(ofClass(hasName("Foo")))));
+ EXPECT_TRUE(notMatches("class Foo { Foo(int i); };",
+ constructor(ofClass(hasName("Bar")))));
+}
+
+TEST(ConstructorDeclaration, IsImplicit) {
+ // This one doesn't match because the constructor is not added by the
+ // compiler (it is not needed).
+ EXPECT_TRUE(notMatches("class Foo { };",
+ constructor(isImplicit())));
+ // The compiler added the implicit default constructor.
+ EXPECT_TRUE(matches("class Foo { }; Foo* f = new Foo();",
+ constructor(isImplicit())));
+ EXPECT_TRUE(matches("class Foo { Foo(){} };",
+ constructor(unless(isImplicit()))));
+}
+
+TEST(DestructorDeclaration, MatchesVirtualDestructor) {
+ EXPECT_TRUE(matches("class Foo { virtual ~Foo(); };",
+ destructor(ofClass(hasName("Foo")))));
+}
+
+TEST(DestructorDeclaration, DoesNotMatchImplicitDestructor) {
+ EXPECT_TRUE(notMatches("class Foo {};", destructor(ofClass(hasName("Foo")))));
+}
+
+TEST(HasAnyConstructorInitializer, SimpleCase) {
+ EXPECT_TRUE(notMatches(
+ "class Foo { Foo() { } };",
+ constructor(hasAnyConstructorInitializer(anything()))));
+ EXPECT_TRUE(matches(
+ "class Foo {"
+ " Foo() : foo_() { }"
+ " int foo_;"
+ "};",
+ constructor(hasAnyConstructorInitializer(anything()))));
+}
+
+TEST(HasAnyConstructorInitializer, ForField) {
+ static const char Code[] =
+ "class Baz { };"
+ "class Foo {"
+ " Foo() : foo_() { }"
+ " Baz foo_;"
+ " Baz bar_;"
+ "};";
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ forField(hasType(record(hasName("Baz"))))))));
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ forField(hasName("foo_"))))));
+ EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer(
+ forField(hasType(record(hasName("Bar"))))))));
+}
+
+TEST(HasAnyConstructorInitializer, WithInitializer) {
+ static const char Code[] =
+ "class Foo {"
+ " Foo() : foo_(0) { }"
+ " int foo_;"
+ "};";
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ withInitializer(integerLiteral(equals(0)))))));
+ EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer(
+ withInitializer(integerLiteral(equals(1)))))));
+}
+
+TEST(HasAnyConstructorInitializer, IsWritten) {
+ static const char Code[] =
+ "struct Bar { Bar(){} };"
+ "class Foo {"
+ " Foo() : foo_() { }"
+ " Bar foo_;"
+ " Bar bar_;"
+ "};";
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ allOf(forField(hasName("foo_")), isWritten())))));
+ EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer(
+ allOf(forField(hasName("bar_")), isWritten())))));
+ EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer(
+ allOf(forField(hasName("bar_")), unless(isWritten()))))));
+}
+
+TEST(Matcher, NewExpression) {
+ StatementMatcher New = expression(newExpression());
+
+ EXPECT_TRUE(matches("class X { public: X(); }; void x() { new X; }", New));
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x() { new X(); }", New));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { new X(0); }", New));
+ EXPECT_TRUE(matches("class X {}; void x(int) { new X; }", New));
+}
+
+TEST(Matcher, NewExpressionArgument) {
+ StatementMatcher New = expression(constructorCall(
+ hasArgument(
+ 0, declarationReference(to(variable(hasName("y")))))));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; new X(y); }",
+ New));
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { int y; new X(y); }",
+ New));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int); }; void x() { int z; new X(z); }",
+ New));
+
+ StatementMatcher WrongIndex = expression(constructorCall(
+ hasArgument(
+ 42, declarationReference(to(variable(hasName("y")))))));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int); }; void x() { int y; new X(y); }",
+ WrongIndex));
+}
+
+TEST(Matcher, NewExpressionArgumentCount) {
+ StatementMatcher New = constructorCall(argumentCountIs(1));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { new X(0); }", New));
+ EXPECT_TRUE(
+ notMatches("class X { public: X(int, int); }; void x() { new X(0, 0); }",
+ New));
+}
+
+TEST(Matcher, DeleteExpression) {
+ EXPECT_TRUE(matches("struct A {}; void f(A* a) { delete a; }",
+ deleteExpression()));
+}
+
+TEST(Matcher, DefaultArgument) {
+ StatementMatcher Arg = defaultArgument();
+
+ EXPECT_TRUE(matches("void x(int, int = 0) { int y; x(y); }", Arg));
+ EXPECT_TRUE(
+ matches("class X { void x(int, int = 0) { int y; x(y); } };", Arg));
+ EXPECT_TRUE(notMatches("void x(int, int = 0) { int y; x(y, 0); }", Arg));
+}
+
+TEST(Matcher, StringLiterals) {
+ StatementMatcher Literal = expression(stringLiteral());
+ EXPECT_TRUE(matches("const char *s = \"string\";", Literal));
+ // wide string
+ EXPECT_TRUE(matches("const wchar_t *s = L\"string\";", Literal));
+ // with escaped characters
+ EXPECT_TRUE(matches("const char *s = \"\x05five\";", Literal));
+ // no matching -- though the data type is the same, there is no string literal
+ EXPECT_TRUE(notMatches("const char s[1] = {'a'};", Literal));
+}
+
+TEST(Matcher, CharacterLiterals) {
+ StatementMatcher CharLiteral = expression(characterLiteral());
+ EXPECT_TRUE(matches("const char c = 'c';", CharLiteral));
+ // wide character
+ EXPECT_TRUE(matches("const char c = L'c';", CharLiteral));
+ // wide character, Hex encoded, NOT MATCHED!
+ EXPECT_TRUE(notMatches("const wchar_t c = 0x2126;", CharLiteral));
+ EXPECT_TRUE(notMatches("const char c = 0x1;", CharLiteral));
+}
+
+TEST(Matcher, IntegerLiterals) {
+ StatementMatcher HasIntLiteral = expression(integerLiteral());
+ EXPECT_TRUE(matches("int i = 10;", HasIntLiteral));
+ EXPECT_TRUE(matches("int i = 0x1AB;", HasIntLiteral));
+ EXPECT_TRUE(matches("int i = 10L;", HasIntLiteral));
+ EXPECT_TRUE(matches("int i = 10U;", HasIntLiteral));
+
+ // Non-matching cases (character literals, float and double)
+ EXPECT_TRUE(notMatches("int i = L'a';",
+ HasIntLiteral)); // this is actually a character
+ // literal cast to int
+ EXPECT_TRUE(notMatches("int i = 'a';", HasIntLiteral));
+ EXPECT_TRUE(notMatches("int i = 1e10;", HasIntLiteral));
+ EXPECT_TRUE(notMatches("int i = 10.0;", HasIntLiteral));
+}
+
+TEST(Matcher, Conditions) {
+ StatementMatcher Condition = ifStmt(hasCondition(boolLiteral(equals(true))));
+
+ EXPECT_TRUE(matches("void x() { if (true) {} }", Condition));
+ EXPECT_TRUE(notMatches("void x() { if (false) {} }", Condition));
+ EXPECT_TRUE(notMatches("void x() { bool a = true; if (a) {} }", Condition));
+ EXPECT_TRUE(notMatches("void x() { if (true || false) {} }", Condition));
+ EXPECT_TRUE(notMatches("void x() { if (1) {} }", Condition));
+}
+
+TEST(MatchBinaryOperator, HasOperatorName) {
+ StatementMatcher OperatorOr = binaryOperator(hasOperatorName("||"));
+
+ EXPECT_TRUE(matches("void x() { true || false; }", OperatorOr));
+ EXPECT_TRUE(notMatches("void x() { true && false; }", OperatorOr));
+}
+
+TEST(MatchBinaryOperator, HasLHSAndHasRHS) {
+ StatementMatcher OperatorTrueFalse =
+ binaryOperator(hasLHS(boolLiteral(equals(true))),
+ hasRHS(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { true || false; }", OperatorTrueFalse));
+ EXPECT_TRUE(matches("void x() { true && false; }", OperatorTrueFalse));
+ EXPECT_TRUE(notMatches("void x() { false || true; }", OperatorTrueFalse));
+}
+
+TEST(MatchBinaryOperator, HasEitherOperand) {
+ StatementMatcher HasOperand =
+ binaryOperator(hasEitherOperand(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { true || false; }", HasOperand));
+ EXPECT_TRUE(matches("void x() { false && true; }", HasOperand));
+ EXPECT_TRUE(notMatches("void x() { true || true; }", HasOperand));
+}
+
+TEST(Matcher, BinaryOperatorTypes) {
+ // Integration test that verifies the AST provides all binary operators in
+ // a way we expect.
+ // FIXME: Operator ','
+ EXPECT_TRUE(
+ matches("void x() { 3, 4; }", binaryOperator(hasOperatorName(","))));
+ EXPECT_TRUE(
+ matches("bool b; bool c = (b = true);",
+ binaryOperator(hasOperatorName("="))));
+ EXPECT_TRUE(
+ matches("bool b = 1 != 2;", binaryOperator(hasOperatorName("!="))));
+ EXPECT_TRUE(
+ matches("bool b = 1 == 2;", binaryOperator(hasOperatorName("=="))));
+ EXPECT_TRUE(matches("bool b = 1 < 2;", binaryOperator(hasOperatorName("<"))));
+ EXPECT_TRUE(
+ matches("bool b = 1 <= 2;", binaryOperator(hasOperatorName("<="))));
+ EXPECT_TRUE(
+ matches("int i = 1 << 2;", binaryOperator(hasOperatorName("<<"))));
+ EXPECT_TRUE(
+ matches("int i = 1; int j = (i <<= 2);",
+ binaryOperator(hasOperatorName("<<="))));
+ EXPECT_TRUE(matches("bool b = 1 > 2;", binaryOperator(hasOperatorName(">"))));
+ EXPECT_TRUE(
+ matches("bool b = 1 >= 2;", binaryOperator(hasOperatorName(">="))));
+ EXPECT_TRUE(
+ matches("int i = 1 >> 2;", binaryOperator(hasOperatorName(">>"))));
+ EXPECT_TRUE(
+ matches("int i = 1; int j = (i >>= 2);",
+ binaryOperator(hasOperatorName(">>="))));
+ EXPECT_TRUE(
+ matches("int i = 42 ^ 23;", binaryOperator(hasOperatorName("^"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i ^= 42);",
+ binaryOperator(hasOperatorName("^="))));
+ EXPECT_TRUE(
+ matches("int i = 42 % 23;", binaryOperator(hasOperatorName("%"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i %= 42);",
+ binaryOperator(hasOperatorName("%="))));
+ EXPECT_TRUE(
+ matches("bool b = 42 &23;", binaryOperator(hasOperatorName("&"))));
+ EXPECT_TRUE(
+ matches("bool b = true && false;",
+ binaryOperator(hasOperatorName("&&"))));
+ EXPECT_TRUE(
+ matches("bool b = true; bool c = (b &= false);",
+ binaryOperator(hasOperatorName("&="))));
+ EXPECT_TRUE(
+ matches("bool b = 42 | 23;", binaryOperator(hasOperatorName("|"))));
+ EXPECT_TRUE(
+ matches("bool b = true || false;",
+ binaryOperator(hasOperatorName("||"))));
+ EXPECT_TRUE(
+ matches("bool b = true; bool c = (b |= false);",
+ binaryOperator(hasOperatorName("|="))));
+ EXPECT_TRUE(
+ matches("int i = 42 *23;", binaryOperator(hasOperatorName("*"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i *= 23);",
+ binaryOperator(hasOperatorName("*="))));
+ EXPECT_TRUE(
+ matches("int i = 42 / 23;", binaryOperator(hasOperatorName("/"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i /= 23);",
+ binaryOperator(hasOperatorName("/="))));
+ EXPECT_TRUE(
+ matches("int i = 42 + 23;", binaryOperator(hasOperatorName("+"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i += 23);",
+ binaryOperator(hasOperatorName("+="))));
+ EXPECT_TRUE(
+ matches("int i = 42 - 23;", binaryOperator(hasOperatorName("-"))));
+ EXPECT_TRUE(
+ matches("int i = 42; int j = (i -= 23);",
+ binaryOperator(hasOperatorName("-="))));
+ EXPECT_TRUE(
+ matches("struct A { void x() { void (A::*a)(); (this->*a)(); } };",
+ binaryOperator(hasOperatorName("->*"))));
+ EXPECT_TRUE(
+ matches("struct A { void x() { void (A::*a)(); ((*this).*a)(); } };",
+ binaryOperator(hasOperatorName(".*"))));
+
+ // Member expressions as operators are not supported in matches.
+ EXPECT_TRUE(
+ notMatches("struct A { void x(A *a) { a->x(this); } };",
+ binaryOperator(hasOperatorName("->"))));
+
+ // Initializer assignments are not represented as operator equals.
+ EXPECT_TRUE(
+ notMatches("bool b = true;", binaryOperator(hasOperatorName("="))));
+
+ // Array indexing is not represented as operator.
+ EXPECT_TRUE(notMatches("int a[42]; void x() { a[23]; }", unaryOperator()));
+
+ // Overloaded operators do not match at all.
+ EXPECT_TRUE(notMatches(
+ "struct A { bool operator&&(const A &a) const { return false; } };"
+ "void x() { A a, b; a && b; }",
+ binaryOperator()));
+}
+
+TEST(MatchUnaryOperator, HasOperatorName) {
+ StatementMatcher OperatorNot = unaryOperator(hasOperatorName("!"));
+
+ EXPECT_TRUE(matches("void x() { !true; } ", OperatorNot));
+ EXPECT_TRUE(notMatches("void x() { true; } ", OperatorNot));
+}
+
+TEST(MatchUnaryOperator, HasUnaryOperand) {
+ StatementMatcher OperatorOnFalse =
+ unaryOperator(hasUnaryOperand(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { !false; }", OperatorOnFalse));
+ EXPECT_TRUE(notMatches("void x() { !true; }", OperatorOnFalse));
+}
+
+TEST(Matcher, UnaryOperatorTypes) {
+ // Integration test that verifies the AST provides all unary operators in
+ // a way we expect.
+ EXPECT_TRUE(matches("bool b = !true;", unaryOperator(hasOperatorName("!"))));
+ EXPECT_TRUE(
+ matches("bool b; bool *p = &b;", unaryOperator(hasOperatorName("&"))));
+ EXPECT_TRUE(matches("int i = ~ 1;", unaryOperator(hasOperatorName("~"))));
+ EXPECT_TRUE(
+ matches("bool *p; bool b = *p;", unaryOperator(hasOperatorName("*"))));
+ EXPECT_TRUE(
+ matches("int i; int j = +i;", unaryOperator(hasOperatorName("+"))));
+ EXPECT_TRUE(
+ matches("int i; int j = -i;", unaryOperator(hasOperatorName("-"))));
+ EXPECT_TRUE(
+ matches("int i; int j = ++i;", unaryOperator(hasOperatorName("++"))));
+ EXPECT_TRUE(
+ matches("int i; int j = i++;", unaryOperator(hasOperatorName("++"))));
+ EXPECT_TRUE(
+ matches("int i; int j = --i;", unaryOperator(hasOperatorName("--"))));
+ EXPECT_TRUE(
+ matches("int i; int j = i--;", unaryOperator(hasOperatorName("--"))));
+
+ // We don't match conversion operators.
+ EXPECT_TRUE(notMatches("int i; double d = (double)i;", unaryOperator()));
+
+ // Function calls are not represented as operator.
+ EXPECT_TRUE(notMatches("void f(); void x() { f(); }", unaryOperator()));
+
+ // Overloaded operators do not match at all.
+ // FIXME: We probably want to add that.
+ EXPECT_TRUE(notMatches(
+ "struct A { bool operator!() const { return false; } };"
+ "void x() { A a; !a; }", unaryOperator(hasOperatorName("!"))));
+}
+
+TEST(Matcher, ConditionalOperator) {
+ StatementMatcher Conditional = conditionalOperator(
+ hasCondition(boolLiteral(equals(true))),
+ hasTrueExpression(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { true ? false : true; }", Conditional));
+ EXPECT_TRUE(notMatches("void x() { false ? false : true; }", Conditional));
+ EXPECT_TRUE(notMatches("void x() { true ? true : false; }", Conditional));
+
+ StatementMatcher ConditionalFalse = conditionalOperator(
+ hasFalseExpression(boolLiteral(equals(false))));
+
+ EXPECT_TRUE(matches("void x() { true ? true : false; }", ConditionalFalse));
+ EXPECT_TRUE(
+ notMatches("void x() { true ? false : true; }", ConditionalFalse));
+}
+
+TEST(ArraySubscriptMatchers, ArraySubscripts) {
+ EXPECT_TRUE(matches("int i[2]; void f() { i[1] = 1; }",
+ arraySubscriptExpr()));
+ EXPECT_TRUE(notMatches("int i; void f() { i = 1; }",
+ arraySubscriptExpr()));
+}
+
+TEST(ArraySubscriptMatchers, ArrayIndex) {
+ EXPECT_TRUE(matches(
+ "int i[2]; void f() { i[1] = 1; }",
+ arraySubscriptExpr(hasIndex(integerLiteral(equals(1))))));
+ EXPECT_TRUE(matches(
+ "int i[2]; void f() { 1[i] = 1; }",
+ arraySubscriptExpr(hasIndex(integerLiteral(equals(1))))));
+ EXPECT_TRUE(notMatches(
+ "int i[2]; void f() { i[1] = 1; }",
+ arraySubscriptExpr(hasIndex(integerLiteral(equals(0))))));
+}
+
+TEST(ArraySubscriptMatchers, MatchesArrayBase) {
+ EXPECT_TRUE(matches(
+ "int i[2]; void f() { i[1] = 2; }",
+ arraySubscriptExpr(hasBase(implicitCast(
+ hasSourceExpression(declarationReference()))))));
+}
+
+TEST(Matcher, HasNameSupportsNamespaces) {
+ EXPECT_TRUE(matches("namespace a { namespace b { class C; } }",
+ record(hasName("a::b::C"))));
+ EXPECT_TRUE(matches("namespace a { namespace b { class C; } }",
+ record(hasName("::a::b::C"))));
+ EXPECT_TRUE(matches("namespace a { namespace b { class C; } }",
+ record(hasName("b::C"))));
+ EXPECT_TRUE(matches("namespace a { namespace b { class C; } }",
+ record(hasName("C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("c::b::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("a::c::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("a::b::A"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("::b::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("z::a::b::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }",
+ record(hasName("a+b::C"))));
+ EXPECT_TRUE(notMatches("namespace a { namespace b { class AC; } }",
+ record(hasName("C"))));
+}
+
+TEST(Matcher, HasNameSupportsOuterClasses) {
+ EXPECT_TRUE(
+ matches("class A { class B { class C; }; };", record(hasName("A::B::C"))));
+ EXPECT_TRUE(
+ matches("class A { class B { class C; }; };",
+ record(hasName("::A::B::C"))));
+ EXPECT_TRUE(
+ matches("class A { class B { class C; }; };", record(hasName("B::C"))));
+ EXPECT_TRUE(
+ matches("class A { class B { class C; }; };", record(hasName("C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("c::B::C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("A::c::C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("A::B::A"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };", record(hasName("::C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("::B::C"))));
+ EXPECT_TRUE(notMatches("class A { class B { class C; }; };",
+ record(hasName("z::A::B::C"))));
+ EXPECT_TRUE(
+ notMatches("class A { class B { class C; }; };",
+ record(hasName("A+B::C"))));
+}
+
+TEST(Matcher, IsDefinition) {
+ DeclarationMatcher DefinitionOfClassA =
+ record(hasName("A"), isDefinition());
+ EXPECT_TRUE(matches("class A {};", DefinitionOfClassA));
+ EXPECT_TRUE(notMatches("class A;", DefinitionOfClassA));
+
+ DeclarationMatcher DefinitionOfVariableA =
+ variable(hasName("a"), isDefinition());
+ EXPECT_TRUE(matches("int a;", DefinitionOfVariableA));
+ EXPECT_TRUE(notMatches("extern int a;", DefinitionOfVariableA));
+
+ DeclarationMatcher DefinitionOfMethodA =
+ method(hasName("a"), isDefinition());
+ EXPECT_TRUE(matches("class A { void a() {} };", DefinitionOfMethodA));
+ EXPECT_TRUE(notMatches("class A { void a(); };", DefinitionOfMethodA));
+}
+
+TEST(Matcher, OfClass) {
+ StatementMatcher Constructor = constructorCall(hasDeclaration(method(
+ ofClass(hasName("X")))));
+
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x(int) { X x; }", Constructor));
+ EXPECT_TRUE(
+ matches("class X { public: X(); }; void x(int) { X x = X(); }",
+ Constructor));
+ EXPECT_TRUE(
+ notMatches("class Y { public: Y(); }; void x(int) { Y y; }",
+ Constructor));
+}
+
+TEST(Matcher, VisitsTemplateInstantiations) {
+ EXPECT_TRUE(matches(
+ "class A { public: void x(); };"
+ "template <typename T> class B { public: void y() { T t; t.x(); } };"
+ "void f() { B<A> b; b.y(); }", call(callee(method(hasName("x"))))));
+
+ EXPECT_TRUE(matches(
+ "class A { public: void x(); };"
+ "class C {"
+ " public:"
+ " template <typename T> class B { public: void y() { T t; t.x(); } };"
+ "};"
+ "void f() {"
+ " C::B<A> b; b.y();"
+ "}", record(hasName("C"),
+ hasDescendant(call(callee(method(hasName("x"))))))));
+}
+
+TEST(Matcher, HandlesNullQualTypes) {
+ // FIXME: Add a Type matcher so we can replace uses of this
+ // variable with Type(True())
+ const TypeMatcher AnyType = anything();
+
+ // We don't really care whether this matcher succeeds; we're testing that
+ // it completes without crashing.
+ EXPECT_TRUE(matches(
+ "struct A { };"
+ "template <typename T>"
+ "void f(T t) {"
+ " T local_t(t /* this becomes a null QualType in the AST */);"
+ "}"
+ "void g() {"
+ " f(0);"
+ "}",
+ expression(hasType(TypeMatcher(
+ anyOf(
+ TypeMatcher(hasDeclaration(anything())),
+ pointsTo(AnyType),
+ references(AnyType)
+ // Other QualType matchers should go here.
+ ))))));
+}
+
+// For testing AST_MATCHER_P().
+AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) {
+ // Make sure all special variables are used: node, match_finder,
+ // bound_nodes_builder, and the parameter named 'AMatcher'.
+ return AMatcher.matches(Node, Finder, Builder);
+}
+
+TEST(AstMatcherPMacro, Works) {
+ DeclarationMatcher HasClassB = just(has(record(hasName("B")).bind("b")));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("a")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
+}
+
+AST_POLYMORPHIC_MATCHER_P(
+ polymorphicHas, internal::Matcher<Decl>, AMatcher) {
+ TOOLING_COMPILE_ASSERT((llvm::is_same<NodeType, Decl>::value) ||
+ (llvm::is_same<NodeType, Stmt>::value),
+ assert_node_type_is_accessible);
+ internal::TypedBaseMatcher<Decl> ChildMatcher(AMatcher);
+ return Finder->matchesChildOf(
+ Node, ChildMatcher, Builder,
+ ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses,
+ ASTMatchFinder::BK_First);
+}
+
+TEST(AstPolymorphicMatcherPMacro, Works) {
+ DeclarationMatcher HasClassB = polymorphicHas(record(hasName("B")).bind("b"));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("a")));
+
+ EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
+ HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
+
+ StatementMatcher StatementHasClassB =
+ polymorphicHas(record(hasName("B")));
+
+ EXPECT_TRUE(matches("void x() { class B {}; }", StatementHasClassB));
+}
+
+TEST(For, FindsForLoops) {
+ EXPECT_TRUE(matches("void f() { for(;;); }", forStmt()));
+ EXPECT_TRUE(matches("void f() { if(true) for(;;); }", forStmt()));
+}
+
+TEST(For, ForLoopInternals) {
+ EXPECT_TRUE(matches("void f(){ int i; for (; i < 3 ; ); }",
+ forStmt(hasCondition(anything()))));
+ EXPECT_TRUE(matches("void f() { for (int i = 0; ;); }",
+ forStmt(hasLoopInit(anything()))));
+}
+
+TEST(For, NegativeForLoopInternals) {
+ EXPECT_TRUE(notMatches("void f(){ for (int i = 0; ; ++i); }",
+ forStmt(hasCondition(expression()))));
+ EXPECT_TRUE(notMatches("void f() {int i; for (; i < 4; ++i) {} }",
+ forStmt(hasLoopInit(anything()))));
+}
+
+TEST(For, ReportsNoFalsePositives) {
+ EXPECT_TRUE(notMatches("void f() { ; }", forStmt()));
+ EXPECT_TRUE(notMatches("void f() { if(true); }", forStmt()));
+}
+
+TEST(CompoundStatement, HandlesSimpleCases) {
+ EXPECT_TRUE(notMatches("void f();", compoundStatement()));
+ EXPECT_TRUE(matches("void f() {}", compoundStatement()));
+ EXPECT_TRUE(matches("void f() {{}}", compoundStatement()));
+}
+
+TEST(CompoundStatement, DoesNotMatchEmptyStruct) {
+ // It's not a compound statement just because there's "{}" in the source
+ // text. This is an AST search, not grep.
+ EXPECT_TRUE(notMatches("namespace n { struct S {}; }",
+ compoundStatement()));
+ EXPECT_TRUE(matches("namespace n { struct S { void f() {{}} }; }",
+ compoundStatement()));
+}
+
+TEST(HasBody, FindsBodyOfForWhileDoLoops) {
+ EXPECT_TRUE(matches("void f() { for(;;) {} }",
+ forStmt(hasBody(compoundStatement()))));
+ EXPECT_TRUE(notMatches("void f() { for(;;); }",
+ forStmt(hasBody(compoundStatement()))));
+ EXPECT_TRUE(matches("void f() { while(true) {} }",
+ whileStmt(hasBody(compoundStatement()))));
+ EXPECT_TRUE(matches("void f() { do {} while(true); }",
+ doStmt(hasBody(compoundStatement()))));
+}
+
+TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) {
+ // The simplest case: every compound statement is in a function
+ // definition, and the function body itself must be a compound
+ // statement.
+ EXPECT_TRUE(matches("void f() { for (;;); }",
+ compoundStatement(hasAnySubstatement(forStmt()))));
+}
+
+TEST(HasAnySubstatement, IsNotRecursive) {
+ // It's really "has any immediate substatement".
+ EXPECT_TRUE(notMatches("void f() { if (true) for (;;); }",
+ compoundStatement(hasAnySubstatement(forStmt()))));
+}
+
+TEST(HasAnySubstatement, MatchesInNestedCompoundStatements) {
+ EXPECT_TRUE(matches("void f() { if (true) { for (;;); } }",
+ compoundStatement(hasAnySubstatement(forStmt()))));
+}
+
+TEST(HasAnySubstatement, FindsSubstatementBetweenOthers) {
+ EXPECT_TRUE(matches("void f() { 1; 2; 3; for (;;); 4; 5; 6; }",
+ compoundStatement(hasAnySubstatement(forStmt()))));
+}
+
+TEST(StatementCountIs, FindsNoStatementsInAnEmptyCompoundStatement) {
+ EXPECT_TRUE(matches("void f() { }",
+ compoundStatement(statementCountIs(0))));
+ EXPECT_TRUE(notMatches("void f() {}",
+ compoundStatement(statementCountIs(1))));
+}
+
+TEST(StatementCountIs, AppearsToMatchOnlyOneCount) {
+ EXPECT_TRUE(matches("void f() { 1; }",
+ compoundStatement(statementCountIs(1))));
+ EXPECT_TRUE(notMatches("void f() { 1; }",
+ compoundStatement(statementCountIs(0))));
+ EXPECT_TRUE(notMatches("void f() { 1; }",
+ compoundStatement(statementCountIs(2))));
+}
+
+TEST(StatementCountIs, WorksWithMultipleStatements) {
+ EXPECT_TRUE(matches("void f() { 1; 2; 3; }",
+ compoundStatement(statementCountIs(3))));
+}
+
+TEST(StatementCountIs, WorksWithNestedCompoundStatements) {
+ EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }",
+ compoundStatement(statementCountIs(1))));
+ EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }",
+ compoundStatement(statementCountIs(2))));
+ EXPECT_TRUE(notMatches("void f() { { 1; } { 1; 2; 3; 4; } }",
+ compoundStatement(statementCountIs(3))));
+ EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }",
+ compoundStatement(statementCountIs(4))));
+}
+
+TEST(Member, WorksInSimplestCase) {
+ EXPECT_TRUE(matches("struct { int first; } s; int i(s.first);",
+ memberExpression(member(hasName("first")))));
+}
+
+TEST(Member, DoesNotMatchTheBaseExpression) {
+ // Don't pick out the wrong part of the member expression, this should
+ // be checking the member (name) only.
+ EXPECT_TRUE(notMatches("struct { int i; } first; int i(first.i);",
+ memberExpression(member(hasName("first")))));
+}
+
+TEST(Member, MatchesInMemberFunctionCall) {
+ EXPECT_TRUE(matches("void f() {"
+ " struct { void first() {}; } s;"
+ " s.first();"
+ "};",
+ memberExpression(member(hasName("first")))));
+}
+
+TEST(HasObjectExpression, DoesNotMatchMember) {
+ EXPECT_TRUE(notMatches(
+ "class X {}; struct Z { X m; }; void f(Z z) { z.m; }",
+ memberExpression(hasObjectExpression(hasType(record(hasName("X")))))));
+}
+
+TEST(HasObjectExpression, MatchesBaseOfVariable) {
+ EXPECT_TRUE(matches(
+ "struct X { int m; }; void f(X x) { x.m; }",
+ memberExpression(hasObjectExpression(hasType(record(hasName("X")))))));
+ EXPECT_TRUE(matches(
+ "struct X { int m; }; void f(X* x) { x->m; }",
+ memberExpression(hasObjectExpression(
+ hasType(pointsTo(record(hasName("X"))))))));
+}
+
+TEST(HasObjectExpression,
+ MatchesObjectExpressionOfImplicitlyFormedMemberExpression) {
+ EXPECT_TRUE(matches(
+ "class X {}; struct S { X m; void f() { this->m; } };",
+ memberExpression(hasObjectExpression(
+ hasType(pointsTo(record(hasName("S"))))))));
+ EXPECT_TRUE(matches(
+ "class X {}; struct S { X m; void f() { m; } };",
+ memberExpression(hasObjectExpression(
+ hasType(pointsTo(record(hasName("S"))))))));
+}
+
+TEST(Field, DoesNotMatchNonFieldMembers) {
+ EXPECT_TRUE(notMatches("class X { void m(); };", field(hasName("m"))));
+ EXPECT_TRUE(notMatches("class X { class m {}; };", field(hasName("m"))));
+ EXPECT_TRUE(notMatches("class X { enum { m }; };", field(hasName("m"))));
+ EXPECT_TRUE(notMatches("class X { enum m {}; };", field(hasName("m"))));
+}
+
+TEST(Field, MatchesField) {
+ EXPECT_TRUE(matches("class X { int m; };", field(hasName("m"))));
+}
+
+TEST(IsConstQualified, MatchesConstInt) {
+ EXPECT_TRUE(matches("const int i = 42;",
+ variable(hasType(isConstQualified()))));
+}
+
+TEST(IsConstQualified, MatchesConstPointer) {
+ EXPECT_TRUE(matches("int i = 42; int* const p(&i);",
+ variable(hasType(isConstQualified()))));
+}
+
+TEST(IsConstQualified, MatchesThroughTypedef) {
+ EXPECT_TRUE(matches("typedef const int const_int; const_int i = 42;",
+ variable(hasType(isConstQualified()))));
+ EXPECT_TRUE(matches("typedef int* int_ptr; const int_ptr p(0);",
+ variable(hasType(isConstQualified()))));
+}
+
+TEST(IsConstQualified, DoesNotMatchInappropriately) {
+ EXPECT_TRUE(notMatches("typedef int nonconst_int; nonconst_int i = 42;",
+ variable(hasType(isConstQualified()))));
+ EXPECT_TRUE(notMatches("int const* p;",
+ variable(hasType(isConstQualified()))));
+}
+
+TEST(ReinterpretCast, MatchesSimpleCase) {
+ EXPECT_TRUE(matches("char* p = reinterpret_cast<char*>(&p);",
+ expression(reinterpretCast())));
+}
+
+TEST(ReinterpretCast, DoesNotMatchOtherCasts) {
+ EXPECT_TRUE(notMatches("char* p = (char*)(&p);",
+ expression(reinterpretCast())));
+ EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);",
+ expression(reinterpretCast())));
+ EXPECT_TRUE(notMatches("void* p = static_cast<void*>(&p);",
+ expression(reinterpretCast())));
+ EXPECT_TRUE(notMatches("struct B { virtual ~B() {} }; struct D : B {};"
+ "B b;"
+ "D* p = dynamic_cast<D*>(&b);",
+ expression(reinterpretCast())));
+}
+
+TEST(FunctionalCast, MatchesSimpleCase) {
+ std::string foo_class = "class Foo { public: Foo(char*); };";
+ EXPECT_TRUE(matches(foo_class + "void r() { Foo f = Foo(\"hello world\"); }",
+ expression(functionalCast())));
+}
+
+TEST(FunctionalCast, DoesNotMatchOtherCasts) {
+ std::string FooClass = "class Foo { public: Foo(char*); };";
+ EXPECT_TRUE(
+ notMatches(FooClass + "void r() { Foo f = (Foo) \"hello world\"; }",
+ expression(functionalCast())));
+ EXPECT_TRUE(
+ notMatches(FooClass + "void r() { Foo f = \"hello world\"; }",
+ expression(functionalCast())));
+}
+
+TEST(DynamicCast, MatchesSimpleCase) {
+ EXPECT_TRUE(matches("struct B { virtual ~B() {} }; struct D : B {};"
+ "B b;"
+ "D* p = dynamic_cast<D*>(&b);",
+ expression(dynamicCast())));
+}
+
+TEST(StaticCast, MatchesSimpleCase) {
+ EXPECT_TRUE(matches("void* p(static_cast<void*>(&p));",
+ expression(staticCast())));
+}
+
+TEST(StaticCast, DoesNotMatchOtherCasts) {
+ EXPECT_TRUE(notMatches("char* p = (char*)(&p);",
+ expression(staticCast())));
+ EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);",
+ expression(staticCast())));
+ EXPECT_TRUE(notMatches("void* p = reinterpret_cast<char*>(&p);",
+ expression(staticCast())));
+ EXPECT_TRUE(notMatches("struct B { virtual ~B() {} }; struct D : B {};"
+ "B b;"
+ "D* p = dynamic_cast<D*>(&b);",
+ expression(staticCast())));
+}
+
+TEST(HasDestinationType, MatchesSimpleCase) {
+ EXPECT_TRUE(matches("char* p = static_cast<char*>(0);",
+ expression(
+ staticCast(hasDestinationType(
+ pointsTo(TypeMatcher(anything())))))));
+}
+
+TEST(HasSourceExpression, MatchesImplicitCasts) {
+ EXPECT_TRUE(matches("class string {}; class URL { public: URL(string s); };"
+ "void r() {string a_string; URL url = a_string; }",
+ expression(implicitCast(
+ hasSourceExpression(constructorCall())))));
+}
+
+TEST(HasSourceExpression, MatchesExplicitCasts) {
+ EXPECT_TRUE(matches("float x = static_cast<float>(42);",
+ expression(explicitCast(
+ hasSourceExpression(hasDescendant(
+ expression(integerLiteral())))))));
+}
+
+TEST(Statement, DoesNotMatchDeclarations) {
+ EXPECT_TRUE(notMatches("class X {};", statement()));
+}
+
+TEST(Statement, MatchesCompoundStatments) {
+ EXPECT_TRUE(matches("void x() {}", statement()));
+}
+
+TEST(DeclarationStatement, DoesNotMatchCompoundStatements) {
+ EXPECT_TRUE(notMatches("void x() {}", declarationStatement()));
+}
+
+TEST(DeclarationStatement, MatchesVariableDeclarationStatements) {
+ EXPECT_TRUE(matches("void x() { int a; }", declarationStatement()));
+}
+
+TEST(InitListExpression, MatchesInitListExpression) {
+ EXPECT_TRUE(matches("int a[] = { 1, 2 };",
+ initListExpr(hasType(asString("int [2]")))));
+ EXPECT_TRUE(matches("struct B { int x, y; }; B b = { 5, 6 };",
+ initListExpr(hasType(record(hasName("B"))))));
+}
+
+TEST(UsingDeclaration, MatchesUsingDeclarations) {
+ EXPECT_TRUE(matches("namespace X { int x; } using X::x;",
+ usingDecl()));
+}
+
+TEST(UsingDeclaration, MatchesShadowUsingDelcarations) {
+ EXPECT_TRUE(matches("namespace f { int a; } using f::a;",
+ usingDecl(hasAnyUsingShadowDecl(hasName("a")))));
+}
+
+TEST(UsingDeclaration, MatchesSpecificTarget) {
+ EXPECT_TRUE(matches("namespace f { int a; void b(); } using f::b;",
+ usingDecl(hasAnyUsingShadowDecl(
+ hasTargetDecl(function())))));
+ EXPECT_TRUE(notMatches("namespace f { int a; void b(); } using f::a;",
+ usingDecl(hasAnyUsingShadowDecl(
+ hasTargetDecl(function())))));
+}
+
+TEST(UsingDeclaration, ThroughUsingDeclaration) {
+ EXPECT_TRUE(matches(
+ "namespace a { void f(); } using a::f; void g() { f(); }",
+ declarationReference(throughUsingDecl(anything()))));
+ EXPECT_TRUE(notMatches(
+ "namespace a { void f(); } using a::f; void g() { a::f(); }",
+ declarationReference(throughUsingDecl(anything()))));
+}
+
+TEST(While, MatchesWhileLoops) {
+ EXPECT_TRUE(notMatches("void x() {}", whileStmt()));
+ EXPECT_TRUE(matches("void x() { while(true); }", whileStmt()));
+ EXPECT_TRUE(notMatches("void x() { do {} while(true); }", whileStmt()));
+}
+
+TEST(Do, MatchesDoLoops) {
+ EXPECT_TRUE(matches("void x() { do {} while(true); }", doStmt()));
+ EXPECT_TRUE(matches("void x() { do ; while(false); }", doStmt()));
+}
+
+TEST(Do, DoesNotMatchWhileLoops) {
+ EXPECT_TRUE(notMatches("void x() { while(true) {} }", doStmt()));
+}
+
+TEST(SwitchCase, MatchesCase) {
+ EXPECT_TRUE(matches("void x() { switch(42) { case 42:; } }", switchCase()));
+ EXPECT_TRUE(matches("void x() { switch(42) { default:; } }", switchCase()));
+ EXPECT_TRUE(matches("void x() { switch(42) default:; }", switchCase()));
+ EXPECT_TRUE(notMatches("void x() { switch(42) {} }", switchCase()));
+}
+
+TEST(HasConditionVariableStatement, DoesNotMatchCondition) {
+ EXPECT_TRUE(notMatches(
+ "void x() { if(true) {} }",
+ ifStmt(hasConditionVariableStatement(declarationStatement()))));
+ EXPECT_TRUE(notMatches(
+ "void x() { int x; if((x = 42)) {} }",
+ ifStmt(hasConditionVariableStatement(declarationStatement()))));
+}
+
+TEST(HasConditionVariableStatement, MatchesConditionVariables) {
+ EXPECT_TRUE(matches(
+ "void x() { if(int* a = 0) {} }",
+ ifStmt(hasConditionVariableStatement(declarationStatement()))));
+}
+
+TEST(ForEach, BindsOneNode) {
+ EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; };",
+ record(hasName("C"), forEach(field(hasName("x")).bind("x"))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("x", 1)));
+}
+
+TEST(ForEach, BindsMultipleNodes) {
+ EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; int y; int z; };",
+ record(hasName("C"), forEach(field().bind("f"))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("f", 3)));
+}
+
+TEST(ForEach, BindsRecursiveCombinations) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { class D { int x; int y; }; class E { int y; int z; }; };",
+ record(hasName("C"), forEach(record(forEach(field().bind("f"))))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("f", 4)));
+}
+
+TEST(ForEachDescendant, BindsOneNode) {
+ EXPECT_TRUE(matchAndVerifyResultTrue("class C { class D { int x; }; };",
+ record(hasName("C"), forEachDescendant(field(hasName("x")).bind("x"))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("x", 1)));
+}
+
+TEST(ForEachDescendant, BindsMultipleNodes) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { class D { int x; int y; }; "
+ " class E { class F { int y; int z; }; }; };",
+ record(hasName("C"), forEachDescendant(field().bind("f"))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("f", 4)));
+}
+
+TEST(ForEachDescendant, BindsRecursiveCombinations) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { class D { "
+ " class E { class F { class G { int y; int z; }; }; }; }; };",
+ record(hasName("C"), forEachDescendant(record(
+ forEachDescendant(field().bind("f"))))),
+ new VerifyIdIsBoundToDecl<FieldDecl>("f", 8)));
+}
+
+
+TEST(IsTemplateInstantiation, MatchesImplicitClassTemplateInstantiation) {
+ // Make sure that we can both match the class by name (::X) and by the type
+ // the template was instantiated with (via a field).
+
+ EXPECT_TRUE(matches(
+ "template <typename T> class X {}; class A {}; X<A> x;",
+ record(hasName("::X"), isTemplateInstantiation())));
+
+ EXPECT_TRUE(matches(
+ "template <typename T> class X { T t; }; class A {}; X<A> x;",
+ record(isTemplateInstantiation(), hasDescendant(
+ field(hasType(record(hasName("A"))))))));
+}
+
+TEST(IsTemplateInstantiation, MatchesImplicitFunctionTemplateInstantiation) {
+ EXPECT_TRUE(matches(
+ "template <typename T> void f(T t) {} class A {}; void g() { f(A()); }",
+ function(hasParameter(0, hasType(record(hasName("A")))),
+ isTemplateInstantiation())));
+}
+
+TEST(IsTemplateInstantiation, MatchesExplicitClassTemplateInstantiation) {
+ EXPECT_TRUE(matches(
+ "template <typename T> class X { T t; }; class A {};"
+ "template class X<A>;",
+ record(isTemplateInstantiation(), hasDescendant(
+ field(hasType(record(hasName("A"))))))));
+}
+
+TEST(IsTemplateInstantiation,
+ MatchesInstantiationOfPartiallySpecializedClassTemplate) {
+ EXPECT_TRUE(matches(
+ "template <typename T> class X {};"
+ "template <typename T> class X<T*> {}; class A {}; X<A*> x;",
+ record(hasName("::X"), isTemplateInstantiation())));
+}
+
+TEST(IsTemplateInstantiation,
+ MatchesInstantiationOfClassTemplateNestedInNonTemplate) {
+ EXPECT_TRUE(matches(
+ "class A {};"
+ "class X {"
+ " template <typename U> class Y { U u; };"
+ " Y<A> y;"
+ "};",
+ record(hasName("::X::Y"), isTemplateInstantiation())));
+}
+
+TEST(IsTemplateInstantiation, DoesNotMatchInstantiationsInsideOfInstantiation) {
+ // FIXME: Figure out whether this makes sense. It doesn't affect the
+ // normal use case as long as the uppermost instantiation always is marked
+ // as template instantiation, but it might be confusing as a predicate.
+ EXPECT_TRUE(matches(
+ "class A {};"
+ "template <typename T> class X {"
+ " template <typename U> class Y { U u; };"
+ " Y<T> y;"
+ "}; X<A> x;",
+ record(hasName("::X<A>::Y"), unless(isTemplateInstantiation()))));
+}
+
+TEST(IsTemplateInstantiation, DoesNotMatchExplicitClassTemplateSpecialization) {
+ EXPECT_TRUE(notMatches(
+ "template <typename T> class X {}; class A {};"
+ "template <> class X<A> {}; X<A> x;",
+ record(hasName("::X"), isTemplateInstantiation())));
+}
+
+TEST(IsTemplateInstantiation, DoesNotMatchNonTemplate) {
+ EXPECT_TRUE(notMatches(
+ "class A {}; class Y { A a; };",
+ record(isTemplateInstantiation())));
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h
new file mode 100644
index 0000000..64816f5
--- /dev/null
+++ b/unittests/ASTMatchers/ASTMatchersTest.h
@@ -0,0 +1,128 @@
+//===- unittest/Tooling/ASTMatchersTest.h - Matcher tests helpers ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
+#define LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ast_matchers {
+
+using clang::tooling::newFrontendActionFactory;
+using clang::tooling::runToolOnCode;
+using clang::tooling::FrontendActionFactory;
+
+class BoundNodesCallback {
+public:
+ virtual ~BoundNodesCallback() {}
+ virtual bool run(const BoundNodes *BoundNodes) = 0;
+};
+
+// If 'FindResultVerifier' is not NULL, sets *Verified to the result of
+// running 'FindResultVerifier' with the bound nodes as argument.
+// If 'FindResultVerifier' is NULL, sets *Verified to true when Run is called.
+class VerifyMatch : public MatchFinder::MatchCallback {
+public:
+ VerifyMatch(BoundNodesCallback *FindResultVerifier, bool *Verified)
+ : Verified(Verified), FindResultReviewer(FindResultVerifier) {}
+
+ virtual void run(const MatchFinder::MatchResult &Result) {
+ if (FindResultReviewer != NULL) {
+ *Verified = FindResultReviewer->run(&Result.Nodes);
+ } else {
+ *Verified = true;
+ }
+ }
+
+private:
+ bool *const Verified;
+ BoundNodesCallback *const FindResultReviewer;
+};
+
+template <typename T>
+testing::AssertionResult matchesConditionally(const std::string &Code,
+ const T &AMatcher,
+ bool ExpectMatch) {
+ bool Found = false;
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher, new VerifyMatch(0, &Found));
+ OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+ if (!runToolOnCode(Factory->create(), Code)) {
+ return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
+ }
+ if (!Found && ExpectMatch) {
+ return testing::AssertionFailure()
+ << "Could not find match in \"" << Code << "\"";
+ } else if (Found && !ExpectMatch) {
+ return testing::AssertionFailure()
+ << "Found unexpected match in \"" << Code << "\"";
+ }
+ return testing::AssertionSuccess();
+}
+
+template <typename T>
+testing::AssertionResult matches(const std::string &Code, const T &AMatcher) {
+ return matchesConditionally(Code, AMatcher, true);
+}
+
+template <typename T>
+testing::AssertionResult notMatches(const std::string &Code,
+ const T &AMatcher) {
+ return matchesConditionally(Code, AMatcher, false);
+}
+
+template <typename T>
+testing::AssertionResult
+matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher,
+ BoundNodesCallback *FindResultVerifier,
+ bool ExpectResult) {
+ llvm::OwningPtr<BoundNodesCallback> ScopedVerifier(FindResultVerifier);
+ bool VerifiedResult = false;
+ MatchFinder Finder;
+ Finder.addMatcher(
+ AMatcher, new VerifyMatch(FindResultVerifier, &VerifiedResult));
+ OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+ if (!runToolOnCode(Factory->create(), Code)) {
+ return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
+ }
+ if (!VerifiedResult && ExpectResult) {
+ return testing::AssertionFailure()
+ << "Could not verify result in \"" << Code << "\"";
+ } else if (VerifiedResult && !ExpectResult) {
+ return testing::AssertionFailure()
+ << "Verified unexpected result in \"" << Code << "\"";
+ }
+ return testing::AssertionSuccess();
+}
+
+// FIXME: Find better names for these functions (or document what they
+// do more precisely).
+template <typename T>
+testing::AssertionResult
+matchAndVerifyResultTrue(const std::string &Code, const T &AMatcher,
+ BoundNodesCallback *FindResultVerifier) {
+ return matchAndVerifyResultConditionally(
+ Code, AMatcher, FindResultVerifier, true);
+}
+
+template <typename T>
+testing::AssertionResult
+matchAndVerifyResultFalse(const std::string &Code, const T &AMatcher,
+ BoundNodesCallback *FindResultVerifier) {
+ return matchAndVerifyResultConditionally(
+ Code, AMatcher, FindResultVerifier, false);
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
+
+#endif // LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
diff --git a/unittests/ASTMatchers/CMakeLists.txt b/unittests/ASTMatchers/CMakeLists.txt
new file mode 100644
index 0000000..b56d756
--- /dev/null
+++ b/unittests/ASTMatchers/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
+
+add_clang_unittest(ASTMatchersTests
+ ASTMatchersTest.cpp)
+
+target_link_libraries(ASTMatchersTests
+ gtest gtest_main clangASTMatchers clangTooling)
diff --git a/unittests/ASTMatchers/Makefile b/unittests/ASTMatchers/Makefile
new file mode 100644
index 0000000..d3e4aa37
--- /dev/null
+++ b/unittests/ASTMatchers/Makefile
@@ -0,0 +1,19 @@
+##===- unittests/ASTMatchers/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 = ASTMatchers
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
+USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
+ clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
+ clangAST.a clangASTMatchers.a clangLex.a clangBasic.a clangEdit.a
+
+include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt
new file mode 100644
index 0000000..300dcd5
--- /dev/null
+++ b/unittests/Basic/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_clang_unittest(BasicTests
+ FileManagerTest.cpp
+ SourceManagerTest.cpp
+ )
+
+target_link_libraries(BasicTests
+ clangBasic
+ clangLex
+ )
diff --git a/unittests/Basic/SourceManagerTest.cpp b/unittests/Basic/SourceManagerTest.cpp
index 429b58d..de3b723 100644
--- a/unittests/Basic/SourceManagerTest.cpp
+++ b/unittests/Basic/SourceManagerTest.cpp
@@ -107,6 +107,54 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnit) {
EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, macroExpEndLoc));
}
+TEST_F(SourceManagerTest, getColumnNumber) {
+ const char *Source =
+ "int x;\n"
+ "int y;";
+
+ MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(Source);
+ FileID MainFileID = SourceMgr.createMainFileIDForMemBuffer(Buf);
+
+ bool Invalid;
+
+ Invalid = false;
+ EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ EXPECT_EQ(5U, SourceMgr.getColumnNumber(MainFileID, 4, &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 7, &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ EXPECT_EQ(5U, SourceMgr.getColumnNumber(MainFileID, 11, &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ EXPECT_EQ(7U, SourceMgr.getColumnNumber(MainFileID, strlen(Source),
+ &Invalid));
+ EXPECT_TRUE(!Invalid);
+
+ Invalid = false;
+ SourceMgr.getColumnNumber(MainFileID, strlen(Source)+1, &Invalid);
+ EXPECT_TRUE(Invalid);
+
+ // Test invalid files
+ Invalid = false;
+ SourceMgr.getColumnNumber(FileID(), 0, &Invalid);
+ EXPECT_TRUE(Invalid);
+
+ Invalid = false;
+ SourceMgr.getColumnNumber(FileID(), 1, &Invalid);
+ EXPECT_TRUE(Invalid);
+
+ // Test with no invalid flag.
+ EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, NULL));
+}
+
#if defined(LLVM_ON_UNIX)
TEST_F(SourceManagerTest, getMacroArgExpandedLocation) {
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
index 0b3eac9..989025a 100644
--- a/unittests/CMakeLists.txt
+++ b/unittests/CMakeLists.txt
@@ -1,73 +1,17 @@
-include(LLVMParseArguments)
+add_custom_target(ClangUnitTests)
+set_target_properties(ClangUnitTests PROPERTIES FOLDER "Clang tests")
-# add_clang_unittest(test_dirname file1.cpp file2.cpp ...
-# [USED_LIBS lib1 lib2]
-# [LINK_COMPONENTS component1 component2])
+# add_clang_unittest(test_dirname file1.cpp file2.cpp)
#
# Will compile the list of files together and link against the clang
-# libraries in the USED_LIBS list and the llvm-config components in
-# the LINK_COMPONENTS list. Produces a binary named
-# 'basename(test_dirname)Tests'.
-function(add_clang_unittest)
- PARSE_ARGUMENTS(CLANG_UNITTEST "USED_LIBS;LINK_COMPONENTS" "" ${ARGN})
- set(LLVM_LINK_COMPONENTS ${CLANG_UNITTEST_LINK_COMPONENTS})
- set(LLVM_USED_LIBS ${CLANG_UNITTEST_USED_LIBS})
- list(GET CLANG_UNITTEST_DEFAULT_ARGS 0 test_dirname)
- list(REMOVE_AT CLANG_UNITTEST_DEFAULT_ARGS 0)
-
- string(REGEX MATCH "([^/]+)$" test_name ${test_dirname})
- if (CMAKE_BUILD_TYPE)
- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
- ${CLANG_BINARY_DIR}/unittests/${test_dirname}/${CMAKE_BUILD_TYPE})
- else()
- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
- ${CLANG_BINARY_DIR}/unittests/${test_dirname})
- endif()
- if( NOT LLVM_BUILD_TESTS )
- set(EXCLUDE_FROM_ALL ON)
- endif()
- add_clang_executable(${test_name}Tests ${CLANG_UNITTEST_DEFAULT_ARGS})
- add_dependencies(ClangUnitTests ${test_name}Tests)
- set_target_properties(${test_name}Tests PROPERTIES FOLDER "Clang tests")
+# Produces a binary named 'basename(test_dirname)'.
+function(add_clang_unittest test_dirname)
+ add_unittest(ClangUnitTests ${test_dirname} ${ARGN})
endfunction()
-add_custom_target(ClangUnitTests)
-set_target_properties(ClangUnitTests PROPERTIES FOLDER "Clang tests")
-
-include_directories(${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include)
-add_definitions(-DGTEST_HAS_RTTI=0)
-if( LLVM_COMPILER_IS_GCC_COMPATIBLE )
- llvm_replace_compiler_option(CMAKE_CXX_FLAGS "-frtti" "-fno-rtti")
-elseif( MSVC )
- llvm_replace_compiler_option(CMAKE_CXX_FLAGS "/GR" "/GR-")
-endif()
-
-if (NOT LLVM_ENABLE_THREADS)
- add_definitions(-DGTEST_HAS_PTHREAD=0)
-endif()
-
-if(SUPPORTS_NO_VARIADIC_MACROS_FLAG)
- add_definitions("-Wno-variadic-macros")
-endif()
-
-add_clang_unittest(Basic
- Basic/FileManagerTest.cpp
- Basic/SourceManagerTest.cpp
- USED_LIBS gtest gtest_main clangLex
- )
-
-add_clang_unittest(Lex
- Lex/LexerTest.cpp
- USED_LIBS gtest gtest_main clangLex
- )
-
-add_clang_unittest(Frontend
- Frontend/FrontendActionTest.cpp
- USED_LIBS gtest gtest_main clangFrontend
- )
-
-add_clang_unittest(Tooling
- Tooling/CompilationDatabaseTest.cpp
- Tooling/ToolingTest.cpp
- USED_LIBS gtest gtest_main clangTooling
- )
+add_subdirectory(ASTMatchers)
+add_subdirectory(AST)
+add_subdirectory(Basic)
+add_subdirectory(Lex)
+add_subdirectory(Frontend)
+add_subdirectory(Tooling)
diff --git a/unittests/Frontend/CMakeLists.txt b/unittests/Frontend/CMakeLists.txt
new file mode 100644
index 0000000..139cf42
--- /dev/null
+++ b/unittests/Frontend/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
+
+add_clang_unittest(FrontendTests
+ FrontendActionTest.cpp
+ )
+target_link_libraries(FrontendTests
+ clangFrontend
+ )
diff --git a/unittests/Frontend/FrontendActionTest.cpp b/unittests/Frontend/FrontendActionTest.cpp
index 2d4befc..84a6545 100644
--- a/unittests/Frontend/FrontendActionTest.cpp
+++ b/unittests/Frontend/FrontendActionTest.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
diff --git a/unittests/Frontend/Makefile b/unittests/Frontend/Makefile
index f3e6396..bfc3494 100644
--- a/unittests/Frontend/Makefile
+++ b/unittests/Frontend/Makefile
@@ -9,7 +9,8 @@
CLANG_LEVEL = ../..
TESTNAME = Frontend
-LINK_COMPONENTS := support mc
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \
clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \
clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \
diff --git a/unittests/Lex/CMakeLists.txt b/unittests/Lex/CMakeLists.txt
new file mode 100644
index 0000000..10c9361
--- /dev/null
+++ b/unittests/Lex/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_clang_unittest(LexTests
+ LexerTest.cpp
+ PreprocessingRecordTest.cpp
+ )
+
+target_link_libraries(LexTests
+ clangLex
+ )
diff --git a/unittests/Makefile b/unittests/Makefile
index 05449d8..f74820b 100644
--- a/unittests/Makefile
+++ b/unittests/Makefile
@@ -14,7 +14,7 @@ ifndef CLANG_LEVEL
IS_UNITTEST_LEVEL := 1
CLANG_LEVEL := ..
-PARALLEL_DIRS = Basic Frontend Lex Tooling
+PARALLEL_DIRS = ASTMatchers Basic AST Frontend Lex Tooling
endif # CLANG_LEVEL
diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt
new file mode 100644
index 0000000..4eaf339
--- /dev/null
+++ b/unittests/Tooling/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
+
+add_clang_unittest(ToolingTests
+ CommentHandlerTest.cpp
+ CompilationDatabaseTest.cpp
+ ToolingTest.cpp
+ RecursiveASTVisitorTest.cpp
+ RefactoringTest.cpp
+ RewriterTest.cpp
+ RefactoringCallbacksTest.cpp
+ )
+
+target_link_libraries(ToolingTests
+ clangAST
+ clangTooling
+ clangRewrite
+ )
diff --git a/unittests/Tooling/CommentHandlerTest.cpp b/unittests/Tooling/CommentHandlerTest.cpp
new file mode 100644
index 0000000..f0f7797
--- /dev/null
+++ b/unittests/Tooling/CommentHandlerTest.cpp
@@ -0,0 +1,221 @@
+//===- unittest/Tooling/CommentHandlerTest.cpp -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang {
+
+struct Comment {
+ Comment(const std::string &Message, unsigned Line, unsigned Col)
+ : Message(Message), Line(Line), Col(Col) { }
+
+ std::string Message;
+ unsigned Line, Col;
+};
+
+class CommentVerifier;
+typedef std::vector<Comment> CommentList;
+
+class CommentHandlerVisitor : public TestVisitor<CommentHandlerVisitor>,
+ public CommentHandler {
+ typedef TestVisitor<CommentHandlerVisitor> base;
+
+public:
+ CommentHandlerVisitor() : base(), PP(0), Verified(false) { }
+
+ ~CommentHandlerVisitor() {
+ EXPECT_TRUE(Verified) << "CommentVerifier not accessed";
+ }
+
+ virtual bool HandleComment(Preprocessor &PP, SourceRange Loc) {
+ assert(&PP == this->PP && "Preprocessor changed!");
+
+ SourceLocation Start = Loc.getBegin();
+ SourceManager &SM = PP.getSourceManager();
+ std::string C(SM.getCharacterData(Start),
+ SM.getCharacterData(Loc.getEnd()));
+
+ bool Invalid;
+ unsigned CLine = SM.getSpellingLineNumber(Start, &Invalid);
+ EXPECT_TRUE(!Invalid) << "Invalid line number on comment " << C;
+
+ unsigned CCol = SM.getSpellingColumnNumber(Start, &Invalid);
+ EXPECT_TRUE(!Invalid) << "Invalid column number on comment " << C;
+
+ Comments.push_back(Comment(C, CLine, CCol));
+ return false;
+ }
+
+ CommentVerifier GetVerifier();
+
+protected:
+ virtual ASTFrontendAction* CreateTestAction() {
+ return new CommentHandlerAction(this);
+ }
+
+private:
+ Preprocessor *PP;
+ CommentList Comments;
+ bool Verified;
+
+ class CommentHandlerAction : public base::TestAction {
+ public:
+ CommentHandlerAction(CommentHandlerVisitor *Visitor)
+ : TestAction(Visitor) { }
+
+ virtual bool BeginSourceFileAction(CompilerInstance &CI,
+ StringRef FileName) {
+ CommentHandlerVisitor *V =
+ static_cast<CommentHandlerVisitor*>(this->Visitor);
+ V->PP = &CI.getPreprocessor();
+ V->PP->addCommentHandler(V);
+ return true;
+ }
+
+ virtual void EndSourceFileAction() {
+ CommentHandlerVisitor *V =
+ static_cast<CommentHandlerVisitor*>(this->Visitor);
+ V->PP->removeCommentHandler(V);
+ }
+ };
+};
+
+class CommentVerifier {
+ CommentList::const_iterator Current;
+ CommentList::const_iterator End;
+ Preprocessor *PP;
+
+public:
+ CommentVerifier(const CommentList &Comments, Preprocessor *PP)
+ : Current(Comments.begin()), End(Comments.end()), PP(PP)
+ { }
+
+ ~CommentVerifier() {
+ if (Current != End) {
+ EXPECT_TRUE(Current == End) << "Unexpected comment \""
+ << Current->Message << "\" at line " << Current->Line << ", column "
+ << Current->Col;
+ }
+ }
+
+ void Match(const char *Message, unsigned Line, unsigned Col) {
+ EXPECT_TRUE(Current != End) << "Comment " << Message << " not found";
+ if (Current == End) return;
+
+ const Comment &C = *Current;
+ EXPECT_TRUE(C.Message == Message && C.Line == Line && C.Col == Col)
+ << "Expected comment \"" << Message
+ << "\" at line " << Line << ", column " << Col
+ << "\nActual comment \"" << C.Message
+ << "\" at line " << C.Line << ", column " << C.Col;
+
+ ++Current;
+ }
+};
+
+CommentVerifier CommentHandlerVisitor::GetVerifier() {
+ Verified = true;
+ return CommentVerifier(Comments, PP);
+}
+
+
+TEST(CommentHandlerTest, BasicTest1) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver("class X {}; int main() { return 0; }"));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+}
+
+TEST(CommentHandlerTest, BasicTest2) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "class X {}; int main() { /* comment */ return 0; }"));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("/* comment */", 1, 26);
+}
+
+TEST(CommentHandlerTest, BasicTest3) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "class X {}; // comment 1\n"
+ "int main() {\n"
+ " // comment 2\n"
+ " return 0;\n"
+ "}"));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("// comment 1", 1, 13);
+ Verifier.Match("// comment 2", 3, 3);
+}
+
+TEST(CommentHandlerTest, IfBlock1) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "#if 0\n"
+ "// ignored comment\n"
+ "#endif\n"
+ "// visible comment\n"));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("// visible comment", 4, 1);
+}
+
+TEST(CommentHandlerTest, IfBlock2) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "#define TEST // visible_1\n"
+ "#ifndef TEST // visible_2\n"
+ " // ignored_3\n"
+ "# ifdef UNDEFINED // ignored_4\n"
+ "# endif // ignored_5\n"
+ "#elif defined(TEST) // visible_6\n"
+ "# if 1 // visible_7\n"
+ " // visible_8\n"
+ "# else // visible_9\n"
+ " // ignored_10\n"
+ "# ifndef TEST // ignored_11\n"
+ "# endif // ignored_12\n"
+ "# endif // visible_13\n"
+ "#endif // visible_14\n"));
+
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("// visible_1", 1, 21);
+ Verifier.Match("// visible_2", 2, 21);
+ Verifier.Match("// visible_6", 6, 21);
+ Verifier.Match("// visible_7", 7, 21);
+ Verifier.Match("// visible_8", 8, 21);
+ Verifier.Match("// visible_9", 9, 21);
+ Verifier.Match("// visible_13", 13, 21);
+ Verifier.Match("// visible_14", 14, 21);
+}
+
+TEST(CommentHandlerTest, IfBlock3) {
+ const char *Source =
+ "/* commented out ...\n"
+ "#if 0\n"
+ "// enclosed\n"
+ "#endif */";
+
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(Source));
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match(Source, 1, 1);
+}
+
+TEST(CommentHandlerTest, PPDirectives) {
+ CommentHandlerVisitor Visitor;
+ EXPECT_TRUE(Visitor.runOver(
+ "#warning Y // ignored_1\n" // #warning takes whole line as message
+ "#undef MACRO // visible_2\n"
+ "#line 1 // visible_3\n"));
+
+ CommentVerifier Verifier = Visitor.GetVerifier();
+ Verifier.Match("// visible_2", 2, 14);
+ Verifier.Match("// visible_3", 3, 14);
+}
+
+} // end namespace clang
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp
index 68d2896..591d48d 100644
--- a/unittests/Tooling/CompilationDatabaseTest.cpp
+++ b/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -18,6 +18,55 @@
namespace clang {
namespace tooling {
+static void expectFailure(StringRef JSONDatabase, StringRef Explanation) {
+ std::string ErrorMessage;
+ EXPECT_EQ(NULL, JSONCompilationDatabase::loadFromBuffer(JSONDatabase,
+ ErrorMessage))
+ << "Expected an error because of: " << Explanation;
+}
+
+TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) {
+ expectFailure("", "Empty database");
+ expectFailure("{", "Invalid JSON");
+ expectFailure("[[]]", "Array instead of object");
+ expectFailure("[{\"a\":[]}]", "Array instead of value");
+ expectFailure("[{\"a\":\"b\"}]", "Unknown key");
+ expectFailure("[{[]:\"\"}]", "Incorrectly typed entry");
+ expectFailure("[{}]", "Empty entry");
+ expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file");
+ expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command");
+ expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory");
+}
+
+static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
+ std::string &ErrorMessage) {
+ llvm::OwningPtr<CompilationDatabase> Database(
+ JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage));
+ if (!Database) {
+ ADD_FAILURE() << ErrorMessage;
+ return std::vector<std::string>();
+ }
+ return Database->getAllFiles();
+}
+
+TEST(JSONCompilationDatabase, GetAllFiles) {
+ std::string ErrorMessage;
+ EXPECT_EQ(std::vector<std::string>(),
+ getAllFiles("[]", ErrorMessage)) << ErrorMessage;
+
+ std::vector<std::string> expected_files;
+ expected_files.push_back("file1");
+ expected_files.push_back("file2");
+ EXPECT_EQ(expected_files, getAllFiles(
+ "[{\"directory\":\"dir\","
+ "\"command\":\"command\","
+ "\"file\":\"file1\"},"
+ " {\"directory\":\"dir\","
+ "\"command\":\"command\","
+ "\"file\":\"file2\"}]",
+ ErrorMessage)) << ErrorMessage;
+}
+
static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
StringRef JSONDatabase,
std::string &ErrorMessage) {
@@ -235,6 +284,15 @@ TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine);
}
+TEST(FixedCompilationDatabase, GetAllFiles) {
+ std::vector<std::string> CommandLine;
+ CommandLine.push_back("one");
+ CommandLine.push_back("two");
+ FixedCompilationDatabase Database(".", CommandLine);
+
+ EXPECT_EQ(0ul, Database.getAllFiles().size());
+}
+
TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
int Argc = 0;
llvm::OwningPtr<FixedCompilationDatabase> Database(
diff --git a/unittests/Tooling/Makefile b/unittests/Tooling/Makefile
index 0829da5..5d2224d 100644
--- a/unittests/Tooling/Makefile
+++ b/unittests/Tooling/Makefile
@@ -9,9 +9,10 @@
CLANG_LEVEL = ../..
TESTNAME = Tooling
-LINK_COMPONENTS := support mc
+include $(CLANG_LEVEL)/../../Makefile.config
+LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
- clangParse.a clangSema.a clangAnalysis.a clangEdit.a clangAST.a \
- clangLex.a clangBasic.a
+ clangParse.a clangRewrite.a clangSema.a clangAnalysis.a clangEdit.a \
+ clangAST.a clangASTMatchers.a clangLex.a clangBasic.a
include $(CLANG_LEVEL)/unittests/Makefile
diff --git a/unittests/Tooling/RecursiveASTVisitorTest.cpp b/unittests/Tooling/RecursiveASTVisitorTest.cpp
new file mode 100644
index 0000000..f3ba646
--- /dev/null
+++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp
@@ -0,0 +1,388 @@
+//===- unittest/Tooling/RecursiveASTVisitorTest.cpp -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+
+namespace clang {
+
+class TypeLocVisitor : public ExpectedLocationVisitor<TypeLocVisitor> {
+public:
+ bool VisitTypeLoc(TypeLoc TypeLocation) {
+ Match(TypeLocation.getType().getAsString(), TypeLocation.getBeginLoc());
+ return true;
+ }
+};
+
+class DeclRefExprVisitor : public ExpectedLocationVisitor<DeclRefExprVisitor> {
+public:
+ bool VisitDeclRefExpr(DeclRefExpr *Reference) {
+ Match(Reference->getNameInfo().getAsString(), Reference->getLocation());
+ return true;
+ }
+};
+
+class VarDeclVisitor : public ExpectedLocationVisitor<VarDeclVisitor> {
+public:
+ bool VisitVarDecl(VarDecl *Variable) {
+ Match(Variable->getNameAsString(), Variable->getLocStart());
+ return true;
+ }
+};
+
+class CXXMemberCallVisitor
+ : public ExpectedLocationVisitor<CXXMemberCallVisitor> {
+public:
+ bool VisitCXXMemberCallExpr(CXXMemberCallExpr *Call) {
+ Match(Call->getMethodDecl()->getQualifiedNameAsString(),
+ Call->getLocStart());
+ return true;
+ }
+};
+
+class NamedDeclVisitor
+ : public ExpectedLocationVisitor<NamedDeclVisitor> {
+public:
+ bool VisitNamedDecl(NamedDecl *Decl) {
+ std::string NameWithTemplateArgs;
+ Decl->getNameForDiagnostic(NameWithTemplateArgs,
+ Decl->getASTContext().getPrintingPolicy(),
+ true);
+ Match(NameWithTemplateArgs, Decl->getLocation());
+ return true;
+ }
+};
+
+class CXXOperatorCallExprTraverser
+ : public ExpectedLocationVisitor<CXXOperatorCallExprTraverser> {
+public:
+ // Use Traverse, not Visit, to check that data recursion optimization isn't
+ // bypassing the call of this function.
+ bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *CE) {
+ Match(getOperatorSpelling(CE->getOperator()), CE->getExprLoc());
+ return ExpectedLocationVisitor<CXXOperatorCallExprTraverser>::
+ TraverseCXXOperatorCallExpr(CE);
+ }
+};
+
+class ParenExprVisitor : public ExpectedLocationVisitor<ParenExprVisitor> {
+public:
+ bool VisitParenExpr(ParenExpr *Parens) {
+ Match("", Parens->getExprLoc());
+ return true;
+ }
+};
+
+class TemplateArgumentLocTraverser
+ : public ExpectedLocationVisitor<TemplateArgumentLocTraverser> {
+public:
+ bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
+ std::string ArgStr;
+ llvm::raw_string_ostream Stream(ArgStr);
+ const TemplateArgument &Arg = ArgLoc.getArgument();
+
+ Arg.print(Context->getPrintingPolicy(), Stream);
+ Match(Stream.str(), ArgLoc.getLocation());
+ return ExpectedLocationVisitor<TemplateArgumentLocTraverser>::
+ TraverseTemplateArgumentLoc(ArgLoc);
+ }
+};
+
+class CXXBoolLiteralExprVisitor
+ : public ExpectedLocationVisitor<CXXBoolLiteralExprVisitor> {
+public:
+ bool VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *BE) {
+ if (BE->getValue())
+ Match("true", BE->getLocation());
+ else
+ Match("false", BE->getLocation());
+ return true;
+ }
+};
+
+TEST(RecursiveASTVisitor, VisitsBaseClassDeclarations) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("class X", 1, 30);
+ EXPECT_TRUE(Visitor.runOver("class X {}; class Y : public X {};"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersOfForwardDeclaredClass) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("class X", 3, 18);
+ EXPECT_TRUE(Visitor.runOver(
+ "class Y;\n"
+ "class X {};\n"
+ "class Y : public X {};"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersWithIncompleteInnerClass) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("class X", 2, 18);
+ EXPECT_TRUE(Visitor.runOver(
+ "class X {};\n"
+ "class Y : public X { class Z; };"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXBaseSpecifiersOfSelfReferentialType) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("X<class Y>", 2, 18);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> class X {};\n"
+ "class Y : public X<Y> {};"));
+}
+
+TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArguments) {
+ DeclRefExprVisitor Visitor;
+ Visitor.ExpectMatch("x", 2, 3);
+ EXPECT_TRUE(Visitor.runOver(
+ "void x(); template <void (*T)()> class X {};\nX<x> y;"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtRange) {
+ DeclRefExprVisitor Visitor;
+ Visitor.ExpectMatch("x", 2, 25);
+ Visitor.ExpectMatch("x", 2, 30);
+ EXPECT_TRUE(Visitor.runOver(
+ "int x[5];\n"
+ "void f() { for (int i : x) { x[0] = 1; } }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtLoopVariable) {
+ VarDeclVisitor Visitor;
+ Visitor.ExpectMatch("i", 2, 17);
+ EXPECT_TRUE(Visitor.runOver(
+ "int x[5];\n"
+ "void f() { for (int i : x) {} }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCallExpr) {
+ DeclRefExprVisitor Visitor;
+ Visitor.ExpectMatch("x", 1, 22);
+ EXPECT_TRUE(Visitor.runOver(
+ "void x(); void y() { x(); }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCallInTemplateInstantiation) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("Y::x", 3, 3);
+ EXPECT_TRUE(Visitor.runOver(
+ "struct Y { void x(); };\n"
+ "template<typename T> void y(T t) {\n"
+ " t.x();\n"
+ "}\n"
+ "void foo() { y<Y>(Y()); }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCallInNestedFunctionTemplateInstantiation) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("Y::x", 4, 5);
+ EXPECT_TRUE(Visitor.runOver(
+ "struct Y { void x(); };\n"
+ "template<typename T> struct Z {\n"
+ " template<typename U> static void f() {\n"
+ " T().x();\n"
+ " }\n"
+ "};\n"
+ "void foo() { Z<Y>::f<int>(); }"));
+}
+
+TEST(RecursiveASTVisitor, VisitsCallInNestedClassTemplateInstantiation) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("A::x", 5, 7);
+ EXPECT_TRUE(Visitor.runOver(
+ "template <typename T1> struct X {\n"
+ " template <typename T2> struct Y {\n"
+ " void f() {\n"
+ " T2 y;\n"
+ " y.x();\n"
+ " }\n"
+ " };\n"
+ "};\n"
+ "struct A { void x(); };\n"
+ "int main() {\n"
+ " (new X<A>::Y<A>())->f();\n"
+ "}"));
+}
+
+/* FIXME: According to Richard Smith this is a bug in the AST.
+TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArgumentsInInstantiation) {
+ DeclRefExprVisitor Visitor;
+ Visitor.ExpectMatch("x", 3, 43);
+ EXPECT_TRUE(Visitor.runOver(
+ "template <typename T> void x();\n"
+ "template <void (*T)()> class X {};\n"
+ "template <typename T> class Y : public X< x<T> > {};\n"
+ "Y<int> y;"));
+}
+*/
+
+TEST(RecursiveASTVisitor, VisitsCallInPartialTemplateSpecialization) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("A::x", 6, 20);
+ EXPECT_TRUE(Visitor.runOver(
+ "template <typename T1> struct X {\n"
+ " template <typename T2, bool B> struct Y { void g(); };\n"
+ "};\n"
+ "template <typename T1> template <typename T2>\n"
+ "struct X<T1>::Y<T2, true> {\n"
+ " void f() { T2 y; y.x(); }\n"
+ "};\n"
+ "struct A { void x(); };\n"
+ "int main() {\n"
+ " (new X<A>::Y<A, true>())->f();\n"
+ "}\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsExplicitTemplateSpecialization) {
+ CXXMemberCallVisitor Visitor;
+ Visitor.ExpectMatch("A::f", 4, 5);
+ EXPECT_TRUE(Visitor.runOver(
+ "struct A {\n"
+ " void f() const {}\n"
+ " template<class T> void g(const T& t) const {\n"
+ " t.f();\n"
+ " }\n"
+ "};\n"
+ "template void A::g(const A& a) const;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsPartialTemplateSpecialization) {
+ // From cfe-commits/Week-of-Mon-20100830/033998.html
+ // Contrary to the approach suggested in that email, we visit all
+ // specializations when we visit the primary template. Visiting them when we
+ // visit the associated specialization is problematic for specializations of
+ // template members of class templates.
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<bool>", 1, 26);
+ Visitor.ExpectMatch("A<char *>", 2, 26);
+ EXPECT_TRUE(Visitor.runOver(
+ "template <class T> class A {};\n"
+ "template <class T> class A<T*> {};\n"
+ "A<bool> ab;\n"
+ "A<char*> acp;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsUndefinedClassTemplateSpecialization) {
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<int>", 1, 29);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> struct A;\n"
+ "A<int> *p;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsNestedUndefinedClassTemplateSpecialization) {
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<int>::B<char>", 2, 31);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> struct A {\n"
+ " template<typename U> struct B;\n"
+ "};\n"
+ "A<int>::B<char> *p;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsUndefinedFunctionTemplateSpecialization) {
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<int>", 1, 26);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> int A();\n"
+ "int k = A<int>();\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsNestedUndefinedFunctionTemplateSpecialization) {
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("A<int>::B<char>", 2, 35);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> struct A {\n"
+ " template<typename U> static int B();\n"
+ "};\n"
+ "int k = A<int>::B<char>();\n"));
+}
+
+TEST(RecursiveASTVisitor, NoRecursionInSelfFriend) {
+ // From cfe-commits/Week-of-Mon-20100830/033977.html
+ NamedDeclVisitor Visitor;
+ Visitor.ExpectMatch("vector_iterator<int>", 2, 7);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename Container>\n"
+ "class vector_iterator {\n"
+ " template <typename C> friend class vector_iterator;\n"
+ "};\n"
+ "vector_iterator<int> it_int;\n"));
+}
+
+TEST(RecursiveASTVisitor, TraversesOverloadedOperator) {
+ CXXOperatorCallExprTraverser Visitor;
+ Visitor.ExpectMatch("()", 4, 9);
+ EXPECT_TRUE(Visitor.runOver(
+ "struct A {\n"
+ " int operator()();\n"
+ "} a;\n"
+ "int k = a();\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsParensDuringDataRecursion) {
+ ParenExprVisitor Visitor;
+ Visitor.ExpectMatch("", 1, 9);
+ EXPECT_TRUE(Visitor.runOver("int k = (4) + 9;\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsClassTemplateNonTypeParmDefaultArgument) {
+ CXXBoolLiteralExprVisitor Visitor;
+ Visitor.ExpectMatch("true", 2, 19);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<bool B> class X;\n"
+ "template<bool B = true> class Y;\n"
+ "template<bool B> class Y {};\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsClassTemplateTypeParmDefaultArgument) {
+ TypeLocVisitor Visitor;
+ Visitor.ExpectMatch("class X", 2, 23);
+ EXPECT_TRUE(Visitor.runOver(
+ "class X;\n"
+ "template<typename T = X> class Y;\n"
+ "template<typename T> class Y {};\n"));
+}
+
+TEST(RecursiveASTVisitor, VisitsClassTemplateTemplateParmDefaultArgument) {
+ TemplateArgumentLocTraverser Visitor;
+ Visitor.ExpectMatch("X", 2, 40);
+ EXPECT_TRUE(Visitor.runOver(
+ "template<typename T> class X;\n"
+ "template<template <typename> class T = X> class Y;\n"
+ "template<template <typename> class T> class Y {};\n"));
+}
+
+// A visitor that visits implicit declarations and matches constructors.
+class ImplicitCtorVisitor
+ : public ExpectedLocationVisitor<ImplicitCtorVisitor> {
+public:
+ bool shouldVisitImplicitCode() const { return true; }
+
+ bool VisitCXXConstructorDecl(CXXConstructorDecl* Ctor) {
+ if (Ctor->isImplicit()) { // Was not written in source code
+ if (const CXXRecordDecl* Class = Ctor->getParent()) {
+ Match(Class->getName(), Ctor->getLocation());
+ }
+ }
+ return true;
+ }
+};
+
+TEST(RecursiveASTVisitor, VisitsImplicitCopyConstructors) {
+ ImplicitCtorVisitor Visitor;
+ Visitor.ExpectMatch("Simple", 2, 8);
+ // Note: Clang lazily instantiates implicit declarations, so we need
+ // to use them in order to force them to appear in the AST.
+ EXPECT_TRUE(Visitor.runOver(
+ "struct WithCtor { WithCtor(); }; \n"
+ "struct Simple { Simple(); WithCtor w; }; \n"
+ "int main() { Simple s; Simple t(s); }\n"));
+}
+
+} // end namespace clang
diff --git a/unittests/Tooling/RefactoringCallbacksTest.cpp b/unittests/Tooling/RefactoringCallbacksTest.cpp
new file mode 100644
index 0000000..00eb193
--- /dev/null
+++ b/unittests/Tooling/RefactoringCallbacksTest.cpp
@@ -0,0 +1,100 @@
+//===- unittest/ASTMatchers/RefactoringCallbacksTest.cpp ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/RefactoringCallbacks.h"
+#include "gtest/gtest.h"
+#include "RewriterTestContext.h"
+
+namespace clang {
+namespace tooling {
+
+using namespace ast_matchers;
+
+template <typename T>
+void expectRewritten(const std::string &Code,
+ const std::string &Expected,
+ const T &AMatcher,
+ RefactoringCallback &Callback) {
+ MatchFinder Finder;
+ Finder.addMatcher(AMatcher, &Callback);
+ OwningPtr<tooling::FrontendActionFactory> Factory(
+ tooling::newFrontendActionFactory(&Finder));
+ ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code))
+ << "Parsing error in \"" << Code << "\"";
+ RewriterTestContext Context;
+ FileID ID = Context.createInMemoryFile("input.cc", Code);
+ EXPECT_TRUE(tooling::applyAllReplacements(Callback.getReplacements(),
+ Context.Rewrite));
+ EXPECT_EQ(Expected, Context.getRewrittenText(ID));
+}
+
+TEST(RefactoringCallbacksTest, ReplacesStmtsWithString) {
+ std::string Code = "void f() { int i = 1; }";
+ std::string Expected = "void f() { ; }";
+ ReplaceStmtWithText Callback("id", ";");
+ expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesStmtsInCalledMacros) {
+ std::string Code = "#define A void f() { int i = 1; }\nA";
+ std::string Expected = "#define A void f() { ; }\nA";
+ ReplaceStmtWithText Callback("id", ";");
+ expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, IgnoresStmtsInUncalledMacros) {
+ std::string Code = "#define A void f() { int i = 1; }";
+ std::string Expected = "#define A void f() { int i = 1; }";
+ ReplaceStmtWithText Callback("id", ";");
+ expectRewritten(Code, Expected, id("id", declarationStatement()), Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesInteger) {
+ std::string Code = "void f() { int i = 1; }";
+ std::string Expected = "void f() { int i = 2; }";
+ ReplaceStmtWithText Callback("id", "2");
+ expectRewritten(Code, Expected, id("id", expression(integerLiteral())),
+ Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) {
+ std::string Code = "void f() { int i = false ? 1 : i * 2; }";
+ std::string Expected = "void f() { int i = i * 2; }";
+ ReplaceStmtWithStmt Callback("always-false", "should-be");
+ expectRewritten(Code, Expected,
+ id("always-false", conditionalOperator(
+ hasCondition(boolLiteral(equals(false))),
+ hasFalseExpression(id("should-be", expression())))),
+ Callback);
+}
+
+TEST(RefactoringCallbacksTest, ReplacesIfStmt) {
+ std::string Code = "bool a; void f() { if (a) f(); else a = true; }";
+ std::string Expected = "bool a; void f() { f(); }";
+ ReplaceIfStmtWithItsBody Callback("id", true);
+ expectRewritten(Code, Expected,
+ id("id", ifStmt(
+ hasCondition(implicitCast(hasSourceExpression(
+ declarationReference(to(variable(hasName("a"))))))))),
+ Callback);
+}
+
+TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) {
+ std::string Code = "void f() { if (false) int i = 0; }";
+ std::string Expected = "void f() { }";
+ ReplaceIfStmtWithItsBody Callback("id", false);
+ expectRewritten(Code, Expected,
+ id("id", ifStmt(hasCondition(boolLiteral(equals(false))))),
+ Callback);
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp
new file mode 100644
index 0000000..8d96955
--- /dev/null
+++ b/unittests/Tooling/RefactoringTest.cpp
@@ -0,0 +1,305 @@
+//===- unittest/Tooling/RefactoringTest.cpp - Refactoring unit tests ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RewriterTestContext.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/DiagnosticOptions.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tooling {
+
+class ReplacementTest : public ::testing::Test {
+ protected:
+ Replacement createReplacement(SourceLocation Start, unsigned Length,
+ llvm::StringRef ReplacementText) {
+ return Replacement(Context.Sources, Start, Length, ReplacementText);
+ }
+
+ RewriterTestContext Context;
+};
+
+TEST_F(ReplacementTest, CanDeleteAllText) {
+ FileID ID = Context.createInMemoryFile("input.cpp", "text");
+ SourceLocation Location = Context.getLocation(ID, 1, 1);
+ Replacement Replace(createReplacement(Location, 4, ""));
+ EXPECT_TRUE(Replace.apply(Context.Rewrite));
+ EXPECT_EQ("", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, CanDeleteAllTextInTextWithNewlines) {
+ FileID ID = Context.createInMemoryFile("input.cpp", "line1\nline2\nline3");
+ SourceLocation Location = Context.getLocation(ID, 1, 1);
+ Replacement Replace(createReplacement(Location, 17, ""));
+ EXPECT_TRUE(Replace.apply(Context.Rewrite));
+ EXPECT_EQ("", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, CanAddText) {
+ FileID ID = Context.createInMemoryFile("input.cpp", "");
+ SourceLocation Location = Context.getLocation(ID, 1, 1);
+ Replacement Replace(createReplacement(Location, 0, "result"));
+ EXPECT_TRUE(Replace.apply(Context.Rewrite));
+ EXPECT_EQ("result", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, CanReplaceTextAtPosition) {
+ FileID ID = Context.createInMemoryFile("input.cpp",
+ "line1\nline2\nline3\nline4");
+ SourceLocation Location = Context.getLocation(ID, 2, 3);
+ Replacement Replace(createReplacement(Location, 12, "x"));
+ EXPECT_TRUE(Replace.apply(Context.Rewrite));
+ EXPECT_EQ("line1\nlixne4", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, CanReplaceTextAtPositionMultipleTimes) {
+ FileID ID = Context.createInMemoryFile("input.cpp",
+ "line1\nline2\nline3\nline4");
+ SourceLocation Location1 = Context.getLocation(ID, 2, 3);
+ Replacement Replace1(createReplacement(Location1, 12, "x\ny\n"));
+ EXPECT_TRUE(Replace1.apply(Context.Rewrite));
+ EXPECT_EQ("line1\nlix\ny\nne4", Context.getRewrittenText(ID));
+
+ // Since the original source has not been modified, the (4, 4) points to the
+ // 'e' in the original content.
+ SourceLocation Location2 = Context.getLocation(ID, 4, 4);
+ Replacement Replace2(createReplacement(Location2, 1, "f"));
+ EXPECT_TRUE(Replace2.apply(Context.Rewrite));
+ EXPECT_EQ("line1\nlix\ny\nnf4", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, ApplyFailsForNonExistentLocation) {
+ Replacement Replace("nonexistent-file.cpp", 0, 1, "");
+ EXPECT_FALSE(Replace.apply(Context.Rewrite));
+}
+
+TEST_F(ReplacementTest, CanRetrivePath) {
+ Replacement Replace("/path/to/file.cpp", 0, 1, "");
+ EXPECT_EQ("/path/to/file.cpp", Replace.getFilePath());
+}
+
+TEST_F(ReplacementTest, ReturnsInvalidPath) {
+ Replacement Replace1(Context.Sources, SourceLocation(), 0, "");
+ EXPECT_TRUE(Replace1.getFilePath().empty());
+
+ Replacement Replace2;
+ EXPECT_TRUE(Replace2.getFilePath().empty());
+}
+
+TEST_F(ReplacementTest, CanApplyReplacements) {
+ FileID ID = Context.createInMemoryFile("input.cpp",
+ "line1\nline2\nline3\nline4");
+ Replacements Replaces;
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 3, 1),
+ 5, "other"));
+ EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
+ EXPECT_EQ("line1\nreplaced\nother\nline4", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, SkipsDuplicateReplacements) {
+ FileID ID = Context.createInMemoryFile("input.cpp",
+ "line1\nline2\nline3\nline4");
+ Replacements Replaces;
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
+ EXPECT_EQ("line1\nreplaced\nline3\nline4", Context.getRewrittenText(ID));
+}
+
+TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) {
+ // This test depends on the value of the file name of an invalid source
+ // location being in the range ]a, z[.
+ FileID IDa = Context.createInMemoryFile("a.cpp", "text");
+ FileID IDz = Context.createInMemoryFile("z.cpp", "text");
+ Replacements Replaces;
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDa, 1, 1),
+ 4, "a"));
+ Replaces.insert(Replacement(Context.Sources, SourceLocation(),
+ 5, "2"));
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(IDz, 1, 1),
+ 4, "z"));
+ EXPECT_FALSE(applyAllReplacements(Replaces, Context.Rewrite));
+ EXPECT_EQ("a", Context.getRewrittenText(IDa));
+ EXPECT_EQ("z", Context.getRewrittenText(IDz));
+}
+
+class FlushRewrittenFilesTest : public ::testing::Test {
+ public:
+ FlushRewrittenFilesTest() {
+ std::string ErrorInfo;
+ TemporaryDirectory = llvm::sys::Path::GetTemporaryDirectory(&ErrorInfo);
+ assert(ErrorInfo.empty());
+ }
+
+ ~FlushRewrittenFilesTest() {
+ std::string ErrorInfo;
+ TemporaryDirectory.eraseFromDisk(true, &ErrorInfo);
+ assert(ErrorInfo.empty());
+ }
+
+ FileID createFile(llvm::StringRef Name, llvm::StringRef Content) {
+ llvm::SmallString<1024> Path(TemporaryDirectory.str());
+ llvm::sys::path::append(Path, Name);
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream OutStream(Path.c_str(),
+ ErrorInfo, llvm::raw_fd_ostream::F_Binary);
+ assert(ErrorInfo.empty());
+ OutStream << Content;
+ OutStream.close();
+ const FileEntry *File = Context.Files.getFile(Path);
+ assert(File != NULL);
+ return Context.Sources.createFileID(File, SourceLocation(), SrcMgr::C_User);
+ }
+
+ std::string getFileContentFromDisk(llvm::StringRef Name) {
+ llvm::SmallString<1024> Path(TemporaryDirectory.str());
+ llvm::sys::path::append(Path, Name);
+ // We need to read directly from the FileManager without relaying through
+ // a FileEntry, as otherwise we'd read through an already opened file
+ // descriptor, which might not see the changes made.
+ // FIXME: Figure out whether there is a way to get the SourceManger to
+ // reopen the file.
+ return Context.Files.getBufferForFile(Path, NULL)->getBuffer();
+ }
+
+ llvm::sys::Path TemporaryDirectory;
+ RewriterTestContext Context;
+};
+
+TEST_F(FlushRewrittenFilesTest, StoresChangesOnDisk) {
+ FileID ID = createFile("input.cpp", "line1\nline2\nline3\nline4");
+ Replacements Replaces;
+ Replaces.insert(Replacement(Context.Sources, Context.getLocation(ID, 2, 1),
+ 5, "replaced"));
+ EXPECT_TRUE(applyAllReplacements(Replaces, Context.Rewrite));
+ EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
+ EXPECT_EQ("line1\nreplaced\nline3\nline4",
+ getFileContentFromDisk("input.cpp"));
+}
+
+namespace {
+template <typename T>
+class TestVisitor : public clang::RecursiveASTVisitor<T> {
+public:
+ bool runOver(StringRef Code) {
+ return runToolOnCode(new TestAction(this), Code);
+ }
+
+protected:
+ clang::SourceManager *SM;
+
+private:
+ class FindConsumer : public clang::ASTConsumer {
+ public:
+ FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
+
+ virtual void HandleTranslationUnit(clang::ASTContext &Context) {
+ Visitor->TraverseDecl(Context.getTranslationUnitDecl());
+ }
+
+ private:
+ TestVisitor *Visitor;
+ };
+
+ class TestAction : public clang::ASTFrontendAction {
+ public:
+ TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
+
+ virtual clang::ASTConsumer* CreateASTConsumer(
+ clang::CompilerInstance& compiler, llvm::StringRef dummy) {
+ Visitor->SM = &compiler.getSourceManager();
+ /// TestConsumer will be deleted by the framework calling us.
+ return new FindConsumer(Visitor);
+ }
+
+ private:
+ TestVisitor *Visitor;
+ };
+};
+} // end namespace
+
+void expectReplacementAt(const Replacement &Replace,
+ StringRef File, unsigned Offset, unsigned Length) {
+ ASSERT_TRUE(Replace.isApplicable());
+ EXPECT_EQ(File, Replace.getFilePath());
+ EXPECT_EQ(Offset, Replace.getOffset());
+ EXPECT_EQ(Length, Replace.getLength());
+}
+
+class ClassDeclXVisitor : public TestVisitor<ClassDeclXVisitor> {
+public:
+ bool VisitCXXRecordDecl(CXXRecordDecl *Record) {
+ if (Record->getName() == "X") {
+ Replace = Replacement(*SM, Record, "");
+ }
+ return true;
+ }
+ Replacement Replace;
+};
+
+TEST(Replacement, CanBeConstructedFromNode) {
+ ClassDeclXVisitor ClassDeclX;
+ EXPECT_TRUE(ClassDeclX.runOver(" class X;"));
+ expectReplacementAt(ClassDeclX.Replace, "input.cc", 5, 7);
+}
+
+TEST(Replacement, ReplacesAtSpellingLocation) {
+ ClassDeclXVisitor ClassDeclX;
+ EXPECT_TRUE(ClassDeclX.runOver("#define A(Y) Y\nA(class X);"));
+ expectReplacementAt(ClassDeclX.Replace, "input.cc", 17, 7);
+}
+
+class CallToFVisitor : public TestVisitor<CallToFVisitor> {
+public:
+ bool VisitCallExpr(CallExpr *Call) {
+ if (Call->getDirectCallee()->getName() == "F") {
+ Replace = Replacement(*SM, Call, "");
+ }
+ return true;
+ }
+ Replacement Replace;
+};
+
+TEST(Replacement, FunctionCall) {
+ CallToFVisitor CallToF;
+ EXPECT_TRUE(CallToF.runOver("void F(); void G() { F(); }"));
+ expectReplacementAt(CallToF.Replace, "input.cc", 21, 3);
+}
+
+TEST(Replacement, TemplatedFunctionCall) {
+ CallToFVisitor CallToF;
+ EXPECT_TRUE(CallToF.runOver(
+ "template <typename T> void F(); void G() { F<int>(); }"));
+ expectReplacementAt(CallToF.Replace, "input.cc", 43, 8);
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/unittests/Tooling/RewriterTest.cpp b/unittests/Tooling/RewriterTest.cpp
new file mode 100644
index 0000000..c53e50a
--- /dev/null
+++ b/unittests/Tooling/RewriterTest.cpp
@@ -0,0 +1,37 @@
+//===- unittest/Tooling/RewriterTest.cpp ----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RewriterTestContext.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+
+TEST(Rewriter, OverwritesChangedFiles) {
+ RewriterTestContext Context;
+ FileID ID = Context.createOnDiskFile("t.cpp", "line1\nline2\nline3\nline4");
+ Context.Rewrite.ReplaceText(Context.getLocation(ID, 2, 1), 5, "replaced");
+ EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
+ EXPECT_EQ("line1\nreplaced\nline3\nline4",
+ Context.getFileContentFromDisk("t.cpp"));
+}
+
+TEST(Rewriter, ContinuesOverwritingFilesOnError) {
+ RewriterTestContext Context;
+ FileID FailingID = Context.createInMemoryFile("invalid/failing.cpp", "test");
+ Context.Rewrite.ReplaceText(Context.getLocation(FailingID, 1, 2), 1, "other");
+ FileID WorkingID = Context.createOnDiskFile(
+ "working.cpp", "line1\nline2\nline3\nline4");
+ Context.Rewrite.ReplaceText(Context.getLocation(WorkingID, 2, 1), 5,
+ "replaced");
+ EXPECT_TRUE(Context.Rewrite.overwriteChangedFiles());
+ EXPECT_EQ("line1\nreplaced\nline3\nline4",
+ Context.getFileContentFromDisk("working.cpp"));
+}
+
+} // end namespace clang
diff --git a/unittests/Tooling/RewriterTestContext.h b/unittests/Tooling/RewriterTestContext.h
new file mode 100644
index 0000000..f68be6b
--- /dev/null
+++ b/unittests/Tooling/RewriterTestContext.h
@@ -0,0 +1,125 @@
+//===--- RewriterTestContext.h ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a utility class for Rewriter related tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_REWRITER_TEST_CONTEXT_H
+#define LLVM_CLANG_REWRITER_TEST_CONTEXT_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/DiagnosticOptions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Rewriter.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+
+/// \brief A class that sets up a ready to use Rewriter.
+///
+/// Useful in unit tests that need a Rewriter. Creates all dependencies
+/// of a Rewriter with default values for testing and provides convenience
+/// methods, which help with writing tests that change files.
+class RewriterTestContext {
+ public:
+ RewriterTestContext()
+ : Diagnostics(llvm::IntrusiveRefCntPtr<DiagnosticIDs>()),
+ DiagnosticPrinter(llvm::outs(), DiagnosticOptions()),
+ Files((FileSystemOptions())),
+ Sources(Diagnostics, Files),
+ Rewrite(Sources, Options) {
+ Diagnostics.setClient(&DiagnosticPrinter, false);
+ }
+
+ ~RewriterTestContext() {
+ if (!TemporaryDirectory.empty()) {
+ uint32_t RemovedCount = 0;
+ llvm::sys::fs::remove_all(TemporaryDirectory.str(), RemovedCount);
+ }
+ }
+
+ FileID createInMemoryFile(StringRef Name, StringRef Content) {
+ const llvm::MemoryBuffer *Source =
+ llvm::MemoryBuffer::getMemBuffer(Content);
+ const FileEntry *Entry =
+ Files.getVirtualFile(Name, Source->getBufferSize(), 0);
+ Sources.overrideFileContents(Entry, Source, true);
+ assert(Entry != NULL);
+ return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
+ }
+
+ FileID createOnDiskFile(StringRef Name, StringRef Content) {
+ if (TemporaryDirectory.empty()) {
+ int FD;
+ bool error =
+ llvm::sys::fs::unique_file("rewriter-test-%%-%%-%%-%%/anchor", FD,
+ TemporaryDirectory);
+ assert(!error); (void)error;
+ llvm::raw_fd_ostream Closer(FD, /*shouldClose=*/true);
+ TemporaryDirectory = llvm::sys::path::parent_path(TemporaryDirectory);
+ }
+ llvm::SmallString<1024> Path(TemporaryDirectory);
+ llvm::sys::path::append(Path, Name);
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream OutStream(Path.c_str(),
+ ErrorInfo, llvm::raw_fd_ostream::F_Binary);
+ assert(ErrorInfo.empty());
+ OutStream << Content;
+ OutStream.close();
+ const FileEntry *File = Files.getFile(Path);
+ assert(File != NULL);
+ return Sources.createFileID(File, SourceLocation(), SrcMgr::C_User);
+ }
+
+ SourceLocation getLocation(FileID ID, unsigned Line, unsigned Column) {
+ SourceLocation Result = Sources.translateFileLineCol(
+ Sources.getFileEntryForID(ID), Line, Column);
+ assert(Result.isValid());
+ return Result;
+ }
+
+ std::string getRewrittenText(FileID ID) {
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ Rewrite.getEditBuffer(ID).write(OS);
+ OS.flush();
+ return Result;
+ }
+
+ std::string getFileContentFromDisk(StringRef Name) {
+ llvm::SmallString<1024> Path(TemporaryDirectory.str());
+ llvm::sys::path::append(Path, Name);
+ // We need to read directly from the FileManager without relaying through
+ // a FileEntry, as otherwise we'd read through an already opened file
+ // descriptor, which might not see the changes made.
+ // FIXME: Figure out whether there is a way to get the SourceManger to
+ // reopen the file.
+ return Files.getBufferForFile(Path, NULL)->getBuffer();
+ }
+
+ DiagnosticsEngine Diagnostics;
+ TextDiagnosticPrinter DiagnosticPrinter;
+ FileManager Files;
+ SourceManager Sources;
+ LangOptions Options;
+ Rewriter Rewrite;
+
+ // Will be set once on disk files are generated.
+ SmallString<128> TemporaryDirectory;
+};
+
+} // end namespace clang
+
+#endif
diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h
new file mode 100644
index 0000000..d439d81
--- /dev/null
+++ b/unittests/Tooling/TestVisitor.h
@@ -0,0 +1,144 @@
+//===--- TestVisitor.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a utility class for RecursiveASTVisitor related tests.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TEST_VISITOR_H
+#define LLVM_CLANG_TEST_VISITOR_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+
+/// \brief Base class for simple RecursiveASTVisitor based tests.
+///
+/// This is a drop-in replacement for RecursiveASTVisitor itself, with the
+/// additional capability of running it over a snippet of code.
+///
+/// Visits template instantiations by default.
+template <typename T>
+class TestVisitor : public RecursiveASTVisitor<T> {
+public:
+ TestVisitor() { }
+
+ virtual ~TestVisitor() { }
+
+ /// \brief Runs the current AST visitor over the given code.
+ bool runOver(StringRef Code) {
+ return tooling::runToolOnCode(CreateTestAction(), Code);
+ }
+
+ bool shouldVisitTemplateInstantiations() const {
+ return true;
+ }
+
+protected:
+ virtual ASTFrontendAction* CreateTestAction() {
+ return new TestAction(this);
+ }
+
+ class FindConsumer : public ASTConsumer {
+ public:
+ FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
+
+ virtual void HandleTranslationUnit(clang::ASTContext &Context) {
+ Visitor->Context = &Context;
+ Visitor->TraverseDecl(Context.getTranslationUnitDecl());
+ }
+
+ private:
+ TestVisitor *Visitor;
+ };
+
+ class TestAction : public ASTFrontendAction {
+ public:
+ TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
+
+ virtual clang::ASTConsumer* CreateASTConsumer(
+ CompilerInstance&, llvm::StringRef dummy) {
+ /// TestConsumer will be deleted by the framework calling us.
+ return new FindConsumer(Visitor);
+ }
+
+ protected:
+ TestVisitor *Visitor;
+ };
+
+ ASTContext *Context;
+};
+
+
+/// \brief A RecursiveASTVisitor for testing the RecursiveASTVisitor itself.
+///
+/// Allows simple creation of test visitors running matches on only a small
+/// subset of the Visit* methods.
+template <typename T, template <typename> class Visitor = TestVisitor>
+class ExpectedLocationVisitor : public Visitor<T> {
+public:
+ ExpectedLocationVisitor()
+ : ExpectedLine(0), ExpectedColumn(0), Found(false) {}
+
+ virtual ~ExpectedLocationVisitor() {
+ EXPECT_TRUE(Found)
+ << "Expected \"" << ExpectedMatch << "\" at " << ExpectedLine
+ << ":" << ExpectedColumn << PartialMatches;
+ }
+
+ /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
+ void ExpectMatch(Twine Match, unsigned Line, unsigned Column) {
+ ExpectedMatch = Match.str();
+ ExpectedLine = Line;
+ ExpectedColumn = Column;
+ }
+
+protected:
+ /// \brief Convenience method to simplify writing test visitors.
+ ///
+ /// Sets 'Found' to true if 'Name' and 'Location' match the expected
+ /// values. If only a partial match is found, record the information
+ /// to produce nice error output when a test fails.
+ ///
+ /// Implementations are required to call this with appropriate values
+ /// for 'Name' during visitation.
+ void Match(StringRef Name, SourceLocation Location) {
+ FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
+ if (Name == ExpectedMatch &&
+ FullLocation.isValid() &&
+ FullLocation.getSpellingLineNumber() == ExpectedLine &&
+ FullLocation.getSpellingColumnNumber() == ExpectedColumn) {
+ EXPECT_TRUE(!Found);
+ Found = true;
+ } else if (Name == ExpectedMatch ||
+ (FullLocation.isValid() &&
+ FullLocation.getSpellingLineNumber() == ExpectedLine &&
+ FullLocation.getSpellingColumnNumber() == ExpectedColumn)) {
+ // If we did not match, record information about partial matches.
+ llvm::raw_string_ostream Stream(PartialMatches);
+ Stream << ", partial match: \"" << Name << "\" at ";
+ Location.print(Stream, this->Context->getSourceManager());
+ }
+ }
+
+ std::string ExpectedMatch;
+ unsigned ExpectedLine;
+ unsigned ExpectedColumn;
+ std::string PartialMatches;
+ bool Found;
+};
+}
+
+#endif /* LLVM_CLANG_TEST_VISITOR_H */
diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp
index c7b2210..fb3af26 100644
--- a/unittests/Tooling/ToolingTest.cpp
+++ b/unittests/Tooling/ToolingTest.cpp
@@ -15,6 +15,7 @@
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
+#include <string>
namespace clang {
namespace tooling {
@@ -52,11 +53,16 @@ class FindTopLevelDeclConsumer : public clang::ASTConsumer {
};
} // end namespace
-TEST(runToolOnCode, FindsTopLevelDeclOnEmptyCode) {
+TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) {
bool FoundTopLevelDecl = false;
EXPECT_TRUE(runToolOnCode(
new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), ""));
+#if !defined(_MSC_VER)
+ EXPECT_FALSE(FoundTopLevelDecl);
+#else
+ // FIXME: LangOpts.MicrosoftExt appends "class type_info;"
EXPECT_TRUE(FoundTopLevelDecl);
+#endif
}
namespace {
@@ -98,7 +104,9 @@ TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) {
}
struct IndependentFrontendActionCreator {
- FrontendAction *newFrontendAction() { return new SyntaxOnlyAction; }
+ ASTConsumer *newASTConsumer() {
+ return new FindTopLevelDeclConsumer(NULL);
+ }
};
TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
@@ -109,5 +117,18 @@ TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) {
EXPECT_TRUE(Action.get() != NULL);
}
+TEST(ToolInvocation, TestMapVirtualFile) {
+ clang::FileManager Files((clang::FileSystemOptions()));
+ std::vector<std::string> Args;
+ Args.push_back("tool-executable");
+ Args.push_back("-Idef");
+ Args.push_back("-fsyntax-only");
+ Args.push_back("test.cpp");
+ clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, &Files);
+ Invocation.mapVirtualFile("test.cpp", "#include <abc>\n");
+ Invocation.mapVirtualFile("def/abc", "\n");
+ EXPECT_TRUE(Invocation.run());
+}
+
} // end namespace tooling
} // end namespace clang
OpenPOWER on IntegriCloud