diff options
Diffstat (limited to 'unittests')
25 files changed, 3993 insertions, 807 deletions
diff --git a/unittests/AST/CMakeLists.txt b/unittests/AST/CMakeLists.txt index 63418a2..1ea293e 100644 --- a/unittests/AST/CMakeLists.txt +++ b/unittests/AST/CMakeLists.txt @@ -1,8 +1,11 @@ add_clang_unittest(ASTTests CommentLexer.cpp CommentParser.cpp + DeclPrinterTest.cpp + SourceLocationTest.cpp + StmtPrinterTest.cpp ) target_link_libraries(ASTTests - clangAST + clangAST clangASTMatchers clangTooling ) diff --git a/unittests/AST/CommentLexer.cpp b/unittests/AST/CommentLexer.cpp index cab0fdd..2723a61 100644 --- a/unittests/AST/CommentLexer.cpp +++ b/unittests/AST/CommentLexer.cpp @@ -10,6 +10,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/AST/CommentLexer.h" #include "clang/AST/CommentCommandTraits.h" #include "llvm/ADT/STLExtras.h" @@ -29,8 +30,9 @@ protected: CommentLexerTest() : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), - Diags(DiagID, new IgnoringDiagConsumer()), - SourceMgr(Diags, FileMgr) { + Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr), + Traits(Allocator) { } FileSystemOptions FileMgrOpts; @@ -39,8 +41,21 @@ protected: DiagnosticsEngine Diags; SourceManager SourceMgr; llvm::BumpPtrAllocator Allocator; + CommandTraits Traits; void lexString(const char *Source, std::vector<Token> &Toks); + + StringRef getCommandName(const Token &Tok) { + return Traits.getCommandInfo(Tok.getCommandID())->Name; + } + + StringRef getVerbatimBlockName(const Token &Tok) { + return Traits.getCommandInfo(Tok.getVerbatimBlockID())->Name; + } + + StringRef getVerbatimLineName(const Token &Tok) { + return Traits.getCommandInfo(Tok.getVerbatimLineID())->Name; + } }; void CommentLexerTest::lexString(const char *Source, @@ -49,9 +64,7 @@ void CommentLexerTest::lexString(const char *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)); + Lexer L(Allocator, Traits, Begin, Source, Source + strlen(Source)); while (1) { Token Tok; @@ -310,7 +323,35 @@ TEST_F(CommentLexerTest, DoxygenCommand4) { } } +// A command marker followed by a non-letter that is not a part of an escape +// sequence. TEST_F(CommentLexerTest, DoxygenCommand5) { + const char *Source = "/// \\^ \\0"; + 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::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::text, Toks[3].getKind()); + ASSERT_EQ(StringRef("\\"), Toks[3].getText()); + + ASSERT_EQ(tok::text, Toks[4].getKind()); + ASSERT_EQ(StringRef("0"), Toks[4].getText()); + + ASSERT_EQ(tok::newline, Toks[5].getKind()); +} + +TEST_F(CommentLexerTest, DoxygenCommand6) { const char *Source = "/// \\brief Aaa."; std::vector<Token> Toks; @@ -322,7 +363,7 @@ TEST_F(CommentLexerTest, DoxygenCommand5) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::command, Toks[1].getKind()); - ASSERT_EQ(StringRef("brief"), Toks[1].getCommandName()); + ASSERT_EQ(StringRef("brief"), getCommandName(Toks[1])); ASSERT_EQ(tok::text, Toks[2].getKind()); ASSERT_EQ(StringRef(" Aaa."), Toks[2].getText()); @@ -330,7 +371,39 @@ TEST_F(CommentLexerTest, DoxygenCommand5) { ASSERT_EQ(tok::newline, Toks[3].getKind()); } -TEST_F(CommentLexerTest, DoxygenCommand6) { +TEST_F(CommentLexerTest, DoxygenCommand7) { + const char *Source = "/// \\em\\em \\em\t\\em\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("em"), getCommandName(Toks[1])); + + ASSERT_EQ(tok::command, Toks[2].getKind()); + ASSERT_EQ(StringRef("em"), getCommandName(Toks[2])); + + ASSERT_EQ(tok::text, Toks[3].getKind()); + ASSERT_EQ(StringRef(" "), Toks[3].getText()); + + ASSERT_EQ(tok::command, Toks[4].getKind()); + ASSERT_EQ(StringRef("em"), getCommandName(Toks[4])); + + ASSERT_EQ(tok::text, Toks[5].getKind()); + ASSERT_EQ(StringRef("\t"), Toks[5].getText()); + + ASSERT_EQ(tok::command, Toks[6].getKind()); + ASSERT_EQ(StringRef("em"), getCommandName(Toks[6])); + + ASSERT_EQ(tok::newline, Toks[7].getKind()); +} + +TEST_F(CommentLexerTest, DoxygenCommand8) { const char *Source = "/// \\aaa\\bbb \\ccc\t\\ddd\n"; std::vector<Token> Toks; @@ -341,28 +414,28 @@ TEST_F(CommentLexerTest, DoxygenCommand6) { 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::unknown_command, Toks[1].getKind()); + ASSERT_EQ(StringRef("aaa"), Toks[1].getUnknownCommandName()); - ASSERT_EQ(tok::command, Toks[2].getKind()); - ASSERT_EQ(StringRef("bbb"), Toks[2].getCommandName()); + ASSERT_EQ(tok::unknown_command, Toks[2].getKind()); + ASSERT_EQ(StringRef("bbb"), Toks[2].getUnknownCommandName()); 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::unknown_command, Toks[4].getKind()); + ASSERT_EQ(StringRef("ccc"), Toks[4].getUnknownCommandName()); 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::unknown_command, Toks[6].getKind()); + ASSERT_EQ(StringRef("ddd"), Toks[6].getUnknownCommandName()); ASSERT_EQ(tok::newline, Toks[7].getKind()); } -TEST_F(CommentLexerTest, DoxygenCommand7) { +TEST_F(CommentLexerTest, DoxygenCommand9) { const char *Source = "// \\c\n"; std::vector<Token> Toks; @@ -374,7 +447,7 @@ TEST_F(CommentLexerTest, DoxygenCommand7) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::command, Toks[1].getKind()); - ASSERT_EQ(StringRef("c"), Toks[1].getCommandName()); + ASSERT_EQ(StringRef("c"), getCommandName(Toks[1])); ASSERT_EQ(tok::newline, Toks[2].getKind()); } @@ -397,10 +470,10 @@ TEST_F(CommentLexerTest, VerbatimBlock1) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind()); - ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName()); + ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1])); ASSERT_EQ(tok::verbatim_block_end, Toks[2].getKind()); - ASSERT_EQ(StringRef("endverbatim"), Toks[2].getVerbatimBlockName()); + ASSERT_EQ(StringRef("endverbatim"), getVerbatimBlockName(Toks[2])); ASSERT_EQ(tok::newline, Toks[3].getKind()); ASSERT_EQ(tok::newline, Toks[4].getKind()); @@ -421,7 +494,7 @@ TEST_F(CommentLexerTest, VerbatimBlock2) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind()); - ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName()); + ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1])); ASSERT_EQ(tok::newline, Toks[2].getKind()); } @@ -440,7 +513,7 @@ TEST_F(CommentLexerTest, VerbatimBlock3) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind()); - ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName()); + ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1])); ASSERT_EQ(tok::newline, Toks[2].getKind()); ASSERT_EQ(tok::newline, Toks[3].getKind()); @@ -464,13 +537,13 @@ TEST_F(CommentLexerTest, VerbatimBlock4) { 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(StringRef("verbatim"), getVerbatimBlockName(Toks[1])); 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(StringRef("endverbatim"), getVerbatimBlockName(Toks[3])); ASSERT_EQ(tok::newline, Toks[4].getKind()); ASSERT_EQ(tok::newline, Toks[5].getKind()); @@ -495,7 +568,7 @@ TEST_F(CommentLexerTest, VerbatimBlock5) { 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(StringRef("verbatim"), getVerbatimBlockName(Toks[1])); ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind()); ASSERT_EQ(StringRef(" aaa "), Toks[2].getVerbatimBlockText()); @@ -523,7 +596,7 @@ TEST_F(CommentLexerTest, VerbatimBlock6) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind()); - ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName()); + ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1])); ASSERT_EQ(tok::newline, Toks[2].getKind()); @@ -540,7 +613,7 @@ TEST_F(CommentLexerTest, VerbatimBlock6) { 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(StringRef("endverbatim"), getVerbatimBlockName(Toks[8])); ASSERT_EQ(tok::newline, Toks[9].getKind()); } @@ -564,7 +637,7 @@ TEST_F(CommentLexerTest, VerbatimBlock7) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind()); - ASSERT_EQ(StringRef("verbatim"), Toks[1].getVerbatimBlockName()); + ASSERT_EQ(StringRef("verbatim"), getVerbatimBlockName(Toks[1])); ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind()); ASSERT_EQ(StringRef(" Aaa"), Toks[2].getVerbatimBlockText()); @@ -576,7 +649,7 @@ TEST_F(CommentLexerTest, VerbatimBlock7) { 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(StringRef("endverbatim"), getVerbatimBlockName(Toks[5])); ASSERT_EQ(tok::newline, Toks[6].getKind()); @@ -605,7 +678,7 @@ TEST_F(CommentLexerTest, VerbatimBlock8) { 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(StringRef("verbatim"), getVerbatimBlockName(Toks[1])); ASSERT_EQ(tok::verbatim_block_line, Toks[2].getKind()); ASSERT_EQ(StringRef(" aaa\\$\\@"), Toks[2].getVerbatimBlockText()); @@ -620,19 +693,19 @@ TEST_F(CommentLexerTest, VerbatimBlock8) { 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(StringRef("endverbatim"), getVerbatimBlockName(Toks[6])); 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(StringRef("verbatim"), getVerbatimBlockName(Toks[8])); 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(StringRef("endverbatim"), getVerbatimBlockName(Toks[10])); ASSERT_EQ(tok::text, Toks[11].getKind()); ASSERT_EQ(StringRef(" BlahBlah"), Toks[11].getText()); @@ -655,37 +728,37 @@ TEST_F(CommentLexerTest, VerbatimBlock9) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::verbatim_block_begin, Toks[1].getKind()); - ASSERT_EQ(StringRef("f$"), Toks[1].getVerbatimBlockName()); + ASSERT_EQ(StringRef("f$"), getVerbatimBlockName(Toks[1])); 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(StringRef("f$"), getVerbatimBlockName(Toks[3])); 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(StringRef("f["), getVerbatimBlockName(Toks[5])); 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(StringRef("f]"), getVerbatimBlockName(Toks[7])); 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(StringRef("f{"), getVerbatimBlockName(Toks[9])); 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(StringRef("f}"), getVerbatimBlockName(Toks[11])); ASSERT_EQ(tok::newline, Toks[12].getKind()); } @@ -708,7 +781,7 @@ TEST_F(CommentLexerTest, VerbatimLine1) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind()); - ASSERT_EQ(StringRef("fn"), Toks[1].getVerbatimLineName()); + ASSERT_EQ(StringRef("fn"), getVerbatimLineName(Toks[1])); ASSERT_EQ(tok::newline, Toks[2].getKind()); ASSERT_EQ(tok::newline, Toks[3].getKind()); @@ -733,7 +806,7 @@ TEST_F(CommentLexerTest, VerbatimLine2) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind()); - ASSERT_EQ(StringRef("fn"), Toks[1].getVerbatimLineName()); + ASSERT_EQ(StringRef("fn"), getVerbatimLineName(Toks[1])); ASSERT_EQ(tok::verbatim_line_text, Toks[2].getKind()); ASSERT_EQ(StringRef(" void *foo(const char *zzz = \"\\$\");"), @@ -761,7 +834,7 @@ TEST_F(CommentLexerTest, VerbatimLine3) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::verbatim_line_name, Toks[1].getKind()); - ASSERT_EQ(StringRef("fn"), Toks[1].getVerbatimLineName()); + ASSERT_EQ(StringRef("fn"), getVerbatimLineName(Toks[1])); ASSERT_EQ(tok::verbatim_line_text, Toks[2].getKind()); ASSERT_EQ(StringRef(" void *foo(const char *zzz = \"\\$\");"), @@ -822,7 +895,7 @@ TEST_F(CommentLexerTest, HTML2) { TEST_F(CommentLexerTest, HTML3) { const char *Source = - "// < tag"; + "// < img"; std::vector<Token> Toks; @@ -837,15 +910,15 @@ TEST_F(CommentLexerTest, HTML3) { ASSERT_EQ(StringRef("<"), Toks[1].getText()); ASSERT_EQ(tok::text, Toks[2].getKind()); - ASSERT_EQ(StringRef(" tag"), Toks[2].getText()); + ASSERT_EQ(StringRef(" img"), Toks[2].getText()); ASSERT_EQ(tok::newline, Toks[3].getKind()); } TEST_F(CommentLexerTest, HTML4) { const char *Sources[] = { - "// <tag", - "// <tag " + "// <img", + "// <img " }; for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) { @@ -859,7 +932,7 @@ TEST_F(CommentLexerTest, HTML4) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::newline, Toks[2].getKind()); } @@ -867,7 +940,7 @@ TEST_F(CommentLexerTest, HTML4) { TEST_F(CommentLexerTest, HTML5) { const char *Source = - "// <tag 42"; + "// <img 42"; std::vector<Token> Toks; @@ -879,7 +952,7 @@ TEST_F(CommentLexerTest, HTML5) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::text, Toks[2].getKind()); ASSERT_EQ(StringRef("42"), Toks[2].getText()); @@ -888,7 +961,7 @@ TEST_F(CommentLexerTest, HTML5) { } TEST_F(CommentLexerTest, HTML6) { - const char *Source = "// <tag> Meow"; + const char *Source = "// <img> Meow"; std::vector<Token> Toks; @@ -900,7 +973,7 @@ TEST_F(CommentLexerTest, HTML6) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::html_greater, Toks[2].getKind()); @@ -911,7 +984,7 @@ TEST_F(CommentLexerTest, HTML6) { } TEST_F(CommentLexerTest, HTML7) { - const char *Source = "// <tag="; + const char *Source = "// <img="; std::vector<Token> Toks; @@ -923,7 +996,7 @@ TEST_F(CommentLexerTest, HTML7) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::text, Toks[2].getKind()); ASSERT_EQ(StringRef("="), Toks[2].getText()); @@ -932,7 +1005,7 @@ TEST_F(CommentLexerTest, HTML7) { } TEST_F(CommentLexerTest, HTML8) { - const char *Source = "// <tag attr=> Meow"; + const char *Source = "// <img src=> Meow"; std::vector<Token> Toks; @@ -944,10 +1017,10 @@ TEST_F(CommentLexerTest, HTML8) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::html_ident, Toks[2].getKind()); - ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent()); + ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent()); ASSERT_EQ(tok::html_equals, Toks[3].getKind()); @@ -961,8 +1034,8 @@ TEST_F(CommentLexerTest, HTML8) { TEST_F(CommentLexerTest, HTML9) { const char *Sources[] = { - "// <tag attr", - "// <tag attr " + "// <img src", + "// <img src " }; for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) { @@ -976,10 +1049,10 @@ TEST_F(CommentLexerTest, HTML9) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::html_ident, Toks[2].getKind()); - ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent()); + ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent()); ASSERT_EQ(tok::newline, Toks[3].getKind()); } @@ -987,8 +1060,8 @@ TEST_F(CommentLexerTest, HTML9) { TEST_F(CommentLexerTest, HTML10) { const char *Sources[] = { - "// <tag attr=", - "// <tag attr =" + "// <img src=", + "// <img src =" }; for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) { @@ -1002,10 +1075,10 @@ TEST_F(CommentLexerTest, HTML10) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::html_ident, Toks[2].getKind()); - ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent()); + ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent()); ASSERT_EQ(tok::html_equals, Toks[3].getKind()); @@ -1015,10 +1088,10 @@ TEST_F(CommentLexerTest, HTML10) { TEST_F(CommentLexerTest, HTML11) { const char *Sources[] = { - "// <tag attr=\"", - "// <tag attr = \"", - "// <tag attr=\'", - "// <tag attr = \'" + "// <img src=\"", + "// <img src = \"", + "// <img src=\'", + "// <img src = \'" }; for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) { @@ -1032,10 +1105,10 @@ TEST_F(CommentLexerTest, HTML11) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::html_ident, Toks[2].getKind()); - ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent()); + ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent()); ASSERT_EQ(tok::html_equals, Toks[3].getKind()); @@ -1047,7 +1120,7 @@ TEST_F(CommentLexerTest, HTML11) { } TEST_F(CommentLexerTest, HTML12) { - const char *Source = "// <tag attr=@"; + const char *Source = "// <img src=@"; std::vector<Token> Toks; @@ -1059,10 +1132,10 @@ TEST_F(CommentLexerTest, HTML12) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::html_ident, Toks[2].getKind()); - ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent()); + ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent()); ASSERT_EQ(tok::html_equals, Toks[3].getKind()); @@ -1074,10 +1147,10 @@ TEST_F(CommentLexerTest, HTML12) { TEST_F(CommentLexerTest, HTML13) { const char *Sources[] = { - "// <tag attr=\"val\\\"\\'val", - "// <tag attr=\"val\\\"\\'val\"", - "// <tag attr=\'val\\\"\\'val", - "// <tag attr=\'val\\\"\\'val\'" + "// <img src=\"val\\\"\\'val", + "// <img src=\"val\\\"\\'val\"", + "// <img src=\'val\\\"\\'val", + "// <img src=\'val\\\"\\'val\'" }; for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) { @@ -1091,10 +1164,10 @@ TEST_F(CommentLexerTest, HTML13) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::html_ident, Toks[2].getKind()); - ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent()); + ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent()); ASSERT_EQ(tok::html_equals, Toks[3].getKind()); @@ -1107,8 +1180,8 @@ TEST_F(CommentLexerTest, HTML13) { TEST_F(CommentLexerTest, HTML14) { const char *Sources[] = { - "// <tag attr=\"val\\\"\\'val\">", - "// <tag attr=\'val\\\"\\'val\'>" + "// <img src=\"val\\\"\\'val\">", + "// <img src=\'val\\\"\\'val\'>" }; for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) { @@ -1122,10 +1195,10 @@ TEST_F(CommentLexerTest, HTML14) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::html_ident, Toks[2].getKind()); - ASSERT_EQ(StringRef("attr"), Toks[2].getHTMLIdent()); + ASSERT_EQ(StringRef("src"), Toks[2].getHTMLIdent()); ASSERT_EQ(tok::html_equals, Toks[3].getKind()); @@ -1140,8 +1213,8 @@ TEST_F(CommentLexerTest, HTML14) { TEST_F(CommentLexerTest, HTML15) { const char *Sources[] = { - "// <tag/>", - "// <tag />" + "// <img/>", + "// <img />" }; for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) { @@ -1155,7 +1228,7 @@ TEST_F(CommentLexerTest, HTML15) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::html_slash_greater, Toks[2].getKind()); @@ -1165,8 +1238,8 @@ TEST_F(CommentLexerTest, HTML15) { TEST_F(CommentLexerTest, HTML16) { const char *Sources[] = { - "// <tag/ Aaa", - "// <tag / Aaa" + "// <img/ Aaa", + "// <img / Aaa" }; for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) { @@ -1180,7 +1253,7 @@ TEST_F(CommentLexerTest, HTML16) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_start_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagStartName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagStartName()); ASSERT_EQ(tok::text, Toks[2].getKind()); ASSERT_EQ(StringRef("/"), Toks[2].getText()); @@ -1201,13 +1274,13 @@ TEST_F(CommentLexerTest, HTML17) { ASSERT_EQ(3U, Toks.size()); - ASSERT_EQ(tok::text, Toks[0].getKind()); - ASSERT_EQ(StringRef(" "), Toks[0].getText()); + 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[1].getKind()); + ASSERT_EQ(StringRef("</"), Toks[1].getText()); - ASSERT_EQ(tok::newline, Toks[2].getKind()); + ASSERT_EQ(tok::newline, Toks[2].getKind()); } TEST_F(CommentLexerTest, HTML18) { @@ -1219,20 +1292,20 @@ TEST_F(CommentLexerTest, HTML18) { ASSERT_EQ(4U, Toks.size()); - ASSERT_EQ(tok::text, Toks[0].getKind()); - ASSERT_EQ(StringRef(" "), Toks[0].getText()); + 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[1].getKind()); + ASSERT_EQ(StringRef("</"), Toks[1].getText()); - ASSERT_EQ(tok::text, Toks[2].getKind()); - ASSERT_EQ(StringRef("@"), Toks[2].getText()); + ASSERT_EQ(tok::text, Toks[2].getKind()); + ASSERT_EQ(StringRef("@"), Toks[2].getText()); - ASSERT_EQ(tok::newline, Toks[3].getKind()); + ASSERT_EQ(tok::newline, Toks[3].getKind()); } TEST_F(CommentLexerTest, HTML19) { - const char *Source = "// </tag"; + const char *Source = "// </img"; std::vector<Token> Toks; @@ -1244,35 +1317,51 @@ TEST_F(CommentLexerTest, HTML19) { ASSERT_EQ(StringRef(" "), Toks[0].getText()); ASSERT_EQ(tok::html_end_tag, Toks[1].getKind()); - ASSERT_EQ(StringRef("tag"), Toks[1].getHTMLTagEndName()); + ASSERT_EQ(StringRef("img"), Toks[1].getHTMLTagEndName()); ASSERT_EQ(tok::newline, Toks[2].getKind()); } -TEST_F(CommentLexerTest, HTML20) { - const char *Sources[] = { - "// </tag>", - "// </ tag>", - "// </ tag >" - }; +TEST_F(CommentLexerTest, NotAKnownHTMLTag1) { + const char *Source = "// <tag>"; - for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) { - std::vector<Token> Toks; + std::vector<Token> Toks; - lexString(Sources[i], Toks); + lexString(Source, Toks); - ASSERT_EQ(4U, Toks.size()); + ASSERT_EQ(4U, Toks.size()); - ASSERT_EQ(tok::text, Toks[0].getKind()); - ASSERT_EQ(StringRef(" "), Toks[0].getText()); + 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::text, Toks[1].getKind()); + ASSERT_EQ(StringRef("<tag"), Toks[1].getText()); - ASSERT_EQ(tok::html_greater, Toks[2].getKind()); + ASSERT_EQ(tok::text, Toks[2].getKind()); + ASSERT_EQ(StringRef(">"), Toks[2].getText()); - ASSERT_EQ(tok::newline, Toks[3].getKind()); - } + ASSERT_EQ(tok::newline, Toks[3].getKind()); +} + +TEST_F(CommentLexerTest, NotAKnownHTMLTag2) { + 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("</tag"), 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, HTMLCharacterReferences1) { diff --git a/unittests/AST/CommentParser.cpp b/unittests/AST/CommentParser.cpp index 7258a7e..8fde247 100644 --- a/unittests/AST/CommentParser.cpp +++ b/unittests/AST/CommentParser.cpp @@ -10,6 +10,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/AST/Comment.h" #include "clang/AST/CommentLexer.h" #include "clang/AST/CommentParser.h" @@ -36,8 +37,9 @@ protected: CommentParserTest() : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), - Diags(DiagID, new IgnoringDiagConsumer()), - SourceMgr(Diags, FileMgr) { + Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr), + Traits(Allocator) { } FileSystemOptions FileMgrOpts; @@ -46,6 +48,7 @@ protected: DiagnosticsEngine Diags; SourceManager SourceMgr; llvm::BumpPtrAllocator Allocator; + CommandTraits Traits; FullComment *parseString(const char *Source); }; @@ -55,17 +58,15 @@ FullComment *CommentParserTest::parseString(const char *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)); + Lexer L(Allocator, Traits, Begin, 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(); + Sema S(Allocator, SourceMgr, Diags, Traits, /*PP=*/ NULL); + Parser P(L, S, Allocator, SourceMgr, Diags, Traits); + FullComment *FC = P.parseFullComment(); if (DEBUG) { llvm::errs() << "=== Source:\n" << Source << "\n=== AST:\n"; - FC->dump(SourceMgr); + FC->dump(llvm::errs(), &Traits, &SourceMgr); } Token Tok; @@ -157,6 +158,7 @@ template <typename T> } ::testing::AssertionResult HasBlockCommandAt(const Comment *C, + const CommandTraits &Traits, size_t Idx, BlockCommandComment *&BCC, StringRef Name, @@ -165,7 +167,7 @@ template <typename T> if (!AR) return AR; - StringRef ActualName = BCC->getCommandName(); + StringRef ActualName = BCC->getCommandName(Traits); if (ActualName != Name) return ::testing::AssertionFailure() << "BlockCommandComment has name \"" << ActualName.str() << "\", " @@ -178,6 +180,7 @@ template <typename T> ::testing::AssertionResult HasParamCommandAt( const Comment *C, + const CommandTraits &Traits, size_t Idx, ParamCommandComment *&PCC, StringRef CommandName, @@ -189,7 +192,7 @@ template <typename T> if (!AR) return AR; - StringRef ActualCommandName = PCC->getCommandName(); + StringRef ActualCommandName = PCC->getCommandName(Traits); if (ActualCommandName != CommandName) return ::testing::AssertionFailure() << "ParamCommandComment has name \"" << ActualCommandName.str() << "\", " @@ -211,7 +214,7 @@ template <typename T> return ::testing::AssertionFailure() << "ParamCommandComment has no parameter name"; - StringRef ActualParamName = PCC->hasParamName() ? PCC->getParamName() : ""; + StringRef ActualParamName = PCC->hasParamName() ? PCC->getParamNameAsWritten() : ""; if (ActualParamName != ParamName) return ::testing::AssertionFailure() << "ParamCommandComment has parameter name \"" << ActualParamName.str() @@ -225,6 +228,7 @@ template <typename T> ::testing::AssertionResult HasTParamCommandAt( const Comment *C, + const CommandTraits &Traits, size_t Idx, TParamCommandComment *&TPCC, StringRef CommandName, @@ -234,7 +238,7 @@ template <typename T> if (!AR) return AR; - StringRef ActualCommandName = TPCC->getCommandName(); + StringRef ActualCommandName = TPCC->getCommandName(Traits); if (ActualCommandName != CommandName) return ::testing::AssertionFailure() << "TParamCommandComment has name \"" << ActualCommandName.str() << "\", " @@ -244,7 +248,7 @@ template <typename T> return ::testing::AssertionFailure() << "TParamCommandComment has no parameter name"; - StringRef ActualParamName = TPCC->hasParamName() ? TPCC->getParamName() : ""; + StringRef ActualParamName = TPCC->hasParamName() ? TPCC->getParamNameAsWritten() : ""; if (ActualParamName != ParamName) return ::testing::AssertionFailure() << "TParamCommandComment has parameter name \"" << ActualParamName.str() @@ -257,6 +261,7 @@ template <typename T> } ::testing::AssertionResult HasInlineCommandAt(const Comment *C, + const CommandTraits &Traits, size_t Idx, InlineCommandComment *&ICC, StringRef Name) { @@ -264,7 +269,7 @@ template <typename T> if (!AR) return AR; - StringRef ActualName = ICC->getCommandName(); + StringRef ActualName = ICC->getCommandName(Traits); if (ActualName != Name) return ::testing::AssertionFailure() << "InlineCommandComment has name \"" << ActualName.str() << "\", " @@ -276,11 +281,12 @@ template <typename T> struct NoArgs {}; ::testing::AssertionResult HasInlineCommandAt(const Comment *C, + const CommandTraits &Traits, size_t Idx, InlineCommandComment *&ICC, StringRef Name, NoArgs) { - ::testing::AssertionResult AR = HasInlineCommandAt(C, Idx, ICC, Name); + ::testing::AssertionResult AR = HasInlineCommandAt(C, Traits, Idx, ICC, Name); if (!AR) return AR; @@ -293,11 +299,12 @@ struct NoArgs {}; } ::testing::AssertionResult HasInlineCommandAt(const Comment *C, + const CommandTraits &Traits, size_t Idx, InlineCommandComment *&ICC, StringRef Name, StringRef Arg) { - ::testing::AssertionResult AR = HasInlineCommandAt(C, Idx, ICC, Name); + ::testing::AssertionResult AR = HasInlineCommandAt(C, Traits, Idx, ICC, Name); if (!AR) return AR; @@ -452,6 +459,7 @@ struct NoAttrs {}; } ::testing::AssertionResult HasVerbatimBlockAt(const Comment *C, + const CommandTraits &Traits, size_t Idx, VerbatimBlockComment *&VBC, StringRef Name, @@ -460,7 +468,7 @@ struct NoAttrs {}; if (!AR) return AR; - StringRef ActualName = VBC->getCommandName(); + StringRef ActualName = VBC->getCommandName(Traits); if (ActualName != Name) return ::testing::AssertionFailure() << "VerbatimBlockComment has name \"" << ActualName.str() << "\", " @@ -480,12 +488,13 @@ struct NoLines {}; struct Lines {}; ::testing::AssertionResult HasVerbatimBlockAt(const Comment *C, + const CommandTraits &Traits, size_t Idx, VerbatimBlockComment *&VBC, StringRef Name, StringRef CloseName, NoLines) { - ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name, + ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Traits, Idx, VBC, Name, CloseName); if (!AR) return AR; @@ -499,13 +508,14 @@ struct Lines {}; } ::testing::AssertionResult HasVerbatimBlockAt(const Comment *C, + const CommandTraits &Traits, size_t Idx, VerbatimBlockComment *&VBC, StringRef Name, StringRef CloseName, Lines, StringRef Line0) { - ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name, + ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Traits, Idx, VBC, Name, CloseName); if (!AR) return AR; @@ -525,6 +535,7 @@ struct Lines {}; } ::testing::AssertionResult HasVerbatimBlockAt(const Comment *C, + const CommandTraits &Traits, size_t Idx, VerbatimBlockComment *&VBC, StringRef Name, @@ -532,7 +543,7 @@ struct Lines {}; Lines, StringRef Line0, StringRef Line1) { - ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Idx, VBC, Name, + ::testing::AssertionResult AR = HasVerbatimBlockAt(C, Traits, Idx, VBC, Name, CloseName); if (!AR) return AR; @@ -558,6 +569,7 @@ struct Lines {}; } ::testing::AssertionResult HasVerbatimLineAt(const Comment *C, + const CommandTraits &Traits, size_t Idx, VerbatimLineComment *&VLC, StringRef Name, @@ -566,7 +578,7 @@ struct Lines {}; if (!AR) return AR; - StringRef ActualName = VLC->getCommandName(); + StringRef ActualName = VLC->getCommandName(Traits); if (ActualName != Name) return ::testing::AssertionFailure() << "VerbatimLineComment has name \"" << ActualName.str() << "\", " @@ -651,7 +663,7 @@ TEST_F(CommentParserTest, Paragraph2) { { BlockCommandComment *BCC; ParagraphComment *PC; - ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC)); + ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "brief", PC)); ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Aaa")); } @@ -668,14 +680,14 @@ TEST_F(CommentParserTest, Paragraph3) { { BlockCommandComment *BCC; ParagraphComment *PC; - ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC)); + ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "brief", PC)); ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " ")); } { BlockCommandComment *BCC; ParagraphComment *PC; - ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "author", PC)); + ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "author", PC)); ASSERT_TRUE(GetChildAt(BCC, 0, PC)); ASSERT_TRUE(HasChildCount(PC, 0)); @@ -695,7 +707,7 @@ TEST_F(CommentParserTest, Paragraph4) { { BlockCommandComment *BCC; ParagraphComment *PC; - ASSERT_TRUE(HasBlockCommandAt(FC, 1, BCC, "brief", PC)); + ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 1, BCC, "brief", PC)); ASSERT_TRUE(GetChildAt(BCC, 0, PC)); ASSERT_TRUE(HasChildCount(PC, 2)); @@ -705,7 +717,7 @@ TEST_F(CommentParserTest, Paragraph4) { { BlockCommandComment *BCC; ParagraphComment *PC; - ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "author", PC)); + ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "author", PC)); ASSERT_TRUE(HasParagraphCommentAt(BCC, 0, " Ccc")); } @@ -721,7 +733,7 @@ TEST_F(CommentParserTest, ParamCommand1) { { ParamCommandComment *PCC; ParagraphComment *PC; - ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param", + ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param", ParamCommandComment::In, /* IsDirectionExplicit = */ false, "aaa", PC)); @@ -740,7 +752,7 @@ TEST_F(CommentParserTest, ParamCommand2) { { ParamCommandComment *PCC; ParagraphComment *PC; - ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param", + ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param", ParamCommandComment::In, /* IsDirectionExplicit = */ false, "", PC)); @@ -750,7 +762,7 @@ TEST_F(CommentParserTest, ParamCommand2) { { BlockCommandComment *BCC; ParagraphComment *PC; - ASSERT_TRUE(HasBlockCommandAt(FC, 2, BCC, "brief", PC)); + ASSERT_TRUE(HasBlockCommandAt(FC, Traits, 2, BCC, "brief", PC)); ASSERT_TRUE(HasChildCount(PC, 0)); } } @@ -774,7 +786,7 @@ TEST_F(CommentParserTest, ParamCommand3) { { ParamCommandComment *PCC; ParagraphComment *PC; - ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param", + ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param", ParamCommandComment::In, /* IsDirectionExplicit = */ false, "aaa", PC)); @@ -804,7 +816,7 @@ TEST_F(CommentParserTest, ParamCommand4) { { ParamCommandComment *PCC; ParagraphComment *PC; - ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param", + ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param", ParamCommandComment::In, /* IsDirectionExplicit = */ true, "aaa", PC)); @@ -834,7 +846,7 @@ TEST_F(CommentParserTest, ParamCommand5) { { ParamCommandComment *PCC; ParagraphComment *PC; - ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param", + ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param", ParamCommandComment::Out, /* IsDirectionExplicit = */ true, "aaa", PC)); @@ -865,7 +877,7 @@ TEST_F(CommentParserTest, ParamCommand6) { { ParamCommandComment *PCC; ParagraphComment *PC; - ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param", + ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param", ParamCommandComment::InOut, /* IsDirectionExplicit = */ true, "aaa", PC)); @@ -886,7 +898,7 @@ TEST_F(CommentParserTest, ParamCommand7) { { ParamCommandComment *PCC; ParagraphComment *PC; - ASSERT_TRUE(HasParamCommandAt(FC, 1, PCC, "param", + ASSERT_TRUE(HasParamCommandAt(FC, Traits, 1, PCC, "param", ParamCommandComment::In, /* IsDirectionExplicit = */ false, "aaa", PC)); @@ -920,7 +932,7 @@ TEST_F(CommentParserTest, TParamCommand1) { { TParamCommandComment *TPCC; ParagraphComment *PC; - ASSERT_TRUE(HasTParamCommandAt(FC, 1, TPCC, "tparam", + ASSERT_TRUE(HasTParamCommandAt(FC, Traits, 1, TPCC, "tparam", "aaa", PC)); ASSERT_TRUE(HasChildCount(TPCC, 1)); ASSERT_TRUE(HasParagraphCommentAt(TPCC, 0, " Bbb")); @@ -938,14 +950,14 @@ TEST_F(CommentParserTest, TParamCommand2) { { TParamCommandComment *TPCC; ParagraphComment *PC; - ASSERT_TRUE(HasTParamCommandAt(FC, 1, TPCC, "tparam", "", PC)); + ASSERT_TRUE(HasTParamCommandAt(FC, Traits, 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(HasBlockCommandAt(FC, Traits, 2, BCC, "brief", PC)); ASSERT_TRUE(HasChildCount(PC, 0)); } } @@ -964,7 +976,7 @@ TEST_F(CommentParserTest, InlineCommand1) { ASSERT_TRUE(HasChildCount(PC, 2)); ASSERT_TRUE(HasTextAt(PC, 0, " ")); - ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", NoArgs())); + ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", NoArgs())); } } @@ -981,7 +993,7 @@ TEST_F(CommentParserTest, InlineCommand2) { ASSERT_TRUE(HasChildCount(PC, 3)); ASSERT_TRUE(HasTextAt(PC, 0, " ")); - ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", NoArgs())); + ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", NoArgs())); ASSERT_TRUE(HasTextAt(PC, 2, " ")); } } @@ -999,7 +1011,7 @@ TEST_F(CommentParserTest, InlineCommand3) { ASSERT_TRUE(HasChildCount(PC, 2)); ASSERT_TRUE(HasTextAt(PC, 0, " ")); - ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", "aaa")); + ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", "aaa")); } } @@ -1016,7 +1028,7 @@ TEST_F(CommentParserTest, InlineCommand4) { ASSERT_TRUE(HasChildCount(PC, 3)); ASSERT_TRUE(HasTextAt(PC, 0, " ")); - ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "c", "aaa")); + ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "c", "aaa")); ASSERT_TRUE(HasTextAt(PC, 2, " bbb")); } } @@ -1034,7 +1046,7 @@ TEST_F(CommentParserTest, InlineCommand5) { ASSERT_TRUE(HasChildCount(PC, 3)); ASSERT_TRUE(HasTextAt(PC, 0, " ")); - ASSERT_TRUE(HasInlineCommandAt(PC, 1, ICC, "unknown", NoArgs())); + ASSERT_TRUE(HasInlineCommandAt(PC, Traits, 1, ICC, "unknown", NoArgs())); ASSERT_TRUE(HasTextAt(PC, 2, " aaa")); } } @@ -1188,7 +1200,8 @@ TEST_F(CommentParserTest, VerbatimBlock1) { ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " ")); { VerbatimBlockComment *VCC; - ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VCC, "verbatim", "endverbatim", + ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VCC, + "verbatim", "endverbatim", NoLines())); } } @@ -1202,7 +1215,8 @@ TEST_F(CommentParserTest, VerbatimBlock2) { ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " ")); { VerbatimBlockComment *VBC; - ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim", + ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC, + "verbatim", "endverbatim", Lines(), " Aaa ")); } } @@ -1216,7 +1230,7 @@ TEST_F(CommentParserTest, VerbatimBlock3) { ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " ")); { VerbatimBlockComment *VBC; - ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "", + ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC, "verbatim", "", Lines(), " Aaa")); } } @@ -1231,7 +1245,8 @@ TEST_F(CommentParserTest, VerbatimBlock4) { { VerbatimBlockComment *VBC; - ASSERT_TRUE(HasVerbatimBlockAt(FC, 0, VBC, "verbatim", "endverbatim", + ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 0, VBC, + "verbatim", "endverbatim", NoLines())); } } @@ -1253,7 +1268,8 @@ TEST_F(CommentParserTest, VerbatimBlock5) { { VerbatimBlockComment *VBC; - ASSERT_TRUE(HasVerbatimBlockAt(FC, 0, VBC, "verbatim", "endverbatim", + ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 0, VBC, + "verbatim", "endverbatim", Lines(), " Aaa")); } } @@ -1277,7 +1293,8 @@ TEST_F(CommentParserTest, VerbatimBlock6) { ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " ")); { VerbatimBlockComment *VBC; - ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim", + ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC, + "verbatim", "endverbatim", Lines(), " Aaa")); } } @@ -1303,7 +1320,8 @@ TEST_F(CommentParserTest, VerbatimBlock7) { ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " ")); { VerbatimBlockComment *VBC; - ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim", + ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC, + "verbatim", "endverbatim", Lines(), " Aaa", " Bbb")); } } @@ -1330,7 +1348,8 @@ TEST_F(CommentParserTest, VerbatimBlock8) { ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " ")); { VerbatimBlockComment *VBC; - ASSERT_TRUE(HasVerbatimBlockAt(FC, 1, VBC, "verbatim", "endverbatim")); + ASSERT_TRUE(HasVerbatimBlockAt(FC, Traits, 1, VBC, + "verbatim", "endverbatim")); ASSERT_EQ(3U, VBC->getNumLines()); ASSERT_EQ(" Aaa", VBC->getText(0)); ASSERT_EQ("", VBC->getText(1)); @@ -1352,7 +1371,7 @@ TEST_F(CommentParserTest, VerbatimLine1) { ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " ")); { VerbatimLineComment *VLC; - ASSERT_TRUE(HasVerbatimLineAt(FC, 1, VLC, "fn", "")); + ASSERT_TRUE(HasVerbatimLineAt(FC, Traits, 1, VLC, "fn", "")); } } } @@ -1370,7 +1389,7 @@ TEST_F(CommentParserTest, VerbatimLine2) { ASSERT_TRUE(HasParagraphCommentAt(FC, 0, " ")); { VerbatimLineComment *VLC; - ASSERT_TRUE(HasVerbatimLineAt(FC, 1, VLC, "fn", + ASSERT_TRUE(HasVerbatimLineAt(FC, Traits, 1, VLC, "fn", " void *foo(const char *zzz = \"\\$\");")); } } diff --git a/unittests/AST/DeclPrinterTest.cpp b/unittests/AST/DeclPrinterTest.cpp new file mode 100644 index 0000000..a2fc839 --- /dev/null +++ b/unittests/AST/DeclPrinterTest.cpp @@ -0,0 +1,1248 @@ +//===- unittests/AST/DeclPrinterTest.cpp --- Declaration printer tests ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains tests for Decl::print() and related methods. +// +// Search this file for WRONG to see test cases that are producing something +// completely wrong, invalid C++ or just misleading. +// +// These tests have a coding convention: +// * declaration to be printed is named 'A' unless it should have some special +// name (e.g., 'operator+'); +// * additional helper declarations are 'Z', 'Y', 'X' and so on. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallString.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace ast_matchers; +using namespace tooling; + +namespace { + +void PrintDecl(raw_ostream &Out, const ASTContext *Context, const Decl *D) { + PrintingPolicy Policy = Context->getPrintingPolicy(); + Policy.TerseOutput = true; + D->print(Out, Policy, /*Indentation*/ 0, /*PrintInstantiation*/ false); +} + +class PrintMatch : public MatchFinder::MatchCallback { + SmallString<1024> Printed; + unsigned NumFoundDecls; + +public: + PrintMatch() : NumFoundDecls(0) {} + + virtual void run(const MatchFinder::MatchResult &Result) { + const Decl *D = Result.Nodes.getDeclAs<Decl>("id"); + if (!D || D->isImplicit()) + return; + NumFoundDecls++; + if (NumFoundDecls > 1) + return; + + llvm::raw_svector_ostream Out(Printed); + PrintDecl(Out, Result.Context, D); + } + + StringRef getPrinted() const { + return Printed; + } + + unsigned getNumFoundDecls() const { + return NumFoundDecls; + } +}; + +::testing::AssertionResult PrintedDeclMatches( + StringRef Code, + const std::vector<std::string> &Args, + const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted, + StringRef FileName) { + PrintMatch Printer; + MatchFinder Finder; + Finder.addMatcher(NodeMatch, &Printer); + OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); + + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName)) + return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; + + if (Printer.getNumFoundDecls() == 0) + return testing::AssertionFailure() + << "Matcher didn't find any declarations"; + + if (Printer.getNumFoundDecls() > 1) + return testing::AssertionFailure() + << "Matcher should match only one declaration " + "(found " << Printer.getNumFoundDecls() << ")"; + + if (Printer.getPrinted() != ExpectedPrinted) + return ::testing::AssertionFailure() + << "Expected \"" << ExpectedPrinted << "\", " + "got \"" << Printer.getPrinted() << "\""; + + return ::testing::AssertionSuccess(); +} + +::testing::AssertionResult PrintedDeclCXX98Matches(StringRef Code, + StringRef DeclName, + StringRef ExpectedPrinted) { + std::vector<std::string> Args(1, "-std=c++98"); + return PrintedDeclMatches(Code, + Args, + namedDecl(hasName(DeclName)).bind("id"), + ExpectedPrinted, + "input.cc"); +} + +::testing::AssertionResult PrintedDeclCXX98Matches( + StringRef Code, + const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted) { + std::vector<std::string> Args(1, "-std=c++98"); + return PrintedDeclMatches(Code, + Args, + NodeMatch, + ExpectedPrinted, + "input.cc"); +} + +::testing::AssertionResult PrintedDeclCXX11Matches(StringRef Code, + StringRef DeclName, + StringRef ExpectedPrinted) { + std::vector<std::string> Args(1, "-std=c++11"); + return PrintedDeclMatches(Code, + Args, + namedDecl(hasName(DeclName)).bind("id"), + ExpectedPrinted, + "input.cc"); +} + +::testing::AssertionResult PrintedDeclCXX11Matches( + StringRef Code, + const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted) { + std::vector<std::string> Args(1, "-std=c++11"); + return PrintedDeclMatches(Code, + Args, + NodeMatch, + ExpectedPrinted, + "input.cc"); +} + +::testing::AssertionResult PrintedDeclObjCMatches( + StringRef Code, + const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted) { + std::vector<std::string> Args(1, ""); + return PrintedDeclMatches(Code, + Args, + NodeMatch, + ExpectedPrinted, + "input.m"); +} + +} // unnamed namespace + +TEST(DeclPrinter, TestNamespace1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "namespace A { int B; }", + "A", + "namespace A {\n}")); + // Should be: with { ... } +} + +TEST(DeclPrinter, TestNamespace2) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "inline namespace A { int B; }", + "A", + "inline namespace A {\n}")); + // Should be: with { ... } +} + +TEST(DeclPrinter, TestNamespaceAlias1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "namespace Z { }" + "namespace A = Z;", + "A", + "namespace A = Z")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestNamespaceAlias2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "namespace X { namespace Y {} }" + "namespace A = X::Y;", + "A", + "namespace A = X::Y")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXRecordDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "class A { int a; };", + "A", + "class A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestCXXRecordDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A { int a; };", + "A", + "struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestCXXRecordDecl3) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "union A { int a; };", + "A", + "union A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestCXXRecordDecl4) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "class Z { int a; };" + "class A : Z { int b; };", + "A", + "class A : Z {\n}")); + // Should be: with semicolon, with { ... }, without two spaces +} + +TEST(DeclPrinter, TestCXXRecordDecl5) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z { int a; };" + "struct A : Z { int b; };", + "A", + "struct A : Z {\n}")); + // Should be: with semicolon, with { ... }, without two spaces +} + +TEST(DeclPrinter, TestCXXRecordDecl6) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "class Z { int a; };" + "class A : public Z { int b; };", + "A", + "class A : public Z {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestCXXRecordDecl7) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "class Z { int a; };" + "class A : protected Z { int b; };", + "A", + "class A : protected Z {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestCXXRecordDecl8) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "class Z { int a; };" + "class A : private Z { int b; };", + "A", + "class A : private Z {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestCXXRecordDecl9) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "class Z { int a; };" + "class A : virtual Z { int b; };", + "A", + "class A : virtual Z {\n}")); + // Should be: with semicolon, with { ... }, without two spaces +} + +TEST(DeclPrinter, TestCXXRecordDecl10) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "class Z { int a; };" + "class A : virtual public Z { int b; };", + "A", + "class A : virtual public Z {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestCXXRecordDecl11) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "class Z { int a; };" + "class Y : virtual public Z { int b; };" + "class A : virtual public Z, private Y { int c; };", + "A", + "class A : virtual public Z, private Y {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestFunctionDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void A();", + "A", + "void A()")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void A() {}", + "A", + "void A()")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl3) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void Z();" + "void A() { Z(); }", + "A", + "void A()")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl4) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "extern void A();", + "A", + "extern void A()")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl5) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "static void A();", + "A", + "static void A()")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl6) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "inline void A();", + "A", + "inline void A()")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl7) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "constexpr int A(int a);", + "A", + "int A(int a)")); + // WRONG; Should be: "constexpr int A(int a);" +} + +TEST(DeclPrinter, TestFunctionDecl8) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void A(int a);", + "A", + "void A(int a)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl9) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void A(...);", + "A", + "void A(...)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl10) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void A(int a, ...);", + "A", + "void A(int a, ...)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl11) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "typedef long size_t;" + "typedef int *pInt;" + "void A(int a, pInt b, size_t c);", + "A", + "void A(int a, pInt b, size_t c)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl12) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void A(int a, int b = 0);", + "A", + "void A(int a, int b = 0)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl13) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void (*A(int a))(int b);", + "A", + "void (*A(int a))(int)")); + // Should be: with semicolon, with parameter name (?) +} + +TEST(DeclPrinter, TestFunctionDecl14) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T>" + "void A(T t) { }" + "template<>" + "void A(int N) { }", + functionDecl(hasName("A"), isExplicitTemplateSpecialization()).bind("id"), + "void A(int N)")); + // WRONG; Should be: "template <> void A(int N);")); +} + + +TEST(DeclPrinter, TestCXXConstructorDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " A();" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "")); + // WRONG; Should be: "A();" +} + +TEST(DeclPrinter, TestCXXConstructorDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " A(int a);" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "")); + // WRONG; Should be: "A(int a);" +} + +TEST(DeclPrinter, TestCXXConstructorDecl3) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " A(const A &a);" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "")); + // WRONG; Should be: "A(const A &a);" +} + +TEST(DeclPrinter, TestCXXConstructorDecl4) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " A(const A &a, int = 0);" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "")); + // WRONG; Should be: "A(const A &a, int = 0);" +} + +TEST(DeclPrinter, TestCXXConstructorDecl5) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct A {" + " A(const A &&a);" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "")); + // WRONG; Should be: "A(const A &&a);" +} + +TEST(DeclPrinter, TestCXXConstructorDecl6) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " explicit A(int a);" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "")); + // WRONG; Should be: "explicit A(int a);" +} + +TEST(DeclPrinter, TestCXXConstructorDecl7) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct A {" + " constexpr A();" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "")); + // WRONG; Should be: "constexpr A();" +} + +TEST(DeclPrinter, TestCXXConstructorDecl8) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct A {" + " A() = default;" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "")); + // WRONG; Should be: "A() = default;" +} + +TEST(DeclPrinter, TestCXXConstructorDecl9) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct A {" + " A() = delete;" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + " = delete")); + // WRONG; Should be: "A() = delete;" +} + +TEST(DeclPrinter, TestCXXConstructorDecl10) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<typename... T>" + "struct A {" + " A(const A &a);" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "")); + // WRONG; Should be: "A(const A &a);" +} + +#if !defined(_MSC_VER) +TEST(DeclPrinter, TestCXXConstructorDecl11) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<typename... T>" + "struct A : public T... {" + " A(T&&... ts) : T(ts)... {}" + "};", + constructorDecl(ofClass(hasName("A"))).bind("id"), + "A<T...>(T &&ts...) : T(ts)")); + // WRONG; Should be: "A(T&&... ts) : T(ts)..." +} +#endif + +TEST(DeclPrinter, TestCXXDestructorDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " ~A();" + "};", + destructorDecl(ofClass(hasName("A"))).bind("id"), + "void ~A()")); + // WRONG; Should be: "~A();" +} + +TEST(DeclPrinter, TestCXXDestructorDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " virtual ~A();" + "};", + destructorDecl(ofClass(hasName("A"))).bind("id"), + "virtual void ~A()")); + // WRONG; Should be: "virtual ~A();" +} + +TEST(DeclPrinter, TestCXXConversionDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " operator int();" + "};", + methodDecl(ofClass(hasName("A"))).bind("id"), + "int operator int()")); + // WRONG; Should be: "operator int();" +} + +TEST(DeclPrinter, TestCXXConversionDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " operator bool();" + "};", + methodDecl(ofClass(hasName("A"))).bind("id"), + "bool operator _Bool()")); + // WRONG; Should be: "operator bool();" +} + +TEST(DeclPrinter, TestCXXConversionDecl3) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {};" + "struct A {" + " operator Z();" + "};", + methodDecl(ofClass(hasName("A"))).bind("id"), + "Z operator struct Z()")); + // WRONG; Should be: "operator Z();" +} + +TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction1) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "namespace std { typedef decltype(sizeof(int)) size_t; }" + "struct Z {" + " void *operator new(std::size_t);" + "};", + methodDecl(ofClass(hasName("Z"))).bind("id"), + "void *operator new(std::size_t)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction2) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "namespace std { typedef decltype(sizeof(int)) size_t; }" + "struct Z {" + " void *operator new[](std::size_t);" + "};", + methodDecl(ofClass(hasName("Z"))).bind("id"), + "void *operator new[](std::size_t)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction3) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct Z {" + " void operator delete(void *);" + "};", + methodDecl(ofClass(hasName("Z"))).bind("id"), + "void operator delete(void *) noexcept")); + // Should be: with semicolon, without noexcept? +} + +TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction4) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " void operator delete(void *);" + "};", + methodDecl(ofClass(hasName("Z"))).bind("id"), + "void operator delete(void *)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl_AllocationFunction5) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct Z {" + " void operator delete[](void *);" + "};", + methodDecl(ofClass(hasName("Z"))).bind("id"), + "void operator delete[](void *) noexcept")); + // Should be: with semicolon, without noexcept? +} + +TEST(DeclPrinter, TestCXXMethodDecl_Operator1) { + const char *OperatorNames[] = { + "+", "-", "*", "/", "%", "^", "&", "|", + "=", "<", ">", "+=", "-=", "*=", "/=", "%=", + "^=", "&=", "|=", "<<", ">>", ">>=", "<<=", "==", "!=", + "<=", ">=", "&&", "||", ",", "->*", + "()", "[]" + }; + + for (unsigned i = 0, e = llvm::array_lengthof(OperatorNames); i != e; ++i) { + SmallString<128> Code; + Code.append("struct Z { void operator"); + Code.append(OperatorNames[i]); + Code.append("(Z z); };"); + + SmallString<128> Expected; + Expected.append("void operator"); + Expected.append(OperatorNames[i]); + Expected.append("(Z z)"); + // Should be: with semicolon + + ASSERT_TRUE(PrintedDeclCXX98Matches( + Code, + methodDecl(ofClass(hasName("Z"))).bind("id"), + Expected)); + } +} + +TEST(DeclPrinter, TestCXXMethodDecl_Operator2) { + const char *OperatorNames[] = { + "~", "!", "++", "--", "->" + }; + + for (unsigned i = 0, e = llvm::array_lengthof(OperatorNames); i != e; ++i) { + SmallString<128> Code; + Code.append("struct Z { void operator"); + Code.append(OperatorNames[i]); + Code.append("(); };"); + + SmallString<128> Expected; + Expected.append("void operator"); + Expected.append(OperatorNames[i]); + Expected.append("()"); + // Should be: with semicolon + + ASSERT_TRUE(PrintedDeclCXX98Matches( + Code, + methodDecl(ofClass(hasName("Z"))).bind("id"), + Expected)); + } +} + +TEST(DeclPrinter, TestCXXMethodDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " void A(int a);" + "};", + "A", + "void A(int a)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " virtual void A(int a);" + "};", + "A", + "virtual void A(int a)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl3) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " virtual void A(int a);" + "};" + "struct ZZ : Z {" + " void A(int a);" + "};", + "ZZ::A", + "void A(int a)")); + // Should be: with semicolon + // TODO: should we print "virtual"? +} + +TEST(DeclPrinter, TestCXXMethodDecl4) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " inline void A(int a);" + "};", + "A", + "inline void A(int a)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl5) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " virtual void A(int a) = 0;" + "};", + "A", + "virtual void A(int a) = 0")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " void A(int a) const;" + "};", + "A", + "void A(int a) const")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " void A(int a) volatile;" + "};", + "A", + "void A(int a) volatile")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl_CVQualifier3) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " void A(int a) const volatile;" + "};", + "A", + "void A(int a) const volatile")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestCXXMethodDecl_RefQualifier1) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct Z {" + " void A(int a) &;" + "};", + "A", + "void A(int a)")); + // WRONG; Should be: "void A(int a) &;" +} + +TEST(DeclPrinter, TestCXXMethodDecl_RefQualifier2) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct Z {" + " void A(int a) &&;" + "};", + "A", + "void A(int a)")); + // WRONG; Should be: "void A(int a) &&;" +} + +TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " void A(int a) throw();" + "};", + "A", + "void A(int a) throw()")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z {" + " void A(int a) throw(int);" + "};", + "A", + "void A(int a) throw(int)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification3) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "class ZZ {};" + "struct Z {" + " void A(int a) throw(ZZ, int);" + "};", + "A", + "void A(int a) throw(ZZ, int)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification4) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct Z {" + " void A(int a) noexcept;" + "};", + "A", + "void A(int a) noexcept")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification5) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct Z {" + " void A(int a) noexcept(true);" + "};", + "A", + "void A(int a) noexcept(trueA(int a) noexcept(true)")); + // WRONG; Should be: "void A(int a) noexcept(true);" +} + +TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification6) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "struct Z {" + " void A(int a) noexcept(1 < 2);" + "};", + "A", + "void A(int a) noexcept(1 < 2A(int a) noexcept(1 < 2)")); + // WRONG; Should be: "void A(int a) noexcept(1 < 2);" +} + +TEST(DeclPrinter, TestFunctionDecl_ExceptionSpecification7) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<int N>" + "struct Z {" + " void A(int a) noexcept(N < 2);" + "};", + "A", + "void A(int a) noexcept(N < 2A(int a) noexcept(N < 2)")); + // WRONG; Should be: "void A(int a) noexcept(N < 2);" +} + +TEST(DeclPrinter, TestVarDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "char *const (*(*A)[5])(int);", + "A", + "char *const (*(*A)[5])(int)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestVarDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void (*A)() throw(int);", + "A", + "void (*A)() throw(int)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestVarDecl3) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "void (*A)() noexcept;", + "A", + "void (*A)() noexcept")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFieldDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T>" + "struct Z { T A; };", + "A", + "T A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFieldDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<int N>" + "struct Z { int A[N]; };", + "A", + "int A[N]")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestClassTemplateDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T>" + "struct A { T a; };", + classTemplateDecl(hasName("A")).bind("id"), + "template <typename T> struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestClassTemplateDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T = int>" + "struct A { T a; };", + classTemplateDecl(hasName("A")).bind("id"), + "template <typename T = int> struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestClassTemplateDecl3) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<class T>" + "struct A { T a; };", + classTemplateDecl(hasName("A")).bind("id"), + "template <class T> struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestClassTemplateDecl4) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T, typename U>" + "struct A { T a; U b; };", + classTemplateDecl(hasName("A")).bind("id"), + "template <typename T, typename U> struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestClassTemplateDecl5) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<int N>" + "struct A { int a[N]; };", + classTemplateDecl(hasName("A")).bind("id"), + "template <int N> struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestClassTemplateDecl6) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<int N = 42>" + "struct A { int a[N]; };", + classTemplateDecl(hasName("A")).bind("id"), + "template <int N = 42> struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestClassTemplateDecl7) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "typedef int MyInt;" + "template<MyInt N>" + "struct A { int a[N]; };", + classTemplateDecl(hasName("A")).bind("id"), + "template <MyInt N> struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestClassTemplateDecl8) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<template<typename U> class T> struct A { };", + classTemplateDecl(hasName("A")).bind("id"), + "template <template <typename U> class T> struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestClassTemplateDecl9) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T> struct Z { };" + "template<template<typename U> class T = Z> struct A { };", + classTemplateDecl(hasName("A")).bind("id"), + "template <template <typename U> class T> struct A {\n}")); + // Should be: with semicolon, with { ... } +} + +TEST(DeclPrinter, TestClassTemplateDecl10) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<typename... T>" + "struct A { int a; };", + classTemplateDecl(hasName("A")).bind("id"), + "template <typename ... T> struct A {\n}")); + // Should be: with semicolon, with { ... }, without spaces before '...' +} + +TEST(DeclPrinter, TestClassTemplateDecl11) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<typename... T>" + "struct A : public T... { int a; };", + classTemplateDecl(hasName("A")).bind("id"), + "template <typename ... T> struct A : public T... {\n}")); + // Should be: with semicolon, with { ... }, without spaces before '...' +} + +TEST(DeclPrinter, TestClassTemplatePartialSpecializationDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T, typename U>" + "struct A { T a; U b; };" + "template<typename T>" + "struct A<T, int> { T a; };", + classTemplateSpecializationDecl().bind("id"), + "struct A {\n}")); + // WRONG; Should be: "template<typename T> struct A<T, int> { ... }" +} + +TEST(DeclPrinter, TestClassTemplatePartialSpecializationDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T>" + "struct A { T a; };" + "template<typename T>" + "struct A<T *> { T a; };", + classTemplateSpecializationDecl().bind("id"), + "struct A {\n}")); + // WRONG; Should be: "template<typename T> struct A<T *> { ... }" +} + +TEST(DeclPrinter, TestClassTemplateSpecializationDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T>" + "struct A { T a; };" + "template<>" + "struct A<int> { int a; };", + classTemplateSpecializationDecl().bind("id"), + "struct A {\n}")); + // WRONG; Should be: "template<> struct A<int> { ... }" +} + +TEST(DeclPrinter, TestFunctionTemplateDecl1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T>" + "void A(T &t);", + functionTemplateDecl(hasName("A")).bind("id"), + "template <typename T> void A(T &t)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionTemplateDecl2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T>" + "void A(T &t) { }", + functionTemplateDecl(hasName("A")).bind("id"), + "template <typename T> void A(T &t)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionTemplateDecl3) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<typename... T>" + "void A(T... a);", + functionTemplateDecl(hasName("A")).bind("id"), + "template <typename ... T> void A(T a...)")); + // WRONG; Should be: "template <typename ... T> void A(T... a)" + // (not "T a...") + // Should be: with semicolon. +} + +TEST(DeclPrinter, TestFunctionTemplateDecl4) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z { template<typename T> void A(T t); };", + functionTemplateDecl(hasName("A")).bind("id"), + "template <typename T> void A(T t)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionTemplateDecl5) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct Z { template<typename T> void A(T t) {} };", + functionTemplateDecl(hasName("A")).bind("id"), + "template <typename T> void A(T t)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestFunctionTemplateDecl6) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T >struct Z {" + " template<typename U> void A(U t) {}" + "};", + functionTemplateDecl(hasName("A")).bind("id"), + "template <typename U> void A(U t)")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList1) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T> struct Z {};" + "struct X {};" + "Z<X> A;", + "A", + "Z<X> A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList2) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T, typename U> struct Z {};" + "struct X {};" + "typedef int Y;" + "Z<X, Y> A;", + "A", + "Z<X, Y> A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList3) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T> struct Z {};" + "template<typename T> struct X {};" + "Z<X<int> > A;", + "A", + "Z<X<int> > A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList4) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<typename T> struct Z {};" + "template<typename T> struct X {};" + "Z<X<int>> A;", + "A", + "Z<X<int> > A")); + // Should be: with semicolon, without extra space in "> >" +} + +TEST(DeclPrinter, TestTemplateArgumentList5) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T> struct Z {};" + "template<typename T> struct X { Z<T> A; };", + "A", + "Z<T> A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList6) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<template<typename T> class U> struct Z {};" + "template<typename T> struct X {};" + "Z<X> A;", + "A", + "Z<X> A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList7) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<template<typename T> class U> struct Z {};" + "template<template<typename T> class U> struct Y {" + " Z<U> A;" + "};", + "A", + "Z<U> A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList8) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<typename T> struct Z {};" + "template<template<typename T> class U> struct Y {" + " Z<U<int> > A;" + "};", + "A", + "Z<U<int> > A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList9) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<unsigned I> struct Z {};" + "Z<0> A;", + "A", + "Z<0> A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList10) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<unsigned I> struct Z {};" + "template<unsigned I> struct X { Z<I> A; };", + "A", + "Z<I> A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList11) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<int I> struct Z {};" + "Z<42 * 10 - 420 / 1> A;", + "A", + "Z<42 * 10 - 420 / 1> A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList12) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "template<const char *p> struct Z {};" + "extern const char X[] = \"aaa\";" + "Z<X> A;", + "A", + "Z<X> A")); + // Should be: with semicolon +} + +TEST(DeclPrinter, TestTemplateArgumentList13) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<typename... T> struct Z {};" + "template<typename... T> struct X {" + " Z<T...> A;" + "};", + "A", + "Z<T...> A")); + // Should be: with semicolon, without extra space in "> >" +} + +TEST(DeclPrinter, TestTemplateArgumentList14) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<typename... T> struct Z {};" + "template<typename T> struct Y {};" + "template<typename... T> struct X {" + " Z<Y<T>...> A;" + "};", + "A", + "Z<Y<T>...> A")); + // Should be: with semicolon, without extra space in "> >" +} + +TEST(DeclPrinter, TestTemplateArgumentList15) { + ASSERT_TRUE(PrintedDeclCXX11Matches( + "template<unsigned I> struct Z {};" + "template<typename... T> struct X {" + " Z<sizeof...(T)> A;" + "};", + "A", + "Z<sizeof...(T)> A")); + // Should be: with semicolon, without extra space in "> >" +} + +TEST(DeclPrinter, TestObjCMethod1) { + ASSERT_TRUE(PrintedDeclObjCMatches( + "__attribute__((objc_root_class)) @interface X\n" + "- (int)A:(id)anObject inRange:(long)range;\n" + "@end\n" + "@implementation X\n" + "- (int)A:(id)anObject inRange:(long)range { int printThis; return 0; }\n" + "@end\n", + namedDecl(hasName("A:inRange:"), + hasDescendant(namedDecl(hasName("printThis")))).bind("id"), + "- (int) A:(id)anObject inRange:(long)range")); +} + diff --git a/unittests/AST/Makefile b/unittests/AST/Makefile index 31cd5de..e07fc45 100644 --- a/unittests/AST/Makefile +++ b/unittests/AST/Makefile @@ -9,7 +9,11 @@ CLANG_LEVEL = ../.. TESTNAME = AST -LINK_COMPONENTS := support mc -USEDLIBS = clangAST.a clangLex.a clangBasic.a +include $(CLANG_LEVEL)/../../Makefile.config +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc +USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ + clangRewriteCore.a clangRewriteFrontend.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/AST/SourceLocationTest.cpp b/unittests/AST/SourceLocationTest.cpp new file mode 100644 index 0000000..dec833d --- /dev/null +++ b/unittests/AST/SourceLocationTest.cpp @@ -0,0 +1,289 @@ +//===- unittest/AST/SourceLocationTest.cpp - AST source loc unit tests ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains tests for SourceLocation and SourceRange fields +// in AST nodes. +// +// FIXME: In the long-term, when we test more than source locations, we may +// want to have a unit test file for an AST node (or group of related nodes), +// rather than a unit test file for source locations for all AST nodes. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.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 { + +using clang::tooling::newFrontendActionFactory; +using clang::tooling::runToolOnCodeWithArgs; +using clang::tooling::FrontendActionFactory; + +enum Language { Lang_C, Lang_C89, Lang_CXX }; + +/// \brief Base class for verifying some property of nodes found by a matcher. +/// +/// FIXME: This class should be shared with other AST tests. +template <typename NodeType> +class MatchVerifier : public MatchFinder::MatchCallback { +public: + template <typename MatcherType> + testing::AssertionResult match(const std::string &Code, + const MatcherType &AMatcher) { + return match(Code, AMatcher, Lang_CXX); + } + + template <typename MatcherType> + testing::AssertionResult match(const std::string &Code, + const MatcherType &AMatcher, Language L); + +protected: + virtual void run(const MatchFinder::MatchResult &Result); + virtual void verify(const MatchFinder::MatchResult &Result, + const NodeType &Node) = 0; + + void setFailure(const Twine &Result) { + Verified = false; + VerifyResult = Result.str(); + } + +private: + bool Verified; + std::string VerifyResult; +}; + +/// \brief Runs a matcher over some code, and returns the result of the +/// verifier for the matched node. +template <typename NodeType> template <typename MatcherType> +testing::AssertionResult MatchVerifier<NodeType>::match( + const std::string &Code, const MatcherType &AMatcher, Language L) { + MatchFinder Finder; + Finder.addMatcher(AMatcher.bind(""), this); + OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); + + std::vector<std::string> Args; + StringRef FileName; + switch (L) { + case Lang_C: + Args.push_back("-std=c99"); + FileName = "input.c"; + break; + case Lang_C89: + Args.push_back("-std=c89"); + FileName = "input.c"; + break; + case Lang_CXX: + Args.push_back("-std=c++98"); + FileName = "input.cc"; + break; + } + + // Default to failure in case callback is never called + setFailure("Could not find match"); + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName)) + return testing::AssertionFailure() << "Parsing error"; + if (!Verified) + return testing::AssertionFailure() << VerifyResult; + return testing::AssertionSuccess(); +} + +template <typename NodeType> +void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) { + const NodeType *Node = Result.Nodes.getNodeAs<NodeType>(""); + if (!Node) { + setFailure("Matched node has wrong type"); + } else { + // Callback has been called, default to success + Verified = true; + verify(Result, *Node); + } +} + +/// \brief Verify whether a node has the correct source location. +/// +/// By default, Node.getSourceLocation() is checked. This can be changed +/// by overriding getLocation(). +template <typename NodeType> +class LocationVerifier : public MatchVerifier<NodeType> { +public: + void expectLocation(unsigned Line, unsigned Column) { + ExpectLine = Line; + ExpectColumn = Column; + } + +protected: + void verify(const MatchFinder::MatchResult &Result, const NodeType &Node) { + SourceLocation Loc = getLocation(Node); + unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc); + unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc); + if (Line != ExpectLine || Column != ExpectColumn) { + std::string MsgStr; + llvm::raw_string_ostream Msg(MsgStr); + Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn + << ">, found <"; + Loc.print(Msg, *Result.SourceManager); + Msg << '>'; + this->setFailure(Msg.str()); + } + } + + virtual SourceLocation getLocation(const NodeType &Node) { + return Node.getLocation(); + } + +private: + unsigned ExpectLine, ExpectColumn; +}; + +/// \brief Verify whether a node has the correct source range. +/// +/// By default, Node.getSourceRange() is checked. This can be changed +/// by overriding getRange(). +template <typename NodeType> +class RangeVerifier : public MatchVerifier<NodeType> { +public: + void expectRange(unsigned BeginLine, unsigned BeginColumn, + unsigned EndLine, unsigned EndColumn) { + ExpectBeginLine = BeginLine; + ExpectBeginColumn = BeginColumn; + ExpectEndLine = EndLine; + ExpectEndColumn = EndColumn; + } + +protected: + void verify(const MatchFinder::MatchResult &Result, const NodeType &Node) { + SourceRange R = getRange(Node); + SourceLocation Begin = R.getBegin(); + SourceLocation End = R.getEnd(); + unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin); + unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin); + unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End); + unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End); + if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn || + EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) { + std::string MsgStr; + llvm::raw_string_ostream Msg(MsgStr); + Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn + << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <"; + Begin.print(Msg, *Result.SourceManager); + Msg << '-'; + End.print(Msg, *Result.SourceManager); + Msg << '>'; + this->setFailure(Msg.str()); + } + } + + virtual SourceRange getRange(const NodeType &Node) { + return Node.getSourceRange(); + } + +private: + unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn; +}; + +TEST(MatchVerifier, ParseError) { + LocationVerifier<VarDecl> Verifier; + Verifier.expectLocation(1, 1); + EXPECT_FALSE(Verifier.match("int i", varDecl())); +} + +TEST(MatchVerifier, NoMatch) { + LocationVerifier<VarDecl> Verifier; + Verifier.expectLocation(1, 1); + EXPECT_FALSE(Verifier.match("int i;", recordDecl())); +} + +TEST(MatchVerifier, WrongType) { + LocationVerifier<RecordDecl> Verifier; + Verifier.expectLocation(1, 1); + EXPECT_FALSE(Verifier.match("int i;", varDecl())); +} + +TEST(LocationVerifier, WrongLocation) { + LocationVerifier<VarDecl> Verifier; + Verifier.expectLocation(1, 1); + EXPECT_FALSE(Verifier.match("int i;", varDecl())); +} + +TEST(RangeVerifier, WrongRange) { + RangeVerifier<VarDecl> Verifier; + Verifier.expectRange(1, 1, 1, 1); + EXPECT_FALSE(Verifier.match("int i;", varDecl())); +} + +class LabelDeclRangeVerifier : public RangeVerifier<LabelStmt> { +protected: + virtual SourceRange getRange(const LabelStmt &Node) { + return Node.getDecl()->getSourceRange(); + } +}; + +TEST(LabelDecl, Range) { + LabelDeclRangeVerifier Verifier; + Verifier.expectRange(1, 12, 1, 12); + EXPECT_TRUE(Verifier.match("void f() { l: return; }", labelStmt())); +} + +TEST(LabelStmt, Range) { + RangeVerifier<LabelStmt> Verifier; + Verifier.expectRange(1, 12, 1, 15); + EXPECT_TRUE(Verifier.match("void f() { l: return; }", labelStmt())); +} + +TEST(ParmVarDecl, KNRLocation) { + LocationVerifier<ParmVarDecl> Verifier; + Verifier.expectLocation(1, 8); + EXPECT_TRUE(Verifier.match("void f(i) {}", varDecl(), Lang_C)); +} + +TEST(ParmVarDecl, KNRRange) { + RangeVerifier<ParmVarDecl> Verifier; + Verifier.expectRange(1, 8, 1, 8); + EXPECT_TRUE(Verifier.match("void f(i) {}", varDecl(), Lang_C)); +} + +TEST(CXXNewExpr, ArrayRange) { + RangeVerifier<CXXNewExpr> Verifier; + Verifier.expectRange(1, 12, 1, 22); + EXPECT_TRUE(Verifier.match("void f() { new int[10]; }", newExpr())); +} + +TEST(CXXNewExpr, ParenRange) { + RangeVerifier<CXXNewExpr> Verifier; + Verifier.expectRange(1, 12, 1, 20); + EXPECT_TRUE(Verifier.match("void f() { new int(); }", newExpr())); +} + +TEST(MemberExpr, ImplicitMemberRange) { + RangeVerifier<MemberExpr> Verifier; + Verifier.expectRange(2, 30, 2, 30); + EXPECT_TRUE(Verifier.match("struct S { operator int() const; };\n" + "int foo(const S& s) { return s; }", + memberExpr())); +} + +TEST(VarDecl, VMTypeFixedVarDeclRange) { + RangeVerifier<VarDecl> Verifier; + Verifier.expectRange(1, 1, 1, 23); + EXPECT_TRUE(Verifier.match("int a[(int)(void*)1234];", + varDecl(), Lang_C89)); +} + +TEST(CXXConstructorDecl, NoRetFunTypeLocRange) { + RangeVerifier<CXXConstructorDecl> Verifier; + Verifier.expectRange(1, 11, 1, 13); + EXPECT_TRUE(Verifier.match("class C { C(); };", functionDecl())); +} + +} // end namespace ast_matchers +} // end namespace clang diff --git a/unittests/AST/StmtPrinterTest.cpp b/unittests/AST/StmtPrinterTest.cpp new file mode 100644 index 0000000..0fd1b2e --- /dev/null +++ b/unittests/AST/StmtPrinterTest.cpp @@ -0,0 +1,172 @@ +//===- unittests/AST/StmtPrinterTest.cpp --- Statement printer tests ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains tests for Stmt::printPretty() and related methods. +// +// Search this file for WRONG to see test cases that are producing something +// completely wrong, invalid C++ or just misleading. +// +// These tests have a coding convention: +// * statements to be printed should be contained within a function named 'A' +// unless it should have some special name (e.g., 'operator+'); +// * additional helper declarations are 'Z', 'Y', 'X' and so on. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallString.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace ast_matchers; +using namespace tooling; + +namespace { + +void PrintStmt(raw_ostream &Out, const ASTContext *Context, const Stmt *S) { + PrintingPolicy Policy = Context->getPrintingPolicy(); + S->printPretty(Out, /*Helper*/ 0, Policy); +} + +class PrintMatch : public MatchFinder::MatchCallback { + SmallString<1024> Printed; + unsigned NumFoundStmts; + +public: + PrintMatch() : NumFoundStmts(0) {} + + virtual void run(const MatchFinder::MatchResult &Result) { + const Stmt *S = Result.Nodes.getStmtAs<Stmt>("id"); + if (!S) + return; + NumFoundStmts++; + if (NumFoundStmts > 1) + return; + + llvm::raw_svector_ostream Out(Printed); + PrintStmt(Out, Result.Context, S); + } + + StringRef getPrinted() const { + return Printed; + } + + unsigned getNumFoundStmts() const { + return NumFoundStmts; + } +}; + +::testing::AssertionResult PrintedStmtMatches( + StringRef Code, + const std::vector<std::string> &Args, + const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted) { + + PrintMatch Printer; + MatchFinder Finder; + Finder.addMatcher(NodeMatch, &Printer); + OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); + + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) + return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; + + if (Printer.getNumFoundStmts() == 0) + return testing::AssertionFailure() + << "Matcher didn't find any statements"; + + if (Printer.getNumFoundStmts() > 1) + return testing::AssertionFailure() + << "Matcher should match only one statement " + "(found " << Printer.getNumFoundStmts() << ")"; + + if (Printer.getPrinted() != ExpectedPrinted) + return ::testing::AssertionFailure() + << "Expected \"" << ExpectedPrinted << "\", " + "got \"" << Printer.getPrinted() << "\""; + + return ::testing::AssertionSuccess(); +} + +::testing::AssertionResult PrintedStmtCXX98Matches( + StringRef Code, + StringRef ContainingFunction, + StringRef ExpectedPrinted) { + std::vector<std::string> Args; + Args.push_back("-std=c++98"); + Args.push_back("-Wno-unused-value"); + return PrintedStmtMatches(Code, + Args, + functionDecl(hasName(ContainingFunction), + has(compoundStmt(has(stmt().bind("id"))))), + ExpectedPrinted); +} + +::testing::AssertionResult PrintedStmtMSMatches( + StringRef Code, + StringRef ContainingFunction, + StringRef ExpectedPrinted) { + std::vector<std::string> Args; + Args.push_back("-std=c++98"); + Args.push_back("-fms-extensions"); + Args.push_back("-Wno-unused-value"); + return PrintedStmtMatches(Code, + Args, + functionDecl(hasName(ContainingFunction), + has(compoundStmt(has(stmt().bind("id"))))), + ExpectedPrinted); +} + +} // unnamed namespace + +TEST(StmtPrinter, TestIntegerLiteral) { + ASSERT_TRUE(PrintedStmtCXX98Matches( + "void A() {" + " 1, -1, 1U, 1u," + " 1L, 1l, -1L, 1UL, 1ul," + " 1LL, -1LL, 1ULL;" + "}", + "A", + "1 , -1 , 1U , 1U , " + "1L , 1L , -1L , 1UL , 1UL , " + "1LL , -1LL , 1ULL")); + // Should be: with semicolon +} + +TEST(StmtPrinter, TestMSIntegerLiteral) { + ASSERT_TRUE(PrintedStmtMSMatches( + "void A() {" + " 1i8, -1i8, 1ui8, " + " 1i16, -1i16, 1ui16, " + " 1i32, -1i32, 1ui32, " + " 1i64, -1i64, 1ui64, " + " 1i128, -1i128, 1ui128, 1Ui128," + " 0x10000000000000000i128;" + "}", + "A", + "1 , -1 , 1U , " + "1 , -1 , 1U , " + "1L , -1L , 1UL , " + "1LL , -1LL , 1ULL , " + "1 , -1 , 1U , 1U , " + "18446744073709551616i128")); + // Should be: with semicolon + // WRONG; all 128-bit literals should be printed as 128-bit. + // (This is because currently we do semantic analysis incorrectly.) +} + +TEST(StmtPrinter, TestFloatingPointLiteral) { + ASSERT_TRUE(PrintedStmtCXX98Matches( + "void A() { 1.0f, -1.0f, 1.0, -1.0, 1.0l, -1.0l; }", + "A", + "1.F , -1.F , 1. , -1. , 1.L , -1.L")); + // Should be: with semicolon +} + diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index adf0e94..e15940a 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -19,21 +19,21 @@ namespace ast_matchers { #if GTEST_HAS_DEATH_TEST TEST(HasNameDeathTest, DiesOnEmptyName) { ASSERT_DEBUG_DEATH({ - DeclarationMatcher HasEmptyName = record(hasName("")); + DeclarationMatcher HasEmptyName = recordDecl(hasName("")); EXPECT_TRUE(notMatches("class X {};", HasEmptyName)); }, ""); } TEST(HasNameDeathTest, DiesOnEmptyPattern) { ASSERT_DEBUG_DEATH({ - DeclarationMatcher HasEmptyName = record(matchesName("")); + DeclarationMatcher HasEmptyName = recordDecl(matchesName("")); EXPECT_TRUE(notMatches("class X {};", HasEmptyName)); }, ""); } TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) { ASSERT_DEBUG_DEATH({ - DeclarationMatcher IsDerivedFromEmpty = record(isDerivedFrom("")); + DeclarationMatcher IsDerivedFromEmpty = recordDecl(isDerivedFrom("")); EXPECT_TRUE(notMatches("class X {};", IsDerivedFromEmpty)); }, ""); } @@ -46,7 +46,7 @@ TEST(Decl, MatchesDeclarations) { } TEST(NameableDeclaration, MatchesVariousDecls) { - DeclarationMatcher NamedX = nameableDeclaration(hasName("X")); + DeclarationMatcher NamedX = namedDecl(hasName("X")); EXPECT_TRUE(matches("typedef int X;", NamedX)); EXPECT_TRUE(matches("int X;", NamedX)); EXPECT_TRUE(matches("class foo { virtual void X(); };", NamedX)); @@ -59,7 +59,7 @@ TEST(NameableDeclaration, MatchesVariousDecls) { } TEST(NameableDeclaration, REMatchesVariousDecls) { - DeclarationMatcher NamedX = nameableDeclaration(matchesName("::X")); + DeclarationMatcher NamedX = namedDecl(matchesName("::X")); EXPECT_TRUE(matches("typedef int Xa;", NamedX)); EXPECT_TRUE(matches("int Xb;", NamedX)); EXPECT_TRUE(matches("class foo { virtual void Xc(); };", NamedX)); @@ -70,11 +70,11 @@ TEST(NameableDeclaration, REMatchesVariousDecls) { EXPECT_TRUE(notMatches("#define Xkl 1", NamedX)); - DeclarationMatcher StartsWithNo = nameableDeclaration(matchesName("::no")); + DeclarationMatcher StartsWithNo = namedDecl(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")); + DeclarationMatcher Abc = namedDecl(matchesName("a.*b.*c")); EXPECT_TRUE(matches("int abc;", Abc)); EXPECT_TRUE(matches("int aFOObBARc;", Abc)); EXPECT_TRUE(notMatches("int cab;", Abc)); @@ -82,7 +82,7 @@ TEST(NameableDeclaration, REMatchesVariousDecls) { } TEST(DeclarationMatcher, MatchClass) { - DeclarationMatcher ClassMatcher(record()); + DeclarationMatcher ClassMatcher(recordDecl()); #if !defined(_MSC_VER) EXPECT_FALSE(matches("", ClassMatcher)); #else @@ -90,7 +90,7 @@ TEST(DeclarationMatcher, MatchClass) { EXPECT_TRUE(matches("", ClassMatcher)); #endif - DeclarationMatcher ClassX = record(record(hasName("X"))); + DeclarationMatcher ClassX = recordDecl(recordDecl(hasName("X"))); EXPECT_TRUE(matches("class X;", ClassX)); EXPECT_TRUE(matches("class X {};", ClassX)); EXPECT_TRUE(matches("template<class T> class X {};", ClassX)); @@ -98,17 +98,24 @@ TEST(DeclarationMatcher, MatchClass) { } TEST(DeclarationMatcher, ClassIsDerived) { - DeclarationMatcher IsDerivedFromX = record(isDerivedFrom("X")); + DeclarationMatcher IsDerivedFromX = recordDecl(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 X {};", IsDerivedFromX)); + EXPECT_TRUE(notMatches("class X;", IsDerivedFromX)); EXPECT_TRUE(notMatches("class Y;", IsDerivedFromX)); EXPECT_TRUE(notMatches("", IsDerivedFromX)); + DeclarationMatcher IsAX = recordDecl(isSameOrDerivedFrom("X")); + + EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsAX)); + EXPECT_TRUE(matches("class X {};", IsAX)); + EXPECT_TRUE(matches("class X;", IsAX)); + EXPECT_TRUE(notMatches("class Y;", IsAX)); + EXPECT_TRUE(notMatches("", IsAX)); + DeclarationMatcher ZIsDerivedFromX = - record(hasName("Z"), isDerivedFrom("X")); + recordDecl(hasName("Z"), isDerivedFrom("X")); EXPECT_TRUE( matches("class X {}; class Y : public X {}; class Z : public Y {};", ZIsDerivedFromX)); @@ -239,19 +246,17 @@ TEST(DeclarationMatcher, ClassIsDerived) { "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"))))))); + varDecl(hasName("z_float"), + hasInitializer(hasType(recordDecl(isDerivedFrom("Base1"))))))); EXPECT_TRUE(notMatches( RecursiveTemplateOneParameter, - variable( - hasName("z_float"), - hasInitializer(hasType(record(isDerivedFrom("Base2"))))))); + varDecl(hasName("z_float"), + hasInitializer(hasType(recordDecl(isDerivedFrom("Base2"))))))); EXPECT_TRUE(matches( RecursiveTemplateOneParameter, - variable( - hasName("z_char"), - hasInitializer(hasType(record(isDerivedFrom("Base1"), - isDerivedFrom("Base2"))))))); + varDecl(hasName("z_char"), + hasInitializer(hasType(recordDecl(isDerivedFrom("Base1"), + isDerivedFrom("Base2"))))))); const char *RecursiveTemplateTwoParameters = "class Base1 {}; class Base2 {};" @@ -266,47 +271,81 @@ TEST(DeclarationMatcher, ClassIsDerived) { " Z<char, void> z_char; }"; EXPECT_TRUE(matches( RecursiveTemplateTwoParameters, - variable( - hasName("z_float"), - hasInitializer(hasType(record(isDerivedFrom("Base1"))))))); + varDecl(hasName("z_float"), + hasInitializer(hasType(recordDecl(isDerivedFrom("Base1"))))))); EXPECT_TRUE(notMatches( RecursiveTemplateTwoParameters, - variable( - hasName("z_float"), - hasInitializer(hasType(record(isDerivedFrom("Base2"))))))); + varDecl(hasName("z_float"), + hasInitializer(hasType(recordDecl(isDerivedFrom("Base2"))))))); EXPECT_TRUE(matches( RecursiveTemplateTwoParameters, - variable( - hasName("z_char"), - hasInitializer(hasType(record(isDerivedFrom("Base1"), - isDerivedFrom("Base2"))))))); + varDecl(hasName("z_char"), + hasInitializer(hasType(recordDecl(isDerivedFrom("Base1"), + isDerivedFrom("Base2"))))))); EXPECT_TRUE(matches( "namespace ns { class X {}; class Y : public X {}; }", - record(isDerivedFrom("::ns::X")))); + recordDecl(isDerivedFrom("::ns::X")))); EXPECT_TRUE(notMatches( "class X {}; class Y : public X {};", - record(isDerivedFrom("::ns::X")))); + recordDecl(isDerivedFrom("::ns::X")))); EXPECT_TRUE(matches( "class X {}; class Y : public X {};", - record(isDerivedFrom(record(hasName("X")).bind("test"))))); + recordDecl(isDerivedFrom(recordDecl(hasName("X")).bind("test"))))); +} + +TEST(DeclarationMatcher, ClassDerivedFromDependentTemplateSpecialization) { + EXPECT_TRUE(matches( + "template <typename T> struct A {" + " template <typename T2> struct F {};" + "};" + "template <typename T> struct B : A<T>::template F<T> {};" + "B<int> b;", + recordDecl(hasName("B"), isDerivedFrom(recordDecl())))); +} + +TEST(ClassTemplate, DoesNotMatchClass) { + DeclarationMatcher ClassX = classTemplateDecl(hasName("X")); + EXPECT_TRUE(notMatches("class X;", ClassX)); + EXPECT_TRUE(notMatches("class X {};", ClassX)); +} + +TEST(ClassTemplate, MatchesClassTemplate) { + DeclarationMatcher ClassX = classTemplateDecl(hasName("X")); + EXPECT_TRUE(matches("template<typename T> class X {};", ClassX)); + EXPECT_TRUE(matches("class Z { template<class T> class X {}; };", ClassX)); +} + +TEST(ClassTemplate, DoesNotMatchClassTemplateExplicitSpecialization) { + EXPECT_TRUE(notMatches("template<typename T> class X { };" + "template<> class X<int> { int a; };", + classTemplateDecl(hasName("X"), + hasDescendant(fieldDecl(hasName("a")))))); +} + +TEST(ClassTemplate, DoesNotMatchClassTemplatePartialSpecialization) { + EXPECT_TRUE(notMatches("template<typename T, typename U> class X { };" + "template<typename T> class X<T, int> { int a; };", + classTemplateDecl(hasName("X"), + hasDescendant(fieldDecl(hasName("a")))))); } 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()))))))); + callExpr(allOf(callee(functionDecl(hasName("f"))), + hasArgument(0, declRefExpr(to(varDecl()))))))); EXPECT_TRUE(matches(Program, - call(allOf(callee(function(hasName("f"))), - hasArgument(0, declarationReference(to(variable()))), - hasArgument(1, hasType(pointsTo(record(hasName("T"))))))))); + callExpr(allOf(callee(functionDecl(hasName("f"))), + hasArgument(0, declRefExpr(to(varDecl()))), + hasArgument(1, hasType(pointsTo( + recordDecl(hasName("T"))))))))); } TEST(DeclarationMatcher, MatchAnyOf) { DeclarationMatcher YOrZDerivedFromX = - record(anyOf(hasName("Y"), allOf(isDerivedFrom("X"), hasName("Z")))); + recordDecl(anyOf(hasName("Y"), allOf(isDerivedFrom("X"), hasName("Z")))); EXPECT_TRUE( matches("class X {}; class Z : public X {};", YOrZDerivedFromX)); EXPECT_TRUE(matches("class Y {};", YOrZDerivedFromX)); @@ -315,13 +354,13 @@ TEST(DeclarationMatcher, MatchAnyOf) { EXPECT_TRUE(notMatches("class Z {};", YOrZDerivedFromX)); DeclarationMatcher XOrYOrZOrU = - record(anyOf(hasName("X"), hasName("Y"), hasName("Z"), hasName("U"))); + recordDecl(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"))); + recordDecl(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)); @@ -331,13 +370,12 @@ TEST(DeclarationMatcher, MatchAnyOf) { } TEST(DeclarationMatcher, MatchHas) { - DeclarationMatcher HasClassX = record(has(record(hasName("X")))); - + DeclarationMatcher HasClassX = recordDecl(has(recordDecl(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")))); + recordDecl(hasName("Y"), has(recordDecl(hasName("X")))); EXPECT_TRUE(matches("class Y { class X {}; };", YHasClassX)); EXPECT_TRUE(notMatches("class X {};", YHasClassX)); EXPECT_TRUE( @@ -346,14 +384,14 @@ TEST(DeclarationMatcher, MatchHas) { TEST(DeclarationMatcher, MatchHasRecursiveAllOf) { DeclarationMatcher Recursive = - record( - has(record( - has(record(hasName("X"))), - has(record(hasName("Y"))), + recordDecl( + has(recordDecl( + has(recordDecl(hasName("X"))), + has(recordDecl(hasName("Y"))), hasName("Z"))), - has(record( - has(record(hasName("A"))), - has(record(hasName("B"))), + has(recordDecl( + has(recordDecl(hasName("A"))), + has(recordDecl(hasName("B"))), hasName("C"))), hasName("F")); @@ -404,21 +442,21 @@ TEST(DeclarationMatcher, MatchHasRecursiveAllOf) { TEST(DeclarationMatcher, MatchHasRecursiveAnyOf) { DeclarationMatcher Recursive = - record( + recordDecl( anyOf( - has(record( + has(recordDecl( anyOf( - has(record( + has(recordDecl( hasName("X"))), - has(record( + has(recordDecl( hasName("Y"))), hasName("Z")))), - has(record( + has(recordDecl( anyOf( hasName("C"), - has(record( + has(recordDecl( hasName("A"))), - has(record( + has(recordDecl( hasName("B")))))), hasName("F"))); @@ -435,9 +473,8 @@ TEST(DeclarationMatcher, MatchHasRecursiveAnyOf) { TEST(DeclarationMatcher, MatchNot) { DeclarationMatcher NotClassX = - record( + recordDecl( isDerivedFrom("Y"), - unless(hasName("Y")), unless(hasName("X"))); EXPECT_TRUE(notMatches("", NotClassX)); EXPECT_TRUE(notMatches("class Y {};", NotClassX)); @@ -448,11 +485,11 @@ TEST(DeclarationMatcher, MatchNot) { NotClassX)); DeclarationMatcher ClassXHasNotClassY = - record( + recordDecl( hasName("X"), - has(record(hasName("Z"))), + has(recordDecl(hasName("Z"))), unless( - has(record(hasName("Y"))))); + has(recordDecl(hasName("Y"))))); EXPECT_TRUE(matches("class X { class Z {}; };", ClassXHasNotClassY)); EXPECT_TRUE(notMatches("class X { class Y {}; class Z {}; };", ClassXHasNotClassY)); @@ -460,8 +497,8 @@ TEST(DeclarationMatcher, MatchNot) { TEST(DeclarationMatcher, HasDescendant) { DeclarationMatcher ZDescendantClassX = - record( - hasDescendant(record(hasName("X"))), + recordDecl( + hasDescendant(recordDecl(hasName("X"))), hasName("Z")); EXPECT_TRUE(matches("class Z { class X {}; };", ZDescendantClassX)); EXPECT_TRUE( @@ -475,8 +512,8 @@ TEST(DeclarationMatcher, HasDescendant) { EXPECT_TRUE(notMatches("class Z {};", ZDescendantClassX)); DeclarationMatcher ZDescendantClassXHasClassY = - record( - hasDescendant(record(has(record(hasName("Y"))), + recordDecl( + hasDescendant(recordDecl(has(recordDecl(hasName("Y"))), hasName("X"))), hasName("Z")); EXPECT_TRUE(matches("class Z { class X { class Y {}; }; };", @@ -498,9 +535,9 @@ TEST(DeclarationMatcher, HasDescendant) { "};", ZDescendantClassXHasClassY)); DeclarationMatcher ZDescendantClassXDescendantClassY = - record( - hasDescendant(record(hasDescendant(record(hasName("Y"))), - hasName("X"))), + recordDecl( + hasDescendant(recordDecl(hasDescendant(recordDecl(hasName("Y"))), + hasName("X"))), hasName("Z")); EXPECT_TRUE( matches("class Z { class A { class X { class B { class Y {}; }; }; }; };", @@ -518,6 +555,118 @@ TEST(DeclarationMatcher, HasDescendant) { "};", ZDescendantClassXDescendantClassY)); } +// Implements a run method that returns whether BoundNodes contains a +// Decl bound to Id that can be dynamically cast to T. +// Optionally checks that the check succeeded a specific number of times. +template <typename T> +class VerifyIdIsBoundTo : public BoundNodesCallback { +public: + // Create an object that checks that a node of type \c T was bound to \c Id. + // Does not check for a certain number of matches. + explicit VerifyIdIsBoundTo(llvm::StringRef Id) + : Id(Id), ExpectedCount(-1), Count(0) {} + + // Create an object that checks that a node of type \c T was bound to \c Id. + // Checks that there were exactly \c ExpectedCount matches. + VerifyIdIsBoundTo(llvm::StringRef Id, int ExpectedCount) + : Id(Id), ExpectedCount(ExpectedCount), Count(0) {} + + // Create an object that checks that a node of type \c T was bound to \c Id. + // Checks that there was exactly one match with the name \c ExpectedName. + // Note that \c T must be a NamedDecl for this to work. + VerifyIdIsBoundTo(llvm::StringRef Id, llvm::StringRef ExpectedName) + : Id(Id), ExpectedCount(1), Count(0), ExpectedName(ExpectedName) {} + + ~VerifyIdIsBoundTo() { + if (ExpectedCount != -1) + EXPECT_EQ(ExpectedCount, Count); + if (!ExpectedName.empty()) + EXPECT_EQ(ExpectedName, Name); + } + + virtual bool run(const BoundNodes *Nodes) { + if (Nodes->getNodeAs<T>(Id)) { + ++Count; + if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) { + Name = Named->getNameAsString(); + } else if (const NestedNameSpecifier *NNS = + Nodes->getNodeAs<NestedNameSpecifier>(Id)) { + llvm::raw_string_ostream OS(Name); + NNS->print(OS, PrintingPolicy(LangOptions())); + } + return true; + } + return false; + } + + virtual bool run(const BoundNodes *Nodes, ASTContext *Context) { + return run(Nodes); + } + +private: + const std::string Id; + const int ExpectedCount; + int Count; + const std::string ExpectedName; + std::string Name; +}; + +TEST(HasDescendant, MatchesDescendantTypes) { + EXPECT_TRUE(matches("void f() { int i = 3; }", + decl(hasDescendant(loc(builtinType()))))); + EXPECT_TRUE(matches("void f() { int i = 3; }", + stmt(hasDescendant(builtinType())))); + + EXPECT_TRUE(matches("void f() { int i = 3; }", + stmt(hasDescendant(loc(builtinType()))))); + EXPECT_TRUE(matches("void f() { int i = 3; }", + stmt(hasDescendant(qualType(builtinType()))))); + + EXPECT_TRUE(notMatches("void f() { float f = 2.0f; }", + stmt(hasDescendant(isInteger())))); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f() { int a; float c; int d; int e; }", + functionDecl(forEachDescendant( + varDecl(hasDescendant(isInteger())).bind("x"))), + new VerifyIdIsBoundTo<Decl>("x", 3))); +} + +TEST(HasDescendant, MatchesDescendantsOfTypes) { + EXPECT_TRUE(matches("void f() { int*** i; }", + qualType(hasDescendant(builtinType())))); + EXPECT_TRUE(matches("void f() { int*** i; }", + qualType(hasDescendant( + pointerType(pointee(builtinType())))))); + EXPECT_TRUE(matches("void f() { int*** i; }", + typeLoc(hasDescendant(builtinTypeLoc())))); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f() { int*** i; }", + qualType(asString("int ***"), forEachDescendant(pointerType().bind("x"))), + new VerifyIdIsBoundTo<Type>("x", 2))); +} + +TEST(Has, MatchesChildrenOfTypes) { + EXPECT_TRUE(matches("int i;", + varDecl(hasName("i"), has(isInteger())))); + EXPECT_TRUE(notMatches("int** i;", + varDecl(hasName("i"), has(isInteger())))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "int (*f)(float, int);", + qualType(functionType(), forEach(qualType(isInteger()).bind("x"))), + new VerifyIdIsBoundTo<QualType>("x", 2))); +} + +TEST(Has, MatchesChildTypes) { + EXPECT_TRUE(matches( + "int* i;", + varDecl(hasName("i"), hasType(qualType(has(builtinType())))))); + EXPECT_TRUE(notMatches( + "int* i;", + varDecl(hasName("i"), hasType(qualType(has(pointerType())))))); +} + TEST(Enum, DoesNotMatchClasses) { EXPECT_TRUE(notMatches("class X {};", enumDecl(hasName("X")))); } @@ -527,7 +676,7 @@ TEST(Enum, MatchesEnums) { } TEST(EnumConstant, Matches) { - DeclarationMatcher Matcher = enumConstant(hasName("A")); + DeclarationMatcher Matcher = enumConstantDecl(hasName("A")); EXPECT_TRUE(matches("enum X{ A };", Matcher)); EXPECT_TRUE(notMatches("enum X{ B };", Matcher)); EXPECT_TRUE(notMatches("enum X {};", Matcher)); @@ -535,9 +684,8 @@ TEST(EnumConstant, Matches) { TEST(StatementMatcher, Has) { StatementMatcher HasVariableI = - expression( - hasType(pointsTo(record(hasName("X")))), - has(declarationReference(to(variable(hasName("i")))))); + expr(hasType(pointsTo(recordDecl(hasName("X")))), + has(declRefExpr(to(varDecl(hasName("i")))))); EXPECT_TRUE(matches( "class X; X *x(int); void c() { int i; x(i); }", HasVariableI)); @@ -547,9 +695,8 @@ TEST(StatementMatcher, Has) { TEST(StatementMatcher, HasDescendant) { StatementMatcher HasDescendantVariableI = - expression( - hasType(pointsTo(record(hasName("X")))), - hasDescendant(declarationReference(to(variable(hasName("i")))))); + expr(hasType(pointsTo(recordDecl(hasName("X")))), + hasDescendant(declRefExpr(to(varDecl(hasName("i")))))); EXPECT_TRUE(matches( "class X; X *x(bool); bool b(int); void c() { int i; x(b(i)); }", @@ -560,160 +707,127 @@ TEST(StatementMatcher, HasDescendant) { } TEST(TypeMatcher, MatchesClassType) { - TypeMatcher TypeA = hasDeclaration(record(hasName("A"))); + TypeMatcher TypeA = hasDeclaration(recordDecl(hasName("A"))); EXPECT_TRUE(matches("class A { public: A *a; };", TypeA)); EXPECT_TRUE(notMatches("class A {};", TypeA)); - TypeMatcher TypeDerivedFromA = hasDeclaration(record(isDerivedFrom("A"))); + TypeMatcher TypeDerivedFromA = hasDeclaration(recordDecl(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"))))); + recordDecl(hasName("A"), has(recordDecl(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")); + DeclarationMatcher ClassX = has(recordDecl(hasName("::X")).bind("x")); EXPECT_TRUE(matchAndVerifyResultTrue("class X {};", - ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("x"))); + ClassX, new VerifyIdIsBoundTo<CXXRecordDecl>("x"))); EXPECT_TRUE(matchAndVerifyResultFalse("class X {};", - ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("other-id"))); + ClassX, new VerifyIdIsBoundTo<CXXRecordDecl>("other-id"))); TypeMatcher TypeAHasClassB = hasDeclaration( - record(hasName("A"), has(record(hasName("B")).bind("b")))); + recordDecl(hasName("A"), has(recordDecl(hasName("B")).bind("b")))); EXPECT_TRUE(matchAndVerifyResultTrue("class A { public: A *a; class B {}; };", TypeAHasClassB, - new VerifyIdIsBoundToDecl<Decl>("b"))); + new VerifyIdIsBoundTo<Decl>("b"))); - StatementMatcher MethodX = call(callee(method(hasName("x")))).bind("x"); + StatementMatcher MethodX = + callExpr(callee(methodDecl(hasName("x")))).bind("x"); EXPECT_TRUE(matchAndVerifyResultTrue("class A { void x() { x(); } };", MethodX, - new VerifyIdIsBoundToStmt<CXXMemberCallExpr>("x"))); + new VerifyIdIsBoundTo<CXXMemberCallExpr>("x"))); } TEST(Matcher, BindTheSameNameInAlternatives) { StatementMatcher matcher = anyOf( binaryOperator(hasOperatorName("+"), - hasLHS(expression().bind("x")), + hasLHS(expr().bind("x")), hasRHS(integerLiteral(equals(0)))), binaryOperator(hasOperatorName("+"), hasLHS(integerLiteral(equals(0))), - hasRHS(expression().bind("x")))); + hasRHS(expr().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"))); + new VerifyIdIsBoundTo<CallExpr>("x"))); +} + +TEST(Matcher, BindsIDForMemoizedResults) { + // Using the same matcher in two match expressions will make memoization + // kick in. + DeclarationMatcher ClassX = recordDecl(hasName("X")).bind("x"); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class A { class B { class X {}; }; };", + DeclarationMatcher(anyOf( + recordDecl(hasName("A"), hasDescendant(ClassX)), + recordDecl(hasName("B"), hasDescendant(ClassX)))), + new VerifyIdIsBoundTo<Decl>("x", 2))); } TEST(HasType, TakesQualTypeMatcherAndMatchesExpr) { - TypeMatcher ClassX = hasDeclaration(record(hasName("X"))); + TypeMatcher ClassX = hasDeclaration(recordDecl(hasName("X"))); EXPECT_TRUE( - matches("class X {}; void y(X &x) { x; }", expression(hasType(ClassX)))); + matches("class X {}; void y(X &x) { x; }", expr(hasType(ClassX)))); EXPECT_TRUE( notMatches("class X {}; void y(X *x) { x; }", - expression(hasType(ClassX)))); + expr(hasType(ClassX)))); EXPECT_TRUE( matches("class X {}; void y(X *x) { x; }", - expression(hasType(pointsTo(ClassX))))); + expr(hasType(pointsTo(ClassX))))); } TEST(HasType, TakesQualTypeMatcherAndMatchesValueDecl) { - TypeMatcher ClassX = hasDeclaration(record(hasName("X"))); + TypeMatcher ClassX = hasDeclaration(recordDecl(hasName("X"))); EXPECT_TRUE( - matches("class X {}; void y() { X x; }", variable(hasType(ClassX)))); + matches("class X {}; void y() { X x; }", varDecl(hasType(ClassX)))); EXPECT_TRUE( - notMatches("class X {}; void y() { X *x; }", variable(hasType(ClassX)))); + notMatches("class X {}; void y() { X *x; }", varDecl(hasType(ClassX)))); EXPECT_TRUE( matches("class X {}; void y() { X *x; }", - variable(hasType(pointsTo(ClassX))))); + varDecl(hasType(pointsTo(ClassX))))); } TEST(HasType, TakesDeclMatcherAndMatchesExpr) { - DeclarationMatcher ClassX = record(hasName("X")); + DeclarationMatcher ClassX = recordDecl(hasName("X")); EXPECT_TRUE( - matches("class X {}; void y(X &x) { x; }", expression(hasType(ClassX)))); + matches("class X {}; void y(X &x) { x; }", expr(hasType(ClassX)))); EXPECT_TRUE( notMatches("class X {}; void y(X *x) { x; }", - expression(hasType(ClassX)))); + expr(hasType(ClassX)))); } TEST(HasType, TakesDeclMatcherAndMatchesValueDecl) { - DeclarationMatcher ClassX = record(hasName("X")); + DeclarationMatcher ClassX = recordDecl(hasName("X")); EXPECT_TRUE( - matches("class X {}; void y() { X x; }", variable(hasType(ClassX)))); + matches("class X {}; void y() { X x; }", varDecl(hasType(ClassX)))); EXPECT_TRUE( - notMatches("class X {}; void y() { X *x; }", variable(hasType(ClassX)))); + notMatches("class X {}; void y() { X *x; }", varDecl(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")))); + StatementMatcher MethodX = callExpr(hasDeclaration(methodDecl(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"))))); + StatementMatcher MethodOnY = + memberCallExpr(on(hasType(recordDecl(hasName("Y"))))); EXPECT_TRUE( matches("class Y { public: void x(); }; void z() { Y y; y.x(); }", @@ -732,7 +846,7 @@ TEST(Matcher, Call) { MethodOnY)); StatementMatcher MethodOnYPointer = - memberCall(on(hasType(pointsTo(record(hasName("Y")))))); + memberCallExpr(on(hasType(pointsTo(recordDecl(hasName("Y")))))); EXPECT_TRUE( matches("class Y { public: void x(); }; void z() { Y *y; y->x(); }", @@ -751,20 +865,50 @@ TEST(Matcher, Call) { MethodOnYPointer)); } +TEST(Matcher, Lambda) { + EXPECT_TRUE(matches("auto f = [&] (int i) { return i; };", + lambdaExpr())); +} + +TEST(Matcher, ForRange) { + EXPECT_TRUE(matches("int as[] = { 1, 2, 3 };" + "void f() { for (auto &a : as); }", + forRangeStmt())); + EXPECT_TRUE(notMatches("void f() { for (int i; i<5; ++i); }", + forRangeStmt())); +} + +TEST(Matcher, UserDefinedLiteral) { + EXPECT_TRUE(matches("constexpr char operator \"\" _inc (const char i) {" + " return i + 1;" + "}" + "char c = 'a'_inc;", + userDefinedLiteral())); +} + +TEST(Matcher, FlowControl) { + EXPECT_TRUE(matches("void f() { while(true) { break; } }", breakStmt())); + EXPECT_TRUE(matches("void f() { while(true) { continue; } }", + continueStmt())); + EXPECT_TRUE(matches("void f() { goto FOO; FOO: ;}", gotoStmt())); + EXPECT_TRUE(matches("void f() { goto FOO; FOO: ;}", labelStmt())); + EXPECT_TRUE(matches("void f() { return; }", returnStmt())); +} + TEST(HasType, MatchesAsString) { EXPECT_TRUE( matches("class Y { public: void x(); }; void z() {Y* y; y->x(); }", - memberCall(on(hasType(asString("class Y *")))))); + memberCallExpr(on(hasType(asString("class Y *")))))); EXPECT_TRUE(matches("class X { void x(int x) {} };", - method(hasParameter(0, hasType(asString("int")))))); + methodDecl(hasParameter(0, hasType(asString("int")))))); EXPECT_TRUE(matches("namespace ns { struct A {}; } struct B { ns::A a; };", - field(hasType(asString("ns::A"))))); + fieldDecl(hasType(asString("ns::A"))))); EXPECT_TRUE(matches("namespace { struct A {}; } struct B { A a; };", - field(hasType(asString("struct <anonymous>::A"))))); + fieldDecl(hasType(asString("struct <anonymous>::A"))))); } TEST(Matcher, OverloadedOperatorCall) { - StatementMatcher OpCall = overloadedOperatorCall(); + StatementMatcher OpCall = operatorCallExpr(); // Unary operator EXPECT_TRUE(matches("class Y { }; " "bool operator!(Y x) { return false; }; " @@ -791,12 +935,12 @@ TEST(Matcher, OverloadedOperatorCall) { TEST(Matcher, HasOperatorNameForOverloadedOperatorCall) { StatementMatcher OpCallAndAnd = - overloadedOperatorCall(hasOverloadedOperatorName("&&")); + operatorCallExpr(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("<<")); + operatorCallExpr(hasOverloadedOperatorName("<<")); EXPECT_TRUE(notMatches("class Y { }; " "bool operator&&(Y x, Y y) { return true; }; " "Y a; Y b; bool c = a && b;", @@ -805,7 +949,7 @@ TEST(Matcher, HasOperatorNameForOverloadedOperatorCall) { TEST(Matcher, ThisPointerType) { StatementMatcher MethodOnY = - memberCall(thisPointerType(record(hasName("Y")))); + memberCallExpr(thisPointerType(recordDecl(hasName("Y")))); EXPECT_TRUE( matches("class Y { public: void x(); }; void z() { Y y; y.x(); }", @@ -835,9 +979,9 @@ TEST(Matcher, ThisPointerType) { TEST(Matcher, VariableUsage) { StatementMatcher Reference = - declarationReference(to( - variable(hasInitializer( - memberCall(thisPointerType(record(hasName("Y")))))))); + declRefExpr(to( + varDecl(hasInitializer( + memberCallExpr(thisPointerType(recordDecl(hasName("Y")))))))); EXPECT_TRUE(matches( "class Y {" @@ -862,12 +1006,12 @@ TEST(Matcher, VariableUsage) { TEST(Matcher, FindsVarDeclInFuncitonParameter) { EXPECT_TRUE(matches( "void f(int i) {}", - variable(hasName("i")))); + varDecl(hasName("i")))); } TEST(Matcher, CalledVariable) { - StatementMatcher CallOnVariableY = expression( - memberCall(on(declarationReference(to(variable(hasName("y"))))))); + StatementMatcher CallOnVariableY = + memberCallExpr(on(declRefExpr(to(varDecl(hasName("y")))))); EXPECT_TRUE(matches( "class Y { public: void x() { Y y; y.x(); } };", CallOnVariableY)); @@ -904,81 +1048,81 @@ TEST(UnaryExpressionOrTypeTraitExpression, MatchesCorrectType) { hasArgumentOfType(asString("float"))))); EXPECT_TRUE(matches( "struct A {}; void x() { A a; int b = sizeof(a); }", - sizeOfExpr(hasArgumentOfType(hasDeclaration(record(hasName("A"))))))); + sizeOfExpr(hasArgumentOfType(hasDeclaration(recordDecl(hasName("A"))))))); EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr( - hasArgumentOfType(hasDeclaration(record(hasName("string"))))))); + hasArgumentOfType(hasDeclaration(recordDecl(hasName("string"))))))); } TEST(MemberExpression, DoesNotMatchClasses) { - EXPECT_TRUE(notMatches("class Y { void x() {} };", memberExpression())); + EXPECT_TRUE(notMatches("class Y { void x() {} };", memberExpr())); } TEST(MemberExpression, MatchesMemberFunctionCall) { - EXPECT_TRUE(matches("class Y { void x() { x(); } };", memberExpression())); + EXPECT_TRUE(matches("class Y { void x() { x(); } };", memberExpr())); } TEST(MemberExpression, MatchesVariable) { EXPECT_TRUE( - matches("class Y { void x() { this->y; } int y; };", memberExpression())); + matches("class Y { void x() { this->y; } int y; };", memberExpr())); EXPECT_TRUE( - matches("class Y { void x() { y; } int y; };", memberExpression())); + matches("class Y { void x() { y; } int y; };", memberExpr())); EXPECT_TRUE( - matches("class Y { void x() { Y y; y.y; } int y; };", - memberExpression())); + matches("class Y { void x() { Y y; y.y; } int y; };", memberExpr())); } TEST(MemberExpression, MatchesStaticVariable) { EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };", - memberExpression())); + memberExpr())); EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };", - memberExpression())); + memberExpr())); EXPECT_TRUE(notMatches("class Y { void x() { Y::y; } static int y; };", - memberExpression())); + memberExpr())); } 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())))))))); + EXPECT_TRUE(matches("int i = 0;", varDecl(hasType(isInteger())))); + EXPECT_TRUE(matches( + "long long i = 0; void f(long long) { }; void g() {f(i);}", + callExpr(hasArgument(0, declRefExpr( + to(varDecl(hasType(isInteger())))))))); } TEST(IsInteger, ReportsNoFalsePositives) { - EXPECT_TRUE(notMatches("int *i;", variable(hasType(isInteger())))); + EXPECT_TRUE(notMatches("int *i;", varDecl(hasType(isInteger())))); EXPECT_TRUE(notMatches("struct T {}; T t; void f(T *) { }; void g() {f(&t);}", - call(hasArgument(0, declarationReference( - to(variable(hasType(isInteger())))))))); + callExpr(hasArgument(0, declRefExpr( + to(varDecl(hasType(isInteger())))))))); } TEST(IsArrow, MatchesMemberVariablesViaArrow) { EXPECT_TRUE(matches("class Y { void x() { this->y; } int y; };", - memberExpression(isArrow()))); + memberExpr(isArrow()))); EXPECT_TRUE(matches("class Y { void x() { y; } int y; };", - memberExpression(isArrow()))); + memberExpr(isArrow()))); EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };", - memberExpression(isArrow()))); + memberExpr(isArrow()))); } TEST(IsArrow, MatchesStaticMemberVariablesViaArrow) { EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };", - memberExpression(isArrow()))); + memberExpr(isArrow()))); EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };", - memberExpression(isArrow()))); + memberExpr(isArrow()))); EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } static int y; };", - memberExpression(isArrow()))); + memberExpr(isArrow()))); } TEST(IsArrow, MatchesMemberCallsViaArrow) { EXPECT_TRUE(matches("class Y { void x() { this->x(); } };", - memberExpression(isArrow()))); + memberExpr(isArrow()))); EXPECT_TRUE(matches("class Y { void x() { x(); } };", - memberExpression(isArrow()))); + memberExpr(isArrow()))); EXPECT_TRUE(notMatches("class Y { void x() { Y y; y.x(); } };", - memberExpression(isArrow()))); + memberExpr(isArrow()))); } TEST(Callee, MatchesDeclarations) { - StatementMatcher CallMethodX = call(callee(method(hasName("x")))); + StatementMatcher CallMethodX = callExpr(callee(methodDecl(hasName("x")))); EXPECT_TRUE(matches("class Y { void x() { x(); } };", CallMethodX)); EXPECT_TRUE(notMatches("class Y { void x() {} };", CallMethodX)); @@ -986,13 +1130,13 @@ TEST(Callee, MatchesDeclarations) { TEST(Callee, MatchesMemberExpressions) { EXPECT_TRUE(matches("class Y { void x() { this->x(); } };", - call(callee(memberExpression())))); + callExpr(callee(memberExpr())))); EXPECT_TRUE( - notMatches("class Y { void x() { this->x(); } };", call(callee(call())))); + notMatches("class Y { void x() { this->x(); } };", callExpr(callee(callExpr())))); } TEST(Function, MatchesFunctionDeclarations) { - StatementMatcher CallFunctionF = call(callee(function(hasName("f")))); + StatementMatcher CallFunctionF = callExpr(callee(functionDecl(hasName("f")))); EXPECT_TRUE(matches("void f() { f(); }", CallFunctionF)); EXPECT_TRUE(notMatches("void f() { }", CallFunctionF)); @@ -1017,30 +1161,51 @@ TEST(Function, MatchesFunctionDeclarations) { CallFunctionF)); } +TEST(FunctionTemplate, MatchesFunctionTemplateDeclarations) { + EXPECT_TRUE( + matches("template <typename T> void f(T t) {}", + functionTemplateDecl(hasName("f")))); +} + +TEST(FunctionTemplate, DoesNotMatchFunctionDeclarations) { + EXPECT_TRUE( + notMatches("void f(double d); void f(int t) {}", + functionTemplateDecl(hasName("f")))); +} + +TEST(FunctionTemplate, DoesNotMatchFunctionTemplateSpecializations) { + EXPECT_TRUE( + notMatches("void g(); template <typename T> void f(T t) {}" + "template <> void f(int t) { g(); }", + functionTemplateDecl(hasName("f"), + hasDescendant(declRefExpr(to( + functionDecl(hasName("g")))))))); +} + TEST(Matcher, Argument) { - StatementMatcher CallArgumentY = expression(call( - hasArgument(0, declarationReference(to(variable(hasName("y"))))))); + StatementMatcher CallArgumentY = callExpr( + hasArgument(0, declRefExpr(to(varDecl(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"))))))); + StatementMatcher WrongIndex = callExpr( + hasArgument(42, declRefExpr(to(varDecl(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"))))))); + StatementMatcher CallArgumentY = callExpr( + hasAnyArgument(declRefExpr(to(varDecl(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))); + StatementMatcher Call1Arg = callExpr(argumentCountIs(1)); EXPECT_TRUE(matches("void x(int) { x(0); }", Call1Arg)); EXPECT_TRUE(matches("class X { void x(int) { x(0); } };", Call1Arg)); @@ -1048,8 +1213,8 @@ TEST(Matcher, ArgumentCount) { } TEST(Matcher, References) { - DeclarationMatcher ReferenceClassX = variable( - hasType(references(record(hasName("X"))))); + DeclarationMatcher ReferenceClassX = varDecl( + hasType(references(recordDecl(hasName("X"))))); EXPECT_TRUE(matches("class X {}; void y(X y) { X &x = y; }", ReferenceClassX)); EXPECT_TRUE( @@ -1062,81 +1227,86 @@ TEST(Matcher, References) { TEST(HasParameter, CallsInnerMatcher) { EXPECT_TRUE(matches("class X { void x(int) {} };", - method(hasParameter(0, variable())))); + methodDecl(hasParameter(0, varDecl())))); EXPECT_TRUE(notMatches("class X { void x(int) {} };", - method(hasParameter(0, hasName("x"))))); + methodDecl(hasParameter(0, hasName("x"))))); } TEST(HasParameter, DoesNotMatchIfIndexOutOfBounds) { EXPECT_TRUE(notMatches("class X { void x(int) {} };", - method(hasParameter(42, variable())))); + methodDecl(hasParameter(42, varDecl())))); } TEST(HasType, MatchesParameterVariableTypesStrictly) { EXPECT_TRUE(matches("class X { void x(X x) {} };", - method(hasParameter(0, hasType(record(hasName("X"))))))); + methodDecl(hasParameter(0, hasType(recordDecl(hasName("X"))))))); EXPECT_TRUE(notMatches("class X { void x(const X &x) {} };", - method(hasParameter(0, hasType(record(hasName("X"))))))); + methodDecl(hasParameter(0, hasType(recordDecl(hasName("X"))))))); EXPECT_TRUE(matches("class X { void x(const X *x) {} };", - method(hasParameter(0, hasType(pointsTo(record(hasName("X")))))))); + methodDecl(hasParameter(0, + hasType(pointsTo(recordDecl(hasName("X")))))))); EXPECT_TRUE(matches("class X { void x(const X &x) {} };", - method(hasParameter(0, hasType(references(record(hasName("X")))))))); + methodDecl(hasParameter(0, + hasType(references(recordDecl(hasName("X")))))))); } TEST(HasAnyParameter, MatchesIndependentlyOfPosition) { EXPECT_TRUE(matches("class Y {}; class X { void x(X x, Y y) {} };", - method(hasAnyParameter(hasType(record(hasName("X"))))))); + methodDecl(hasAnyParameter(hasType(recordDecl(hasName("X"))))))); EXPECT_TRUE(matches("class Y {}; class X { void x(Y y, X x) {} };", - method(hasAnyParameter(hasType(record(hasName("X"))))))); + methodDecl(hasAnyParameter(hasType(recordDecl(hasName("X"))))))); } TEST(Returns, MatchesReturnTypes) { EXPECT_TRUE(matches("class Y { int f() { return 1; } };", - function(returns(asString("int"))))); + functionDecl(returns(asString("int"))))); EXPECT_TRUE(notMatches("class Y { int f() { return 1; } };", - function(returns(asString("float"))))); + functionDecl(returns(asString("float"))))); EXPECT_TRUE(matches("class Y { Y getMe() { return *this; } };", - function(returns(hasDeclaration(record(hasName("Y"))))))); + functionDecl(returns(hasDeclaration( + recordDecl(hasName("Y"))))))); } TEST(IsExternC, MatchesExternCFunctionDeclarations) { - EXPECT_TRUE(matches("extern \"C\" void f() {}", function(isExternC()))); - EXPECT_TRUE(matches("extern \"C\" { void f() {} }", function(isExternC()))); - EXPECT_TRUE(notMatches("void f() {}", function(isExternC()))); + EXPECT_TRUE(matches("extern \"C\" void f() {}", functionDecl(isExternC()))); + EXPECT_TRUE(matches("extern \"C\" { void f() {} }", + functionDecl(isExternC()))); + EXPECT_TRUE(notMatches("void f() {}", functionDecl(isExternC()))); } TEST(HasAnyParameter, DoesntMatchIfInnerMatcherDoesntMatch) { EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };", - method(hasAnyParameter(hasType(record(hasName("X"))))))); + methodDecl(hasAnyParameter(hasType(recordDecl(hasName("X"))))))); } TEST(HasAnyParameter, DoesNotMatchThisPointer) { EXPECT_TRUE(notMatches("class Y {}; class X { void x() {} };", - method(hasAnyParameter(hasType(pointsTo(record(hasName("X")))))))); + methodDecl(hasAnyParameter(hasType(pointsTo( + recordDecl(hasName("X")))))))); } TEST(HasName, MatchesParameterVariableDeclartions) { EXPECT_TRUE(matches("class Y {}; class X { void x(int x) {} };", - method(hasAnyParameter(hasName("x"))))); + methodDecl(hasAnyParameter(hasName("x"))))); EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };", - method(hasAnyParameter(hasName("x"))))); + methodDecl(hasAnyParameter(hasName("x"))))); } TEST(Matcher, MatchesClassTemplateSpecialization) { EXPECT_TRUE(matches("template<typename T> struct A {};" "template<> struct A<int> {};", - classTemplateSpecialization())); + classTemplateSpecializationDecl())); EXPECT_TRUE(matches("template<typename T> struct A {}; A<int> a;", - classTemplateSpecialization())); + classTemplateSpecializationDecl())); EXPECT_TRUE(notMatches("template<typename T> struct A {};", - classTemplateSpecialization())); + classTemplateSpecializationDecl())); } TEST(Matcher, MatchesTypeTemplateArgument) { EXPECT_TRUE(matches( "template<typename T> struct B {};" "B<int> b;", - classTemplateSpecialization(hasAnyTemplateArgument(refersToType( + classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType( asString("int")))))); } @@ -1145,25 +1315,31 @@ TEST(Matcher, MatchesDeclarationReferenceTemplateArgument) { "struct B { int next; };" "template<int(B::*next_ptr)> struct A {};" "A<&B::next> a;", - classTemplateSpecialization(hasAnyTemplateArgument( - refersToDeclaration(field(hasName("next"))))))); + classTemplateSpecializationDecl(hasAnyTemplateArgument( + refersToDeclaration(fieldDecl(hasName("next"))))))); + + EXPECT_TRUE(notMatches( + "template <typename T> struct A {};" + "A<int> a;", + classTemplateSpecializationDecl(hasAnyTemplateArgument( + refersToDeclaration(decl()))))); } TEST(Matcher, MatchesSpecificArgument) { EXPECT_TRUE(matches( "template<typename T, typename U> class A {};" "A<bool, int> a;", - classTemplateSpecialization(hasTemplateArgument( + classTemplateSpecializationDecl(hasTemplateArgument( 1, refersToType(asString("int")))))); EXPECT_TRUE(notMatches( "template<typename T, typename U> class A {};" "A<int, bool> a;", - classTemplateSpecialization(hasTemplateArgument( + classTemplateSpecializationDecl(hasTemplateArgument( 1, refersToType(asString("int")))))); } TEST(Matcher, ConstructorCall) { - StatementMatcher Constructor = expression(constructorCall()); + StatementMatcher Constructor = constructExpr(); EXPECT_TRUE( matches("class X { public: X(); }; void x() { X x; }", Constructor)); @@ -1177,8 +1353,8 @@ TEST(Matcher, ConstructorCall) { } TEST(Matcher, ConstructorArgument) { - StatementMatcher Constructor = expression(constructorCall( - hasArgument(0, declarationReference(to(variable(hasName("y"))))))); + StatementMatcher Constructor = constructExpr( + hasArgument(0, declRefExpr(to(varDecl(hasName("y")))))); EXPECT_TRUE( matches("class X { public: X(int); }; void x() { int y; X x(y); }", @@ -1193,16 +1369,15 @@ TEST(Matcher, ConstructorArgument) { 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"))))))); + StatementMatcher WrongIndex = constructExpr( + hasArgument(42, declRefExpr(to(varDecl(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))); + StatementMatcher Constructor1Arg = constructExpr(argumentCountIs(1)); EXPECT_TRUE( matches("class X { public: X(int); }; void x() { X x(0); }", @@ -1218,8 +1393,15 @@ TEST(Matcher, ConstructorArgumentCount) { Constructor1Arg)); } +TEST(Matcher,ThisExpr) { + EXPECT_TRUE( + matches("struct X { int a; int f () { return a; } };", thisExpr())); + EXPECT_TRUE( + notMatches("struct X { int f () { int a; return a; } };", thisExpr())); +} + TEST(Matcher, BindTemporaryExpression) { - StatementMatcher TempExpression = expression(bindTemporaryExpression()); + StatementMatcher TempExpression = bindTemporaryExpr(); std::string ClassString = "class string { public: string(); ~string(); }; "; @@ -1249,44 +1431,80 @@ TEST(Matcher, BindTemporaryExpression) { TempExpression)); } +TEST(MaterializeTemporaryExpr, MatchesTemporary) { + std::string ClassString = + "class string { public: string(); int length(); }; "; + + EXPECT_TRUE( + matches(ClassString + + "string GetStringByValue();" + "void FunctionTakesString(string s);" + "void run() { FunctionTakesString(GetStringByValue()); }", + materializeTemporaryExpr())); + + EXPECT_TRUE( + notMatches(ClassString + + "string* GetStringPointer(); " + "void FunctionTakesStringPtr(string* s);" + "void run() {" + " string* s = GetStringPointer();" + " FunctionTakesStringPtr(GetStringPointer());" + " FunctionTakesStringPtr(s);" + "}", + materializeTemporaryExpr())); + + EXPECT_TRUE( + notMatches(ClassString + + "string GetStringByValue();" + "void run() { int k = GetStringByValue().length(); }", + materializeTemporaryExpr())); + + EXPECT_TRUE( + notMatches(ClassString + + "string GetStringByValue();" + "void run() { GetStringByValue(); }", + materializeTemporaryExpr())); +} + TEST(ConstructorDeclaration, SimpleCase) { EXPECT_TRUE(matches("class Foo { Foo(int i); };", - constructor(ofClass(hasName("Foo"))))); + constructorDecl(ofClass(hasName("Foo"))))); EXPECT_TRUE(notMatches("class Foo { Foo(int i); };", - constructor(ofClass(hasName("Bar"))))); + constructorDecl(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()))); + constructorDecl(isImplicit()))); // The compiler added the implicit default constructor. EXPECT_TRUE(matches("class Foo { }; Foo* f = new Foo();", - constructor(isImplicit()))); + constructorDecl(isImplicit()))); EXPECT_TRUE(matches("class Foo { Foo(){} };", - constructor(unless(isImplicit())))); + constructorDecl(unless(isImplicit())))); } TEST(DestructorDeclaration, MatchesVirtualDestructor) { EXPECT_TRUE(matches("class Foo { virtual ~Foo(); };", - destructor(ofClass(hasName("Foo"))))); + destructorDecl(ofClass(hasName("Foo"))))); } TEST(DestructorDeclaration, DoesNotMatchImplicitDestructor) { - EXPECT_TRUE(notMatches("class Foo {};", destructor(ofClass(hasName("Foo"))))); + EXPECT_TRUE(notMatches("class Foo {};", + destructorDecl(ofClass(hasName("Foo"))))); } TEST(HasAnyConstructorInitializer, SimpleCase) { EXPECT_TRUE(notMatches( "class Foo { Foo() { } };", - constructor(hasAnyConstructorInitializer(anything())))); + constructorDecl(hasAnyConstructorInitializer(anything())))); EXPECT_TRUE(matches( "class Foo {" " Foo() : foo_() { }" " int foo_;" "};", - constructor(hasAnyConstructorInitializer(anything())))); + constructorDecl(hasAnyConstructorInitializer(anything())))); } TEST(HasAnyConstructorInitializer, ForField) { @@ -1297,12 +1515,12 @@ TEST(HasAnyConstructorInitializer, ForField) { " Baz foo_;" " Baz bar_;" "};"; - EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( - forField(hasType(record(hasName("Baz")))))))); - EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( + EXPECT_TRUE(matches(Code, constructorDecl(hasAnyConstructorInitializer( + forField(hasType(recordDecl(hasName("Baz")))))))); + EXPECT_TRUE(matches(Code, constructorDecl(hasAnyConstructorInitializer( forField(hasName("foo_")))))); - EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer( - forField(hasType(record(hasName("Bar")))))))); + EXPECT_TRUE(notMatches(Code, constructorDecl(hasAnyConstructorInitializer( + forField(hasType(recordDecl(hasName("Bar")))))))); } TEST(HasAnyConstructorInitializer, WithInitializer) { @@ -1311,9 +1529,9 @@ TEST(HasAnyConstructorInitializer, WithInitializer) { " Foo() : foo_(0) { }" " int foo_;" "};"; - EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( + EXPECT_TRUE(matches(Code, constructorDecl(hasAnyConstructorInitializer( withInitializer(integerLiteral(equals(0))))))); - EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer( + EXPECT_TRUE(notMatches(Code, constructorDecl(hasAnyConstructorInitializer( withInitializer(integerLiteral(equals(1))))))); } @@ -1325,16 +1543,16 @@ TEST(HasAnyConstructorInitializer, IsWritten) { " Bar foo_;" " Bar bar_;" "};"; - EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( + EXPECT_TRUE(matches(Code, constructorDecl(hasAnyConstructorInitializer( allOf(forField(hasName("foo_")), isWritten()))))); - EXPECT_TRUE(notMatches(Code, constructor(hasAnyConstructorInitializer( + EXPECT_TRUE(notMatches(Code, constructorDecl(hasAnyConstructorInitializer( allOf(forField(hasName("bar_")), isWritten()))))); - EXPECT_TRUE(matches(Code, constructor(hasAnyConstructorInitializer( + EXPECT_TRUE(matches(Code, constructorDecl(hasAnyConstructorInitializer( allOf(forField(hasName("bar_")), unless(isWritten())))))); } TEST(Matcher, NewExpression) { - StatementMatcher New = expression(newExpression()); + StatementMatcher New = newExpr(); EXPECT_TRUE(matches("class X { public: X(); }; void x() { new X; }", New)); EXPECT_TRUE( @@ -1345,9 +1563,8 @@ TEST(Matcher, NewExpression) { } TEST(Matcher, NewExpressionArgument) { - StatementMatcher New = expression(constructorCall( - hasArgument( - 0, declarationReference(to(variable(hasName("y"))))))); + StatementMatcher New = constructExpr( + hasArgument(0, declRefExpr(to(varDecl(hasName("y")))))); EXPECT_TRUE( matches("class X { public: X(int); }; void x() { int y; new X(y); }", @@ -1359,16 +1576,15 @@ TEST(Matcher, NewExpressionArgument) { 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"))))))); + StatementMatcher WrongIndex = constructExpr( + hasArgument(42, declRefExpr(to(varDecl(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)); + StatementMatcher New = constructExpr(argumentCountIs(1)); EXPECT_TRUE( matches("class X { public: X(int); }; void x() { new X(0); }", New)); @@ -1379,11 +1595,11 @@ TEST(Matcher, NewExpressionArgumentCount) { TEST(Matcher, DeleteExpression) { EXPECT_TRUE(matches("struct A {}; void f(A* a) { delete a; }", - deleteExpression())); + deleteExpr())); } TEST(Matcher, DefaultArgument) { - StatementMatcher Arg = defaultArgument(); + StatementMatcher Arg = defaultArgExpr(); EXPECT_TRUE(matches("void x(int, int = 0) { int y; x(y); }", Arg)); EXPECT_TRUE( @@ -1392,7 +1608,7 @@ TEST(Matcher, DefaultArgument) { } TEST(Matcher, StringLiterals) { - StatementMatcher Literal = expression(stringLiteral()); + StatementMatcher Literal = stringLiteral(); EXPECT_TRUE(matches("const char *s = \"string\";", Literal)); // wide string EXPECT_TRUE(matches("const wchar_t *s = L\"string\";", Literal)); @@ -1403,7 +1619,7 @@ TEST(Matcher, StringLiterals) { } TEST(Matcher, CharacterLiterals) { - StatementMatcher CharLiteral = expression(characterLiteral()); + StatementMatcher CharLiteral = characterLiteral(); EXPECT_TRUE(matches("const char c = 'c';", CharLiteral)); // wide character EXPECT_TRUE(matches("const char c = L'c';", CharLiteral)); @@ -1413,7 +1629,7 @@ TEST(Matcher, CharacterLiterals) { } TEST(Matcher, IntegerLiterals) { - StatementMatcher HasIntLiteral = expression(integerLiteral()); + StatementMatcher HasIntLiteral = integerLiteral(); EXPECT_TRUE(matches("int i = 10;", HasIntLiteral)); EXPECT_TRUE(matches("int i = 0x1AB;", HasIntLiteral)); EXPECT_TRUE(matches("int i = 10L;", HasIntLiteral)); @@ -1428,6 +1644,14 @@ TEST(Matcher, IntegerLiterals) { EXPECT_TRUE(notMatches("int i = 10.0;", HasIntLiteral)); } +TEST(Matcher, NullPtrLiteral) { + EXPECT_TRUE(matches("int* i = nullptr;", nullPtrLiteralExpr())); +} + +TEST(Matcher, AsmStatement) { + EXPECT_TRUE(matches("void foo() { __asm(\"mov al, 2\"); }", asmStmt())); +} + TEST(Matcher, Conditions) { StatementMatcher Condition = ifStmt(hasCondition(boolLiteral(equals(true)))); @@ -1654,87 +1878,91 @@ TEST(ArraySubscriptMatchers, ArrayIndex) { TEST(ArraySubscriptMatchers, MatchesArrayBase) { EXPECT_TRUE(matches( "int i[2]; void f() { i[1] = 2; }", - arraySubscriptExpr(hasBase(implicitCast( - hasSourceExpression(declarationReference())))))); + arraySubscriptExpr(hasBase(implicitCastExpr( + hasSourceExpression(declRefExpr())))))); } TEST(Matcher, HasNameSupportsNamespaces) { EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", - record(hasName("a::b::C")))); + recordDecl(hasName("a::b::C")))); EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", - record(hasName("::a::b::C")))); + recordDecl(hasName("::a::b::C")))); EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", - record(hasName("b::C")))); + recordDecl(hasName("b::C")))); EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", - record(hasName("C")))); + recordDecl(hasName("C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", - record(hasName("c::b::C")))); + recordDecl(hasName("c::b::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", - record(hasName("a::c::C")))); + recordDecl(hasName("a::c::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", - record(hasName("a::b::A")))); + recordDecl(hasName("a::b::A")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", - record(hasName("::C")))); + recordDecl(hasName("::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", - record(hasName("::b::C")))); + recordDecl(hasName("::b::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", - record(hasName("z::a::b::C")))); + recordDecl(hasName("z::a::b::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", - record(hasName("a+b::C")))); + recordDecl(hasName("a+b::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class AC; } }", - record(hasName("C")))); + recordDecl(hasName("C")))); } TEST(Matcher, HasNameSupportsOuterClasses) { EXPECT_TRUE( - matches("class A { class B { class C; }; };", record(hasName("A::B::C")))); + matches("class A { class B { class C; }; };", + recordDecl(hasName("A::B::C")))); EXPECT_TRUE( matches("class A { class B { class C; }; };", - record(hasName("::A::B::C")))); + recordDecl(hasName("::A::B::C")))); EXPECT_TRUE( - matches("class A { class B { class C; }; };", record(hasName("B::C")))); + matches("class A { class B { class C; }; };", + recordDecl(hasName("B::C")))); EXPECT_TRUE( - matches("class A { class B { class C; }; };", record(hasName("C")))); + matches("class A { class B { class C; }; };", + recordDecl(hasName("C")))); EXPECT_TRUE( notMatches("class A { class B { class C; }; };", - record(hasName("c::B::C")))); + recordDecl(hasName("c::B::C")))); EXPECT_TRUE( notMatches("class A { class B { class C; }; };", - record(hasName("A::c::C")))); + recordDecl(hasName("A::c::C")))); EXPECT_TRUE( notMatches("class A { class B { class C; }; };", - record(hasName("A::B::A")))); + recordDecl(hasName("A::B::A")))); EXPECT_TRUE( - notMatches("class A { class B { class C; }; };", record(hasName("::C")))); + notMatches("class A { class B { class C; }; };", + recordDecl(hasName("::C")))); EXPECT_TRUE( notMatches("class A { class B { class C; }; };", - record(hasName("::B::C")))); + recordDecl(hasName("::B::C")))); EXPECT_TRUE(notMatches("class A { class B { class C; }; };", - record(hasName("z::A::B::C")))); + recordDecl(hasName("z::A::B::C")))); EXPECT_TRUE( notMatches("class A { class B { class C; }; };", - record(hasName("A+B::C")))); + recordDecl(hasName("A+B::C")))); } TEST(Matcher, IsDefinition) { DeclarationMatcher DefinitionOfClassA = - record(hasName("A"), isDefinition()); + recordDecl(hasName("A"), isDefinition()); EXPECT_TRUE(matches("class A {};", DefinitionOfClassA)); EXPECT_TRUE(notMatches("class A;", DefinitionOfClassA)); DeclarationMatcher DefinitionOfVariableA = - variable(hasName("a"), isDefinition()); + varDecl(hasName("a"), isDefinition()); EXPECT_TRUE(matches("int a;", DefinitionOfVariableA)); EXPECT_TRUE(notMatches("extern int a;", DefinitionOfVariableA)); DeclarationMatcher DefinitionOfMethodA = - method(hasName("a"), isDefinition()); + methodDecl(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( + StatementMatcher Constructor = constructExpr(hasDeclaration(methodDecl( ofClass(hasName("X"))))); EXPECT_TRUE( @@ -1751,7 +1979,8 @@ 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")))))); + "void f() { B<A> b; b.y(); }", + callExpr(callee(methodDecl(hasName("x")))))); EXPECT_TRUE(matches( "class A { public: void x(); };" @@ -1761,8 +1990,9 @@ TEST(Matcher, VisitsTemplateInstantiations) { "};" "void f() {" " C::B<A> b; b.y();" - "}", record(hasName("C"), - hasDescendant(call(callee(method(hasName("x")))))))); + "}", + recordDecl(hasName("C"), + hasDescendant(callExpr(callee(methodDecl(hasName("x")))))))); } TEST(Matcher, HandlesNullQualTypes) { @@ -1781,7 +2011,7 @@ TEST(Matcher, HandlesNullQualTypes) { "void g() {" " f(0);" "}", - expression(hasType(TypeMatcher( + expr(hasType(TypeMatcher( anyOf( TypeMatcher(hasDeclaration(anything())), pointsTo(AnyType), @@ -1798,16 +2028,16 @@ AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) { } TEST(AstMatcherPMacro, Works) { - DeclarationMatcher HasClassB = just(has(record(hasName("B")).bind("b"))); + DeclarationMatcher HasClassB = just(has(recordDecl(hasName("B")).bind("b"))); EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("b"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("b"))); EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("a"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("a"))); EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("b"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("b"))); } AST_POLYMORPHIC_MATCHER_P( @@ -1815,27 +2045,27 @@ AST_POLYMORPHIC_MATCHER_P( 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, + Node, AMatcher, Builder, ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses, ASTMatchFinder::BK_First); } TEST(AstPolymorphicMatcherPMacro, Works) { - DeclarationMatcher HasClassB = polymorphicHas(record(hasName("B")).bind("b")); + DeclarationMatcher HasClassB = + polymorphicHas(recordDecl(hasName("B")).bind("b")); EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("b"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("b"))); EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("a"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("a"))); EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };", - HasClassB, new VerifyIdIsBoundToDecl<Decl>("b"))); + HasClassB, new VerifyIdIsBoundTo<Decl>("b"))); StatementMatcher StatementHasClassB = - polymorphicHas(record(hasName("B"))); + polymorphicHas(recordDecl(hasName("B"))); EXPECT_TRUE(matches("void x() { class B {}; }", StatementHasClassB)); } @@ -1843,6 +2073,9 @@ TEST(AstPolymorphicMatcherPMacro, Works) { TEST(For, FindsForLoops) { EXPECT_TRUE(matches("void f() { for(;;); }", forStmt())); EXPECT_TRUE(matches("void f() { if(true) for(;;); }", forStmt())); + EXPECT_TRUE(notMatches("int as[] = { 1, 2, 3 };" + "void f() { for (auto &a : as); }", + forStmt())); } TEST(For, ForLoopInternals) { @@ -1854,7 +2087,7 @@ TEST(For, ForLoopInternals) { TEST(For, NegativeForLoopInternals) { EXPECT_TRUE(notMatches("void f(){ for (int i = 0; ; ++i); }", - forStmt(hasCondition(expression())))); + forStmt(hasCondition(expr())))); EXPECT_TRUE(notMatches("void f() {int i; for (; i < 4; ++i) {} }", forStmt(hasLoopInit(anything())))); } @@ -1865,29 +2098,29 @@ TEST(For, ReportsNoFalsePositives) { } TEST(CompoundStatement, HandlesSimpleCases) { - EXPECT_TRUE(notMatches("void f();", compoundStatement())); - EXPECT_TRUE(matches("void f() {}", compoundStatement())); - EXPECT_TRUE(matches("void f() {{}}", compoundStatement())); + EXPECT_TRUE(notMatches("void f();", compoundStmt())); + EXPECT_TRUE(matches("void f() {}", compoundStmt())); + EXPECT_TRUE(matches("void f() {{}}", compoundStmt())); } 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())); + compoundStmt())); EXPECT_TRUE(matches("namespace n { struct S { void f() {{}} }; }", - compoundStatement())); + compoundStmt())); } TEST(HasBody, FindsBodyOfForWhileDoLoops) { EXPECT_TRUE(matches("void f() { for(;;) {} }", - forStmt(hasBody(compoundStatement())))); + forStmt(hasBody(compoundStmt())))); EXPECT_TRUE(notMatches("void f() { for(;;); }", - forStmt(hasBody(compoundStatement())))); + forStmt(hasBody(compoundStmt())))); EXPECT_TRUE(matches("void f() { while(true) {} }", - whileStmt(hasBody(compoundStatement())))); + whileStmt(hasBody(compoundStmt())))); EXPECT_TRUE(matches("void f() { do {} while(true); }", - doStmt(hasBody(compoundStatement())))); + doStmt(hasBody(compoundStmt())))); } TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) { @@ -1895,67 +2128,67 @@ TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) { // definition, and the function body itself must be a compound // statement. EXPECT_TRUE(matches("void f() { for (;;); }", - compoundStatement(hasAnySubstatement(forStmt())))); + compoundStmt(hasAnySubstatement(forStmt())))); } TEST(HasAnySubstatement, IsNotRecursive) { // It's really "has any immediate substatement". EXPECT_TRUE(notMatches("void f() { if (true) for (;;); }", - compoundStatement(hasAnySubstatement(forStmt())))); + compoundStmt(hasAnySubstatement(forStmt())))); } TEST(HasAnySubstatement, MatchesInNestedCompoundStatements) { EXPECT_TRUE(matches("void f() { if (true) { for (;;); } }", - compoundStatement(hasAnySubstatement(forStmt())))); + compoundStmt(hasAnySubstatement(forStmt())))); } TEST(HasAnySubstatement, FindsSubstatementBetweenOthers) { EXPECT_TRUE(matches("void f() { 1; 2; 3; for (;;); 4; 5; 6; }", - compoundStatement(hasAnySubstatement(forStmt())))); + compoundStmt(hasAnySubstatement(forStmt())))); } TEST(StatementCountIs, FindsNoStatementsInAnEmptyCompoundStatement) { EXPECT_TRUE(matches("void f() { }", - compoundStatement(statementCountIs(0)))); + compoundStmt(statementCountIs(0)))); EXPECT_TRUE(notMatches("void f() {}", - compoundStatement(statementCountIs(1)))); + compoundStmt(statementCountIs(1)))); } TEST(StatementCountIs, AppearsToMatchOnlyOneCount) { EXPECT_TRUE(matches("void f() { 1; }", - compoundStatement(statementCountIs(1)))); + compoundStmt(statementCountIs(1)))); EXPECT_TRUE(notMatches("void f() { 1; }", - compoundStatement(statementCountIs(0)))); + compoundStmt(statementCountIs(0)))); EXPECT_TRUE(notMatches("void f() { 1; }", - compoundStatement(statementCountIs(2)))); + compoundStmt(statementCountIs(2)))); } TEST(StatementCountIs, WorksWithMultipleStatements) { EXPECT_TRUE(matches("void f() { 1; 2; 3; }", - compoundStatement(statementCountIs(3)))); + compoundStmt(statementCountIs(3)))); } TEST(StatementCountIs, WorksWithNestedCompoundStatements) { EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", - compoundStatement(statementCountIs(1)))); + compoundStmt(statementCountIs(1)))); EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", - compoundStatement(statementCountIs(2)))); + compoundStmt(statementCountIs(2)))); EXPECT_TRUE(notMatches("void f() { { 1; } { 1; 2; 3; 4; } }", - compoundStatement(statementCountIs(3)))); + compoundStmt(statementCountIs(3)))); EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", - compoundStatement(statementCountIs(4)))); + compoundStmt(statementCountIs(4)))); } TEST(Member, WorksInSimplestCase) { EXPECT_TRUE(matches("struct { int first; } s; int i(s.first);", - memberExpression(member(hasName("first"))))); + memberExpr(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"))))); + memberExpr(member(hasName("first"))))); } TEST(Member, MatchesInMemberFunctionCall) { @@ -1963,211 +2196,246 @@ TEST(Member, MatchesInMemberFunctionCall) { " struct { void first() {}; } s;" " s.first();" "};", - memberExpression(member(hasName("first"))))); + memberExpr(member(hasName("first"))))); +} + +TEST(Member, MatchesMember) { + EXPECT_TRUE(matches( + "struct A { int i; }; void f() { A a; a.i = 2; }", + memberExpr(hasDeclaration(fieldDecl(hasType(isInteger())))))); + EXPECT_TRUE(notMatches( + "struct A { float f; }; void f() { A a; a.f = 2.0f; }", + memberExpr(hasDeclaration(fieldDecl(hasType(isInteger())))))); +} + +TEST(Member, MatchesMemberAllocationFunction) { + // Fails in C++11 mode + EXPECT_TRUE(matchesConditionally( + "namespace std { typedef typeof(sizeof(int)) size_t; }" + "class X { void *operator new(std::size_t); };", + methodDecl(ofClass(hasName("X"))), true, "-std=gnu++98")); + + EXPECT_TRUE(matches("class X { void operator delete(void*); };", + methodDecl(ofClass(hasName("X"))))); + + // Fails in C++11 mode + EXPECT_TRUE(matchesConditionally( + "namespace std { typedef typeof(sizeof(int)) size_t; }" + "class X { void operator delete[](void*, std::size_t); };", + methodDecl(ofClass(hasName("X"))), true, "-std=gnu++98")); } TEST(HasObjectExpression, DoesNotMatchMember) { EXPECT_TRUE(notMatches( "class X {}; struct Z { X m; }; void f(Z z) { z.m; }", - memberExpression(hasObjectExpression(hasType(record(hasName("X"))))))); + memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X"))))))); } TEST(HasObjectExpression, MatchesBaseOfVariable) { EXPECT_TRUE(matches( "struct X { int m; }; void f(X x) { x.m; }", - memberExpression(hasObjectExpression(hasType(record(hasName("X"))))))); + memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X"))))))); EXPECT_TRUE(matches( "struct X { int m; }; void f(X* x) { x->m; }", - memberExpression(hasObjectExpression( - hasType(pointsTo(record(hasName("X")))))))); + memberExpr(hasObjectExpression( + hasType(pointsTo(recordDecl(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")))))))); + memberExpr(hasObjectExpression( + hasType(pointsTo(recordDecl(hasName("S")))))))); EXPECT_TRUE(matches( "class X {}; struct S { X m; void f() { m; } };", - memberExpression(hasObjectExpression( - hasType(pointsTo(record(hasName("S")))))))); + memberExpr(hasObjectExpression( + hasType(pointsTo(recordDecl(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")))); + EXPECT_TRUE(notMatches("class X { void m(); };", fieldDecl(hasName("m")))); + EXPECT_TRUE(notMatches("class X { class m {}; };", fieldDecl(hasName("m")))); + EXPECT_TRUE(notMatches("class X { enum { m }; };", fieldDecl(hasName("m")))); + EXPECT_TRUE(notMatches("class X { enum m {}; };", fieldDecl(hasName("m")))); } TEST(Field, MatchesField) { - EXPECT_TRUE(matches("class X { int m; };", field(hasName("m")))); + EXPECT_TRUE(matches("class X { int m; };", fieldDecl(hasName("m")))); } TEST(IsConstQualified, MatchesConstInt) { EXPECT_TRUE(matches("const int i = 42;", - variable(hasType(isConstQualified())))); + varDecl(hasType(isConstQualified())))); } TEST(IsConstQualified, MatchesConstPointer) { EXPECT_TRUE(matches("int i = 42; int* const p(&i);", - variable(hasType(isConstQualified())))); + varDecl(hasType(isConstQualified())))); } TEST(IsConstQualified, MatchesThroughTypedef) { EXPECT_TRUE(matches("typedef const int const_int; const_int i = 42;", - variable(hasType(isConstQualified())))); + varDecl(hasType(isConstQualified())))); EXPECT_TRUE(matches("typedef int* int_ptr; const int_ptr p(0);", - variable(hasType(isConstQualified())))); + varDecl(hasType(isConstQualified())))); } TEST(IsConstQualified, DoesNotMatchInappropriately) { EXPECT_TRUE(notMatches("typedef int nonconst_int; nonconst_int i = 42;", - variable(hasType(isConstQualified())))); + varDecl(hasType(isConstQualified())))); EXPECT_TRUE(notMatches("int const* p;", - variable(hasType(isConstQualified())))); + varDecl(hasType(isConstQualified())))); } TEST(CastExpression, MatchesExplicitCasts) { - EXPECT_TRUE(matches("char *p = reinterpret_cast<char *>(&p);", - expression(castExpr()))); - EXPECT_TRUE(matches("void *p = (void *)(&p);", expression(castExpr()))); - EXPECT_TRUE(matches("char q, *p = const_cast<char *>(&q);", - expression(castExpr()))); - EXPECT_TRUE(matches("char c = char(0);", expression(castExpr()))); + EXPECT_TRUE(matches("char *p = reinterpret_cast<char *>(&p);",castExpr())); + EXPECT_TRUE(matches("void *p = (void *)(&p);", castExpr())); + EXPECT_TRUE(matches("char q, *p = const_cast<char *>(&q);", castExpr())); + EXPECT_TRUE(matches("char c = char(0);", castExpr())); } TEST(CastExpression, MatchesImplicitCasts) { // This test creates an implicit cast from int to char. - EXPECT_TRUE(matches("char c = 0;", expression(castExpr()))); + EXPECT_TRUE(matches("char c = 0;", castExpr())); // This test creates an implicit cast from lvalue to rvalue. - EXPECT_TRUE(matches("char c = 0, d = c;", expression(castExpr()))); + EXPECT_TRUE(matches("char c = 0, d = c;", castExpr())); } TEST(CastExpression, DoesNotMatchNonCasts) { - EXPECT_TRUE(notMatches("char c = '0';", expression(castExpr()))); - EXPECT_TRUE(notMatches("char c, &q = c;", expression(castExpr()))); - EXPECT_TRUE(notMatches("int i = (0);", expression(castExpr()))); - EXPECT_TRUE(notMatches("int i = 0;", expression(castExpr()))); + EXPECT_TRUE(notMatches("char c = '0';", castExpr())); + EXPECT_TRUE(notMatches("char c, &q = c;", castExpr())); + EXPECT_TRUE(notMatches("int i = (0);", castExpr())); + EXPECT_TRUE(notMatches("int i = 0;", castExpr())); } TEST(ReinterpretCast, MatchesSimpleCase) { EXPECT_TRUE(matches("char* p = reinterpret_cast<char*>(&p);", - expression(reinterpretCast()))); + reinterpretCastExpr())); } TEST(ReinterpretCast, DoesNotMatchOtherCasts) { - EXPECT_TRUE(notMatches("char* p = (char*)(&p);", - expression(reinterpretCast()))); + EXPECT_TRUE(notMatches("char* p = (char*)(&p);", reinterpretCastExpr())); EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);", - expression(reinterpretCast()))); + reinterpretCastExpr())); EXPECT_TRUE(notMatches("void* p = static_cast<void*>(&p);", - expression(reinterpretCast()))); + reinterpretCastExpr())); EXPECT_TRUE(notMatches("struct B { virtual ~B() {} }; struct D : B {};" "B b;" "D* p = dynamic_cast<D*>(&b);", - expression(reinterpretCast()))); + reinterpretCastExpr())); } 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()))); + functionalCastExpr())); } TEST(FunctionalCast, DoesNotMatchOtherCasts) { std::string FooClass = "class Foo { public: Foo(char*); };"; EXPECT_TRUE( notMatches(FooClass + "void r() { Foo f = (Foo) \"hello world\"; }", - expression(functionalCast()))); + functionalCastExpr())); EXPECT_TRUE( notMatches(FooClass + "void r() { Foo f = \"hello world\"; }", - expression(functionalCast()))); + functionalCastExpr())); } TEST(DynamicCast, MatchesSimpleCase) { EXPECT_TRUE(matches("struct B { virtual ~B() {} }; struct D : B {};" "B b;" "D* p = dynamic_cast<D*>(&b);", - expression(dynamicCast()))); + dynamicCastExpr())); } TEST(StaticCast, MatchesSimpleCase) { EXPECT_TRUE(matches("void* p(static_cast<void*>(&p));", - expression(staticCast()))); + staticCastExpr())); } TEST(StaticCast, DoesNotMatchOtherCasts) { - EXPECT_TRUE(notMatches("char* p = (char*)(&p);", - expression(staticCast()))); + EXPECT_TRUE(notMatches("char* p = (char*)(&p);", staticCastExpr())); EXPECT_TRUE(notMatches("char q, *p = const_cast<char*>(&q);", - expression(staticCast()))); + staticCastExpr())); EXPECT_TRUE(notMatches("void* p = reinterpret_cast<char*>(&p);", - expression(staticCast()))); + staticCastExpr())); EXPECT_TRUE(notMatches("struct B { virtual ~B() {} }; struct D : B {};" "B b;" "D* p = dynamic_cast<D*>(&b);", - expression(staticCast()))); + staticCastExpr())); +} + +TEST(CStyleCast, MatchesSimpleCase) { + EXPECT_TRUE(matches("int i = (int) 2.2f;", cStyleCastExpr())); +} + +TEST(CStyleCast, DoesNotMatchOtherCasts) { + EXPECT_TRUE(notMatches("char* p = static_cast<char*>(0);" + "char q, *r = const_cast<char*>(&q);" + "void* s = reinterpret_cast<char*>(&s);" + "struct B { virtual ~B() {} }; struct D : B {};" + "B b;" + "D* t = dynamic_cast<D*>(&b);", + cStyleCastExpr())); } TEST(HasDestinationType, MatchesSimpleCase) { EXPECT_TRUE(matches("char* p = static_cast<char*>(0);", - expression( - staticCast(hasDestinationType( - pointsTo(TypeMatcher(anything()))))))); + staticCastExpr(hasDestinationType( + pointsTo(TypeMatcher(anything())))))); } TEST(HasImplicitDestinationType, MatchesSimpleCase) { // This test creates an implicit const cast. EXPECT_TRUE(matches("int x; const int i = x;", - expression(implicitCast( - hasImplicitDestinationType(isInteger()))))); + implicitCastExpr( + hasImplicitDestinationType(isInteger())))); // This test creates an implicit array-to-pointer cast. EXPECT_TRUE(matches("int arr[3]; int *p = arr;", - expression(implicitCast(hasImplicitDestinationType( - pointsTo(TypeMatcher(anything()))))))); + implicitCastExpr(hasImplicitDestinationType( + pointsTo(TypeMatcher(anything())))))); } TEST(HasImplicitDestinationType, DoesNotMatchIncorrectly) { // This test creates an implicit cast from int to char. EXPECT_TRUE(notMatches("char c = 0;", - expression(implicitCast(hasImplicitDestinationType( - unless(anything())))))); + implicitCastExpr(hasImplicitDestinationType( + unless(anything()))))); // This test creates an implicit array-to-pointer cast. EXPECT_TRUE(notMatches("int arr[3]; int *p = arr;", - expression(implicitCast(hasImplicitDestinationType( - unless(anything())))))); + implicitCastExpr(hasImplicitDestinationType( + unless(anything()))))); } TEST(ImplicitCast, MatchesSimpleCase) { // This test creates an implicit const cast. EXPECT_TRUE(matches("int x = 0; const int y = x;", - variable(hasInitializer(implicitCast())))); + varDecl(hasInitializer(implicitCastExpr())))); // This test creates an implicit cast from int to char. EXPECT_TRUE(matches("char c = 0;", - variable(hasInitializer(implicitCast())))); + varDecl(hasInitializer(implicitCastExpr())))); // This test creates an implicit array-to-pointer cast. EXPECT_TRUE(matches("int arr[6]; int *p = arr;", - variable(hasInitializer(implicitCast())))); + varDecl(hasInitializer(implicitCastExpr())))); } TEST(ImplicitCast, DoesNotMatchIncorrectly) { - // This test verifies that implicitCast() matches exactly when implicit casts + // This test verifies that implicitCastExpr() matches exactly when implicit casts // are present, and that it ignores explicit and paren casts. // These two test cases have no casts. EXPECT_TRUE(notMatches("int x = 0;", - variable(hasInitializer(implicitCast())))); + varDecl(hasInitializer(implicitCastExpr())))); EXPECT_TRUE(notMatches("int x = 0, &y = x;", - variable(hasInitializer(implicitCast())))); + varDecl(hasInitializer(implicitCastExpr())))); EXPECT_TRUE(notMatches("int x = 0; double d = (double) x;", - variable(hasInitializer(implicitCast())))); + varDecl(hasInitializer(implicitCastExpr())))); EXPECT_TRUE(notMatches("const int *p; int *q = const_cast<int *>(p);", - variable(hasInitializer(implicitCast())))); + varDecl(hasInitializer(implicitCastExpr())))); EXPECT_TRUE(notMatches("int x = (0);", - variable(hasInitializer(implicitCast())))); + varDecl(hasInitializer(implicitCastExpr())))); } TEST(IgnoringImpCasts, MatchesImpCasts) { @@ -2175,11 +2443,11 @@ TEST(IgnoringImpCasts, MatchesImpCasts) { // present and its inner matcher alone does not match. // Note that this test creates an implicit const cast. EXPECT_TRUE(matches("int x = 0; const int y = x;", - variable(hasInitializer(ignoringImpCasts( - declarationReference(to(variable(hasName("x"))))))))); + varDecl(hasInitializer(ignoringImpCasts( + declRefExpr(to(varDecl(hasName("x"))))))))); // This test creates an implict cast from int to char. EXPECT_TRUE(matches("char x = 0;", - variable(hasInitializer(ignoringImpCasts( + varDecl(hasInitializer(ignoringImpCasts( integerLiteral(equals(0))))))); } @@ -2188,82 +2456,82 @@ TEST(IgnoringImpCasts, DoesNotMatchIncorrectly) { // matcher does not match. // Note that the first test creates an implicit const cast. EXPECT_TRUE(notMatches("int x; const int y = x;", - variable(hasInitializer(ignoringImpCasts( + varDecl(hasInitializer(ignoringImpCasts( unless(anything())))))); EXPECT_TRUE(notMatches("int x; int y = x;", - variable(hasInitializer(ignoringImpCasts( + varDecl(hasInitializer(ignoringImpCasts( unless(anything())))))); // These tests verify that ignoringImplictCasts does not look through explicit // casts or parentheses. EXPECT_TRUE(notMatches("char* p = static_cast<char*>(0);", - variable(hasInitializer(ignoringImpCasts( - integerLiteral()))))); + varDecl(hasInitializer(ignoringImpCasts( + integerLiteral()))))); EXPECT_TRUE(notMatches("int i = (0);", - variable(hasInitializer(ignoringImpCasts( - integerLiteral()))))); + varDecl(hasInitializer(ignoringImpCasts( + integerLiteral()))))); EXPECT_TRUE(notMatches("float i = (float)0;", - variable(hasInitializer(ignoringImpCasts( - integerLiteral()))))); + varDecl(hasInitializer(ignoringImpCasts( + integerLiteral()))))); EXPECT_TRUE(notMatches("float i = float(0);", - variable(hasInitializer(ignoringImpCasts( - integerLiteral()))))); + varDecl(hasInitializer(ignoringImpCasts( + integerLiteral()))))); } TEST(IgnoringImpCasts, MatchesWithoutImpCasts) { // This test verifies that expressions that do not have implicit casts // still match the inner matcher. EXPECT_TRUE(matches("int x = 0; int &y = x;", - variable(hasInitializer(ignoringImpCasts( - declarationReference(to(variable(hasName("x"))))))))); + varDecl(hasInitializer(ignoringImpCasts( + declRefExpr(to(varDecl(hasName("x"))))))))); } TEST(IgnoringParenCasts, MatchesParenCasts) { // This test checks that ignoringParenCasts matches when parentheses and/or // casts are present and its inner matcher alone does not match. EXPECT_TRUE(matches("int x = (0);", - variable(hasInitializer(ignoringParenCasts( - integerLiteral(equals(0))))))); + varDecl(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); EXPECT_TRUE(matches("int x = (((((0)))));", - variable(hasInitializer(ignoringParenCasts( - integerLiteral(equals(0))))))); + varDecl(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); // This test creates an implict cast from int to char in addition to the // parentheses. EXPECT_TRUE(matches("char x = (0);", - variable(hasInitializer(ignoringParenCasts( - integerLiteral(equals(0))))))); + varDecl(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); EXPECT_TRUE(matches("char x = (char)0;", - variable(hasInitializer(ignoringParenCasts( - integerLiteral(equals(0))))))); + varDecl(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); EXPECT_TRUE(matches("char* p = static_cast<char*>(0);", - variable(hasInitializer(ignoringParenCasts( + varDecl(hasInitializer(ignoringParenCasts( integerLiteral(equals(0))))))); } TEST(IgnoringParenCasts, MatchesWithoutParenCasts) { // This test verifies that expressions that do not have any casts still match. EXPECT_TRUE(matches("int x = 0;", - variable(hasInitializer(ignoringParenCasts( - integerLiteral(equals(0))))))); + varDecl(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); } TEST(IgnoringParenCasts, DoesNotMatchIncorrectly) { // These tests verify that ignoringImpCasts does not match if the inner // matcher does not match. EXPECT_TRUE(notMatches("int x = ((0));", - variable(hasInitializer(ignoringParenCasts( + varDecl(hasInitializer(ignoringParenCasts( unless(anything())))))); // This test creates an implicit cast from int to char in addition to the // parentheses. EXPECT_TRUE(notMatches("char x = ((0));", - variable(hasInitializer(ignoringParenCasts( + varDecl(hasInitializer(ignoringParenCasts( unless(anything())))))); EXPECT_TRUE(notMatches("char *x = static_cast<char *>((0));", - variable(hasInitializer(ignoringParenCasts( + varDecl(hasInitializer(ignoringParenCasts( unless(anything())))))); } @@ -2273,23 +2541,23 @@ TEST(IgnoringParenAndImpCasts, MatchesParenImpCasts) { // does not match. // Note that this test creates an implicit const cast. EXPECT_TRUE(matches("int x = 0; const int y = x;", - variable(hasInitializer(ignoringParenImpCasts( - declarationReference(to(variable(hasName("x"))))))))); + varDecl(hasInitializer(ignoringParenImpCasts( + declRefExpr(to(varDecl(hasName("x"))))))))); // This test creates an implicit cast from int to char. EXPECT_TRUE(matches("const char x = (0);", - variable(hasInitializer(ignoringParenImpCasts( - integerLiteral(equals(0))))))); + varDecl(hasInitializer(ignoringParenImpCasts( + integerLiteral(equals(0))))))); } TEST(IgnoringParenAndImpCasts, MatchesWithoutParenImpCasts) { // This test verifies that expressions that do not have parentheses or // implicit casts still match. EXPECT_TRUE(matches("int x = 0; int &y = x;", - variable(hasInitializer(ignoringParenImpCasts( - declarationReference(to(variable(hasName("x"))))))))); + varDecl(hasInitializer(ignoringParenImpCasts( + declRefExpr(to(varDecl(hasName("x"))))))))); EXPECT_TRUE(matches("int x = 0;", - variable(hasInitializer(ignoringParenImpCasts( - integerLiteral(equals(0))))))); + varDecl(hasInitializer(ignoringParenImpCasts( + integerLiteral(equals(0))))))); } TEST(IgnoringParenAndImpCasts, DoesNotMatchIncorrectly) { @@ -2297,56 +2565,56 @@ TEST(IgnoringParenAndImpCasts, DoesNotMatchIncorrectly) { // the inner matcher does not match. // This test creates an implicit cast. EXPECT_TRUE(notMatches("char c = ((3));", - variable(hasInitializer(ignoringParenImpCasts( + varDecl(hasInitializer(ignoringParenImpCasts( unless(anything())))))); // These tests verify that ignoringParenAndImplictCasts does not look // through explicit casts. EXPECT_TRUE(notMatches("float y = (float(0));", - variable(hasInitializer(ignoringParenImpCasts( - integerLiteral()))))); + varDecl(hasInitializer(ignoringParenImpCasts( + integerLiteral()))))); EXPECT_TRUE(notMatches("float y = (float)0;", - variable(hasInitializer(ignoringParenImpCasts( - integerLiteral()))))); + varDecl(hasInitializer(ignoringParenImpCasts( + integerLiteral()))))); EXPECT_TRUE(notMatches("char* p = static_cast<char*>(0);", - variable(hasInitializer(ignoringParenImpCasts( - integerLiteral()))))); + varDecl(hasInitializer(ignoringParenImpCasts( + integerLiteral()))))); } 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()))))); + implicitCastExpr( + hasSourceExpression(constructExpr())))); } TEST(HasSourceExpression, MatchesExplicitCasts) { EXPECT_TRUE(matches("float x = static_cast<float>(42);", - expression(explicitCast( - hasSourceExpression(hasDescendant( - expression(integerLiteral()))))))); + explicitCastExpr( + hasSourceExpression(hasDescendant( + expr(integerLiteral())))))); } TEST(Statement, DoesNotMatchDeclarations) { - EXPECT_TRUE(notMatches("class X {};", statement())); + EXPECT_TRUE(notMatches("class X {};", stmt())); } TEST(Statement, MatchesCompoundStatments) { - EXPECT_TRUE(matches("void x() {}", statement())); + EXPECT_TRUE(matches("void x() {}", stmt())); } TEST(DeclarationStatement, DoesNotMatchCompoundStatements) { - EXPECT_TRUE(notMatches("void x() {}", declarationStatement())); + EXPECT_TRUE(notMatches("void x() {}", declStmt())); } TEST(DeclarationStatement, MatchesVariableDeclarationStatements) { - EXPECT_TRUE(matches("void x() { int a; }", declarationStatement())); + EXPECT_TRUE(matches("void x() { int a; }", declStmt())); } 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")))))); + initListExpr(hasType(recordDecl(hasName("B")))))); } TEST(UsingDeclaration, MatchesUsingDeclarations) { @@ -2362,24 +2630,24 @@ TEST(UsingDeclaration, MatchesShadowUsingDelcarations) { TEST(UsingDeclaration, MatchesSpecificTarget) { EXPECT_TRUE(matches("namespace f { int a; void b(); } using f::b;", usingDecl(hasAnyUsingShadowDecl( - hasTargetDecl(function()))))); + hasTargetDecl(functionDecl()))))); EXPECT_TRUE(notMatches("namespace f { int a; void b(); } using f::a;", usingDecl(hasAnyUsingShadowDecl( - hasTargetDecl(function()))))); + hasTargetDecl(functionDecl()))))); } TEST(UsingDeclaration, ThroughUsingDeclaration) { EXPECT_TRUE(matches( "namespace a { void f(); } using a::f; void g() { f(); }", - declarationReference(throughUsingDecl(anything())))); + declRefExpr(throughUsingDecl(anything())))); EXPECT_TRUE(notMatches( "namespace a { void f(); } using a::f; void g() { a::f(); }", - declarationReference(throughUsingDecl(anything())))); + declRefExpr(throughUsingDecl(anything())))); } TEST(SingleDecl, IsSingleDecl) { StatementMatcher SingleDeclStmt = - declarationStatement(hasSingleDecl(variable(hasInitializer(anything())))); + declStmt(hasSingleDecl(varDecl(hasInitializer(anything())))); EXPECT_TRUE(matches("void f() {int a = 4;}", SingleDeclStmt)); EXPECT_TRUE(notMatches("void f() {int a;}", SingleDeclStmt)); EXPECT_TRUE(notMatches("void f() {int a = 4, b = 3;}", @@ -2387,28 +2655,26 @@ TEST(SingleDecl, IsSingleDecl) { } TEST(DeclStmt, ContainsDeclaration) { - DeclarationMatcher MatchesInit = variable(hasInitializer(anything())); + DeclarationMatcher MatchesInit = varDecl(hasInitializer(anything())); EXPECT_TRUE(matches("void f() {int a = 4;}", - declarationStatement(containsDeclaration(0, - MatchesInit)))); + declStmt(containsDeclaration(0, MatchesInit)))); EXPECT_TRUE(matches("void f() {int a = 4, b = 3;}", - declarationStatement(containsDeclaration(0, MatchesInit), - containsDeclaration(1, - MatchesInit)))); + declStmt(containsDeclaration(0, MatchesInit), + containsDeclaration(1, MatchesInit)))); unsigned WrongIndex = 42; EXPECT_TRUE(notMatches("void f() {int a = 4, b = 3;}", - declarationStatement(containsDeclaration(WrongIndex, + declStmt(containsDeclaration(WrongIndex, MatchesInit)))); } TEST(DeclCount, DeclCountIsCorrect) { EXPECT_TRUE(matches("void f() {int i,j;}", - declarationStatement(declCountIs(2)))); + declStmt(declCountIs(2)))); EXPECT_TRUE(notMatches("void f() {int i,j; int k;}", - declarationStatement(declCountIs(3)))); + declStmt(declCountIs(3)))); EXPECT_TRUE(notMatches("void f() {int i,j, k, l;}", - declarationStatement(declCountIs(3)))); + declStmt(declCountIs(3)))); } TEST(While, MatchesWhileLoops) { @@ -2433,61 +2699,91 @@ TEST(SwitchCase, MatchesCase) { EXPECT_TRUE(notMatches("void x() { switch(42) {} }", switchCase())); } +TEST(SwitchCase, MatchesSwitch) { + EXPECT_TRUE(matches("void x() { switch(42) { case 42:; } }", switchStmt())); + EXPECT_TRUE(matches("void x() { switch(42) { default:; } }", switchStmt())); + EXPECT_TRUE(matches("void x() { switch(42) default:; }", switchStmt())); + EXPECT_TRUE(notMatches("void x() {}", switchStmt())); +} + +TEST(ExceptionHandling, SimpleCases) { + EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", catchStmt())); + EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", tryStmt())); + EXPECT_TRUE(notMatches("void foo() try { } catch(int X) { }", throwExpr())); + EXPECT_TRUE(matches("void foo() try { throw; } catch(int X) { }", + throwExpr())); + EXPECT_TRUE(matches("void foo() try { throw 5;} catch(int X) { }", + throwExpr())); +} + TEST(HasConditionVariableStatement, DoesNotMatchCondition) { EXPECT_TRUE(notMatches( "void x() { if(true) {} }", - ifStmt(hasConditionVariableStatement(declarationStatement())))); + ifStmt(hasConditionVariableStatement(declStmt())))); EXPECT_TRUE(notMatches( "void x() { int x; if((x = 42)) {} }", - ifStmt(hasConditionVariableStatement(declarationStatement())))); + ifStmt(hasConditionVariableStatement(declStmt())))); } TEST(HasConditionVariableStatement, MatchesConditionVariables) { EXPECT_TRUE(matches( "void x() { if(int* a = 0) {} }", - ifStmt(hasConditionVariableStatement(declarationStatement())))); + ifStmt(hasConditionVariableStatement(declStmt())))); } TEST(ForEach, BindsOneNode) { EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; };", - record(hasName("C"), forEach(field(hasName("x")).bind("x"))), - new VerifyIdIsBoundToDecl<FieldDecl>("x", 1))); + recordDecl(hasName("C"), forEach(fieldDecl(hasName("x")).bind("x"))), + new VerifyIdIsBoundTo<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))); + recordDecl(hasName("C"), forEach(fieldDecl().bind("f"))), + new VerifyIdIsBoundTo<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))); + recordDecl(hasName("C"), + forEach(recordDecl(forEach(fieldDecl().bind("f"))))), + new VerifyIdIsBoundTo<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))); + recordDecl(hasName("C"), + forEachDescendant(fieldDecl(hasName("x")).bind("x"))), + new VerifyIdIsBoundTo<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))); + recordDecl(hasName("C"), forEachDescendant(fieldDecl().bind("f"))), + new VerifyIdIsBoundTo<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))); + recordDecl(hasName("C"), forEachDescendant(recordDecl( + forEachDescendant(fieldDecl().bind("f"))))), + new VerifyIdIsBoundTo<FieldDecl>("f", 8))); +} + +TEST(ForEachDescendant, BindsCorrectNodes) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { void f(); int i; };", + recordDecl(hasName("C"), forEachDescendant(decl().bind("decl"))), + new VerifyIdIsBoundTo<FieldDecl>("decl", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { void f() {} int i; };", + recordDecl(hasName("C"), forEachDescendant(decl().bind("decl"))), + new VerifyIdIsBoundTo<FunctionDecl>("decl", 1))); } @@ -2497,18 +2793,18 @@ TEST(IsTemplateInstantiation, MatchesImplicitClassTemplateInstantiation) { EXPECT_TRUE(matches( "template <typename T> class X {}; class A {}; X<A> x;", - record(hasName("::X"), isTemplateInstantiation()))); + recordDecl(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")))))))); + recordDecl(isTemplateInstantiation(), hasDescendant( + fieldDecl(hasType(recordDecl(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")))), + functionDecl(hasParameter(0, hasType(recordDecl(hasName("A")))), isTemplateInstantiation()))); } @@ -2516,8 +2812,8 @@ 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")))))))); + recordDecl(isTemplateInstantiation(), hasDescendant( + fieldDecl(hasType(recordDecl(hasName("A")))))))); } TEST(IsTemplateInstantiation, @@ -2525,7 +2821,7 @@ TEST(IsTemplateInstantiation, EXPECT_TRUE(matches( "template <typename T> class X {};" "template <typename T> class X<T*> {}; class A {}; X<A*> x;", - record(hasName("::X"), isTemplateInstantiation()))); + recordDecl(hasName("::X"), isTemplateInstantiation()))); } TEST(IsTemplateInstantiation, @@ -2536,7 +2832,7 @@ TEST(IsTemplateInstantiation, " template <typename U> class Y { U u; };" " Y<A> y;" "};", - record(hasName("::X::Y"), isTemplateInstantiation()))); + recordDecl(hasName("::X::Y"), isTemplateInstantiation()))); } TEST(IsTemplateInstantiation, DoesNotMatchInstantiationsInsideOfInstantiation) { @@ -2549,20 +2845,557 @@ TEST(IsTemplateInstantiation, DoesNotMatchInstantiationsInsideOfInstantiation) { " template <typename U> class Y { U u; };" " Y<T> y;" "}; X<A> x;", - record(hasName("::X<A>::Y"), unless(isTemplateInstantiation())))); + recordDecl(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()))); + recordDecl(hasName("::X"), isTemplateInstantiation()))); } TEST(IsTemplateInstantiation, DoesNotMatchNonTemplate) { EXPECT_TRUE(notMatches( "class A {}; class Y { A a; };", - record(isTemplateInstantiation()))); + recordDecl(isTemplateInstantiation()))); +} + +TEST(IsExplicitTemplateSpecialization, + DoesNotMatchPrimaryTemplate) { + EXPECT_TRUE(notMatches( + "template <typename T> class X {};", + recordDecl(isExplicitTemplateSpecialization()))); + EXPECT_TRUE(notMatches( + "template <typename T> void f(T t);", + functionDecl(isExplicitTemplateSpecialization()))); +} + +TEST(IsExplicitTemplateSpecialization, + DoesNotMatchExplicitTemplateInstantiations) { + EXPECT_TRUE(notMatches( + "template <typename T> class X {};" + "template class X<int>; extern template class X<long>;", + recordDecl(isExplicitTemplateSpecialization()))); + EXPECT_TRUE(notMatches( + "template <typename T> void f(T t) {}" + "template void f(int t); extern template void f(long t);", + functionDecl(isExplicitTemplateSpecialization()))); +} + +TEST(IsExplicitTemplateSpecialization, + DoesNotMatchImplicitTemplateInstantiations) { + EXPECT_TRUE(notMatches( + "template <typename T> class X {}; X<int> x;", + recordDecl(isExplicitTemplateSpecialization()))); + EXPECT_TRUE(notMatches( + "template <typename T> void f(T t); void g() { f(10); }", + functionDecl(isExplicitTemplateSpecialization()))); +} + +TEST(IsExplicitTemplateSpecialization, + MatchesExplicitTemplateSpecializations) { + EXPECT_TRUE(matches( + "template <typename T> class X {};" + "template<> class X<int> {};", + recordDecl(isExplicitTemplateSpecialization()))); + EXPECT_TRUE(matches( + "template <typename T> void f(T t) {}" + "template<> void f(int t) {}", + functionDecl(isExplicitTemplateSpecialization()))); +} + +TEST(HasAncenstor, MatchesDeclarationAncestors) { + EXPECT_TRUE(matches( + "class A { class B { class C {}; }; };", + recordDecl(hasName("C"), hasAncestor(recordDecl(hasName("A")))))); +} + +TEST(HasAncenstor, FailsIfNoAncestorMatches) { + EXPECT_TRUE(notMatches( + "class A { class B { class C {}; }; };", + recordDecl(hasName("C"), hasAncestor(recordDecl(hasName("X")))))); +} + +TEST(HasAncestor, MatchesDeclarationsThatGetVisitedLater) { + EXPECT_TRUE(matches( + "class A { class B { void f() { C c; } class C {}; }; };", + varDecl(hasName("c"), hasType(recordDecl(hasName("C"), + hasAncestor(recordDecl(hasName("A")))))))); +} + +TEST(HasAncenstor, MatchesStatementAncestors) { + EXPECT_TRUE(matches( + "void f() { if (true) { while (false) { 42; } } }", + integerLiteral(equals(42), hasAncestor(ifStmt())))); +} + +TEST(HasAncestor, DrillsThroughDifferentHierarchies) { + EXPECT_TRUE(matches( + "void f() { if (true) { int x = 42; } }", + integerLiteral(equals(42), hasAncestor(functionDecl(hasName("f")))))); +} + +TEST(HasAncestor, BindsRecursiveCombinations) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { class D { class E { class F { int y; }; }; }; };", + fieldDecl(hasAncestor(recordDecl(hasAncestor(recordDecl().bind("r"))))), + new VerifyIdIsBoundTo<CXXRecordDecl>("r", 1))); +} + +TEST(HasAncestor, BindsCombinationsWithHasDescendant) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { class D { class E { class F { int y; }; }; }; };", + fieldDecl(hasAncestor( + decl( + hasDescendant(recordDecl(isDefinition(), + hasAncestor(recordDecl()))) + ).bind("d") + )), + new VerifyIdIsBoundTo<CXXRecordDecl>("d", "E"))); +} + +TEST(HasAncestor, MatchesInTemplateInstantiations) { + EXPECT_TRUE(matches( + "template <typename T> struct A { struct B { struct C { T t; }; }; }; " + "A<int>::B::C a;", + fieldDecl(hasType(asString("int")), + hasAncestor(recordDecl(hasName("A")))))); +} + +TEST(HasAncestor, MatchesInImplicitCode) { + EXPECT_TRUE(matches( + "struct X {}; struct A { A() {} X x; };", + constructorDecl( + hasAnyConstructorInitializer(withInitializer(expr( + hasAncestor(recordDecl(hasName("A"))))))))); +} + +TEST(HasParent, MatchesOnlyParent) { + EXPECT_TRUE(matches( + "void f() { if (true) { int x = 42; } }", + compoundStmt(hasParent(ifStmt())))); + EXPECT_TRUE(notMatches( + "void f() { for (;;) { int x = 42; } }", + compoundStmt(hasParent(ifStmt())))); + EXPECT_TRUE(notMatches( + "void f() { if (true) for (;;) { int x = 42; } }", + compoundStmt(hasParent(ifStmt())))); +} + +TEST(TypeMatching, MatchesTypes) { + EXPECT_TRUE(matches("struct S {};", qualType().bind("loc"))); +} + +TEST(TypeMatching, MatchesArrayTypes) { + EXPECT_TRUE(matches("int a[] = {2,3};", arrayType())); + EXPECT_TRUE(matches("int a[42];", arrayType())); + EXPECT_TRUE(matches("void f(int b) { int a[b]; }", arrayType())); + + EXPECT_TRUE(notMatches("struct A {}; A a[7];", + arrayType(hasElementType(builtinType())))); + + EXPECT_TRUE(matches( + "int const a[] = { 2, 3 };", + qualType(arrayType(hasElementType(builtinType()))))); + EXPECT_TRUE(matches( + "int const a[] = { 2, 3 };", + qualType(isConstQualified(), arrayType(hasElementType(builtinType()))))); + EXPECT_TRUE(matches( + "typedef const int T; T x[] = { 1, 2 };", + qualType(isConstQualified(), arrayType()))); + + EXPECT_TRUE(notMatches( + "int a[] = { 2, 3 };", + qualType(isConstQualified(), arrayType(hasElementType(builtinType()))))); + EXPECT_TRUE(notMatches( + "int a[] = { 2, 3 };", + qualType(arrayType(hasElementType(isConstQualified(), builtinType()))))); + EXPECT_TRUE(notMatches( + "int const a[] = { 2, 3 };", + qualType(arrayType(hasElementType(builtinType())), + unless(isConstQualified())))); + + EXPECT_TRUE(matches("int a[2];", + constantArrayType(hasElementType(builtinType())))); + EXPECT_TRUE(matches("const int a = 0;", qualType(isInteger()))); +} + +TEST(TypeMatching, MatchesComplexTypes) { + EXPECT_TRUE(matches("_Complex float f;", complexType())); + EXPECT_TRUE(matches( + "_Complex float f;", + complexType(hasElementType(builtinType())))); + EXPECT_TRUE(notMatches( + "_Complex float f;", + complexType(hasElementType(isInteger())))); +} + +TEST(TypeMatching, MatchesConstantArrayTypes) { + EXPECT_TRUE(matches("int a[2];", constantArrayType())); + EXPECT_TRUE(notMatches( + "void f() { int a[] = { 2, 3 }; int b[a[0]]; }", + constantArrayType(hasElementType(builtinType())))); + + EXPECT_TRUE(matches("int a[42];", constantArrayType(hasSize(42)))); + EXPECT_TRUE(matches("int b[2*21];", constantArrayType(hasSize(42)))); + EXPECT_TRUE(notMatches("int c[41], d[43];", constantArrayType(hasSize(42)))); +} + +TEST(TypeMatching, MatchesDependentSizedArrayTypes) { + EXPECT_TRUE(matches( + "template <typename T, int Size> class array { T data[Size]; };", + dependentSizedArrayType())); + EXPECT_TRUE(notMatches( + "int a[42]; int b[] = { 2, 3 }; void f() { int c[b[0]]; }", + dependentSizedArrayType())); +} + +TEST(TypeMatching, MatchesIncompleteArrayType) { + EXPECT_TRUE(matches("int a[] = { 2, 3 };", incompleteArrayType())); + EXPECT_TRUE(matches("void f(int a[]) {}", incompleteArrayType())); + + EXPECT_TRUE(notMatches("int a[42]; void f() { int b[a[0]]; }", + incompleteArrayType())); +} + +TEST(TypeMatching, MatchesVariableArrayType) { + EXPECT_TRUE(matches("void f(int b) { int a[b]; }", variableArrayType())); + EXPECT_TRUE(notMatches("int a[] = {2, 3}; int b[42];", variableArrayType())); + + EXPECT_TRUE(matches( + "void f(int b) { int a[b]; }", + variableArrayType(hasSizeExpr(ignoringImpCasts(declRefExpr(to( + varDecl(hasName("b"))))))))); +} + +TEST(TypeMatching, MatchesAtomicTypes) { + EXPECT_TRUE(matches("_Atomic(int) i;", atomicType())); + + EXPECT_TRUE(matches("_Atomic(int) i;", + atomicType(hasValueType(isInteger())))); + EXPECT_TRUE(notMatches("_Atomic(float) f;", + atomicType(hasValueType(isInteger())))); +} + +TEST(TypeMatching, MatchesAutoTypes) { + EXPECT_TRUE(matches("auto i = 2;", autoType())); + EXPECT_TRUE(matches("int v[] = { 2, 3 }; void f() { for (int i : v) {} }", + autoType())); + + EXPECT_TRUE(matches("auto a = 1;", + autoType(hasDeducedType(isInteger())))); + EXPECT_TRUE(notMatches("auto b = 2.0;", + autoType(hasDeducedType(isInteger())))); +} + +TEST(TypeMatching, MatchesFunctionTypes) { + EXPECT_TRUE(matches("int (*f)(int);", functionType())); + EXPECT_TRUE(matches("void f(int i) {}", functionType())); +} + +TEST(TypeMatching, PointerTypes) { + // FIXME: Reactive when these tests can be more specific (not matching + // implicit code on certain platforms), likely when we have hasDescendant for + // Types/TypeLocs. + //EXPECT_TRUE(matchAndVerifyResultTrue( + // "int* a;", + // pointerTypeLoc(pointeeLoc(typeLoc().bind("loc"))), + // new VerifyIdIsBoundTo<TypeLoc>("loc", 1))); + //EXPECT_TRUE(matchAndVerifyResultTrue( + // "int* a;", + // pointerTypeLoc().bind("loc"), + // new VerifyIdIsBoundTo<TypeLoc>("loc", 1))); + EXPECT_TRUE(matches( + "int** a;", + pointerTypeLoc(pointeeLoc(loc(qualType()))))); + EXPECT_TRUE(matches( + "int** a;", + loc(pointerType(pointee(pointerType()))))); + EXPECT_TRUE(matches( + "int* b; int* * const a = &b;", + loc(qualType(isConstQualified(), pointerType())))); + + std::string Fragment = "struct A { int i; }; int A::* ptr = &A::i;"; + EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), + hasType(blockPointerType())))); + EXPECT_TRUE(matches(Fragment, varDecl(hasName("ptr"), + hasType(memberPointerType())))); + EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), + hasType(pointerType())))); + EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), + hasType(referenceType())))); + + Fragment = "int *ptr;"; + EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), + hasType(blockPointerType())))); + EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), + hasType(memberPointerType())))); + EXPECT_TRUE(matches(Fragment, varDecl(hasName("ptr"), + hasType(pointerType())))); + EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ptr"), + hasType(referenceType())))); + + Fragment = "int a; int &ref = a;"; + EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), + hasType(blockPointerType())))); + EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), + hasType(memberPointerType())))); + EXPECT_TRUE(notMatches(Fragment, varDecl(hasName("ref"), + hasType(pointerType())))); + EXPECT_TRUE(matches(Fragment, varDecl(hasName("ref"), + hasType(referenceType())))); +} + +TEST(TypeMatching, PointeeTypes) { + EXPECT_TRUE(matches("int b; int &a = b;", + referenceType(pointee(builtinType())))); + EXPECT_TRUE(matches("int *a;", pointerType(pointee(builtinType())))); + + EXPECT_TRUE(matches("int *a;", + pointerTypeLoc(pointeeLoc(loc(builtinType()))))); + + EXPECT_TRUE(matches( + "int const *A;", + pointerType(pointee(isConstQualified(), builtinType())))); + EXPECT_TRUE(notMatches( + "int *A;", + pointerType(pointee(isConstQualified(), builtinType())))); +} + +TEST(TypeMatching, MatchesPointersToConstTypes) { + EXPECT_TRUE(matches("int b; int * const a = &b;", + loc(pointerType()))); + EXPECT_TRUE(matches("int b; int * const a = &b;", + pointerTypeLoc())); + EXPECT_TRUE(matches( + "int b; const int * a = &b;", + pointerTypeLoc(pointeeLoc(builtinTypeLoc())))); + EXPECT_TRUE(matches( + "int b; const int * a = &b;", + pointerType(pointee(builtinType())))); +} + +TEST(TypeMatching, MatchesTypedefTypes) { + EXPECT_TRUE(matches("typedef int X; X a;", varDecl(hasName("a"), + hasType(typedefType())))); + + EXPECT_TRUE(matches("typedef int X; X a;", + varDecl(hasName("a"), + hasType(typedefType(hasDecl(decl())))))); +} + +TEST(NNS, MatchesNestedNameSpecifiers) { + EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;", + nestedNameSpecifier())); + EXPECT_TRUE(matches("template <typename T> class A { typename T::B b; };", + nestedNameSpecifier())); + EXPECT_TRUE(matches("struct A { void f(); }; void A::f() {}", + nestedNameSpecifier())); + + EXPECT_TRUE(matches( + "struct A { static void f() {} }; void g() { A::f(); }", + nestedNameSpecifier())); + EXPECT_TRUE(notMatches( + "struct A { static void f() {} }; void g(A* a) { a->f(); }", + nestedNameSpecifier())); +} + +TEST(NullStatement, SimpleCases) { + EXPECT_TRUE(matches("void f() {int i;;}", nullStmt())); + EXPECT_TRUE(notMatches("void f() {int i;}", nullStmt())); +} + +TEST(NNS, MatchesTypes) { + NestedNameSpecifierMatcher Matcher = nestedNameSpecifier( + specifiesType(hasDeclaration(recordDecl(hasName("A"))))); + EXPECT_TRUE(matches("struct A { struct B {}; }; A::B b;", Matcher)); + EXPECT_TRUE(matches("struct A { struct B { struct C {}; }; }; A::B::C c;", + Matcher)); + EXPECT_TRUE(notMatches("namespace A { struct B {}; } A::B b;", Matcher)); +} + +TEST(NNS, MatchesNamespaceDecls) { + NestedNameSpecifierMatcher Matcher = nestedNameSpecifier( + specifiesNamespace(hasName("ns"))); + EXPECT_TRUE(matches("namespace ns { struct A {}; } ns::A a;", Matcher)); + EXPECT_TRUE(notMatches("namespace xx { struct A {}; } xx::A a;", Matcher)); + EXPECT_TRUE(notMatches("struct ns { struct A {}; }; ns::A a;", Matcher)); +} + +TEST(NNS, BindsNestedNameSpecifiers) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "namespace ns { struct E { struct B {}; }; } ns::E::B b;", + nestedNameSpecifier(specifiesType(asString("struct ns::E"))).bind("nns"), + new VerifyIdIsBoundTo<NestedNameSpecifier>("nns", "ns::struct E::"))); +} + +TEST(NNS, BindsNestedNameSpecifierLocs) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "namespace ns { struct B {}; } ns::B b;", + loc(nestedNameSpecifier()).bind("loc"), + new VerifyIdIsBoundTo<NestedNameSpecifierLoc>("loc", 1))); +} + +TEST(NNS, MatchesNestedNameSpecifierPrefixes) { + EXPECT_TRUE(matches( + "struct A { struct B { struct C {}; }; }; A::B::C c;", + nestedNameSpecifier(hasPrefix(specifiesType(asString("struct A")))))); + EXPECT_TRUE(matches( + "struct A { struct B { struct C {}; }; }; A::B::C c;", + nestedNameSpecifierLoc(hasPrefix( + specifiesTypeLoc(loc(qualType(asString("struct A")))))))); +} + +TEST(NNS, DescendantsOfNestedNameSpecifiers) { + std::string Fragment = + "namespace a { struct A { struct B { struct C {}; }; }; };" + "void f() { a::A::B::C c; }"; + EXPECT_TRUE(matches( + Fragment, + nestedNameSpecifier(specifiesType(asString("struct a::A::B")), + hasDescendant(nestedNameSpecifier( + specifiesNamespace(hasName("a"))))))); + EXPECT_TRUE(notMatches( + Fragment, + nestedNameSpecifier(specifiesType(asString("struct a::A::B")), + has(nestedNameSpecifier( + specifiesNamespace(hasName("a"))))))); + EXPECT_TRUE(matches( + Fragment, + nestedNameSpecifier(specifiesType(asString("struct a::A")), + has(nestedNameSpecifier( + specifiesNamespace(hasName("a"))))))); + + // Not really useful because a NestedNameSpecifier can af at most one child, + // but to complete the interface. + EXPECT_TRUE(matchAndVerifyResultTrue( + Fragment, + nestedNameSpecifier(specifiesType(asString("struct a::A::B")), + forEach(nestedNameSpecifier().bind("x"))), + new VerifyIdIsBoundTo<NestedNameSpecifier>("x", 1))); +} + +TEST(NNS, NestedNameSpecifiersAsDescendants) { + std::string Fragment = + "namespace a { struct A { struct B { struct C {}; }; }; };" + "void f() { a::A::B::C c; }"; + EXPECT_TRUE(matches( + Fragment, + decl(hasDescendant(nestedNameSpecifier(specifiesType( + asString("struct a::A"))))))); + EXPECT_TRUE(matchAndVerifyResultTrue( + Fragment, + functionDecl(hasName("f"), + forEachDescendant(nestedNameSpecifier().bind("x"))), + // Nested names: a, a::A and a::A::B. + new VerifyIdIsBoundTo<NestedNameSpecifier>("x", 3))); +} + +TEST(NNSLoc, DescendantsOfNestedNameSpecifierLocs) { + std::string Fragment = + "namespace a { struct A { struct B { struct C {}; }; }; };" + "void f() { a::A::B::C c; }"; + EXPECT_TRUE(matches( + Fragment, + nestedNameSpecifierLoc(loc(specifiesType(asString("struct a::A::B"))), + hasDescendant(loc(nestedNameSpecifier( + specifiesNamespace(hasName("a")))))))); + EXPECT_TRUE(notMatches( + Fragment, + nestedNameSpecifierLoc(loc(specifiesType(asString("struct a::A::B"))), + has(loc(nestedNameSpecifier( + specifiesNamespace(hasName("a")))))))); + EXPECT_TRUE(matches( + Fragment, + nestedNameSpecifierLoc(loc(specifiesType(asString("struct a::A"))), + has(loc(nestedNameSpecifier( + specifiesNamespace(hasName("a")))))))); + + EXPECT_TRUE(matchAndVerifyResultTrue( + Fragment, + nestedNameSpecifierLoc(loc(specifiesType(asString("struct a::A::B"))), + forEach(nestedNameSpecifierLoc().bind("x"))), + new VerifyIdIsBoundTo<NestedNameSpecifierLoc>("x", 1))); +} + +TEST(NNSLoc, NestedNameSpecifierLocsAsDescendants) { + std::string Fragment = + "namespace a { struct A { struct B { struct C {}; }; }; };" + "void f() { a::A::B::C c; }"; + EXPECT_TRUE(matches( + Fragment, + decl(hasDescendant(loc(nestedNameSpecifier(specifiesType( + asString("struct a::A")))))))); + EXPECT_TRUE(matchAndVerifyResultTrue( + Fragment, + functionDecl(hasName("f"), + forEachDescendant(nestedNameSpecifierLoc().bind("x"))), + // Nested names: a, a::A and a::A::B. + new VerifyIdIsBoundTo<NestedNameSpecifierLoc>("x", 3))); +} + +template <typename T> +class VerifyRecursiveMatch : public BoundNodesCallback { +public: + explicit VerifyRecursiveMatch(StringRef Id, + const internal::Matcher<T> &InnerMatcher) + : Id(Id), InnerMatcher(InnerMatcher) {} + + virtual bool run(const BoundNodes *Nodes) { + return false; + } + + virtual bool run(const BoundNodes *Nodes, ASTContext *Context) { + const T *Node = Nodes->getNodeAs<T>(Id); + bool Found = false; + MatchFinder Finder; + Finder.addMatcher(InnerMatcher, new VerifyMatch(0, &Found)); + Finder.findAll(*Node, *Context); + return Found; + } +private: + std::string Id; + internal::Matcher<T> InnerMatcher; +}; + +TEST(MatchFinder, CanMatchDeclarationsRecursively) { + EXPECT_TRUE(matchAndVerifyResultTrue("class X { class Y {}; };", + recordDecl(hasName("::X")).bind("X"), + new VerifyRecursiveMatch<clang::Decl>("X", recordDecl(hasName("X::Y"))))); + EXPECT_TRUE(matchAndVerifyResultFalse("class X { class Y {}; };", + recordDecl(hasName("::X")).bind("X"), + new VerifyRecursiveMatch<clang::Decl>("X", recordDecl(hasName("X::Z"))))); +} + +TEST(MatchFinder, CanMatchStatementsRecursively) { + EXPECT_TRUE(matchAndVerifyResultTrue("void f() { if (1) { for (;;) { } } }", + ifStmt().bind("if"), + new VerifyRecursiveMatch<clang::Stmt>("if", forStmt()))); + EXPECT_TRUE(matchAndVerifyResultFalse("void f() { if (1) { for (;;) { } } }", + ifStmt().bind("if"), + new VerifyRecursiveMatch<clang::Stmt>("if", declStmt()))); +} + +class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback { +public: + VerifyStartOfTranslationUnit() : Called(false) {} + virtual void run(const MatchFinder::MatchResult &Result) { + EXPECT_TRUE(Called); + } + virtual void onStartOfTranslationUnit() { + Called = true; + } + bool Called; +}; + +TEST(MatchFinder, InterceptsStartOfTranslationUnit) { + MatchFinder Finder; + VerifyStartOfTranslationUnit VerifyCallback; + Finder.addMatcher(decl(), &VerifyCallback); + OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); + ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;")); + EXPECT_TRUE(VerifyCallback.Called); } } // end namespace ast_matchers diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h index 64816f5..3b23ada 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.h +++ b/unittests/ASTMatchers/ASTMatchersTest.h @@ -18,13 +18,14 @@ namespace clang { namespace ast_matchers { using clang::tooling::newFrontendActionFactory; -using clang::tooling::runToolOnCode; +using clang::tooling::runToolOnCodeWithArgs; using clang::tooling::FrontendActionFactory; class BoundNodesCallback { public: virtual ~BoundNodesCallback() {} virtual bool run(const BoundNodes *BoundNodes) = 0; + virtual bool run(const BoundNodes *BoundNodes, ASTContext *Context) = 0; }; // If 'FindResultVerifier' is not NULL, sets *Verified to the result of @@ -37,7 +38,7 @@ public: virtual void run(const MatchFinder::MatchResult &Result) { if (FindResultReviewer != NULL) { - *Verified = FindResultReviewer->run(&Result.Nodes); + *Verified |= FindResultReviewer->run(&Result.Nodes, Result.Context); } else { *Verified = true; } @@ -51,12 +52,15 @@ private: template <typename T> testing::AssertionResult matchesConditionally(const std::string &Code, const T &AMatcher, - bool ExpectMatch) { + bool ExpectMatch, + llvm::StringRef CompileArg) { bool Found = false; MatchFinder Finder; Finder.addMatcher(AMatcher, new VerifyMatch(0, &Found)); OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); - if (!runToolOnCode(Factory->create(), Code)) { + // Some tests use typeof, which is a gnu extension. + std::vector<std::string> Args(1, CompileArg); + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; } if (!Found && ExpectMatch) { @@ -71,13 +75,13 @@ testing::AssertionResult matchesConditionally(const std::string &Code, template <typename T> testing::AssertionResult matches(const std::string &Code, const T &AMatcher) { - return matchesConditionally(Code, AMatcher, true); + return matchesConditionally(Code, AMatcher, true, "-std=c++11"); } template <typename T> testing::AssertionResult notMatches(const std::string &Code, const T &AMatcher) { - return matchesConditionally(Code, AMatcher, false); + return matchesConditionally(Code, AMatcher, false, "-std=c++11"); } template <typename T> @@ -91,7 +95,9 @@ matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher, Finder.addMatcher( AMatcher, new VerifyMatch(FindResultVerifier, &VerifiedResult)); OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); - if (!runToolOnCode(Factory->create(), Code)) { + // Some tests use typeof, which is a gnu extension. + std::vector<std::string> Args(1, "-std=gnu++98"); + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; } if (!VerifiedResult && ExpectResult) { diff --git a/unittests/ASTMatchers/Makefile b/unittests/ASTMatchers/Makefile index d3e4aa37..9ca1006 100644 --- a/unittests/ASTMatchers/Makefile +++ b/unittests/ASTMatchers/Makefile @@ -13,7 +13,8 @@ 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 \ + clangRewriteCore.a clangRewriteFrontend.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/SourceManagerTest.cpp b/unittests/Basic/SourceManagerTest.cpp index de3b723..6f404b5 100644 --- a/unittests/Basic/SourceManagerTest.cpp +++ b/unittests/Basic/SourceManagerTest.cpp @@ -10,12 +10,15 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetOptions.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" @@ -32,10 +35,11 @@ protected: SourceManagerTest() : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), - Diags(DiagID, new IgnoringDiagConsumer()), - SourceMgr(Diags, FileMgr) { - TargetOpts.Triple = "x86_64-apple-darwin11.1.0"; - Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); + Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr), + TargetOpts(new TargetOptions) { + TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; + Target = TargetInfo::CreateTargetInfo(Diags, *TargetOpts); } FileSystemOptions FileMgrOpts; @@ -44,7 +48,7 @@ protected: DiagnosticsEngine Diags; SourceManager SourceMgr; LangOptions LangOpts; - TargetOptions TargetOpts; + IntrusiveRefCntPtr<TargetOptions> TargetOpts; IntrusiveRefCntPtr<TargetInfo> Target; }; @@ -64,9 +68,9 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnit) { FileID mainFileID = SourceMgr.createMainFileIDForMemBuffer(buf); VoidModuleLoader ModLoader; - HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target); - Preprocessor PP(Diags, LangOpts, - Target.getPtr(), + HeaderSearch HeaderInfo(new HeaderSearchOptions, FileMgr, Diags, LangOpts, + &*Target); + Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, Target.getPtr(), SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/ 0, /*OwnsHeaderSearch =*/false, @@ -179,9 +183,9 @@ TEST_F(SourceManagerTest, getMacroArgExpandedLocation) { SourceMgr.overrideFileContents(headerFile, headerBuf); VoidModuleLoader ModLoader; - HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target); - Preprocessor PP(Diags, LangOpts, - Target.getPtr(), + HeaderSearch HeaderInfo(new HeaderSearchOptions, FileMgr, Diags, LangOpts, + &*Target); + Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, Target.getPtr(), SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/ 0, /*OwnsHeaderSearch =*/false, @@ -276,9 +280,9 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) { SourceMgr.overrideFileContents(headerFile, headerBuf); VoidModuleLoader ModLoader; - HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target); - Preprocessor PP(Diags, LangOpts, - Target.getPtr(), + HeaderSearch HeaderInfo(new HeaderSearchOptions, FileMgr, Diags, LangOpts, + &*Target); + Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, Target.getPtr(), SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/ 0, /*OwnsHeaderSearch =*/false, diff --git a/unittests/Frontend/Makefile b/unittests/Frontend/Makefile index bfc3494..4b6f875 100644 --- a/unittests/Frontend/Makefile +++ b/unittests/Frontend/Makefile @@ -14,7 +14,8 @@ 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 \ - clangARCMigrate.a clangRewrite.a clangEdit.a \ + clangARCMigrate.a clangRewriteCore.a \ + clangRewriteFrontend.a clangEdit.a \ clangAnalysis.a clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/unittests/Lex/CMakeLists.txt b/unittests/Lex/CMakeLists.txt index 10c9361..03c8cd5 100644 --- a/unittests/Lex/CMakeLists.txt +++ b/unittests/Lex/CMakeLists.txt @@ -1,6 +1,7 @@ add_clang_unittest(LexTests LexerTest.cpp PreprocessingRecordTest.cpp + PPCallbacksTest.cpp ) target_link_libraries(LexTests diff --git a/unittests/Lex/LexerTest.cpp b/unittests/Lex/LexerTest.cpp index e43ad86..e95cd02 100644 --- a/unittests/Lex/LexerTest.cpp +++ b/unittests/Lex/LexerTest.cpp @@ -10,12 +10,15 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetOptions.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" #include "llvm/Config/config.h" #include "gtest/gtest.h" @@ -31,10 +34,12 @@ protected: LexerTest() : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), - Diags(DiagID, new IgnoringDiagConsumer()), - SourceMgr(Diags, FileMgr) { - TargetOpts.Triple = "x86_64-apple-darwin11.1.0"; - Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); + Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr), + TargetOpts(new TargetOptions) + { + TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; + Target = TargetInfo::CreateTargetInfo(Diags, *TargetOpts); } FileSystemOptions FileMgrOpts; @@ -43,7 +48,7 @@ protected: DiagnosticsEngine Diags; SourceManager SourceMgr; LangOptions LangOpts; - TargetOptions TargetOpts; + IntrusiveRefCntPtr<TargetOptions> TargetOpts; IntrusiveRefCntPtr<TargetInfo> Target; }; @@ -69,9 +74,9 @@ TEST_F(LexerTest, LexAPI) { (void)SourceMgr.createMainFileIDForMemBuffer(buf); VoidModuleLoader ModLoader; - HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, Target.getPtr()); - Preprocessor PP(Diags, LangOpts, - Target.getPtr(), + HeaderSearch HeaderInfo(new HeaderSearchOptions, FileMgr, Diags, LangOpts, + Target.getPtr()); + Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, Target.getPtr(), SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/ 0, /*OwnsHeaderSearch =*/false, diff --git a/unittests/Lex/PPCallbacksTest.cpp b/unittests/Lex/PPCallbacksTest.cpp new file mode 100644 index 0000000..6e7efa9 --- /dev/null +++ b/unittests/Lex/PPCallbacksTest.cpp @@ -0,0 +1,246 @@ +//===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks 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/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Lex/ModuleLoader.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/PathV2.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::sys; +using namespace clang; + +namespace { + +// Stub out module loading. +class VoidModuleLoader : public ModuleLoader { + virtual Module *loadModule(SourceLocation ImportLoc, ModuleIdPath Path, + Module::NameVisibilityKind Visibility, + bool IsInclusionDirective) { + return 0; + } +}; + +// Stub to collect data from InclusionDirective callbacks. +class InclusionDirectiveCallbacks : public PPCallbacks { +public: + void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) { + this->HashLoc = HashLoc; + this->IncludeTok = IncludeTok; + this->FileName = FileName.str(); + this->IsAngled = IsAngled; + this->FilenameRange = FilenameRange; + this->File = File; + this->SearchPath = SearchPath.str(); + this->RelativePath = RelativePath.str(); + this->Imported = Imported; + } + + SourceLocation HashLoc; + Token IncludeTok; + SmallString<16> FileName; + bool IsAngled; + CharSourceRange FilenameRange; + const FileEntry* File; + SmallString<16> SearchPath; + SmallString<16> RelativePath; + const Module* Imported; +}; + +// PPCallbacks test fixture. +class PPCallbacksTest : public ::testing::Test { +protected: + PPCallbacksTest() + : FileMgr(FileMgrOpts), + DiagID(new DiagnosticIDs()), + DiagOpts(new DiagnosticOptions()), + Diags(DiagID, DiagOpts.getPtr(), new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr) { + TargetOpts = new TargetOptions(); + TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; + Target = TargetInfo::CreateTargetInfo(Diags, *TargetOpts); + } + + FileSystemOptions FileMgrOpts; + FileManager FileMgr; + IntrusiveRefCntPtr<DiagnosticIDs> DiagID; + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; + DiagnosticsEngine Diags; + SourceManager SourceMgr; + LangOptions LangOpts; + IntrusiveRefCntPtr<TargetOptions> TargetOpts; + IntrusiveRefCntPtr<TargetInfo> Target; + + // Register a header path as a known file and add its location + // to search path. + void AddFakeHeader(HeaderSearch& HeaderInfo, const char* HeaderPath, + bool IsSystemHeader) { + // Tell FileMgr about header. + FileMgr.getVirtualFile(HeaderPath, 0, 0); + + // Add header's parent path to search path. + StringRef SearchPath = path::parent_path(HeaderPath); + const DirectoryEntry *DE = FileMgr.getDirectory(SearchPath); + DirectoryLookup DL(DE, SrcMgr::C_User, true, false); + HeaderInfo.AddSearchPath(DL, IsSystemHeader); + } + + // Get the raw source string of the range. + StringRef GetSourceString(CharSourceRange Range) { + const char* B = SourceMgr.getCharacterData(Range.getBegin()); + const char* E = SourceMgr.getCharacterData(Range.getEnd()); + + return StringRef(B, E - B); + } + + // Run lexer over SourceText and collect FilenameRange from + // the InclusionDirective callback. + CharSourceRange InclusionDirectiveFilenameRange(const char* SourceText, + const char* HeaderPath, bool SystemHeader) { + MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(SourceText); + (void)SourceMgr.createMainFileIDForMemBuffer(Buf); + + VoidModuleLoader ModLoader; + + IntrusiveRefCntPtr<HeaderSearchOptions> HSOpts = new HeaderSearchOptions(); + HeaderSearch HeaderInfo(HSOpts, FileMgr, Diags, LangOpts, Target.getPtr()); + AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); + + IntrusiveRefCntPtr<PreprocessorOptions> PPOpts = new PreprocessorOptions(); + Preprocessor PP(PPOpts, Diags, LangOpts, + Target.getPtr(), + SourceMgr, HeaderInfo, ModLoader, + /*IILookup =*/ 0, + /*OwnsHeaderSearch =*/false, + /*DelayInitialization =*/ false); + InclusionDirectiveCallbacks* Callbacks = new InclusionDirectiveCallbacks; + PP.addPPCallbacks(Callbacks); // Takes ownership. + + // Lex source text. + PP.EnterMainSourceFile(); + + while (true) { + Token Tok; + PP.Lex(Tok); + if (Tok.is(tok::eof)) + break; + } + + // Callbacks have been executed at this point -- return filename range. + return Callbacks->FilenameRange; + } +}; + +TEST_F(PPCallbacksTest, QuotedFilename) { + const char* Source = + "#include \"quoted.h\"\n"; + + CharSourceRange Range = + InclusionDirectiveFilenameRange(Source, "/quoted.h", false); + + ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); +} + +TEST_F(PPCallbacksTest, AngledFilename) { + const char* Source = + "#include <angled.h>\n"; + + CharSourceRange Range = + InclusionDirectiveFilenameRange(Source, "/angled.h", true); + + ASSERT_EQ("<angled.h>", GetSourceString(Range)); +} + +TEST_F(PPCallbacksTest, QuotedInMacro) { + const char* Source = + "#define MACRO_QUOTED \"quoted.h\"\n" + "#include MACRO_QUOTED\n"; + + CharSourceRange Range = + InclusionDirectiveFilenameRange(Source, "/quoted.h", false); + + ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); +} + +TEST_F(PPCallbacksTest, AngledInMacro) { + const char* Source = + "#define MACRO_ANGLED <angled.h>\n" + "#include MACRO_ANGLED\n"; + + CharSourceRange Range = + InclusionDirectiveFilenameRange(Source, "/angled.h", true); + + ASSERT_EQ("<angled.h>", GetSourceString(Range)); +} + +TEST_F(PPCallbacksTest, StringizedMacroArgument) { + const char* Source = + "#define MACRO_STRINGIZED(x) #x\n" + "#include MACRO_STRINGIZED(quoted.h)\n"; + + CharSourceRange Range = + InclusionDirectiveFilenameRange(Source, "/quoted.h", false); + + ASSERT_EQ("\"quoted.h\"", GetSourceString(Range)); +} + +TEST_F(PPCallbacksTest, ConcatenatedMacroArgument) { + const char* Source = + "#define MACRO_ANGLED <angled.h>\n" + "#define MACRO_CONCAT(x, y) x ## _ ## y\n" + "#include MACRO_CONCAT(MACRO, ANGLED)\n"; + + CharSourceRange Range = + InclusionDirectiveFilenameRange(Source, "/angled.h", false); + + ASSERT_EQ("<angled.h>", GetSourceString(Range)); +} + +TEST_F(PPCallbacksTest, TrigraphFilename) { + const char* Source = + "#include \"tri\?\?-graph.h\"\n"; + + CharSourceRange Range = + InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); + + ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); +} + +TEST_F(PPCallbacksTest, TrigraphInMacro) { + const char* Source = + "#define MACRO_TRIGRAPH \"tri\?\?-graph.h\"\n" + "#include MACRO_TRIGRAPH\n"; + + CharSourceRange Range = + InclusionDirectiveFilenameRange(Source, "/tri~graph.h", false); + + ASSERT_EQ("\"tri\?\?-graph.h\"", GetSourceString(Range)); +} + +} // anonoymous namespace diff --git a/unittests/Lex/PreprocessingRecordTest.cpp b/unittests/Lex/PreprocessingRecordTest.cpp index 5b5d933..815081a 100644 --- a/unittests/Lex/PreprocessingRecordTest.cpp +++ b/unittests/Lex/PreprocessingRecordTest.cpp @@ -10,12 +10,15 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetOptions.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" #include "clang/Lex/PreprocessingRecord.h" #include "llvm/Config/config.h" @@ -32,10 +35,12 @@ protected: PreprocessingRecordTest() : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), - Diags(DiagID, new IgnoringDiagConsumer()), - SourceMgr(Diags, FileMgr) { - TargetOpts.Triple = "x86_64-apple-darwin11.1.0"; - Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); + Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr), + TargetOpts(new TargetOptions) + { + TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; + Target = TargetInfo::CreateTargetInfo(Diags, *TargetOpts); } FileSystemOptions FileMgrOpts; @@ -44,7 +49,7 @@ protected: DiagnosticsEngine Diags; SourceManager SourceMgr; LangOptions LangOpts; - TargetOptions TargetOpts; + IntrusiveRefCntPtr<TargetOptions> TargetOpts; IntrusiveRefCntPtr<TargetInfo> Target; }; @@ -80,9 +85,9 @@ TEST_F(PreprocessingRecordTest, PPRecAPI) { SourceMgr.createMainFileIDForMemBuffer(buf); VoidModuleLoader ModLoader; - HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, Target.getPtr()); - Preprocessor PP(Diags, LangOpts, - Target.getPtr(), + HeaderSearch HeaderInfo(new HeaderSearchOptions, FileMgr, Diags, LangOpts, + Target.getPtr()); + Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts,Target.getPtr(), SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/ 0, /*OwnsHeaderSearch =*/false, diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt index 4eaf339..bd7317f 100644 --- a/unittests/Tooling/CMakeLists.txt +++ b/unittests/Tooling/CMakeLists.txt @@ -18,5 +18,5 @@ add_clang_unittest(ToolingTests target_link_libraries(ToolingTests clangAST clangTooling - clangRewrite + clangRewriteCore ) diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp index 591d48d..5ed4240 100644 --- a/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/unittests/Tooling/CompilationDatabaseTest.cpp @@ -11,8 +11,10 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclGroup.h" #include "clang/Frontend/FrontendAction.h" -#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/FileMatchTrie.h" +#include "clang/Tooling/JSONCompilationDatabase.h" #include "clang/Tooling/Tooling.h" +#include "llvm/Support/PathV2.h" #include "gtest/gtest.h" namespace clang { @@ -55,13 +57,16 @@ TEST(JSONCompilationDatabase, GetAllFiles) { getAllFiles("[]", ErrorMessage)) << ErrorMessage; std::vector<std::string> expected_files; - expected_files.push_back("file1"); - expected_files.push_back("file2"); + SmallString<16> PathStorage; + llvm::sys::path::native("//net/dir/file1", PathStorage); + expected_files.push_back(PathStorage.str()); + llvm::sys::path::native("//net/dir/file2", PathStorage); + expected_files.push_back(PathStorage.str()); EXPECT_EQ(expected_files, getAllFiles( - "[{\"directory\":\"dir\"," + "[{\"directory\":\"//net/dir\"," "\"command\":\"command\"," "\"file\":\"file1\"}," - " {\"directory\":\"dir\"," + " {\"directory\":\"//net/dir\"," "\"command\":\"command\"," "\"file\":\"file2\"}]", ErrorMessage)) << ErrorMessage; @@ -81,6 +86,82 @@ static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName, return Commands[0]; } +struct FakeComparator : public PathComparator { + virtual ~FakeComparator() {} + virtual bool equivalent(StringRef FileA, StringRef FileB) const { + return FileA.equals_lower(FileB); + } +}; + +class FileMatchTrieTest : public ::testing::Test { +protected: + FileMatchTrieTest() : Trie(new FakeComparator()) {} + + StringRef find(StringRef Path) { + llvm::raw_string_ostream ES(Error); + return Trie.findEquivalent(Path, ES); + } + + FileMatchTrie Trie; + std::string Error; +}; + +TEST_F(FileMatchTrieTest, InsertingRelativePath) { + Trie.insert("//net/path/file.cc"); + Trie.insert("file.cc"); + EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc")); +} + +TEST_F(FileMatchTrieTest, MatchingRelativePath) { + EXPECT_EQ("", find("file.cc")); +} + +TEST_F(FileMatchTrieTest, ReturnsBestResults) { + Trie.insert("//net/d/c/b.cc"); + Trie.insert("//net/d/b/b.cc"); + EXPECT_EQ("//net/d/b/b.cc", find("//net/d/b/b.cc")); +} + +TEST_F(FileMatchTrieTest, HandlesSymlinks) { + Trie.insert("//net/AA/file.cc"); + EXPECT_EQ("//net/AA/file.cc", find("//net/aa/file.cc")); +} + +TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) { + Trie.insert("//net/Aa/file.cc"); + Trie.insert("//net/aA/file.cc"); + EXPECT_TRUE(find("//net/aa/file.cc").empty()); + EXPECT_EQ("Path is ambiguous", Error); +} + +TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) { + Trie.insert("//net/src/Aa/file.cc"); + Trie.insert("//net/src/aA/file.cc"); + Trie.insert("//net/SRC/aa/file.cc"); + EXPECT_EQ("//net/SRC/aa/file.cc", find("//net/src/aa/file.cc")); +} + +TEST_F(FileMatchTrieTest, EmptyTrie) { + EXPECT_TRUE(find("//net/some/path").empty()); +} + +TEST_F(FileMatchTrieTest, NoResult) { + Trie.insert("//net/somepath/otherfile.cc"); + Trie.insert("//net/otherpath/somefile.cc"); + EXPECT_EQ("", find("//net/somepath/somefile.cc")); +} + +TEST_F(FileMatchTrieTest, RootElementDifferent) { + Trie.insert("//net/path/file.cc"); + Trie.insert("//net/otherpath/file.cc"); + EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc")); +} + +TEST_F(FileMatchTrieTest, CannotResolveRelativePath) { + EXPECT_EQ("", find("relative-path.cc")); + EXPECT_EQ("Cannot resolve relative paths", Error); +} + TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) { std::string ErrorMessage; CompileCommand NotFound = findCompileArgsInJsonDatabase( @@ -90,9 +171,9 @@ TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) { } TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) { - StringRef Directory("/some/directory"); - StringRef FileName("/path/to/a-file.cpp"); - StringRef Command("/path/to/compiler and some arguments"); + StringRef Directory("//net/some/directory"); + StringRef FileName("//net/path/to/a-file.cpp"); + StringRef Command("//net/path/to/compiler and some arguments"); std::string ErrorMessage; CompileCommand FoundCommand = findCompileArgsInJsonDatabase( FileName, @@ -102,7 +183,8 @@ TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) { ErrorMessage); EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage; - EXPECT_EQ("/path/to/compiler", FoundCommand.CommandLine[0]) << ErrorMessage; + EXPECT_EQ("//net/path/to/compiler", + FoundCommand.CommandLine[0]) << ErrorMessage; EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage; EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage; EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage; @@ -118,9 +200,9 @@ TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) { } TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) { - StringRef Directory("/some/directory"); - StringRef FileName("/path/to/a-file.cpp"); - StringRef Command("\\\"/path to compiler\\\" \\\"and an argument\\\""); + StringRef Directory("//net/some/directory"); + StringRef FileName("//net/path/to/a-file.cpp"); + StringRef Command("\\\"//net/path to compiler\\\" \\\"and an argument\\\""); std::string ErrorMessage; CompileCommand FoundCommand = findCompileArgsInJsonDatabase( FileName, @@ -129,13 +211,14 @@ TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) { "\"file\":\"" + FileName + "\"}]").str(), ErrorMessage); ASSERT_EQ(2u, FoundCommand.CommandLine.size()); - EXPECT_EQ("/path to compiler", FoundCommand.CommandLine[0]) << ErrorMessage; + EXPECT_EQ("//net/path to compiler", + FoundCommand.CommandLine[0]) << ErrorMessage; EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage; } TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) { - StringRef Directory("/some directory / with spaces"); - StringRef FileName("/path/to/a-file.cpp"); + StringRef Directory("//net/some directory / with spaces"); + StringRef FileName("//net/path/to/a-file.cpp"); StringRef Command("a command"); std::string ErrorMessage; CompileCommand FoundCommand = findCompileArgsInJsonDatabase( @@ -148,7 +231,7 @@ TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) { } TEST(findCompileArgsInJsonDatabase, FindsEntry) { - StringRef Directory("directory"); + StringRef Directory("//net/directory"); StringRef FileName("file"); StringRef Command("command"); std::string JsonDatabase = "["; @@ -162,19 +245,19 @@ TEST(findCompileArgsInJsonDatabase, FindsEntry) { JsonDatabase += "]"; std::string ErrorMessage; CompileCommand FoundCommand = findCompileArgsInJsonDatabase( - "file4", JsonDatabase, ErrorMessage); - EXPECT_EQ("directory4", FoundCommand.Directory) << ErrorMessage; + "//net/directory4/file4", JsonDatabase, ErrorMessage); + EXPECT_EQ("//net/directory4", FoundCommand.Directory) << ErrorMessage; ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage; EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage; } static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) { std::string JsonDatabase = - ("[{\"directory\":\"\", \"file\":\"test\", \"command\": \"" + + ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" + Command + "\"}]").str(); std::string ErrorMessage; CompileCommand FoundCommand = findCompileArgsInJsonDatabase( - "test", JsonDatabase, ErrorMessage); + "//net/root/test", JsonDatabase, ErrorMessage); EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage; return FoundCommand.CommandLine; } diff --git a/unittests/Tooling/Makefile b/unittests/Tooling/Makefile index 5d2224d..5ed99fc 100644 --- a/unittests/Tooling/Makefile +++ b/unittests/Tooling/Makefile @@ -12,7 +12,8 @@ TESTNAME = Tooling include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser support mc USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ - clangParse.a clangRewrite.a clangSema.a clangAnalysis.a clangEdit.a \ + clangParse.a clangRewriteCore.a clangRewriteFrontend.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 index 4b53906..a68a869 100644 --- a/unittests/Tooling/RecursiveASTVisitorTest.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp @@ -385,6 +385,66 @@ TEST(RecursiveASTVisitor, VisitsImplicitCopyConstructors) { "int main() { Simple s; Simple t(s); }\n")); } +/// \brief A visitor that optionally includes implicit code and matches +/// CXXConstructExpr. +/// +/// The name recorded for the match is the name of the class whose constructor +/// is invoked by the CXXConstructExpr, not the name of the class whose +/// constructor the CXXConstructExpr is contained in. +class ConstructExprVisitor + : public ExpectedLocationVisitor<ConstructExprVisitor> { +public: + ConstructExprVisitor() : ShouldVisitImplicitCode(false) {} + + bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; } + + void setShouldVisitImplicitCode(bool NewValue) { + ShouldVisitImplicitCode = NewValue; + } + + bool VisitCXXConstructExpr(CXXConstructExpr* Expr) { + if (const CXXConstructorDecl* Ctor = Expr->getConstructor()) { + if (const CXXRecordDecl* Class = Ctor->getParent()) { + Match(Class->getName(), Expr->getLocation()); + } + } + return true; + } + + private: + bool ShouldVisitImplicitCode; +}; + +TEST(RecursiveASTVisitor, CanVisitImplicitMemberInitializations) { + ConstructExprVisitor Visitor; + Visitor.setShouldVisitImplicitCode(true); + Visitor.ExpectMatch("WithCtor", 2, 8); + // Simple has a constructor that implicitly initializes 'w'. Test + // that a visitor that visits implicit code visits that initialization. + // 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 { WithCtor w; }; \n" + "int main() { Simple s; }\n")); +} + +// The same as CanVisitImplicitMemberInitializations, but checking that the +// visits are omitted when the visitor does not include implicit code. +TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) { + ConstructExprVisitor Visitor; + Visitor.setShouldVisitImplicitCode(false); + Visitor.DisallowMatch("WithCtor", 2, 8); + // Simple has a constructor that implicitly initializes 'w'. Test + // that a visitor that skips implicit code skips that initialization. + // 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 { WithCtor w; }; \n" + "int main() { Simple s; }\n")); +} + TEST(RecursiveASTVisitor, VisitsExtension) { DeclRefExprVisitor Visitor; Visitor.ExpectMatch("s", 1, 24); @@ -392,4 +452,12 @@ TEST(RecursiveASTVisitor, VisitsExtension) { "int s = __extension__ (s);\n")); } +TEST(RecursiveASTVisitor, VisitsCompoundLiteralType) { + TypeLocVisitor Visitor; + Visitor.ExpectMatch("struct S", 1, 26); + EXPECT_TRUE(Visitor.runOver( + "int f() { return (struct S { int a; }){.a = 0}.a; }", + TypeLocVisitor::Lang_C)); +} + } // end namespace clang diff --git a/unittests/Tooling/RefactoringCallbacksTest.cpp b/unittests/Tooling/RefactoringCallbacksTest.cpp index 00eb193..4e30cfd 100644 --- a/unittests/Tooling/RefactoringCallbacksTest.cpp +++ b/unittests/Tooling/RefactoringCallbacksTest.cpp @@ -10,8 +10,8 @@ #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Tooling/RefactoringCallbacks.h" -#include "gtest/gtest.h" #include "RewriterTestContext.h" +#include "gtest/gtest.h" namespace clang { namespace tooling { @@ -40,28 +40,28 @@ 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); + expectRewritten(Code, Expected, id("id", declStmt()), 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); + expectRewritten(Code, Expected, id("id", declStmt()), 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); + expectRewritten(Code, Expected, id("id", declStmt()), 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())), + expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback); } @@ -72,7 +72,7 @@ TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) { expectRewritten(Code, Expected, id("always-false", conditionalOperator( hasCondition(boolLiteral(equals(false))), - hasFalseExpression(id("should-be", expression())))), + hasFalseExpression(id("should-be", expr())))), Callback); } @@ -82,8 +82,8 @@ TEST(RefactoringCallbacksTest, ReplacesIfStmt) { ReplaceIfStmtWithItsBody Callback("id", true); expectRewritten(Code, Expected, id("id", ifStmt( - hasCondition(implicitCast(hasSourceExpression( - declarationReference(to(variable(hasName("a"))))))))), + hasCondition(implicitCastExpr(hasSourceExpression( + declRefExpr(to(varDecl(hasName("a"))))))))), Callback); } diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp index 8d96955..ff278bf 100644 --- a/unittests/Tooling/RefactoringTest.cpp +++ b/unittests/Tooling/RefactoringTest.cpp @@ -15,14 +15,14 @@ #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Tooling/Refactoring.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.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/Rewrite/Core/Rewriter.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Path.h" diff --git a/unittests/Tooling/RewriterTestContext.h b/unittests/Tooling/RewriterTestContext.h index f68be6b..d790ac1 100644 --- a/unittests/Tooling/RewriterTestContext.h +++ b/unittests/Tooling/RewriterTestContext.h @@ -15,12 +15,12 @@ #define LLVM_CLANG_REWRITER_TEST_CONTEXT_H #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.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 "clang/Rewrite/Core/Rewriter.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -35,8 +35,10 @@ namespace clang { class RewriterTestContext { public: RewriterTestContext() - : Diagnostics(llvm::IntrusiveRefCntPtr<DiagnosticIDs>()), - DiagnosticPrinter(llvm::outs(), DiagnosticOptions()), + : DiagOpts(new DiagnosticOptions()), + Diagnostics(llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), + &*DiagOpts), + DiagnosticPrinter(llvm::outs(), &*DiagOpts), Files((FileSystemOptions())), Sources(Diagnostics, Files), Rewrite(Sources, Options) { @@ -109,6 +111,7 @@ class RewriterTestContext { return Files.getBufferForFile(Path, NULL)->getBuffer(); } + llvm::IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; DiagnosticsEngine Diagnostics; TextDiagnosticPrinter DiagnosticPrinter; FileManager Files; diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h index d439d81..8333c24 100644 --- a/unittests/Tooling/TestVisitor.h +++ b/unittests/Tooling/TestVisitor.h @@ -6,14 +6,17 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// This file defines a utility class for RecursiveASTVisitor related tests. -// +/// +/// \file +/// \brief Defines utility templates for RecursiveASTVisitor related tests. +/// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TEST_VISITOR_H #define LLVM_CLANG_TEST_VISITOR_H +#include <vector> + #include "clang/AST/ASTContext.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/RecursiveASTVisitor.h" @@ -29,7 +32,7 @@ namespace clang { /// 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. +/// Visits template instantiations (but not implicit code) by default. template <typename T> class TestVisitor : public RecursiveASTVisitor<T> { public: @@ -37,9 +40,16 @@ public: virtual ~TestVisitor() { } + enum Language { Lang_C, Lang_CXX }; + /// \brief Runs the current AST visitor over the given code. - bool runOver(StringRef Code) { - return tooling::runToolOnCode(CreateTestAction(), Code); + bool runOver(StringRef Code, Language L = Lang_CXX) { + std::vector<std::string> Args; + switch (L) { + case Lang_C: Args.push_back("-std=c99"); break; + case Lang_CXX: Args.push_back("-std=c++98"); break; + } + return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args); } bool shouldVisitTemplateInstantiations() const { @@ -81,63 +91,126 @@ protected: ASTContext *Context; }; - -/// \brief A RecursiveASTVisitor for testing the RecursiveASTVisitor itself. +/// \brief A RecursiveASTVisitor to check that certain matches are (or are +/// not) observed during visitation. /// -/// Allows simple creation of test visitors running matches on only a small +/// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself, +/// and 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' *not* to occur at the given 'Line' and 'Column'. + /// + /// Any number of matches can be disallowed. + void DisallowMatch(Twine Match, unsigned Line, unsigned Column) { + DisallowedMatches.push_back(MatchCandidate(Match, Line, Column)); } /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'. + /// + /// Any number of expected matches can be set by calling this repeatedly. + /// Each is expected to be matched exactly once. void ExpectMatch(Twine Match, unsigned Line, unsigned Column) { - ExpectedMatch = Match.str(); - ExpectedLine = Line; - ExpectedColumn = Column; + ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column)); + } + + /// \brief Checks that all expected matches have been found. + virtual ~ExpectedLocationVisitor() { + for (typename std::vector<ExpectedMatch>::const_iterator + It = ExpectedMatches.begin(), End = ExpectedMatches.end(); + It != End; ++It) { + It->ExpectFound(); + } } 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. + /// \brief Checks an actual match against expected and disallowed matches. /// /// 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()); + const FullSourceLoc FullLocation = this->Context->getFullLoc(Location); + + for (typename std::vector<MatchCandidate>::const_iterator + It = DisallowedMatches.begin(), End = DisallowedMatches.end(); + It != End; ++It) { + EXPECT_FALSE(It->Matches(Name, FullLocation)) + << "Matched disallowed " << *It; + } + + for (typename std::vector<ExpectedMatch>::iterator + It = ExpectedMatches.begin(), End = ExpectedMatches.end(); + It != End; ++It) { + It->UpdateFor(Name, FullLocation, this->Context->getSourceManager()); } } - std::string ExpectedMatch; - unsigned ExpectedLine; - unsigned ExpectedColumn; - std::string PartialMatches; - bool Found; + private: + struct MatchCandidate { + std::string ExpectedName; + unsigned LineNumber; + unsigned ColumnNumber; + + MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber) + : ExpectedName(Name.str()), LineNumber(LineNumber), + ColumnNumber(ColumnNumber) { + } + + bool Matches(StringRef Name, FullSourceLoc const &Location) const { + return MatchesName(Name) && MatchesLocation(Location); + } + + bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const { + return MatchesName(Name) || MatchesLocation(Location); + } + + bool MatchesName(StringRef Name) const { + return Name == ExpectedName; + } + + bool MatchesLocation(FullSourceLoc const &Location) const { + return Location.isValid() && + Location.getSpellingLineNumber() == LineNumber && + Location.getSpellingColumnNumber() == ColumnNumber; + } + + friend std::ostream &operator<<(std::ostream &Stream, + MatchCandidate const &Match) { + return Stream << Match.ExpectedName + << " at " << Match.LineNumber << ":" << Match.ColumnNumber; + } + }; + + struct ExpectedMatch { + ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber) + : Candidate(Name, LineNumber, ColumnNumber), Found(false) {} + + void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) { + if (Candidate.Matches(Name, Location)) { + EXPECT_TRUE(!Found); + Found = true; + } else if (!Found && Candidate.PartiallyMatches(Name, Location)) { + llvm::raw_string_ostream Stream(PartialMatches); + Stream << ", partial match: \"" << Name << "\" at "; + Location.print(Stream, SM); + } + } + + void ExpectFound() const { + EXPECT_TRUE(Found) + << "Expected \"" << Candidate.ExpectedName + << "\" at " << Candidate.LineNumber + << ":" << Candidate.ColumnNumber << PartialMatches; + } + + MatchCandidate Candidate; + std::string PartialMatches; + bool Found; + }; + + std::vector<MatchCandidate> DisallowedMatches; + std::vector<ExpectedMatch> ExpectedMatches; }; } diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp index fb3af26..d40c613 100644 --- a/unittests/Tooling/ToolingTest.cpp +++ b/unittests/Tooling/ToolingTest.cpp @@ -130,5 +130,37 @@ TEST(ToolInvocation, TestMapVirtualFile) { EXPECT_TRUE(Invocation.run()); } +struct VerifyEndCallback : public EndOfSourceFileCallback { + VerifyEndCallback() : Called(0), Matched(false) {} + virtual void run() { + ++Called; + } + ASTConsumer *newASTConsumer() { + return new FindTopLevelDeclConsumer(&Matched); + } + unsigned Called; + bool Matched; +}; + +#if !defined(_WIN32) +TEST(newFrontendActionFactory, InjectsEndOfSourceFileCallback) { + VerifyEndCallback EndCallback; + + FixedCompilationDatabase Compilations("/", std::vector<std::string>()); + std::vector<std::string> Sources; + Sources.push_back("/a.cc"); + Sources.push_back("/b.cc"); + ClangTool Tool(Compilations, Sources); + + Tool.mapVirtualFile("/a.cc", "void a() {}"); + Tool.mapVirtualFile("/b.cc", "void b() {}"); + + Tool.run(newFrontendActionFactory(&EndCallback, &EndCallback)); + + EXPECT_TRUE(EndCallback.Matched); + EXPECT_EQ(2u, EndCallback.Called); +} +#endif + } // end namespace tooling } // end namespace clang |