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 #include "clang/ASTMatchers/GtestMatchers.h"
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/AST/ASTConsumer.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/AST/RecursiveASTVisitor.h"
14 #include "llvm/ADT/DenseMap.h"
15 #include "llvm/ADT/StringMap.h"
16 #include "llvm/Support/Timer.h"
17 #include <deque>
18 #include <memory>
19 #include <set>
20 
21 namespace clang {
22 namespace ast_matchers {
23 
24 static DeclarationMatcher getComparisonDecl(GtestCmp Cmp) {
25   switch (Cmp) {
26     case GtestCmp::Eq:
27       return cxxMethodDecl(hasName("Compare"),
28                            ofClass(cxxRecordDecl(isSameOrDerivedFrom(
29                                hasName("::testing::internal::EqHelper")))));
30     case GtestCmp::Ne:
31       return functionDecl(hasName("::testing::internal::CmpHelperNE"));
32     case GtestCmp::Ge:
33       return functionDecl(hasName("::testing::internal::CmpHelperGE"));
34     case GtestCmp::Gt:
35       return functionDecl(hasName("::testing::internal::CmpHelperGT"));
36     case GtestCmp::Le:
37       return functionDecl(hasName("::testing::internal::CmpHelperLE"));
38     case GtestCmp::Lt:
39       return functionDecl(hasName("::testing::internal::CmpHelperLT"));
40   }
41   llvm_unreachable("Unhandled GtestCmp enum");
42 }
43 
44 static llvm::StringRef getAssertMacro(GtestCmp Cmp) {
45   switch (Cmp) {
46     case GtestCmp::Eq:
47       return "ASSERT_EQ";
48     case GtestCmp::Ne:
49       return "ASSERT_NE";
50     case GtestCmp::Ge:
51       return "ASSERT_GE";
52     case GtestCmp::Gt:
53       return "ASSERT_GT";
54     case GtestCmp::Le:
55       return "ASSERT_LE";
56     case GtestCmp::Lt:
57       return "ASSERT_LT";
58   }
59   llvm_unreachable("Unhandled GtestCmp enum");
60 }
61 
62 static llvm::StringRef getExpectMacro(GtestCmp Cmp) {
63   switch (Cmp) {
64     case GtestCmp::Eq:
65       return "EXPECT_EQ";
66     case GtestCmp::Ne:
67       return "EXPECT_NE";
68     case GtestCmp::Ge:
69       return "EXPECT_GE";
70     case GtestCmp::Gt:
71       return "EXPECT_GT";
72     case GtestCmp::Le:
73       return "EXPECT_LE";
74     case GtestCmp::Lt:
75       return "EXPECT_LT";
76   }
77   llvm_unreachable("Unhandled GtestCmp enum");
78 }
79 
80 // In general, AST matchers cannot match calls to macros. However, we can
81 // simulate such matches if the macro definition has identifiable elements that
82 // themselves can be matched. In that case, we can match on those elements and
83 // then check that the match occurs within an expansion of the desired
84 // macro. The more uncommon the identified elements, the more efficient this
85 // process will be.
86 //
87 // We use this approach to implement the derived matchers gtestAssert and
88 // gtestExpect.
89 internal::BindableMatcher<Stmt> gtestAssert(GtestCmp Cmp, StatementMatcher Left,
90                                             StatementMatcher Right) {
91   return callExpr(callee(getComparisonDecl(Cmp)),
92                   isExpandedFromMacro(getAssertMacro(Cmp).str()),
93                   hasArgument(2, Left), hasArgument(3, Right));
94 }
95 
96 internal::BindableMatcher<Stmt> gtestExpect(GtestCmp Cmp, StatementMatcher Left,
97                                             StatementMatcher Right) {
98   return callExpr(callee(getComparisonDecl(Cmp)),
99                   isExpandedFromMacro(getExpectMacro(Cmp).str()),
100                   hasArgument(2, Left), hasArgument(3, Right));
101 }
102 
103 } // end namespace ast_matchers
104 } // end namespace clang
105