diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderKotlinTest.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderKotlinTest.java index 0581b79b8f..5c072264e6 100644 --- a/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderKotlinTest.java +++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderKotlinTest.java @@ -408,4 +408,26 @@ public void kotlinWithTypeParameters() { assertThat(data.getV()).isEqualTo(2.0); assertThat(data.getM()).isEqualTo(map); } + + @AutoBuilder(ofClass = KotlinDataWithNullableParameters.class) + interface KotlinDataWithNullableParametersBuilder { + static KotlinDataWithNullableParametersBuilder builder() { + return new AutoBuilder_AutoBuilderKotlinTest_KotlinDataWithNullableParametersBuilder(); + } + + KotlinDataWithNullableParametersBuilder value(@org.jetbrains.annotations.Nullable T value); + + KotlinDataWithNullableParameters build(); + } + + @Test + public void kotlinWithNullableParameters() { + KotlinDataWithNullableParameters x = + KotlinDataWithNullableParametersBuilder.builder().value("hello").build(); + assertThat(x.getValue()).isEqualTo("hello"); + + KotlinDataWithNullableParameters nullX = + KotlinDataWithNullableParametersBuilder.builder().value(null).build(); + assertThat(nullX.getValue()).isNull(); + } } diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/KotlinData.kt b/value/src/it/functional/src/test/java/com/google/auto/value/KotlinData.kt index 09e7cca6ac..180c03ddee 100644 --- a/value/src/it/functional/src/test/java/com/google/auto/value/KotlinData.kt +++ b/value/src/it/functional/src/test/java/com/google/auto/value/KotlinData.kt @@ -102,3 +102,5 @@ data class KotlinDataWithTypeParameters< >(val t: T? = null, val u: U, val v: V, val m: M) { fun foo(w: W) {} } + +data class KotlinDataWithNullableParameters(val value: T?) diff --git a/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java index 28a69418c2..94d1da4f0b 100644 --- a/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java +++ b/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java @@ -657,7 +657,8 @@ private String findCallMethodValue(AnnotationMirror autoBuilderAnnotation) { } @Override - Optional nullableAnnotationForMethod(ExecutableElement propertyMethod) { + Optional nullableAnnotationForMethod( + ExecutableElement propertyMethod, AnnotatedTypeMirror propertyType) { // TODO(b/183005059): implement return Optional.empty(); } diff --git a/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java index 343fd34989..f3adce5578 100644 --- a/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java +++ b/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java @@ -275,8 +275,9 @@ private void defineVarsForType( } @Override - Optional nullableAnnotationForMethod(ExecutableElement propertyMethod) { - if (nullableAnnotationFor(propertyMethod, propertyMethod.getReturnType()).isPresent()) { + Optional nullableAnnotationForMethod( + ExecutableElement propertyMethod, AnnotatedTypeMirror propertyType) { + if (nullableAnnotationFor(propertyMethod, propertyType).isPresent()) { errorReporter() .reportError( propertyMethod, "[AutoOneOfNullable] @AutoOneOf properties cannot be @Nullable"); diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java index 8b37840c32..475f56ae48 100644 --- a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java +++ b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java @@ -514,8 +514,9 @@ private void defineVarsForType( } @Override - Optional nullableAnnotationForMethod(ExecutableElement propertyMethod) { - return nullableAnnotationFor(propertyMethod, propertyMethod.getReturnType()); + Optional nullableAnnotationForMethod( + ExecutableElement propertyMethod, AnnotatedTypeMirror propertyType) { + return nullableAnnotationFor(propertyMethod, propertyType); } static ImmutableSet prefixedGettersIn(Iterable methods) { diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java index b42af90115..8a6e7a3672 100644 --- a/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java +++ b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java @@ -526,7 +526,8 @@ private void validateType(TypeElement type) { * is nullable but the method isn't. The {@code @Nullable} annotation will instead appear * when the return type of the method is spelled out in the implementation. */ - abstract Optional nullableAnnotationForMethod(ExecutableElement propertyMethod); + abstract Optional nullableAnnotationForMethod( + ExecutableElement propertyMethod, AnnotatedTypeMirror propertyType); /** * Returns the ordered set of {@link Property} definitions for the given {@code @AutoValue} or @@ -561,7 +562,8 @@ final ImmutableSet propertySet( ImmutableList methodAnnotationMirrors = annotatedPropertyMethods.get(propertyMethod); ImmutableList methodAnnotations = annotationStrings(methodAnnotationMirrors); - Optional nullableAnnotation = nullableAnnotationForMethod(propertyMethod); + Optional nullableAnnotation = + nullableAnnotationForMethod(propertyMethod, returnType); Property p = new GetterProperty( propertyName, @@ -727,6 +729,13 @@ private static boolean gettersAllPrefixed(Set methods) { * @param elementType the relevant type of the element: the return type for a method, or the * parameter type for a parameter. */ + static Optional nullableAnnotationFor(Element element, AnnotatedTypeMirror elementType) { + if (isNullable(elementType)) { + return Optional.of(""); + } + return nullableAnnotationFor(element, elementType.getType()); + } + static Optional nullableAnnotationFor(Element element, TypeMirror elementType) { if (isNullable(elementType)) { return Optional.of(""); @@ -752,8 +761,22 @@ private static boolean isNullableAnnotation(AnnotationMirror annotation) { return annotation.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable"); } + private static boolean isNullable(AnnotatedTypeMirror type) { + return isNullable(type.getType()) || nullableAnnotationIndex(type.annotations()).isPresent(); + } + private static boolean isNullable(TypeMirror type) { - return nullableAnnotationIndex(type.getAnnotationMirrors()).isPresent(); + if (nullableAnnotationIndex(type.getAnnotationMirrors()).isPresent()) { + return true; + } + if (type.getKind().equals(TypeKind.DECLARED)) { + DeclaredType declaredType = (DeclaredType) type; + TypeMirror enclosing = declaredType.getEnclosingType(); + if (enclosing != null && enclosing.getKind().equals(TypeKind.DECLARED)) { + return isNullable(enclosing); + } + } + return false; } private static boolean isTypeVariableWithNullableBound(TypeMirror type) { @@ -1282,7 +1305,7 @@ private ImmutableList propertyFieldAnnotations( private static Set getReturnTypeAnnotations( ExecutableElement method, Predicate typeFilter) { - return method.getReturnType().getAnnotationMirrors().stream() + return getAllAnnotationMirrors(method.getReturnType()).stream() .map(a -> a.getAnnotationType().asElement()) .map(MoreElements::asType) .filter(typeFilter) @@ -1290,6 +1313,19 @@ private static Set getReturnTypeAnnotations( .collect(toSet()); } + private static ImmutableList getAllAnnotationMirrors(TypeMirror type) { + ImmutableList.Builder builder = ImmutableList.builder(); + builder.addAll(type.getAnnotationMirrors()); + if (type.getKind().equals(TypeKind.DECLARED)) { + DeclaredType declaredType = (DeclaredType) type; + TypeMirror enclosing = declaredType.getEnclosingType(); + if (enclosing != null && enclosing.getKind().equals(TypeKind.DECLARED)) { + builder.addAll(getAllAnnotationMirrors(enclosing)); + } + } + return builder.build(); + } + private boolean annotationAppliesToFields(TypeElement annotation) { Target target = annotation.getAnnotation(Target.class); return target == null || Arrays.asList(target.value()).contains(ElementType.FIELD); diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java index acac75f775..7f7d67fa53 100644 --- a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java +++ b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java @@ -469,8 +469,7 @@ public static class PropertySetter { primitiveParameter = parameterType.getKind().isPrimitive(); this.parameterTypeString = parameterTypeString(setter, parameterType); VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters()); - Optional maybeNullable = - nullableAnnotationFor(parameterElement, parameterType.getType()); + Optional maybeNullable = nullableAnnotationFor(parameterElement, parameterType); this.nullableAnnotation = maybeNullable.orElse(""); }