From 2d4f308cc107f9fafc330c53f14eecd236f283cd Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 9 Sep 2022 11:33:48 +0200 Subject: [PATCH] Provide more context when the code of a value cannot be generated Closes gh-29118 --- ...nDefinitionPropertyValueCodeGenerator.java | 42 ++++++++++++----- ...nitionPropertyValueCodeGeneratorTests.java | 45 +++++++++++++++++++ 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGenerator.java index 87b82d0b8b5..e6547aaebfe 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGenerator.java @@ -85,7 +85,30 @@ class BeanDefinitionPropertyValueCodeGenerator { CodeBlock generateCode(@Nullable Object value) { ResolvableType type = (value != null) ? ResolvableType.forInstance(value) : ResolvableType.NONE; - return generateCode(value, type); + try { + return generateCode(value, type); + } + catch (Exception ex) { + throw new IllegalArgumentException(buildErrorMessage(value, type), ex); + } + } + + private CodeBlock generateCodeForElement(@Nullable Object value, ResolvableType type) { + try { + return generateCode(value, type); + } + catch (Exception ex) { + throw new IllegalArgumentException(buildErrorMessage(value, type), ex); + } + } + + private static String buildErrorMessage(@Nullable Object value, ResolvableType type) { + StringBuilder message = new StringBuilder("Failed to generate code for '"); + message.append(value).append("'"); + if (type != ResolvableType.NONE) { + message.append(" with type ").append(type); + } + return message.toString(); } private CodeBlock generateCode(@Nullable Object value, ResolvableType type) { @@ -98,8 +121,7 @@ class BeanDefinitionPropertyValueCodeGenerator { return code; } } - throw new IllegalArgumentException( - "'type' " + type + " must be supported for instance code generation"); + throw new IllegalArgumentException("Code generation does not support " + type); } @@ -304,7 +326,7 @@ class BeanDefinitionPropertyValueCodeGenerator { while (iterator.hasNext()) { Object element = iterator.next(); code.add("$L", BeanDefinitionPropertyValueCodeGenerator.this - .generateCode(element, elementType)); + .generateCodeForElement(element, elementType)); if (iterator.hasNext()) { code.add(", "); } @@ -371,9 +393,9 @@ class BeanDefinitionPropertyValueCodeGenerator { Entry entry = iterator.next(); code.add("$T.entry($L,$L)", Map.class, BeanDefinitionPropertyValueCodeGenerator.this - .generateCode(entry.getKey(), keyType), + .generateCodeForElement(entry.getKey(), keyType), BeanDefinitionPropertyValueCodeGenerator.this - .generateCode(entry.getValue(), valueType)); + .generateCodeForElement(entry.getValue(), valueType)); if (iterator.hasNext()) { code.add(", "); } @@ -457,9 +479,9 @@ class BeanDefinitionPropertyValueCodeGenerator { while (iterator.hasNext()) { Entry entry = iterator.next(); CodeBlock keyCode = BeanDefinitionPropertyValueCodeGenerator.this - .generateCode(entry.getKey(), keyType); + .generateCodeForElement(entry.getKey(), keyType); CodeBlock valueCode = BeanDefinitionPropertyValueCodeGenerator.this - .generateCode(entry.getValue(), valueType); + .generateCodeForElement(entry.getValue(), valueType); if (!useOfEntries) { code.add("$L, $L", keyCode, valueCode); } @@ -490,9 +512,9 @@ class BeanDefinitionPropertyValueCodeGenerator { LinkedHashMap.class, map.size()); map.forEach((key, value) -> method.addStatement("map.put($L, $L)", BeanDefinitionPropertyValueCodeGenerator.this - .generateCode(key, keyType), + .generateCodeForElement(key, keyType), BeanDefinitionPropertyValueCodeGenerator.this - .generateCode(value, valueType))); + .generateCodeForElement(value, valueType))); method.addStatement("return map"); }); return CodeBlock.of("$L()", generatedMethod.getName()); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGeneratorTests.java index 80351663ad8..11fd98eee3a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGeneratorTests.java @@ -50,6 +50,7 @@ import org.springframework.javapoet.MethodSpec; import org.springframework.javapoet.ParameterizedTypeName; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; /** * Tests for {@link BeanDefinitionPropertyValueCodeGenerator}. @@ -492,4 +493,48 @@ class BeanDefinitionPropertyValueCodeGeneratorTests { } + @Nested + static class ExceptionTests { + + @Test + void generateWhenUnsupportedDataTypeThrowsException() { + SampleValue sampleValue = new SampleValue("one"); + assertThatIllegalArgumentException().isThrownBy(() -> generateCode(sampleValue)) + .withMessageContaining("Failed to generate code for") + .withMessageContaining(sampleValue.toString()) + .withMessageContaining(SampleValue.class.getName()) + .havingCause() + .withMessageContaining("Code generation does not support") + .withMessageContaining(SampleValue.class.getName()); + } + + @Test + void generateWhenListOfUnsupportedElement() { + SampleValue one = new SampleValue("one"); + SampleValue two = new SampleValue("two"); + List list = List.of(one, two); + assertThatIllegalArgumentException().isThrownBy(() -> generateCode(list)) + .withMessageContaining("Failed to generate code for") + .withMessageContaining(list.toString()) + .withMessageContaining(list.getClass().getName()) + .havingCause() + .withMessageContaining("Failed to generate code for") + .withMessageContaining(one.toString()) + .withMessageContaining("?") + .havingCause() + .withMessageContaining("Code generation does not support ?"); + } + + private void generateCode(Object value) { + TestGenerationContext context = new TestGenerationContext(); + GeneratedClass generatedClass = context.getGeneratedClasses() + .addForFeature("Test", type -> {}); + new BeanDefinitionPropertyValueCodeGenerator(generatedClass.getMethods()) + .generateCode(value); + } + + record SampleValue(String name) {} + + } + }