Skip to content

Commit 06b1473

Browse files
authored
Better error messages for unknown variant type (#2377)
* Better error messages for unknown variant type * MSVC fix * precompute length for compilers
1 parent 5cc407f commit 06b1473

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

include/glaze/core/meta.hpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,60 @@ namespace glz
721721
}
722722
}();
723723

724+
// Compile-time string listing the variant type IDs (e.g. "TYPE_A, TYPE_B, TYPE_C")
725+
// Used for error messages when no matching variant type is found
726+
namespace detail
727+
{
728+
template <is_variant T>
729+
inline constexpr size_t variant_ids_string_len = [] {
730+
constexpr auto& ids = ids_v<T>;
731+
constexpr auto N = ids.size();
732+
size_t len = 17; // "supported types: "
733+
for (size_t i = 0; i < N; ++i) {
734+
if (i > 0) len += 2; // ", "
735+
len += ids[i].size();
736+
}
737+
return len;
738+
}();
739+
740+
template <is_variant T, size_t Len>
741+
inline constexpr auto variant_ids_joined = [] {
742+
constexpr auto& ids = ids_v<T>;
743+
constexpr auto N = ids.size();
744+
std::array<char, Len + 1> arr{};
745+
const char* prefix = "supported types: ";
746+
size_t pos = 0;
747+
for (size_t i = 0; i < 17; ++i) arr[pos++] = prefix[i];
748+
for (size_t i = 0; i < N; ++i) {
749+
if (i > 0) {
750+
arr[pos++] = ',';
751+
arr[pos++] = ' ';
752+
}
753+
for (auto c : ids[i]) arr[pos++] = c;
754+
}
755+
arr[Len] = '\0';
756+
return arr;
757+
}();
758+
}
759+
760+
template <is_variant T>
761+
inline constexpr std::string_view variant_ids_string_v = [] {
762+
constexpr auto& ids = ids_v<T>;
763+
constexpr auto N = ids.size();
764+
765+
if constexpr (N == 0) {
766+
return std::string_view{};
767+
}
768+
else if constexpr (std::is_same_v<std::decay_t<decltype(ids[0])>, std::string_view>) {
769+
constexpr auto Len = detail::variant_ids_string_len<T>;
770+
auto& static_arr = detail::make_static<detail::variant_ids_joined<T, Len>>::value;
771+
return std::string_view{static_arr.data(), Len};
772+
}
773+
else {
774+
return std::string_view{};
775+
}
776+
}();
777+
724778
template <class T>
725779
concept versioned = requires { meta<std::decay_t<T>>::version; };
726780

include/glaze/json/read.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3916,6 +3916,7 @@ namespace glz
39163916
}
39173917
else {
39183918
ctx.error = error_code::no_matching_variant_type;
3919+
ctx.custom_error_message = variant_ids_string_v<T>;
39193920
return;
39203921
}
39213922
}
@@ -3972,6 +3973,7 @@ namespace glz
39723973
}
39733974
else {
39743975
ctx.error = error_code::no_matching_variant_type;
3976+
ctx.custom_error_message = variant_ids_string_v<T>;
39753977
return;
39763978
}
39773979
}

tests/json_test/variant_ambiguous_test.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,44 @@ suite variant_default_tests = [] {
11001100
};
11011101
};
11021102

1103+
// ============================================================================
1104+
// Variant error message tests - verify supported types in error messages
1105+
// ============================================================================
1106+
1107+
// Variant without a default handler (ids_size == variant_size)
1108+
using StrictActionVariant = std::variant<CreateAction, UpdateAction>;
1109+
1110+
template <>
1111+
struct glz::meta<StrictActionVariant>
1112+
{
1113+
static constexpr std::string_view tag = "action";
1114+
static constexpr auto ids = std::array{"CREATE", "UPDATE"};
1115+
};
1116+
1117+
suite variant_error_message_tests = [] {
1118+
"variant error message includes supported types - tag first"_test = [] {
1119+
std::string_view json = R"({"action":"DELETE","id":"456"})";
1120+
StrictActionVariant av{};
1121+
auto ec = glz::read_json(av, json);
1122+
expect(ec == glz::error_code::no_matching_variant_type);
1123+
auto error_msg = glz::format_error(ec, json);
1124+
expect(error_msg == R"(1:19: no_matching_variant_type
1125+
{"action":"DELETE","id":"456"}
1126+
^ supported types: CREATE, UPDATE)") << error_msg;
1127+
};
1128+
1129+
"variant error message includes supported types - tag last"_test = [] {
1130+
std::string_view json = R"({"id":"456","action":"UNKNOWN"})";
1131+
StrictActionVariant av{};
1132+
auto ec = glz::read_json(av, json);
1133+
expect(ec == glz::error_code::no_matching_variant_type);
1134+
auto error_msg = glz::format_error(ec, json);
1135+
expect(error_msg == R"(1:31: no_matching_variant_type
1136+
{"id":"456","action":"UNKNOWN"}
1137+
^ supported types: CREATE, UPDATE)") << error_msg;
1138+
};
1139+
};
1140+
11031141
// ============================================================================
11041142
// Numeric variant tests - int64_t and double
11051143
// ============================================================================

0 commit comments

Comments
 (0)