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