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 | |