From 982937b24c48b28dd3d66d60dc174409f6223a95 Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Sat, 15 Nov 2025 16:02:29 +0100 Subject: [PATCH] Fix panic when using EqualValues with uncomparable types --- assert/assertions.go | 34 +++++++++++++++++++++++++++++++--- assert/assertions_test.go | 4 ++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 6950636d3..3b49aff0f 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -177,10 +177,25 @@ func ObjectsAreEqualValues(expected, actual interface{}) bool { return false } + // Attempt conversion of expected to actual type + // This handles more cases than just the ConvertibleTo check above + if !expectedValue.CanConvert(actualType) { + // Types are not convertible, so they cannot be equal + // This prevents panics when calling [reflect.Value.Convert] + return false + } + + expectedConverted := expectedValue.Convert(actualType) + if !expectedConverted.CanInterface() { + // Cannot interface after conversion, so cannot be equal + // This prevents panics when calling [reflect.Value.Interface] + return false + } + if !isNumericType(expectedType) || !isNumericType(actualType) { // Attempt comparison after type conversion return reflect.DeepEqual( - expectedValue.Convert(actualType).Interface(), actual, + expectedConverted.Interface(), actual, ) } @@ -188,10 +203,23 @@ func ObjectsAreEqualValues(expected, actual interface{}) bool { // to overflow or underflow. So, we need to make sure to always convert // the smaller type to a larger type before comparing. if expectedType.Size() >= actualType.Size() { - return actualValue.Convert(expectedType).Interface() == expected + if !actualValue.CanConvert(expectedType) { + // Cannot convert actual to the expected type, so cannot be equal + // This is a hypothetical case to prevent panics when calling [reflect.Value.Convert] + return false + } + + actualConverted := actualValue.Convert(expectedType) + if !actualConverted.CanInterface() { + // Cannot interface after conversion, so cannot be equal + // This is a hypothetical case to prevent panics when calling [reflect.Value.Convert] + return false + } + + return actualConverted.Interface() == expected } - return expectedValue.Convert(actualType).Interface() == actual + return expectedConverted.Interface() == actual } // isNumericType returns true if the type is one of: diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 4975f5e41..e8656ad8f 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -161,6 +161,10 @@ func TestObjectsAreEqualValues(t *testing.T) { {3.14, complex128(1e+100 + 1e+100i), false}, {complex128(1e+10 + 1e+10i), complex64(1e+10 + 1e+10i), true}, {complex64(1e+10 + 1e+10i), complex128(1e+10 + 1e+10i), true}, + + // panics should be caught and treated as inequality + // https://github.com/stretchr/testify/issues/1699 + {[]int{1, 2}, (*[3]int)(nil), false}, } for _, c := range cases {