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 
getComparisonDecl(GtestCmp Cmp)38 static DeclarationMatcher getComparisonDecl(GtestCmp Cmp) {
39   switch (Cmp) {
40   case GtestCmp::Eq:
41     return cxxMethodDecl(hasName("Compare"),
42                          ofClass(cxxRecordDecl(isSameOrDerivedFrom(
43                              hasName("::testing::internal::EqHelper")))));
44   case GtestCmp::Ne:
45     return functionDecl(hasName("::testing::internal::CmpHelperNE"));
46   case GtestCmp::Ge:
47     return functionDecl(hasName("::testing::internal::CmpHelperGE"));
48   case GtestCmp::Gt:
49     return functionDecl(hasName("::testing::internal::CmpHelperGT"));
50   case GtestCmp::Le:
51     return functionDecl(hasName("::testing::internal::CmpHelperLE"));
52   case GtestCmp::Lt:
53     return functionDecl(hasName("::testing::internal::CmpHelperLT"));
54   }
55   llvm_unreachable("Unhandled GtestCmp enum");
56 }
57 
getMacroTypeName(MacroType Macro)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 
getComparisonTypeName(GtestCmp Cmp)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 
getMacroName(MacroType Macro,GtestCmp Cmp)88 static std::string getMacroName(MacroType Macro, GtestCmp Cmp) {
89   return (getMacroTypeName(Macro) + "_" + getComparisonTypeName(Cmp)).str();
90 }
91 
getMacroName(MacroType Macro,llvm::StringRef Operation)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.
getSpecSetterName(MacroType Macro)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>
gtestComparisonInternal(MacroType Macro,GtestCmp Cmp,StatementMatcher Left,StatementMatcher Right)122 gtestComparisonInternal(MacroType Macro, GtestCmp Cmp, StatementMatcher Left,
123                         StatementMatcher Right) {
124   return callExpr(isExpandedFromMacro(getMacroName(Macro, Cmp)),
125                   callee(getComparisonDecl(Cmp)), hasArgument(2, Left),
126                   hasArgument(3, Right));
127 }
128 
129 static internal::BindableMatcher<Stmt>
gtestThatInternal(MacroType Macro,StatementMatcher Actual,StatementMatcher Matcher)130 gtestThatInternal(MacroType Macro, StatementMatcher Actual,
131                   StatementMatcher Matcher) {
132   return cxxOperatorCallExpr(
133       isExpandedFromMacro(getMacroName(Macro, "THAT")),
134       hasOverloadedOperatorName("()"), hasArgument(2, Actual),
135       hasArgument(
136           0, expr(hasType(classTemplateSpecializationDecl(hasName(
137                       "::testing::internal::PredicateFormatterFromMatcher"))),
138                   ignoringImplicit(
139                       callExpr(callee(functionDecl(hasName(
140                                    "::testing::internal::"
141                                    "MakePredicateFormatterFromMatcher"))),
142                                hasArgument(0, ignoringImplicit(Matcher)))))));
143 }
144 
145 static internal::BindableMatcher<Stmt>
gtestCallInternal(MacroType Macro,StatementMatcher MockCall,MockArgs Args)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(getMacroName(Macro, "CALL")),
160         callee(functionDecl(hasName(getSpecSetterName(Macro)))),
161         onImplicitObjectArgument(ignoringImplicit(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(getMacroName(Macro, "CALL")),
171         callee(functionDecl(hasName(getSpecSetterName(Macro)))),
172         onImplicitObjectArgument(ignoringImplicit(cxxOperatorCallExpr(
173             hasOverloadedOperatorName("()"), argumentCountIs(3),
174             hasArgument(0, ignoringImplicit(MockCall))))));
175   }
176   llvm_unreachable("Unhandled MockArgs enum");
177 }
178 
179 static internal::BindableMatcher<Stmt>
gtestCallInternal(MacroType Macro,StatementMatcher MockObject,llvm::StringRef MockMethodName,MockArgs Args)180 gtestCallInternal(MacroType Macro, StatementMatcher MockObject,
181                   llvm::StringRef MockMethodName, MockArgs Args) {
182   return gtestCallInternal(
183       Macro,
184       cxxMemberCallExpr(
185           onImplicitObjectArgument(MockObject),
186           callee(functionDecl(hasName(("gmock_" + MockMethodName).str())))),
187       Args);
188 }
189 
gtestAssert(GtestCmp Cmp,StatementMatcher Left,StatementMatcher Right)190 internal::BindableMatcher<Stmt> gtestAssert(GtestCmp Cmp, StatementMatcher Left,
191                                             StatementMatcher Right) {
192   return gtestComparisonInternal(MacroType::Assert, Cmp, Left, Right);
193 }
194 
gtestExpect(GtestCmp Cmp,StatementMatcher Left,StatementMatcher Right)195 internal::BindableMatcher<Stmt> gtestExpect(GtestCmp Cmp, StatementMatcher Left,
196                                             StatementMatcher Right) {
197   return gtestComparisonInternal(MacroType::Expect, Cmp, Left, Right);
198 }
199 
gtestAssertThat(StatementMatcher Actual,StatementMatcher Matcher)200 internal::BindableMatcher<Stmt> gtestAssertThat(StatementMatcher Actual,
201                                                 StatementMatcher Matcher) {
202   return gtestThatInternal(MacroType::Assert, Actual, Matcher);
203 }
204 
gtestExpectThat(StatementMatcher Actual,StatementMatcher Matcher)205 internal::BindableMatcher<Stmt> gtestExpectThat(StatementMatcher Actual,
206                                                 StatementMatcher Matcher) {
207   return gtestThatInternal(MacroType::Expect, Actual, Matcher);
208 }
209 
gtestOnCall(StatementMatcher MockObject,llvm::StringRef MockMethodName,MockArgs Args)210 internal::BindableMatcher<Stmt> gtestOnCall(StatementMatcher MockObject,
211                                             llvm::StringRef MockMethodName,
212                                             MockArgs Args) {
213   return gtestCallInternal(MacroType::On, MockObject, MockMethodName, Args);
214 }
215 
gtestOnCall(StatementMatcher MockCall,MockArgs Args)216 internal::BindableMatcher<Stmt> gtestOnCall(StatementMatcher MockCall,
217                                             MockArgs Args) {
218   return gtestCallInternal(MacroType::On, MockCall, Args);
219 }
220 
gtestExpectCall(StatementMatcher MockObject,llvm::StringRef MockMethodName,MockArgs Args)221 internal::BindableMatcher<Stmt> gtestExpectCall(StatementMatcher MockObject,
222                                                 llvm::StringRef MockMethodName,
223                                                 MockArgs Args) {
224   return gtestCallInternal(MacroType::Expect, MockObject, MockMethodName, Args);
225 }
226 
gtestExpectCall(StatementMatcher MockCall,MockArgs Args)227 internal::BindableMatcher<Stmt> gtestExpectCall(StatementMatcher MockCall,
228                                                 MockArgs Args) {
229   return gtestCallInternal(MacroType::Expect, MockCall, Args);
230 }
231 
232 } // end namespace ast_matchers
233 } // end namespace clang
234