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 7eafd46ad5..3e04622099 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 @@ -19,6 +19,7 @@ import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -31,6 +32,10 @@ static KotlinDataBuilder builder() { return new AutoBuilder_AutoBuilderKotlinTest_KotlinDataBuilder(); } + static KotlinDataBuilder builder(KotlinData kotlinData) { + return new AutoBuilder_AutoBuilderKotlinTest_KotlinDataBuilder(kotlinData); + } + abstract KotlinDataBuilder setInt(int x); abstract KotlinDataBuilder setString(String x); @@ -43,6 +48,11 @@ public void simpleKotlin() { KotlinData x = KotlinDataBuilder.builder().setInt(23).setString("skidoo").build(); assertThat(x.getInt()).isEqualTo(23); assertThat(x.getString()).isEqualTo("skidoo"); + + KotlinData y = KotlinDataBuilder.builder(x).setString("chromosomes").build(); + assertThat(y.getInt()).isEqualTo(23); + assertThat(y.getString()).isEqualTo("chromosomes"); + assertThrows(IllegalStateException.class, () -> KotlinDataBuilder.builder().build()); } @@ -240,4 +250,34 @@ public void kotlinSomeDefaults_missingRequired() { assertThat(e).hasMessageThat().contains("requiredInt"); assertThat(e).hasMessageThat().contains("requiredString"); } + + @AutoBuilder(ofClass = KotlinDataWithList.class) + interface KotlinDataWithListBuilder { + static KotlinDataWithListBuilder builder() { + return new AutoBuilder_AutoBuilderKotlinTest_KotlinDataWithListBuilder(); + } + + static KotlinDataWithListBuilder builder(KotlinDataWithList kotlinData) { + return new AutoBuilder_AutoBuilderKotlinTest_KotlinDataWithListBuilder(kotlinData); + } + + KotlinDataWithListBuilder list(List list); + + KotlinDataWithListBuilder number(int number); + + KotlinDataWithList build(); + } + + // The `getList()` method returns `List` as seen from Java, but the `list` parameter + // to the constructor has type `List`. + @Test + public void kotlinWildcards() { + List strings = ImmutableList.of("foo"); + KotlinDataWithList x = KotlinDataWithListBuilder.builder().list(strings).number(17).build(); + assertThat(x.getList()).isEqualTo(strings); + assertThat(x.getNumber()).isEqualTo(17); + KotlinDataWithList y = KotlinDataWithListBuilder.builder(x).number(23).build(); + assertThat(y.getList()).isEqualTo(strings); + assertThat(y.getNumber()).isEqualTo(23); + } } diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderTest.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderTest.java index 25d1d6db86..934cae6a6e 100644 --- a/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderTest.java +++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderTest.java @@ -502,7 +502,7 @@ public void propertyBuilder() { } static String concatList(ImmutableList list) { - // We're avoiding streams for now so we compile this in Java 7 mode in CompileWithEclipseTest. + // We're avoiding streams for now since we compile this in Java 7 mode in CompileWithEclipseTest StringBuilder sb = new StringBuilder(); for (T element : list) { sb.append(element); 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 8652af501c..c518a4d15a 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 @@ -46,3 +46,7 @@ data class KotlinDataSomeDefaults( val optionalInt: Int = 23, val optionalString: String = "Skidoo" ) + +// CharSequence is an interface so the parameter appears from Java as List, +// but getList() appears as returning List. +data class KotlinDataWithList(val list: List, val number: Int) 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 c32d882801..0185313618 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 @@ -387,7 +387,7 @@ private ImmutableMap propertyToGetterName( return ImmutableMap.of(); } TypeElement type = MoreTypes.asTypeElement(builtType); - Map noArgInstanceMethods = + Map nameToMethod = MoreElements.getLocalAndInheritedMethods(type, typeUtils(), elementUtils()).stream() .filter(m -> m.getParameters().isEmpty()) .filter(m -> !m.getModifiers().contains(Modifier.STATIC)) @@ -405,16 +405,18 @@ private ImmutableMap propertyToGetterName( String name = param.getSimpleName().toString(); // Parameter name is `bar`; we look for `bar()` and `getBar()` (or `getbar()` etc) // in that order. If `bar` is boolean we also look for `isBar()`. - ExecutableElement getter = noArgInstanceMethods.get(name); + ExecutableElement getter = nameToMethod.get(name); if (getter == null) { - getter = noArgInstanceMethods.get("get" + name); + getter = nameToMethod.get("get" + name); if (getter == null && param.asType().getKind() == TypeKind.BOOLEAN) { - getter = noArgInstanceMethods.get("is" + name); + getter = nameToMethod.get("is" + name); } } if (getter != null + && !typeUtils().isAssignable(getter.getReturnType(), param.asType()) && !MoreTypes.equivalence() .equivalent(getter.getReturnType(), param.asType())) { + // TODO(b/268680785): we should not need to have two type checks here getter = null; } return new SimpleEntry<>(name, getter);