diff options
author | dim <dim@FreeBSD.org> | 2013-12-22 00:07:40 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2013-12-22 00:07:40 +0000 |
commit | 952eddef9aff85b1e92626e89baaf7a360e2ac85 (patch) | |
tree | df8df0b0067b381eab470a3b8f28d14a552a6340 /unittests/ASTMatchers/Dynamic | |
parent | ea266cad53e3d49771fa38103913d3ec7a166694 (diff) | |
download | FreeBSD-src-952eddef9aff85b1e92626e89baaf7a360e2ac85.zip FreeBSD-src-952eddef9aff85b1e92626e89baaf7a360e2ac85.tar.gz |
Vendor import of clang release_34 branch r197841 (effectively, 3.4 RC3):
https://llvm.org/svn/llvm-project/cfe/branches/release_34@197841
Diffstat (limited to 'unittests/ASTMatchers/Dynamic')
-rw-r--r-- | unittests/ASTMatchers/Dynamic/CMakeLists.txt | 7 | ||||
-rw-r--r-- | unittests/ASTMatchers/Dynamic/Makefile | 20 | ||||
-rw-r--r-- | unittests/ASTMatchers/Dynamic/ParserTest.cpp | 238 | ||||
-rw-r--r-- | unittests/ASTMatchers/Dynamic/RegistryTest.cpp | 347 | ||||
-rw-r--r-- | unittests/ASTMatchers/Dynamic/VariantValueTest.cpp | 144 |
5 files changed, 756 insertions, 0 deletions
diff --git a/unittests/ASTMatchers/Dynamic/CMakeLists.txt b/unittests/ASTMatchers/Dynamic/CMakeLists.txt new file mode 100644 index 0000000..eb9fa54 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/CMakeLists.txt @@ -0,0 +1,7 @@ +add_clang_unittest(DynamicASTMatchersTests + VariantValueTest.cpp + ParserTest.cpp + RegistryTest.cpp) + +target_link_libraries(DynamicASTMatchersTests + gtest gtest_main clangASTMatchers clangDynamicASTMatchers clangTooling) diff --git a/unittests/ASTMatchers/Dynamic/Makefile b/unittests/ASTMatchers/Dynamic/Makefile new file mode 100644 index 0000000..66b183c --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/Makefile @@ -0,0 +1,20 @@ +##===- unittests/ASTMatchers/Dynamic/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../../.. + +TESTNAME = DynamicASTMatchers +include $(CLANG_LEVEL)/../../Makefile.config +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option +USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ + clangRewriteCore.a clangRewriteFrontend.a clangParse.a clangSema.a \ + clangAnalysis.a clangEdit.a clangAST.a clangASTMatchers.a \ + clangLex.a clangBasic.a clangDynamicASTMatchers.a + +include $(CLANG_LEVEL)/unittests/Makefile diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp new file mode 100644 index 0000000..f19ec51 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -0,0 +1,238 @@ +//===- unittest/ASTMatchers/Dynamic/ParserTest.cpp - Parser unit tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-------------------------------------------------------------------===// + +#include <string> +#include <vector> + +#include "../ASTMatchersTest.h" +#include "clang/ASTMatchers/Dynamic/Parser.h" +#include "clang/ASTMatchers/Dynamic/Registry.h" +#include "gtest/gtest.h" +#include "llvm/ADT/StringMap.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { +namespace { + +class MockSema : public Parser::Sema { +public: + virtual ~MockSema() {} + + uint64_t expectMatcher(StringRef MatcherName) { + ast_matchers::internal::Matcher<Stmt> M = stmt(); + ExpectedMatchers.insert(std::make_pair(MatcherName, M)); + return M.getID(); + } + + void parse(StringRef Code) { + Diagnostics Error; + VariantValue Value; + Parser::parseExpression(Code, this, &Value, &Error); + Values.push_back(Value); + Errors.push_back(Error.toStringFull()); + } + + VariantMatcher actOnMatcherExpression(StringRef MatcherName, + const SourceRange &NameRange, + StringRef BindID, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { + MatcherInfo ToStore = { MatcherName, NameRange, Args, BindID }; + Matchers.push_back(ToStore); + return VariantMatcher::SingleMatcher( + ExpectedMatchers.find(MatcherName)->second); + } + + struct MatcherInfo { + StringRef MatcherName; + SourceRange NameRange; + std::vector<ParserValue> Args; + std::string BoundID; + }; + + std::vector<std::string> Errors; + std::vector<VariantValue> Values; + std::vector<MatcherInfo> Matchers; + std::map<std::string, ast_matchers::internal::Matcher<Stmt> > + ExpectedMatchers; +}; + +TEST(ParserTest, ParseUnsigned) { + MockSema Sema; + Sema.parse("0"); + Sema.parse("123"); + Sema.parse("0x1f"); + Sema.parse("12345678901"); + Sema.parse("1a1"); + EXPECT_EQ(5U, Sema.Values.size()); + EXPECT_EQ(0U, Sema.Values[0].getUnsigned()); + EXPECT_EQ(123U, Sema.Values[1].getUnsigned()); + EXPECT_EQ(31U, Sema.Values[2].getUnsigned()); + EXPECT_EQ("1:1: Error parsing unsigned token: <12345678901>", Sema.Errors[3]); + EXPECT_EQ("1:1: Error parsing unsigned token: <1a1>", Sema.Errors[4]); +} + +TEST(ParserTest, ParseString) { + MockSema Sema; + Sema.parse("\"Foo\""); + Sema.parse("\"\""); + Sema.parse("\"Baz"); + EXPECT_EQ(3ULL, Sema.Values.size()); + EXPECT_EQ("Foo", Sema.Values[0].getString()); + EXPECT_EQ("", Sema.Values[1].getString()); + EXPECT_EQ("1:1: Error parsing string token: <\"Baz>", Sema.Errors[2]); +} + +bool matchesRange(const SourceRange &Range, unsigned StartLine, + unsigned EndLine, unsigned StartColumn, unsigned EndColumn) { + EXPECT_EQ(StartLine, Range.Start.Line); + EXPECT_EQ(EndLine, Range.End.Line); + EXPECT_EQ(StartColumn, Range.Start.Column); + EXPECT_EQ(EndColumn, Range.End.Column); + return Range.Start.Line == StartLine && Range.End.Line == EndLine && + Range.Start.Column == StartColumn && Range.End.Column == EndColumn; +} + +llvm::Optional<DynTypedMatcher> getSingleMatcher(const VariantValue &Value) { + llvm::Optional<DynTypedMatcher> Result = + Value.getMatcher().getSingleMatcher(); + EXPECT_TRUE(Result.hasValue()); + return Result; +} + +TEST(ParserTest, ParseMatcher) { + MockSema Sema; + const uint64_t ExpectedFoo = Sema.expectMatcher("Foo"); + const uint64_t ExpectedBar = Sema.expectMatcher("Bar"); + const uint64_t ExpectedBaz = Sema.expectMatcher("Baz"); + Sema.parse(" Foo ( Bar ( 17), Baz( \n \"B A,Z\") ) .bind( \"Yo!\") "); + for (size_t i = 0, e = Sema.Errors.size(); i != e; ++i) { + EXPECT_EQ("", Sema.Errors[i]); + } + + EXPECT_EQ(1ULL, Sema.Values.size()); + EXPECT_EQ(ExpectedFoo, getSingleMatcher(Sema.Values[0])->getID()); + + EXPECT_EQ(3ULL, Sema.Matchers.size()); + const MockSema::MatcherInfo Bar = Sema.Matchers[0]; + EXPECT_EQ("Bar", Bar.MatcherName); + EXPECT_TRUE(matchesRange(Bar.NameRange, 1, 1, 8, 17)); + EXPECT_EQ(1ULL, Bar.Args.size()); + EXPECT_EQ(17U, Bar.Args[0].Value.getUnsigned()); + + const MockSema::MatcherInfo Baz = Sema.Matchers[1]; + EXPECT_EQ("Baz", Baz.MatcherName); + EXPECT_TRUE(matchesRange(Baz.NameRange, 1, 2, 19, 10)); + EXPECT_EQ(1ULL, Baz.Args.size()); + EXPECT_EQ("B A,Z", Baz.Args[0].Value.getString()); + + const MockSema::MatcherInfo Foo = Sema.Matchers[2]; + EXPECT_EQ("Foo", Foo.MatcherName); + EXPECT_TRUE(matchesRange(Foo.NameRange, 1, 2, 2, 12)); + EXPECT_EQ(2ULL, Foo.Args.size()); + EXPECT_EQ(ExpectedBar, getSingleMatcher(Foo.Args[0].Value)->getID()); + EXPECT_EQ(ExpectedBaz, getSingleMatcher(Foo.Args[1].Value)->getID()); + EXPECT_EQ("Yo!", Foo.BoundID); +} + +using ast_matchers::internal::Matcher; + +TEST(ParserTest, FullParserTest) { + Diagnostics Error; + llvm::Optional<DynTypedMatcher> VarDecl(Parser::parseMatcherExpression( + "varDecl(hasInitializer(binaryOperator(hasLHS(integerLiteral())," + " hasOperatorName(\"+\"))))", + &Error)); + EXPECT_EQ("", Error.toStringFull()); + Matcher<Decl> M = VarDecl->unconditionalConvertTo<Decl>(); + EXPECT_TRUE(matches("int x = 1 + false;", M)); + EXPECT_FALSE(matches("int x = true + 1;", M)); + EXPECT_FALSE(matches("int x = 1 - false;", M)); + EXPECT_FALSE(matches("int x = true - 1;", M)); + + llvm::Optional<DynTypedMatcher> HasParameter(Parser::parseMatcherExpression( + "functionDecl(hasParameter(1, hasName(\"x\")))", &Error)); + EXPECT_EQ("", Error.toStringFull()); + M = HasParameter->unconditionalConvertTo<Decl>(); + + EXPECT_TRUE(matches("void f(int a, int x);", M)); + EXPECT_FALSE(matches("void f(int x, int a);", M)); + + EXPECT_TRUE(!Parser::parseMatcherExpression( + "hasInitializer(\n binaryOperator(hasLHS(\"A\")))", + &Error).hasValue()); + EXPECT_EQ("1:1: Error parsing argument 1 for matcher hasInitializer.\n" + "2:5: Error parsing argument 1 for matcher binaryOperator.\n" + "2:20: Error building matcher hasLHS.\n" + "2:27: Incorrect type for arg 1. " + "(Expected = Matcher<Expr>) != (Actual = String)", + Error.toStringFull()); +} + +std::string ParseWithError(StringRef Code) { + Diagnostics Error; + VariantValue Value; + Parser::parseExpression(Code, &Value, &Error); + return Error.toStringFull(); +} + +std::string ParseMatcherWithError(StringRef Code) { + Diagnostics Error; + Parser::parseMatcherExpression(Code, &Error); + return Error.toStringFull(); +} + +TEST(ParserTest, Errors) { + EXPECT_EQ( + "1:5: Error parsing matcher. Found token <123> while looking for '('.", + ParseWithError("Foo 123")); + EXPECT_EQ( + "1:9: Error parsing matcher. Found token <123> while looking for ','.", + ParseWithError("Foo(\"A\" 123)")); + EXPECT_EQ( + "1:4: Error parsing matcher. Found end-of-code while looking for ')'.", + ParseWithError("Foo(")); + EXPECT_EQ("1:1: End of code found while looking for token.", + ParseWithError("")); + EXPECT_EQ("Input value is not a matcher expression.", + ParseMatcherWithError("\"A\"")); + EXPECT_EQ("1:1: Error parsing argument 1 for matcher Foo.\n" + "1:5: Invalid token <(> found when looking for a value.", + ParseWithError("Foo((")); + EXPECT_EQ("1:7: Expected end of code.", ParseWithError("expr()a")); + EXPECT_EQ("1:11: Malformed bind() expression.", + ParseWithError("isArrow().biind")); + EXPECT_EQ("1:15: Malformed bind() expression.", + ParseWithError("isArrow().bind")); + EXPECT_EQ("1:16: Malformed bind() expression.", + ParseWithError("isArrow().bind(foo")); + EXPECT_EQ("1:21: Malformed bind() expression.", + ParseWithError("isArrow().bind(\"foo\"")); + EXPECT_EQ("1:1: Error building matcher isArrow.\n" + "1:1: Matcher does not support binding.", + ParseWithError("isArrow().bind(\"foo\")")); + EXPECT_EQ("Input value has unresolved overloaded type: " + "Matcher<DoStmt|ForStmt|WhileStmt>", + ParseMatcherWithError("hasBody(stmt())")); +} + +TEST(ParserTest, OverloadErrors) { + EXPECT_EQ("1:1: Error building matcher callee.\n" + "1:8: Candidate 1: Incorrect type for arg 1. " + "(Expected = Matcher<Stmt>) != (Actual = String)\n" + "1:8: Candidate 2: Incorrect type for arg 1. " + "(Expected = Matcher<Decl>) != (Actual = String)", + ParseWithError("callee(\"A\")")); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang diff --git a/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp new file mode 100644 index 0000000..e716484 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -0,0 +1,347 @@ +//===- unittest/ASTMatchers/Dynamic/RegistryTest.cpp - Registry unit tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===// + +#include <vector> + +#include "../ASTMatchersTest.h" +#include "clang/ASTMatchers/Dynamic/Registry.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { +namespace { + +using ast_matchers::internal::Matcher; + +class RegistryTest : public ::testing::Test { +public: + std::vector<ParserValue> Args() { return std::vector<ParserValue>(); } + std::vector<ParserValue> Args(const VariantValue &Arg1) { + std::vector<ParserValue> Out(1); + Out[0].Value = Arg1; + return Out; + } + std::vector<ParserValue> Args(const VariantValue &Arg1, + const VariantValue &Arg2) { + std::vector<ParserValue> Out(2); + Out[0].Value = Arg1; + Out[1].Value = Arg2; + return Out; + } + + VariantMatcher constructMatcher(StringRef MatcherName, + Diagnostics *Error = NULL) { + Diagnostics DummyError; + if (!Error) Error = &DummyError; + const VariantMatcher Out = + Registry::constructMatcher(MatcherName, SourceRange(), Args(), Error); + EXPECT_EQ("", DummyError.toStringFull()); + return Out; + } + + VariantMatcher constructMatcher(StringRef MatcherName, + const VariantValue &Arg1, + Diagnostics *Error = NULL) { + Diagnostics DummyError; + if (!Error) Error = &DummyError; + const VariantMatcher Out = Registry::constructMatcher( + MatcherName, SourceRange(), Args(Arg1), Error); + EXPECT_EQ("", DummyError.toStringFull()); + return Out; + } + + VariantMatcher constructMatcher(StringRef MatcherName, + const VariantValue &Arg1, + const VariantValue &Arg2, + Diagnostics *Error = NULL) { + Diagnostics DummyError; + if (!Error) Error = &DummyError; + const VariantMatcher Out = Registry::constructMatcher( + MatcherName, SourceRange(), Args(Arg1, Arg2), Error); + EXPECT_EQ("", DummyError.toStringFull()); + return Out; + } +}; + +TEST_F(RegistryTest, CanConstructNoArgs) { + Matcher<Stmt> IsArrowValue = constructMatcher( + "memberExpr", constructMatcher("isArrow")).getTypedMatcher<Stmt>(); + Matcher<Stmt> BoolValue = + constructMatcher("boolLiteral").getTypedMatcher<Stmt>(); + + const std::string ClassSnippet = "struct Foo { int x; };\n" + "Foo *foo = new Foo;\n" + "int i = foo->x;\n"; + const std::string BoolSnippet = "bool Foo = true;\n"; + + EXPECT_TRUE(matches(ClassSnippet, IsArrowValue)); + EXPECT_TRUE(matches(BoolSnippet, BoolValue)); + EXPECT_FALSE(matches(ClassSnippet, BoolValue)); + EXPECT_FALSE(matches(BoolSnippet, IsArrowValue)); +} + +TEST_F(RegistryTest, ConstructWithSimpleArgs) { + Matcher<Decl> Value = constructMatcher( + "namedDecl", constructMatcher("hasName", std::string("X"))) + .getTypedMatcher<Decl>(); + EXPECT_TRUE(matches("class X {};", Value)); + EXPECT_FALSE(matches("int x;", Value)); + + Value = functionDecl(constructMatcher("parameterCountIs", 2) + .getTypedMatcher<FunctionDecl>()); + EXPECT_TRUE(matches("void foo(int,int);", Value)); + EXPECT_FALSE(matches("void foo(int);", Value)); +} + +TEST_F(RegistryTest, ConstructWithMatcherArgs) { + Matcher<Decl> HasInitializerSimple = constructMatcher( + "varDecl", constructMatcher("hasInitializer", constructMatcher("stmt"))) + .getTypedMatcher<Decl>(); + Matcher<Decl> HasInitializerComplex = constructMatcher( + "varDecl", + constructMatcher("hasInitializer", constructMatcher("callExpr"))) + .getTypedMatcher<Decl>(); + + std::string code = "int i;"; + EXPECT_FALSE(matches(code, HasInitializerSimple)); + EXPECT_FALSE(matches(code, HasInitializerComplex)); + + code = "int i = 1;"; + EXPECT_TRUE(matches(code, HasInitializerSimple)); + EXPECT_FALSE(matches(code, HasInitializerComplex)); + + code = "int y(); int i = y();"; + EXPECT_TRUE(matches(code, HasInitializerSimple)); + EXPECT_TRUE(matches(code, HasInitializerComplex)); + + Matcher<Decl> HasParameter = + functionDecl(constructMatcher( + "hasParameter", 1, constructMatcher("hasName", std::string("x"))) + .getTypedMatcher<FunctionDecl>()); + EXPECT_TRUE(matches("void f(int a, int x);", HasParameter)); + EXPECT_FALSE(matches("void f(int x, int a);", HasParameter)); +} + +TEST_F(RegistryTest, OverloadedMatchers) { + Matcher<Stmt> CallExpr0 = constructMatcher( + "callExpr", + constructMatcher("callee", constructMatcher("memberExpr", + constructMatcher("isArrow")))) + .getTypedMatcher<Stmt>(); + + Matcher<Stmt> CallExpr1 = constructMatcher( + "callExpr", + constructMatcher( + "callee", + constructMatcher("methodDecl", + constructMatcher("hasName", std::string("x"))))) + .getTypedMatcher<Stmt>(); + + std::string Code = "class Y { public: void x(); }; void z() { Y y; y.x(); }"; + EXPECT_FALSE(matches(Code, CallExpr0)); + EXPECT_TRUE(matches(Code, CallExpr1)); + + Code = "class Z { public: void z() { this->z(); } };"; + EXPECT_TRUE(matches(Code, CallExpr0)); + EXPECT_FALSE(matches(Code, CallExpr1)); +} + +TEST_F(RegistryTest, PolymorphicMatchers) { + const VariantMatcher IsDefinition = constructMatcher("isDefinition"); + Matcher<Decl> Var = + constructMatcher("varDecl", IsDefinition).getTypedMatcher<Decl>(); + Matcher<Decl> Class = + constructMatcher("recordDecl", IsDefinition).getTypedMatcher<Decl>(); + Matcher<Decl> Func = + constructMatcher("functionDecl", IsDefinition).getTypedMatcher<Decl>(); + EXPECT_TRUE(matches("int a;", Var)); + EXPECT_FALSE(matches("extern int a;", Var)); + EXPECT_TRUE(matches("class A {};", Class)); + EXPECT_FALSE(matches("class A;", Class)); + EXPECT_TRUE(matches("void f(){};", Func)); + EXPECT_FALSE(matches("void f();", Func)); + + Matcher<Decl> Anything = constructMatcher("anything").getTypedMatcher<Decl>(); + Matcher<Decl> RecordDecl = constructMatcher( + "recordDecl", constructMatcher("hasName", std::string("Foo")), + VariantMatcher::SingleMatcher(Anything)).getTypedMatcher<Decl>(); + + EXPECT_TRUE(matches("int Foo;", Anything)); + EXPECT_TRUE(matches("class Foo {};", Anything)); + EXPECT_TRUE(matches("void Foo(){};", Anything)); + EXPECT_FALSE(matches("int Foo;", RecordDecl)); + EXPECT_TRUE(matches("class Foo {};", RecordDecl)); + EXPECT_FALSE(matches("void Foo(){};", RecordDecl)); + + Matcher<Stmt> ConstructExpr = constructMatcher( + "constructExpr", + constructMatcher( + "hasDeclaration", + constructMatcher( + "methodDecl", + constructMatcher( + "ofClass", constructMatcher("hasName", std::string("Foo")))))) + .getTypedMatcher<Stmt>(); + EXPECT_FALSE(matches("class Foo { public: Foo(); };", ConstructExpr)); + EXPECT_TRUE( + matches("class Foo { public: Foo(); }; Foo foo = Foo();", ConstructExpr)); +} + +TEST_F(RegistryTest, TemplateArgument) { + Matcher<Decl> HasTemplateArgument = constructMatcher( + "classTemplateSpecializationDecl", + constructMatcher( + "hasAnyTemplateArgument", + constructMatcher("refersToType", + constructMatcher("asString", std::string("int"))))) + .getTypedMatcher<Decl>(); + EXPECT_TRUE(matches("template<typename T> class A {}; A<int> a;", + HasTemplateArgument)); + EXPECT_FALSE(matches("template<typename T> class A {}; A<char> a;", + HasTemplateArgument)); +} + +TEST_F(RegistryTest, TypeTraversal) { + Matcher<Type> M = constructMatcher( + "pointerType", + constructMatcher("pointee", constructMatcher("isConstQualified"), + constructMatcher("isInteger"))).getTypedMatcher<Type>(); + EXPECT_FALSE(matches("int *a;", M)); + EXPECT_TRUE(matches("int const *b;", M)); + + M = constructMatcher( + "arrayType", + constructMatcher("hasElementType", constructMatcher("builtinType"))) + .getTypedMatcher<Type>(); + EXPECT_FALSE(matches("struct A{}; A a[7];;", M)); + EXPECT_TRUE(matches("int b[7];", M)); +} + +TEST_F(RegistryTest, CXXCtorInitializer) { + Matcher<Decl> CtorDecl = constructMatcher( + "constructorDecl", + constructMatcher( + "hasAnyConstructorInitializer", + constructMatcher("forField", + constructMatcher("hasName", std::string("foo"))))) + .getTypedMatcher<Decl>(); + EXPECT_TRUE(matches("struct Foo { Foo() : foo(1) {} int foo; };", CtorDecl)); + EXPECT_FALSE(matches("struct Foo { Foo() {} int foo; };", CtorDecl)); + EXPECT_FALSE(matches("struct Foo { Foo() : bar(1) {} int bar; };", CtorDecl)); +} + +TEST_F(RegistryTest, Adaptative) { + Matcher<Decl> D = constructMatcher( + "recordDecl", + constructMatcher( + "has", + constructMatcher("recordDecl", + constructMatcher("hasName", std::string("X"))))) + .getTypedMatcher<Decl>(); + EXPECT_TRUE(matches("class X {};", D)); + EXPECT_TRUE(matches("class Y { class X {}; };", D)); + EXPECT_FALSE(matches("class Y { class Z {}; };", D)); + + Matcher<Stmt> S = constructMatcher( + "forStmt", + constructMatcher( + "hasDescendant", + constructMatcher("varDecl", + constructMatcher("hasName", std::string("X"))))) + .getTypedMatcher<Stmt>(); + EXPECT_TRUE(matches("void foo() { for(int X;;); }", S)); + EXPECT_TRUE(matches("void foo() { for(;;) { int X; } }", S)); + EXPECT_FALSE(matches("void foo() { for(;;); }", S)); + EXPECT_FALSE(matches("void foo() { if (int X = 0){} }", S)); + + S = constructMatcher( + "compoundStmt", constructMatcher("hasParent", constructMatcher("ifStmt"))) + .getTypedMatcher<Stmt>(); + EXPECT_TRUE(matches("void foo() { if (true) { int x = 42; } }", S)); + EXPECT_FALSE(matches("void foo() { if (true) return; }", S)); +} + +TEST_F(RegistryTest, VariadicOp) { + Matcher<Decl> D = constructMatcher( + "anyOf", + constructMatcher("recordDecl", + constructMatcher("hasName", std::string("Foo"))), + constructMatcher("namedDecl", + constructMatcher("hasName", std::string("foo")))) + .getTypedMatcher<Decl>(); + + EXPECT_TRUE(matches("void foo(){}", D)); + EXPECT_TRUE(matches("struct Foo{};", D)); + EXPECT_FALSE(matches("int i = 0;", D)); + + D = constructMatcher( + "allOf", constructMatcher("recordDecl"), + constructMatcher( + "namedDecl", + constructMatcher("anyOf", + constructMatcher("hasName", std::string("Foo")), + constructMatcher("hasName", std::string("Bar"))))) + .getTypedMatcher<Decl>(); + + EXPECT_FALSE(matches("void foo(){}", D)); + EXPECT_TRUE(matches("struct Foo{};", D)); + EXPECT_FALSE(matches("int i = 0;", D)); + EXPECT_TRUE(matches("class Bar{};", D)); + EXPECT_FALSE(matches("class OtherBar{};", D)); +} + +TEST_F(RegistryTest, Errors) { + // Incorrect argument count. + OwningPtr<Diagnostics> Error(new Diagnostics()); + EXPECT_TRUE(constructMatcher("hasInitializer", Error.get()).isNull()); + EXPECT_EQ("Incorrect argument count. (Expected = 1) != (Actual = 0)", + Error->toString()); + Error.reset(new Diagnostics()); + EXPECT_TRUE(constructMatcher("isArrow", std::string(), Error.get()).isNull()); + EXPECT_EQ("Incorrect argument count. (Expected = 0) != (Actual = 1)", + Error->toString()); + + // Bad argument type + Error.reset(new Diagnostics()); + EXPECT_TRUE(constructMatcher("ofClass", std::string(), Error.get()).isNull()); + EXPECT_EQ("Incorrect type for arg 1. (Expected = Matcher<CXXRecordDecl>) != " + "(Actual = String)", + Error->toString()); + Error.reset(new Diagnostics()); + EXPECT_TRUE(constructMatcher("recordDecl", constructMatcher("recordDecl"), + constructMatcher("parameterCountIs", 3), + Error.get()).isNull()); + EXPECT_EQ("Incorrect type for arg 2. (Expected = Matcher<CXXRecordDecl>) != " + "(Actual = Matcher<FunctionDecl>)", + Error->toString()); + + // Bad argument type with variadic. + Error.reset(new Diagnostics()); + EXPECT_TRUE(constructMatcher("anyOf", std::string(), Error.get()).isNull()); + EXPECT_EQ( + "Incorrect type for arg 1. (Expected = Matcher<>) != (Actual = String)", + Error->toString()); + Error.reset(new Diagnostics()); + EXPECT_TRUE(constructMatcher( + "recordDecl", + constructMatcher("allOf", + constructMatcher("isDerivedFrom", std::string("FOO")), + constructMatcher("isArrow")), + Error.get()).isNull()); + EXPECT_EQ("Incorrect type for arg 1. " + "(Expected = Matcher<CXXRecordDecl>) != " + "(Actual = Matcher<CXXRecordDecl>&Matcher<MemberExpr>)", + Error->toString()); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang diff --git a/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp new file mode 100644 index 0000000..d2b8a58 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp @@ -0,0 +1,144 @@ +//===- unittest/ASTMatchers/Dynamic/VariantValueTest.cpp - VariantValue unit tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------------===// + +#include "../ASTMatchersTest.h" +#include "clang/ASTMatchers/Dynamic/VariantValue.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { +namespace { + +using ast_matchers::internal::DynTypedMatcher; +using ast_matchers::internal::Matcher; + +TEST(VariantValueTest, Unsigned) { + const unsigned kUnsigned = 17; + VariantValue Value = kUnsigned; + + EXPECT_TRUE(Value.isUnsigned()); + EXPECT_EQ(kUnsigned, Value.getUnsigned()); + + EXPECT_FALSE(Value.isString()); + EXPECT_FALSE(Value.isMatcher()); +} + +TEST(VariantValueTest, String) { + const ::std::string kString = "string"; + VariantValue Value = kString; + + EXPECT_TRUE(Value.isString()); + EXPECT_EQ(kString, Value.getString()); + EXPECT_EQ("String", Value.getTypeAsString()); + + EXPECT_FALSE(Value.isUnsigned()); + EXPECT_FALSE(Value.isMatcher()); +} + +TEST(VariantValueTest, DynTypedMatcher) { + VariantValue Value = VariantMatcher::SingleMatcher(stmt()); + + EXPECT_FALSE(Value.isUnsigned()); + EXPECT_FALSE(Value.isString()); + + EXPECT_TRUE(Value.isMatcher()); + EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<Decl>()); + EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<UnaryOperator>()); + EXPECT_EQ("Matcher<Stmt>", Value.getTypeAsString()); + + // Can only convert to compatible matchers. + Value = VariantMatcher::SingleMatcher(recordDecl()); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<Decl>()); + EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<UnaryOperator>()); + EXPECT_EQ("Matcher<Decl>", Value.getTypeAsString()); + + Value = VariantMatcher::SingleMatcher(ignoringImpCasts(expr())); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<Decl>()); + EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<Stmt>()); + EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<Expr>()); + EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<IntegerLiteral>()); + EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<GotoStmt>()); + EXPECT_EQ("Matcher<Expr>", Value.getTypeAsString()); +} + +TEST(VariantValueTest, Assignment) { + VariantValue Value = std::string("A"); + EXPECT_TRUE(Value.isString()); + EXPECT_EQ("A", Value.getString()); + EXPECT_FALSE(Value.isUnsigned()); + EXPECT_FALSE(Value.isMatcher()); + EXPECT_EQ("String", Value.getTypeAsString()); + + Value = VariantMatcher::SingleMatcher(recordDecl()); + EXPECT_FALSE(Value.isUnsigned()); + EXPECT_FALSE(Value.isString()); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.getMatcher().hasTypedMatcher<Decl>()); + EXPECT_FALSE(Value.getMatcher().hasTypedMatcher<UnaryOperator>()); + EXPECT_EQ("Matcher<Decl>", Value.getTypeAsString()); + + Value = 17; + EXPECT_TRUE(Value.isUnsigned()); + EXPECT_EQ(17U, Value.getUnsigned()); + EXPECT_FALSE(Value.isMatcher()); + EXPECT_FALSE(Value.isString()); + + Value = VariantValue(); + EXPECT_FALSE(Value.isUnsigned()); + EXPECT_FALSE(Value.isString()); + EXPECT_FALSE(Value.isMatcher()); + EXPECT_EQ("Nothing", Value.getTypeAsString()); +} + +TEST(VariantValueTest, Matcher) { + EXPECT_TRUE(matches("class X {};", VariantValue(VariantMatcher::SingleMatcher( + recordDecl(hasName("X")))) + .getMatcher() + .getTypedMatcher<Decl>())); + EXPECT_TRUE( + matches("int x;", VariantValue(VariantMatcher::SingleMatcher(varDecl())) + .getMatcher() + .getTypedMatcher<Decl>())); + EXPECT_TRUE( + matches("int foo() { return 1 + 1; }", + VariantValue(VariantMatcher::SingleMatcher(functionDecl())) + .getMatcher() + .getTypedMatcher<Decl>())); + // Can't get the wrong matcher. + EXPECT_FALSE(VariantValue(VariantMatcher::SingleMatcher(varDecl())) + .getMatcher() + .hasTypedMatcher<Stmt>()); +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST && !defined(_MSC_VER) + // Trying to get the wrong matcher fails an assertion in Matcher<T>. We don't + // do this test when building with MSVC because its debug C runtime prints the + // assertion failure message as a wide string, which gtest doesn't understand. + EXPECT_DEATH(VariantValue(VariantMatcher::SingleMatcher(varDecl())) + .getMatcher() + .getTypedMatcher<Stmt>(), + "hasTypedMatcher"); +#endif + + EXPECT_FALSE(matches( + "int x;", VariantValue(VariantMatcher::SingleMatcher(functionDecl())) + .getMatcher() + .getTypedMatcher<Decl>())); + EXPECT_FALSE( + matches("int foo() { return 1 + 1; }", + VariantValue(VariantMatcher::SingleMatcher(declRefExpr())) + .getMatcher() + .getTypedMatcher<Stmt>())); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang |