diff options
Diffstat (limited to 'clang/unittests/ASTMatchers/Dynamic')
| -rw-r--r-- | clang/unittests/ASTMatchers/Dynamic/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | clang/unittests/ASTMatchers/Dynamic/Makefile | 18 | ||||
| -rw-r--r-- | clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp | 194 | ||||
| -rw-r--r-- | clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp | 112 | ||||
| -rw-r--r-- | clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp | 97 |
5 files changed, 428 insertions, 0 deletions
diff --git a/clang/unittests/ASTMatchers/Dynamic/CMakeLists.txt b/clang/unittests/ASTMatchers/Dynamic/CMakeLists.txt new file mode 100644 index 00000000000..eb9fa549e11 --- /dev/null +++ b/clang/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/clang/unittests/ASTMatchers/Dynamic/Makefile b/clang/unittests/ASTMatchers/Dynamic/Makefile new file mode 100644 index 00000000000..082a4c04c12 --- /dev/null +++ b/clang/unittests/ASTMatchers/Dynamic/Makefile @@ -0,0 +1,18 @@ +##===- 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 +LINK_COMPONENTS := support mc +USEDLIBS = clangEdit.a clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ + clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \ + clangAST.a clangASTMatchers.a clangLex.a clangBasic.a \ + clangDynamicASTMatchers.a + +include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp new file mode 100644 index 00000000000..41f522856df --- /dev/null +++ b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -0,0 +1,194 @@ +//===- 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 DummyDynTypedMatcher : public DynTypedMatcher { +public: + DummyDynTypedMatcher(uint64_t ID) : ID(ID) {} + + typedef ast_matchers::internal::ASTMatchFinder ASTMatchFinder; + typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder; + virtual bool matches(const ast_type_traits::DynTypedNode DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return false; + } + + /// \brief Makes a copy of this matcher object. + virtual DynTypedMatcher *clone() const { + return new DummyDynTypedMatcher(ID); + } + + /// \brief Returns a unique ID for the matcher. + virtual uint64_t getID() const { return ID; } + +private: + uint64_t ID; +}; + +class MockSema : public Parser::Sema { +public: + virtual ~MockSema() {} + + uint64_t expectMatcher(StringRef MatcherName) { + uint64_t ID = ExpectedMatchers.size() + 1; + ExpectedMatchers[MatcherName] = ID; + return ID; + } + + void parse(StringRef Code) { + Diagnostics Error; + VariantValue Value; + Parser::parseExpression(Code, this, &Value, &Error); + Values.push_back(Value); + Errors.push_back(Error.ToStringFull()); + } + + DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef<ParserValue> Args, + Diagnostics *Error) { + MatcherInfo ToStore = { MatcherName, NameRange, Args }; + Matchers.push_back(ToStore); + return new DummyDynTypedMatcher(ExpectedMatchers[MatcherName]); + } + + struct MatcherInfo { + StringRef MatcherName; + SourceRange NameRange; + std::vector<ParserValue> Args; + }; + + std::vector<std::string> Errors; + std::vector<VariantValue> Values; + std::vector<MatcherInfo> Matchers; + llvm::StringMap<uint64_t> ExpectedMatchers; +}; + +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; +} + +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 (), Baz( \n \"B A,Z\") ) "); + 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, Sema.Values[0].getMatcher().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, 14)); + EXPECT_EQ(0ULL, Bar.Args.size()); + + const MockSema::MatcherInfo Baz = Sema.Matchers[1]; + EXPECT_EQ("Baz", Baz.MatcherName); + EXPECT_TRUE(matchesRange(Baz.NameRange, 1, 2, 16, 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, Foo.Args[0].Value.getMatcher().getID()); + EXPECT_EQ(ExpectedBaz, Foo.Args[1].Value.getMatcher().getID()); +} + +using ast_matchers::internal::Matcher; + +TEST(ParserTest, FullParserTest) { + OwningPtr<DynTypedMatcher> Matcher(Parser::parseMatcherExpression( + "hasInitializer(binaryOperator(hasLHS(integerLiteral())))", NULL)); + EXPECT_TRUE(matchesDynamic("int x = 1 + false;", *Matcher)); + EXPECT_FALSE(matchesDynamic("int x = true + 1;", *Matcher)); + + Diagnostics Error; + EXPECT_TRUE(Parser::parseMatcherExpression( + "hasInitializer(\n binaryOperator(hasLHS(\"A\")))", &Error) == NULL); + 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 on function hasLHS for arg 1.", + 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((")); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang diff --git a/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp new file mode 100644 index 00000000000..64af120193b --- /dev/null +++ b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -0,0 +1,112 @@ +//===- 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; + +DynTypedMatcher *constructMatcher(StringRef MatcherName, Diagnostics *Error) { + const std::vector<ParserValue> Args; + return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error); +} + +DynTypedMatcher *constructMatcher(StringRef MatcherName, + const VariantValue &Arg1, + Diagnostics *Error) { + std::vector<ParserValue> Args(1); + Args[0].Value = Arg1; + return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error); +} + +DynTypedMatcher *constructMatcher(StringRef MatcherName, + const VariantValue &Arg1, + const VariantValue &Arg2, + Diagnostics *Error) { + std::vector<ParserValue> Args(2); + Args[0].Value = Arg1; + Args[1].Value = Arg2; + return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error); +} + +TEST(RegistryTest, CanConstructNoArgs) { + OwningPtr<DynTypedMatcher> IsArrowValue(constructMatcher("isArrow", NULL)); + OwningPtr<DynTypedMatcher> BoolValue(constructMatcher("boolLiteral", NULL)); + + 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(matchesDynamic(ClassSnippet, *IsArrowValue)); + EXPECT_TRUE(matchesDynamic(BoolSnippet, *BoolValue)); + EXPECT_FALSE(matchesDynamic(ClassSnippet, *BoolValue)); + EXPECT_FALSE(matchesDynamic(BoolSnippet, *IsArrowValue)); +} + +TEST(RegistryTest, ConstructWithSimpleArgs) { + OwningPtr<DynTypedMatcher> Value( + constructMatcher("hasName", std::string("X"), NULL)); + EXPECT_TRUE(matchesDynamic("class X {};", *Value)); + EXPECT_FALSE(matchesDynamic("int x;", *Value)); +} + +TEST(RegistryTest, ConstructWithMatcherArgs) { + OwningPtr<DynTypedMatcher> HasInitializerSimple( + constructMatcher("hasInitializer", stmt(), NULL)); + OwningPtr<DynTypedMatcher> HasInitializerComplex( + constructMatcher("hasInitializer", callExpr(), NULL)); + + std::string code = "int i;"; + EXPECT_FALSE(matchesDynamic(code, *HasInitializerSimple)); + EXPECT_FALSE(matchesDynamic(code, *HasInitializerComplex)); + + code = "int i = 1;"; + EXPECT_TRUE(matchesDynamic(code, *HasInitializerSimple)); + EXPECT_FALSE(matchesDynamic(code, *HasInitializerComplex)); + + code = "int y(); int i = y();"; + EXPECT_TRUE(matchesDynamic(code, *HasInitializerSimple)); + EXPECT_TRUE(matchesDynamic(code, *HasInitializerComplex)); +} + +TEST(RegistryTest, Errors) { + // Incorrect argument count. + OwningPtr<Diagnostics> Error(new Diagnostics()); + EXPECT_TRUE(NULL == constructMatcher("hasInitializer", Error.get())); + EXPECT_EQ("Incorrect argument count. (Expected = 1) != (Actual = 0)", + Error->ToString()); + Error.reset(new Diagnostics()); + EXPECT_TRUE(NULL == constructMatcher("isArrow", std::string(), Error.get())); + EXPECT_EQ("Incorrect argument count. (Expected = 0) != (Actual = 1)", + Error->ToString()); + + // Bad argument type + Error.reset(new Diagnostics()); + EXPECT_TRUE(NULL == constructMatcher("ofClass", std::string(), Error.get())); + EXPECT_EQ("Incorrect type on function ofClass for arg 1.", Error->ToString()); + Error.reset(new Diagnostics()); + EXPECT_TRUE(NULL == constructMatcher("recordDecl", recordDecl(), + ::std::string(), Error.get())); + EXPECT_EQ("Incorrect type on function recordDecl for arg 2.", + Error->ToString()); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang diff --git a/clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp b/clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp new file mode 100644 index 00000000000..6c202e52fa4 --- /dev/null +++ b/clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp @@ -0,0 +1,97 @@ +//===- 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, String) { + const ::std::string kString = "string"; + VariantValue Value = kString; + + EXPECT_TRUE(Value.isString()); + EXPECT_EQ(kString, Value.getString()); + + EXPECT_FALSE(Value.isMatcher()); + EXPECT_FALSE(Value.isTypedMatcher<clang::Decl>()); + EXPECT_FALSE(Value.isTypedMatcher<clang::UnaryOperator>()); +} + +TEST(VariantValueTest, DynTypedMatcher) { + VariantValue Value = stmt(); + + EXPECT_FALSE(Value.isString()); + + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>()); + EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>()); + + // Conversion to any type of matcher works. + // If they are not compatible it would just return a matcher that matches + // nothing. We test this below. + Value = recordDecl(); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>()); + EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>()); + + Value = unaryOperator(); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>()); + EXPECT_TRUE(Value.isTypedMatcher<clang::Stmt>()); + EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>()); +} + +TEST(VariantValueTest, Assignment) { + VariantValue Value = std::string("A"); + EXPECT_TRUE(Value.isString()); + EXPECT_EQ("A", Value.getString()); + EXPECT_FALSE(Value.isMatcher()); + + Value = recordDecl(); + EXPECT_FALSE(Value.isString()); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher<clang::Decl>()); + EXPECT_TRUE(Value.isTypedMatcher<clang::UnaryOperator>()); + + Value = VariantValue(); + EXPECT_FALSE(Value.isString()); + EXPECT_FALSE(Value.isMatcher()); +} + +TEST(GeneicValueTest, Matcher) { + EXPECT_TRUE(matchesDynamic( + "class X {};", VariantValue(recordDecl(hasName("X"))).getMatcher())); + EXPECT_TRUE(matchesDynamic( + "int x;", VariantValue(varDecl()).getTypedMatcher<clang::Decl>())); + EXPECT_TRUE(matchesDynamic("int foo() { return 1 + 1; }", + VariantValue(functionDecl()).getMatcher())); + // Going through the wrong Matcher<T> will fail to match, even if the + // underlying matcher is correct. + EXPECT_FALSE(matchesDynamic( + "int x;", VariantValue(varDecl()).getTypedMatcher<clang::Stmt>())); + + EXPECT_FALSE( + matchesDynamic("int x;", VariantValue(functionDecl()).getMatcher())); + EXPECT_FALSE(matchesDynamic( + "int foo() { return 1 + 1; }", + VariantValue(declRefExpr()).getTypedMatcher<clang::DeclRefExpr>())); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang |

