From 35d8c4d06ffdeb0714874ac094da84952cdf9f66 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Sat, 5 Sep 2026 13:31:10 +0200 Subject: [PATCH] Align synthesized annotation toString() with JDK for NaN/Infinity Closes gh-37244 --- ...izedMergedAnnotationInvocationHandler.java | 28 +++++++++++++++---- .../annotation/MergedAnnotationsTests.java | 24 ++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java index 24c21acf6af..1b60933326a 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java @@ -160,14 +160,12 @@ final class SynthesizedMergedAnnotationInvocationHandler i } /** - * This method currently does not address the following issues which we may + * This method currently does not address the following issue which we may * choose to address at a later point in time. * * * @param value the attribute value to format * @return the formatted string representation @@ -199,10 +197,30 @@ final class SynthesizedMergedAnnotationInvocationHandler i return Long.toString((Long) value) + 'L'; } if (type == Float.class) { - return Float.toString((Float) value) + 'f'; + float floatValue = (Float) value; + if (Float.isNaN(floatValue)) { + return "0.0f/0.0f"; + } + if (floatValue == Float.POSITIVE_INFINITY) { + return "1.0f/0.0f"; + } + if (floatValue == Float.NEGATIVE_INFINITY) { + return "-1.0f/0.0f"; + } + return Float.toString(floatValue) + 'f'; } if (type == Double.class) { - return Double.toString((Double) value); + double doubleValue = (Double) value; + if (Double.isNaN(doubleValue)) { + return "0.0/0.0"; + } + if (doubleValue == Double.POSITIVE_INFINITY) { + return "1.0/0.0"; + } + if (doubleValue == Double.NEGATIVE_INFINITY) { + return "-1.0/0.0"; + } + return Double.toString(doubleValue); } if (value instanceof Enum e) { return e.name(); diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java index c5c386e71d6..61fc0ffe367 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/MergedAnnotationsTests.java @@ -2060,6 +2060,30 @@ class MergedAnnotationsTests { .isEqualTo("@%s({\"FromValueAttributeMeta\"})", ValueAttribute.class.getCanonicalName()); } + @Test // gh-37244 + void toStringForSynthesizedAnnotationsWithNonFiniteFloatingPointValues() { + Map attributes = Map.of( + "name", "test", + "floatValue", Float.NaN, + "doubleValue", Double.NaN); + RequestMapping mapping = MergedAnnotation.of(RequestMapping.class, attributes).synthesize(); + assertThat(mapping).asString().contains("floatValue=0.0f/0.0f", "doubleValue=0.0/0.0"); + + attributes = Map.of( + "name", "test", + "floatValue", Float.POSITIVE_INFINITY, + "doubleValue", Double.POSITIVE_INFINITY); + mapping = MergedAnnotation.of(RequestMapping.class, attributes).synthesize(); + assertThat(mapping).asString().contains("floatValue=1.0f/0.0f", "doubleValue=1.0/0.0"); + + attributes = Map.of( + "name", "test", + "floatValue", Float.NEGATIVE_INFINITY, + "doubleValue", Double.NEGATIVE_INFINITY); + mapping = MergedAnnotation.of(RequestMapping.class, attributes).synthesize(); + assertThat(mapping).asString().contains("floatValue=-1.0f/0.0f", "doubleValue=-1.0/0.0"); + } + @Test void equalsForSynthesizedAnnotations() throws Exception { Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute");