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