summaryrefslogtreecommitdiffstats
path: root/clang/unittests/ASTMatchers/Dynamic
diff options
context:
space:
mode:
Diffstat (limited to 'clang/unittests/ASTMatchers/Dynamic')
-rw-r--r--clang/unittests/ASTMatchers/Dynamic/CMakeLists.txt7
-rw-r--r--clang/unittests/ASTMatchers/Dynamic/Makefile18
-rw-r--r--clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp194
-rw-r--r--clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp112
-rw-r--r--clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp97
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
OpenPOWER on IntegriCloud