--- /dev/null
+//===- GtestMatchers.cpp - AST Matchers for Gtest ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/GtestMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Timer.h"
+#include <deque>
+#include <memory>
+#include <set>
+
+namespace clang {
+namespace ast_matchers {
+
+static DeclarationMatcher getComparisonDecl(GtestCmp Cmp) {
+ switch (Cmp) {
+ case GtestCmp::Eq:
+ return cxxMethodDecl(hasName("Compare"),
+ ofClass(cxxRecordDecl(isSameOrDerivedFrom(
+ hasName("::testing::internal::EqHelper")))));
+ case GtestCmp::Ne:
+ return functionDecl(hasName("::testing::internal::CmpHelperNE"));
+ case GtestCmp::Ge:
+ return functionDecl(hasName("::testing::internal::CmpHelperGE"));
+ case GtestCmp::Gt:
+ return functionDecl(hasName("::testing::internal::CmpHelperGT"));
+ case GtestCmp::Le:
+ return functionDecl(hasName("::testing::internal::CmpHelperLE"));
+ case GtestCmp::Lt:
+ return functionDecl(hasName("::testing::internal::CmpHelperLT"));
+ }
+}
+
+static llvm::StringRef getAssertMacro(GtestCmp Cmp) {
+ switch (Cmp) {
+ case GtestCmp::Eq:
+ return "ASSERT_EQ";
+ case GtestCmp::Ne:
+ return "ASSERT_NE";
+ case GtestCmp::Ge:
+ return "ASSERT_GE";
+ case GtestCmp::Gt:
+ return "ASSERT_GT";
+ case GtestCmp::Le:
+ return "ASSERT_LE";
+ case GtestCmp::Lt:
+ return "ASSERT_LT";
+ }
+}
+
+static llvm::StringRef getExpectMacro(GtestCmp Cmp) {
+ switch (Cmp) {
+ case GtestCmp::Eq:
+ return "EXPECT_EQ";
+ case GtestCmp::Ne:
+ return "EXPECT_NE";
+ case GtestCmp::Ge:
+ return "EXPECT_GE";
+ case GtestCmp::Gt:
+ return "EXPECT_GT";
+ case GtestCmp::Le:
+ return "EXPECT_LE";
+ case GtestCmp::Lt:
+ return "EXPECT_LT";
+ }
+}
+
+// In general, AST matchers cannot match calls to macros. However, we can
+// simulate such matches if the macro definition has identifiable elements that
+// themselves can be matched. In that case, we can match on those elements and
+// then check that the match occurs within an expansion of the desired
+// macro. The more uncommon the identified elements, the more efficient this
+// process will be.
+//
+// We use this approach to implement the derived matchers gtestAssert and
+// gtestExpect.
+internal::BindableMatcher<Stmt> gtestAssert(GtestCmp Cmp, StatementMatcher Left,
+ StatementMatcher Right) {
+ return callExpr(callee(getComparisonDecl(Cmp)),
+ isExpandedFromMacro(getAssertMacro(Cmp)),
+ hasArgument(2, Left), hasArgument(3, Right));
+}
+
+internal::BindableMatcher<Stmt> gtestExpect(GtestCmp Cmp, StatementMatcher Left,
+ StatementMatcher Right) {
+ return callExpr(callee(getComparisonDecl(Cmp)),
+ isExpandedFromMacro(getExpectMacro(Cmp)),
+ hasArgument(2, Left), hasArgument(3, Right));
+}
+
+} // end namespace ast_matchers
+} // end namespace clang
--- /dev/null
+//===- unittests/ASTMatchers/GTestMatchersTest.cpp - GTest matcher unit tests //
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTMatchersTest.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/GtestMatchers.h"
+
+namespace clang {
+namespace ast_matchers {
+
+constexpr llvm::StringLiteral GtestMockDecls = R"cc(
+ static int testerr;
+
+#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ switch (0) \
+ case 0: \
+ default: // NOLINT
+
+#define GTEST_NONFATAL_FAILURE_(code) testerr = code
+
+#define GTEST_FATAL_FAILURE_(code) testerr = code
+
+#define GTEST_ASSERT_(expression, on_failure) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (const int gtest_ar = (expression)) \
+ ; \
+ else \
+ on_failure(gtest_ar)
+
+ // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
+ // Don't use this in your code.
+#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) \
+ GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), on_failure)
+
+#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
+ GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
+#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
+ GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
+
+#define EXPECT_EQ(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
+#define EXPECT_NE(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
+#define EXPECT_GE(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
+#define EXPECT_GT(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
+#define EXPECT_LE(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
+#define EXPECT_LT(val1, val2) \
+ EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
+
+#define ASSERT_EQ(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
+#define ASSERT_NE(val1, val2) \
+ ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
+
+ namespace testing {
+ namespace internal {
+ class EqHelper {
+ public:
+ // This templatized version is for the general case.
+ template <typename T1, typename T2>
+ static int Compare(const char* lhs_expression, const char* rhs_expression,
+ const T1& lhs, const T2& rhs) {
+ return 0;
+ }
+ };
+ template <typename T1, typename T2>
+ int CmpHelperNE(const char* expr1, const char* expr2, const T1& val1,
+ const T2& val2) {
+ return 0;
+ }
+ template <typename T1, typename T2>
+ int CmpHelperGE(const char* expr1, const char* expr2, const T1& val1,
+ const T2& val2) {
+ return 0;
+ }
+ template <typename T1, typename T2>
+ int CmpHelperGT(const char* expr1, const char* expr2, const T1& val1,
+ const T2& val2) {
+ return 0;
+ }
+ template <typename T1, typename T2>
+ int CmpHelperLE(const char* expr1, const char* expr2, const T1& val1,
+ const T2& val2) {
+ return 0;
+ }
+ template <typename T1, typename T2>
+ int CmpHelperLT(const char* expr1, const char* expr2, const T1& val1,
+ const T2& val2) {
+ return 0;
+ }
+ } // namespace internal
+ } // namespace testing
+)cc";
+
+static std::string wrapGtest(llvm::StringRef Input) {
+ return (GtestMockDecls + Input).str();
+}
+
+TEST(GtestAssertTest, ShouldMatchAssert) {
+ std::string Input = R"cc(
+ void Test() { ASSERT_EQ(1010, 4321); }
+ )cc";
+ EXPECT_TRUE(matches(wrapGtest(Input),
+ gtestAssert(GtestCmp::Eq, integerLiteral(equals(1010)),
+ integerLiteral(equals(4321)))));
+}
+
+TEST(GtestAssertTest, ShouldNotMatchExpect) {
+ std::string Input = R"cc(
+ void Test() { EXPECT_EQ(2, 3); }
+ )cc";
+ EXPECT_TRUE(
+ notMatches(wrapGtest(Input), gtestAssert(GtestCmp::Eq, expr(), expr())));
+}
+
+TEST(GtestAssertTest, ShouldMatchNestedAssert) {
+ std::string Input = R"cc(
+ #define WRAPPER(a, b) ASSERT_EQ(a, b)
+ void Test() { WRAPPER(2, 3); }
+ )cc";
+ EXPECT_TRUE(
+ matches(wrapGtest(Input), gtestAssert(GtestCmp::Eq, expr(), expr())));
+}
+
+TEST(GtestExpectTest, ShouldMatchExpect) {
+ std::string Input = R"cc(
+ void Test() { EXPECT_EQ(1010, 4321); }
+ )cc";
+ EXPECT_TRUE(matches(wrapGtest(Input),
+ gtestExpect(GtestCmp::Eq, integerLiteral(equals(1010)),
+ integerLiteral(equals(4321)))));
+}
+
+TEST(GtestExpectTest, ShouldNotMatchAssert) {
+ std::string Input = R"cc(
+ void Test() { ASSERT_EQ(2, 3); }
+ )cc";
+ EXPECT_TRUE(
+ notMatches(wrapGtest(Input), gtestExpect(GtestCmp::Eq, expr(), expr())));
+}
+
+TEST(GtestExpectTest, NeShouldMatchExpectNe) {
+ std::string Input = R"cc(
+ void Test() { EXPECT_NE(2, 3); }
+ )cc";
+ EXPECT_TRUE(
+ matches(wrapGtest(Input), gtestExpect(GtestCmp::Ne, expr(), expr())));
+}
+
+TEST(GtestExpectTest, LeShouldMatchExpectLe) {
+ std::string Input = R"cc(
+ void Test() { EXPECT_LE(2, 3); }
+ )cc";
+ EXPECT_TRUE(
+ matches(wrapGtest(Input), gtestExpect(GtestCmp::Le, expr(), expr())));
+}
+
+TEST(GtestExpectTest, LtShouldMatchExpectLt) {
+ std::string Input = R"cc(
+ void Test() { EXPECT_LT(2, 3); }
+ )cc";
+ EXPECT_TRUE(
+ matches(wrapGtest(Input), gtestExpect(GtestCmp::Lt, expr(), expr())));
+}
+
+TEST(GtestExpectTest, GeShouldMatchExpectGe) {
+ std::string Input = R"cc(
+ void Test() { EXPECT_GE(2, 3); }
+ )cc";
+ EXPECT_TRUE(
+ matches(wrapGtest(Input), gtestExpect(GtestCmp::Ge, expr(), expr())));
+}
+
+TEST(GtestExpectTest, GtShouldMatchExpectGt) {
+ std::string Input = R"cc(
+ void Test() { EXPECT_GT(2, 3); }
+ )cc";
+ EXPECT_TRUE(
+ matches(wrapGtest(Input), gtestExpect(GtestCmp::Gt, expr(), expr())));
+}
+
+} // end namespace ast_matchers
+} // end namespace clang