| 1 | //===- GtestMatchers.cpp - AST Matchers for Gtest ---------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file implements several matchers for popular gtest macros. In general, |
| 10 | // AST matchers cannot match calls to macros. However, we can simulate such |
| 11 | // matches if the macro definition has identifiable elements that themselves can |
| 12 | // be matched. In that case, we can match on those elements and then check that |
| 13 | // the match occurs within an expansion of the desired macro. The more uncommon |
| 14 | // the identified elements, the more efficient this process will be. |
| 15 | // |
| 16 | //===----------------------------------------------------------------------===// |
| 17 | |
| 18 | #include "clang/ASTMatchers/GtestMatchers.h" |
| 19 | #include "llvm/ADT/StringRef.h" |
| 20 | |
| 21 | namespace clang { |
| 22 | namespace ast_matchers { |
| 23 | namespace { |
| 24 | |
| 25 | enum class MacroType { |
| 26 | Expect, |
| 27 | Assert, |
| 28 | On, |
| 29 | }; |
| 30 | |
| 31 | } // namespace |
| 32 | |
| 33 | static DeclarationMatcher getComparisonDecl(GtestCmp Cmp) { |
| 34 | switch (Cmp) { |
| 35 | case GtestCmp::Eq: |
| 36 | return cxxMethodDecl(hasName(Name: "Compare" ), |
| 37 | ofClass(InnerMatcher: cxxRecordDecl(isSameOrDerivedFrom( |
| 38 | Base: hasName(Name: "::testing::internal::EqHelper" ))))); |
| 39 | case GtestCmp::Ne: |
| 40 | return functionDecl(hasName(Name: "::testing::internal::CmpHelperNE" )); |
| 41 | case GtestCmp::Ge: |
| 42 | return functionDecl(hasName(Name: "::testing::internal::CmpHelperGE" )); |
| 43 | case GtestCmp::Gt: |
| 44 | return functionDecl(hasName(Name: "::testing::internal::CmpHelperGT" )); |
| 45 | case GtestCmp::Le: |
| 46 | return functionDecl(hasName(Name: "::testing::internal::CmpHelperLE" )); |
| 47 | case GtestCmp::Lt: |
| 48 | return functionDecl(hasName(Name: "::testing::internal::CmpHelperLT" )); |
| 49 | } |
| 50 | llvm_unreachable("Unhandled GtestCmp enum" ); |
| 51 | } |
| 52 | |
| 53 | static llvm::StringRef getMacroTypeName(MacroType Macro) { |
| 54 | switch (Macro) { |
| 55 | case MacroType::Expect: |
| 56 | return "EXPECT" ; |
| 57 | case MacroType::Assert: |
| 58 | return "ASSERT" ; |
| 59 | case MacroType::On: |
| 60 | return "ON" ; |
| 61 | } |
| 62 | llvm_unreachable("Unhandled MacroType enum" ); |
| 63 | } |
| 64 | |
| 65 | static llvm::StringRef getComparisonTypeName(GtestCmp Cmp) { |
| 66 | switch (Cmp) { |
| 67 | case GtestCmp::Eq: |
| 68 | return "EQ" ; |
| 69 | case GtestCmp::Ne: |
| 70 | return "NE" ; |
| 71 | case GtestCmp::Ge: |
| 72 | return "GE" ; |
| 73 | case GtestCmp::Gt: |
| 74 | return "GT" ; |
| 75 | case GtestCmp::Le: |
| 76 | return "LE" ; |
| 77 | case GtestCmp::Lt: |
| 78 | return "LT" ; |
| 79 | } |
| 80 | llvm_unreachable("Unhandled GtestCmp enum" ); |
| 81 | } |
| 82 | |
| 83 | static std::string getMacroName(MacroType Macro, GtestCmp Cmp) { |
| 84 | return (getMacroTypeName(Macro) + "_" + getComparisonTypeName(Cmp)).str(); |
| 85 | } |
| 86 | |
| 87 | static std::string getMacroName(MacroType Macro, llvm::StringRef Operation) { |
| 88 | return (getMacroTypeName(Macro) + "_" + Operation).str(); |
| 89 | } |
| 90 | |
| 91 | // Under the hood, ON_CALL is expanded to a call to `InternalDefaultActionSetAt` |
| 92 | // to set a default action spec to the underlying function mocker, while |
| 93 | // EXPECT_CALL is expanded to a call to `InternalExpectedAt` to set a new |
| 94 | // expectation spec. |
| 95 | static llvm::StringRef getSpecSetterName(MacroType Macro) { |
| 96 | switch (Macro) { |
| 97 | case MacroType::On: |
| 98 | return "InternalDefaultActionSetAt" ; |
| 99 | case MacroType::Expect: |
| 100 | return "InternalExpectedAt" ; |
| 101 | default: |
| 102 | llvm_unreachable("Unhandled MacroType enum" ); |
| 103 | } |
| 104 | llvm_unreachable("Unhandled MacroType enum" ); |
| 105 | } |
| 106 | |
| 107 | // In general, AST matchers cannot match calls to macros. However, we can |
| 108 | // simulate such matches if the macro definition has identifiable elements that |
| 109 | // themselves can be matched. In that case, we can match on those elements and |
| 110 | // then check that the match occurs within an expansion of the desired |
| 111 | // macro. The more uncommon the identified elements, the more efficient this |
| 112 | // process will be. |
| 113 | // |
| 114 | // We use this approach to implement the derived matchers gtestAssert and |
| 115 | // gtestExpect. |
| 116 | static internal::BindableMatcher<Stmt> |
| 117 | gtestComparisonInternal(MacroType Macro, GtestCmp Cmp, StatementMatcher Left, |
| 118 | StatementMatcher Right) { |
| 119 | return callExpr(isExpandedFromMacro(MacroName: getMacroName(Macro, Cmp)), |
| 120 | callee(InnerMatcher: getComparisonDecl(Cmp)), hasArgument(N: 2, InnerMatcher: Left), |
| 121 | hasArgument(N: 3, InnerMatcher: Right)); |
| 122 | } |
| 123 | |
| 124 | static internal::BindableMatcher<Stmt> |
| 125 | gtestThatInternal(MacroType Macro, StatementMatcher Actual, |
| 126 | StatementMatcher Matcher) { |
| 127 | return cxxOperatorCallExpr( |
| 128 | isExpandedFromMacro(MacroName: getMacroName(Macro, Operation: "THAT" )), |
| 129 | hasOverloadedOperatorName(Name: "()" ), hasArgument(N: 2, InnerMatcher: Actual), |
| 130 | hasArgument( |
| 131 | N: 0, InnerMatcher: expr(hasType(InnerMatcher: classTemplateSpecializationDecl(hasName( |
| 132 | Name: "::testing::internal::PredicateFormatterFromMatcher" ))), |
| 133 | ignoringImplicit( |
| 134 | InnerMatcher: callExpr(callee(InnerMatcher: functionDecl(hasName( |
| 135 | Name: "::testing::internal::" |
| 136 | "MakePredicateFormatterFromMatcher" ))), |
| 137 | hasArgument(N: 0, InnerMatcher: ignoringImplicit(InnerMatcher: Matcher))))))); |
| 138 | } |
| 139 | |
| 140 | static internal::BindableMatcher<Stmt> |
| 141 | gtestCallInternal(MacroType Macro, StatementMatcher MockCall, MockArgs Args) { |
| 142 | // A ON_CALL or EXPECT_CALL macro expands to different AST structures |
| 143 | // depending on whether the mock method has arguments or not. |
| 144 | switch (Args) { |
| 145 | // For example, |
| 146 | // `ON_CALL(mock, TwoParamMethod)` is expanded to |
| 147 | // `mock.gmock_TwoArgsMethod(WithoutMatchers(), |
| 148 | // nullptr).InternalDefaultActionSetAt(...)`. |
| 149 | // EXPECT_CALL is the same except |
| 150 | // that it calls `InternalExpectedAt` instead of `InternalDefaultActionSetAt` |
| 151 | // in the end. |
| 152 | case MockArgs::None: |
| 153 | return cxxMemberCallExpr( |
| 154 | isExpandedFromMacro(MacroName: getMacroName(Macro, Operation: "CALL" )), |
| 155 | callee(InnerMatcher: functionDecl(hasName(Name: getSpecSetterName(Macro)))), |
| 156 | onImplicitObjectArgument(InnerMatcher: ignoringImplicit(InnerMatcher: MockCall))); |
| 157 | // For example, |
| 158 | // `ON_CALL(mock, TwoParamMethod(m1, m2))` is expanded to |
| 159 | // `mock.gmock_TwoParamMethod(m1,m2)(WithoutMatchers(), |
| 160 | // nullptr).InternalDefaultActionSetAt(...)`. |
| 161 | // EXPECT_CALL is the same except that it calls `InternalExpectedAt` instead |
| 162 | // of `InternalDefaultActionSetAt` in the end. |
| 163 | case MockArgs::Some: |
| 164 | return cxxMemberCallExpr( |
| 165 | isExpandedFromMacro(MacroName: getMacroName(Macro, Operation: "CALL" )), |
| 166 | callee(InnerMatcher: functionDecl(hasName(Name: getSpecSetterName(Macro)))), |
| 167 | onImplicitObjectArgument(InnerMatcher: ignoringImplicit(InnerMatcher: cxxOperatorCallExpr( |
| 168 | hasOverloadedOperatorName(Name: "()" ), argumentCountIs(N: 3), |
| 169 | hasArgument(N: 0, InnerMatcher: ignoringImplicit(InnerMatcher: MockCall)))))); |
| 170 | } |
| 171 | llvm_unreachable("Unhandled MockArgs enum" ); |
| 172 | } |
| 173 | |
| 174 | static internal::BindableMatcher<Stmt> |
| 175 | gtestCallInternal(MacroType Macro, StatementMatcher MockObject, |
| 176 | llvm::StringRef MockMethodName, MockArgs Args) { |
| 177 | return gtestCallInternal( |
| 178 | Macro, |
| 179 | MockCall: cxxMemberCallExpr( |
| 180 | onImplicitObjectArgument(InnerMatcher: MockObject), |
| 181 | callee(InnerMatcher: functionDecl(hasName(Name: ("gmock_" + MockMethodName).str())))), |
| 182 | Args); |
| 183 | } |
| 184 | |
| 185 | internal::BindableMatcher<Stmt> gtestAssert(GtestCmp Cmp, StatementMatcher Left, |
| 186 | StatementMatcher Right) { |
| 187 | return gtestComparisonInternal(Macro: MacroType::Assert, Cmp, Left, Right); |
| 188 | } |
| 189 | |
| 190 | internal::BindableMatcher<Stmt> gtestExpect(GtestCmp Cmp, StatementMatcher Left, |
| 191 | StatementMatcher Right) { |
| 192 | return gtestComparisonInternal(Macro: MacroType::Expect, Cmp, Left, Right); |
| 193 | } |
| 194 | |
| 195 | internal::BindableMatcher<Stmt> gtestAssertThat(StatementMatcher Actual, |
| 196 | StatementMatcher Matcher) { |
| 197 | return gtestThatInternal(Macro: MacroType::Assert, Actual, Matcher); |
| 198 | } |
| 199 | |
| 200 | internal::BindableMatcher<Stmt> gtestExpectThat(StatementMatcher Actual, |
| 201 | StatementMatcher Matcher) { |
| 202 | return gtestThatInternal(Macro: MacroType::Expect, Actual, Matcher); |
| 203 | } |
| 204 | |
| 205 | internal::BindableMatcher<Stmt> gtestOnCall(StatementMatcher MockObject, |
| 206 | llvm::StringRef MockMethodName, |
| 207 | MockArgs Args) { |
| 208 | return gtestCallInternal(Macro: MacroType::On, MockObject, MockMethodName, Args); |
| 209 | } |
| 210 | |
| 211 | internal::BindableMatcher<Stmt> gtestOnCall(StatementMatcher MockCall, |
| 212 | MockArgs Args) { |
| 213 | return gtestCallInternal(Macro: MacroType::On, MockCall, Args); |
| 214 | } |
| 215 | |
| 216 | internal::BindableMatcher<Stmt> gtestExpectCall(StatementMatcher MockObject, |
| 217 | llvm::StringRef MockMethodName, |
| 218 | MockArgs Args) { |
| 219 | return gtestCallInternal(Macro: MacroType::Expect, MockObject, MockMethodName, Args); |
| 220 | } |
| 221 | |
| 222 | internal::BindableMatcher<Stmt> gtestExpectCall(StatementMatcher MockCall, |
| 223 | MockArgs Args) { |
| 224 | return gtestCallInternal(Macro: MacroType::Expect, MockCall, Args); |
| 225 | } |
| 226 | |
| 227 | } // end namespace ast_matchers |
| 228 | } // end namespace clang |
| 229 | |