forked from OSchip/llvm-project
235 lines
8.9 KiB
C++
235 lines
8.9 KiB
C++
//===- GtestMatchers.cpp - AST Matchers for Gtest ---------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements several matchers for popular gtest macros. In general,
|
|
// AST matchers cannot match calls to macros. However, we can simulate such
|
|
// matches if the macro definition has identifiable elements that themselves can
|
|
// be matched. In that case, we can match on those elements and then check that
|
|
// the match occurs within an expansion of the desired macro. The more uncommon
|
|
// the identified elements, the more efficient this process will be.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/ASTMatchers/GtestMatchers.h"
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
namespace clang {
|
|
namespace ast_matchers {
|
|
namespace {
|
|
|
|
enum class MacroType {
|
|
Expect,
|
|
Assert,
|
|
On,
|
|
};
|
|
|
|
} // namespace
|
|
|
|
static DeclarationMatcher getComparisonDecl(GtestCmp Cmp) {
|
|
switch (Cmp) {
|
|
case GtestCmp::Eq:
|
|
return cxxMethodDecl(hasName("Compare"),
|
|
ofClass(cxxRecordDecl(isSameOrDerivedFrom(
|
|
hasName("::testing::internal::EqHelper")))));
|
|
case GtestCmp::Ne:
|
|
return functionDecl(hasName("::testing::internal::CmpHelperNE"));
|
|
case GtestCmp::Ge:
|
|
return functionDecl(hasName("::testing::internal::CmpHelperGE"));
|
|
case GtestCmp::Gt:
|
|
return functionDecl(hasName("::testing::internal::CmpHelperGT"));
|
|
case GtestCmp::Le:
|
|
return functionDecl(hasName("::testing::internal::CmpHelperLE"));
|
|
case GtestCmp::Lt:
|
|
return functionDecl(hasName("::testing::internal::CmpHelperLT"));
|
|
}
|
|
llvm_unreachable("Unhandled GtestCmp enum");
|
|
}
|
|
|
|
static llvm::StringRef getMacroTypeName(MacroType Macro) {
|
|
switch (Macro) {
|
|
case MacroType::Expect:
|
|
return "EXPECT";
|
|
case MacroType::Assert:
|
|
return "ASSERT";
|
|
case MacroType::On:
|
|
return "ON";
|
|
}
|
|
llvm_unreachable("Unhandled MacroType enum");
|
|
}
|
|
|
|
static llvm::StringRef getComparisonTypeName(GtestCmp Cmp) {
|
|
switch (Cmp) {
|
|
case GtestCmp::Eq:
|
|
return "EQ";
|
|
case GtestCmp::Ne:
|
|
return "NE";
|
|
case GtestCmp::Ge:
|
|
return "GE";
|
|
case GtestCmp::Gt:
|
|
return "GT";
|
|
case GtestCmp::Le:
|
|
return "LE";
|
|
case GtestCmp::Lt:
|
|
return "LT";
|
|
}
|
|
llvm_unreachable("Unhandled GtestCmp enum");
|
|
}
|
|
|
|
static std::string getMacroName(MacroType Macro, GtestCmp Cmp) {
|
|
return (getMacroTypeName(Macro) + "_" + getComparisonTypeName(Cmp)).str();
|
|
}
|
|
|
|
static std::string getMacroName(MacroType Macro, llvm::StringRef Operation) {
|
|
return (getMacroTypeName(Macro) + "_" + Operation).str();
|
|
}
|
|
|
|
// Under the hood, ON_CALL is expanded to a call to `InternalDefaultActionSetAt`
|
|
// to set a default action spec to the underlying function mocker, while
|
|
// EXPECT_CALL is expanded to a call to `InternalExpectedAt` to set a new
|
|
// expectation spec.
|
|
static llvm::StringRef getSpecSetterName(MacroType Macro) {
|
|
switch (Macro) {
|
|
case MacroType::On:
|
|
return "InternalDefaultActionSetAt";
|
|
case MacroType::Expect:
|
|
return "InternalExpectedAt";
|
|
default:
|
|
llvm_unreachable("Unhandled MacroType enum");
|
|
}
|
|
llvm_unreachable("Unhandled MacroType enum");
|
|
}
|
|
|
|
// In general, AST matchers cannot match calls to macros. However, we can
|
|
// simulate such matches if the macro definition has identifiable elements that
|
|
// themselves can be matched. In that case, we can match on those elements and
|
|
// then check that the match occurs within an expansion of the desired
|
|
// macro. The more uncommon the identified elements, the more efficient this
|
|
// process will be.
|
|
//
|
|
// We use this approach to implement the derived matchers gtestAssert and
|
|
// gtestExpect.
|
|
static internal::BindableMatcher<Stmt>
|
|
gtestComparisonInternal(MacroType Macro, GtestCmp Cmp, StatementMatcher Left,
|
|
StatementMatcher Right) {
|
|
return callExpr(isExpandedFromMacro(getMacroName(Macro, Cmp)),
|
|
callee(getComparisonDecl(Cmp)), hasArgument(2, Left),
|
|
hasArgument(3, Right));
|
|
}
|
|
|
|
static internal::BindableMatcher<Stmt>
|
|
gtestThatInternal(MacroType Macro, StatementMatcher Actual,
|
|
StatementMatcher Matcher) {
|
|
return cxxOperatorCallExpr(
|
|
isExpandedFromMacro(getMacroName(Macro, "THAT")),
|
|
hasOverloadedOperatorName("()"), hasArgument(2, Actual),
|
|
hasArgument(
|
|
0, expr(hasType(classTemplateSpecializationDecl(hasName(
|
|
"::testing::internal::PredicateFormatterFromMatcher"))),
|
|
ignoringImplicit(
|
|
callExpr(callee(functionDecl(hasName(
|
|
"::testing::internal::"
|
|
"MakePredicateFormatterFromMatcher"))),
|
|
hasArgument(0, ignoringImplicit(Matcher)))))));
|
|
}
|
|
|
|
static internal::BindableMatcher<Stmt>
|
|
gtestCallInternal(MacroType Macro, StatementMatcher MockCall, MockArgs Args) {
|
|
// A ON_CALL or EXPECT_CALL macro expands to different AST structures
|
|
// depending on whether the mock method has arguments or not.
|
|
switch (Args) {
|
|
// For example,
|
|
// `ON_CALL(mock, TwoParamMethod)` is expanded to
|
|
// `mock.gmock_TwoArgsMethod(WithoutMatchers(),
|
|
// nullptr).InternalDefaultActionSetAt(...)`.
|
|
// EXPECT_CALL is the same except
|
|
// that it calls `InternalExpectedAt` instead of `InternalDefaultActionSetAt`
|
|
// in the end.
|
|
case MockArgs::None:
|
|
return cxxMemberCallExpr(
|
|
isExpandedFromMacro(getMacroName(Macro, "CALL")),
|
|
callee(functionDecl(hasName(getSpecSetterName(Macro)))),
|
|
onImplicitObjectArgument(ignoringImplicit(MockCall)));
|
|
// For example,
|
|
// `ON_CALL(mock, TwoParamMethod(m1, m2))` is expanded to
|
|
// `mock.gmock_TwoParamMethod(m1,m2)(WithoutMatchers(),
|
|
// nullptr).InternalDefaultActionSetAt(...)`.
|
|
// EXPECT_CALL is the same except that it calls `InternalExpectedAt` instead
|
|
// of `InternalDefaultActionSetAt` in the end.
|
|
case MockArgs::Some:
|
|
return cxxMemberCallExpr(
|
|
isExpandedFromMacro(getMacroName(Macro, "CALL")),
|
|
callee(functionDecl(hasName(getSpecSetterName(Macro)))),
|
|
onImplicitObjectArgument(ignoringImplicit(cxxOperatorCallExpr(
|
|
hasOverloadedOperatorName("()"), argumentCountIs(3),
|
|
hasArgument(0, ignoringImplicit(MockCall))))));
|
|
}
|
|
llvm_unreachable("Unhandled MockArgs enum");
|
|
}
|
|
|
|
static internal::BindableMatcher<Stmt>
|
|
gtestCallInternal(MacroType Macro, StatementMatcher MockObject,
|
|
llvm::StringRef MockMethodName, MockArgs Args) {
|
|
return gtestCallInternal(
|
|
Macro,
|
|
cxxMemberCallExpr(
|
|
onImplicitObjectArgument(MockObject),
|
|
callee(functionDecl(hasName(("gmock_" + MockMethodName).str())))),
|
|
Args);
|
|
}
|
|
|
|
internal::BindableMatcher<Stmt> gtestAssert(GtestCmp Cmp, StatementMatcher Left,
|
|
StatementMatcher Right) {
|
|
return gtestComparisonInternal(MacroType::Assert, Cmp, Left, Right);
|
|
}
|
|
|
|
internal::BindableMatcher<Stmt> gtestExpect(GtestCmp Cmp, StatementMatcher Left,
|
|
StatementMatcher Right) {
|
|
return gtestComparisonInternal(MacroType::Expect, Cmp, Left, Right);
|
|
}
|
|
|
|
internal::BindableMatcher<Stmt> gtestAssertThat(StatementMatcher Actual,
|
|
StatementMatcher Matcher) {
|
|
return gtestThatInternal(MacroType::Assert, Actual, Matcher);
|
|
}
|
|
|
|
internal::BindableMatcher<Stmt> gtestExpectThat(StatementMatcher Actual,
|
|
StatementMatcher Matcher) {
|
|
return gtestThatInternal(MacroType::Expect, Actual, Matcher);
|
|
}
|
|
|
|
internal::BindableMatcher<Stmt> gtestOnCall(StatementMatcher MockObject,
|
|
llvm::StringRef MockMethodName,
|
|
MockArgs Args) {
|
|
return gtestCallInternal(MacroType::On, MockObject, MockMethodName, Args);
|
|
}
|
|
|
|
internal::BindableMatcher<Stmt> gtestOnCall(StatementMatcher MockCall,
|
|
MockArgs Args) {
|
|
return gtestCallInternal(MacroType::On, MockCall, Args);
|
|
}
|
|
|
|
internal::BindableMatcher<Stmt> gtestExpectCall(StatementMatcher MockObject,
|
|
llvm::StringRef MockMethodName,
|
|
MockArgs Args) {
|
|
return gtestCallInternal(MacroType::Expect, MockObject, MockMethodName, Args);
|
|
}
|
|
|
|
internal::BindableMatcher<Stmt> gtestExpectCall(StatementMatcher MockCall,
|
|
MockArgs Args) {
|
|
return gtestCallInternal(MacroType::Expect, MockCall, Args);
|
|
}
|
|
|
|
} // end namespace ast_matchers
|
|
} // end namespace clang
|