From 8f8a1a02d5c5cb967d240feee3ffac00d66f22a2 Mon Sep 17 00:00:00 2001
From: Victor Zverovich <viz@fb.com>
Date: Fri, 14 Jan 2022 13:08:14 -0800
Subject: [PATCH] Fix handling of formattable types implicitly convertible to
 pointers

---
 include/fmt/core.h |  5 +++--
 test/core-test.cc  | 21 ++++++++++++++++++++-
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/include/fmt/core.h b/include/fmt/core.h
index f2d21e5c5a..12571ce0da 100644
--- a/include/fmt/core.h
+++ b/include/fmt/core.h
@@ -1398,10 +1398,11 @@ template <typename Context> struct arg_mapper {
   template <
       typename T,
       FMT_ENABLE_IF(
-          std::is_member_pointer<T>::value ||
+          std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
           std::is_function<typename std::remove_pointer<T>::type>::value ||
           (std::is_convertible<const T&, const void*>::value &&
-           !std::is_convertible<const T&, const char_type*>::value))>
+           !std::is_convertible<const T&, const char_type*>::value &&
+           !has_formatter<T, Context>::value))>
   FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
     return {};
   }
diff --git a/test/core-test.cc b/test/core-test.cc
index b2f2097ea1..c9eea8ffd8 100644
--- a/test/core-test.cc
+++ b/test/core-test.cc
@@ -737,6 +737,24 @@ struct convertible_to_pointer {
   operator const int*() const { return nullptr; }
 };
 
+struct convertible_to_pointer_formattable {
+  operator const int*() const { return nullptr; }
+};
+
+FMT_BEGIN_NAMESPACE
+template <> struct formatter<convertible_to_pointer_formattable> {
+  auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  auto format(convertible_to_pointer_formattable, format_context& ctx) const
+      -> decltype(ctx.out()) {
+    auto test = string_view("test");
+    return std::copy_n(test.data(), test.size(), ctx.out());
+  }
+};
+FMT_END_NAMESPACE
+
 enum class test_scoped_enum {};
 
 TEST(core_test, is_formattable) {
@@ -770,11 +788,12 @@ TEST(core_test, is_formattable) {
 #endif
 
   static_assert(!fmt::is_formattable<convertible_to_pointer>::value, "");
+  const auto f = convertible_to_pointer_formattable();
+  EXPECT_EQ(fmt::format("{}", f), "test");
 
   static_assert(!fmt::is_formattable<void (*)()>::value, "");
 
   struct s;
-
   static_assert(!fmt::is_formattable<int(s::*)>::value, "");
   static_assert(!fmt::is_formattable<int (s::*)()>::value, "");
   static_assert(!fmt::is_formattable<test_scoped_enum>::value, "");
