google / googletest

GoogleTest - Google Testing and Mocking Framework
https://google.github.io/googletest/
BSD 3-Clause "New" or "Revised" License
34.96k stars 10.17k forks source link

[Bug]: Cannot mock recursive lambda with deducing this parameter #4549

Open zmajeed opened 6 months ago

zmajeed commented 6 months ago

Describe the issue

Using MockFunction to mock a lambda function with deducing this self parameter gives a compile error with gcc 14.1 on Ubuntu 24.04

test.cpp: In member function ‘virtual void ::testing::Expr_test_1002_Test::TestBody()’:
test.cpp:975:20: error: ‘auto’ parameter not permitted in this context
  975 |   MockFunction<int(this const auto&, const Neg&)> mockNegVisit;
      |                    ^~~~~~~~~~~~~~~~
test.cpp:975:20: error: a function type cannot have an explicit object parameter
test.cpp:975:20: note: the type of an explicit object member function is a regular function type
In file included from googletest/googlemock/include/gmock/gmock-function-mocker.h:43,
                 from googletest/googlemock/include/gmock/gmock.h:58,
                 from test.cpp:14:
googletest/googlemock/include/gmock/gmock-spec-builders.h: In instantiation of ‘class testing::MockFunction<int(...)>’:
test.cpp:975:44:   required from here
test.cpp:975:20: note:   975 |   MockFunction<int(this const auto&, const Neg&)> mockNegVisit;
test.cpp:975:20: note:       |                                            ^~~~~~~~~~~~
googletest/googlemock/include/gmock/gmock-spec-builders.h:2033:7: error: invalid use of incomplete type ‘struct testing::internal::SignatureOf<int(...), void>’
 2033 | class MockFunction : public internal::MockFunction<internal::SignatureOfT<F>> {
      |       ^~~~~~~~~~~~
googletest/googlemock/include/gmock/gmock-spec-builders.h:1955:8: note: declaration of ‘struct testing::internal::SignatureOf<int(...), void>’
 1955 | struct SignatureOf;
      |        ^~~~~~~~~~~
googletest/googlemock/include/gmock/gmock-spec-builders.h:2034:9: error: invalid use of incomplete type ‘struct testing::internal::SignatureOf<int(...), void>’
 2034 |   using Base = internal::MockFunction<internal::SignatureOfT<F>>;
      |         ^~~~
googletest/googlemock/include/gmock/gmock-spec-builders.h:1955:8: note: declaration of ‘struct testing::internal::SignatureOf<int(...), void>’
 1955 | struct SignatureOf;
      |        ^~~~~~~~~~~
test.cpp: In lambda function:
test.cpp:983:20: error: ‘class testing::MockFunction<int(...)>’ has no member named ‘AsStdFunction’
  983 |       mockNegVisit.AsStdFunction(),

The code defines a recursive variant with visit lambda overloads

namespace {
struct Expr;
struct Neg {
  shared_ptr<Expr> expr;
};
struct Add {
  shared_ptr<Expr> lhs, rhs;
};
struct Mul {
  shared_ptr<Expr> lhs, rhs;
};
struct Expr: variant<int, Neg, Add, Mul> {
  using variant::variant;
};

auto intVisit = [](int i) -> int { return i; };
auto negVisit = [](this const auto& self, const Neg& n) -> int { return -visit(self, *n.expr); };
auto addVisit = [](this const auto& self, const Add& a) -> int { return visit(self, *a.lhs) + visit(self, *a.rhs); };
auto mulVisit = [](this const auto& self, const Mul& m) -> int { return visit(self, *m.lhs) * visit(self, *m.rhs); };

template<class... Ts>
struct overload: Ts... { using Ts::operator()...; };

auto evalExpr(const Expr& expr) -> int {
  return visit(overload{
    intVisit,
    negVisit,
    addVisit,
    mulVisit,
  },
  expr);
}
}

test_1001 works

TEST(Expr, test_1001) {
// 5 + 2 = 7
  EXPECT_EQ(evalExpr(Add{make_shared<Expr>(5), make_shared<Expr>(2)}), 7);
}

test_1002 fails to compile mockNegVisit though mockIntVisit without the auto self parameter works

TEST(Expr, test_1002) {
// 5 + 2 = 7
  Add a{make_shared<Expr>(5), make_shared<Expr>(2)};

  MockFunction<int(int)> mockIntVisit;
  MockFunction<int(this const auto&, const Neg&)> mockNegVisit;

  ON_CALL(mockIntVisit, Call).WillByDefault(ReturnArg<0>());

  auto evalExprMock = [&mockIntVisit, &mockNegVisit](const Expr& expr) -> int {
    return visit(overload{
      mockIntVisit.AsStdFunction(),
      mockNegVisit.AsStdFunction(),
      addVisit,
      mulVisit,
    },
    expr);
  };
  auto result = evalExprMock(a);
  EXPECT_EQ(result, 7);
}

Is there a way to mock a recursive lambda?

Steps to reproduce the problem

As above

What version of GoogleTest are you using?

latest main branch of GoogleTest repo

What operating system and version are you using?

Ubuntu 24.04

What compiler and version are you using?

GCC 14.1

What build system are you using?

CMake 3.29.3

Additional context

No response