aboutsummaryrefslogtreecommitdiff
path: root/value/src/main/java/com/google/auto
diff options
context:
space:
mode:
authorÉamonn McManus <emcmanus@google.com>2020-11-16 10:46:55 -0800
committerGoogle Java Core Libraries <java-core-libraries-team+copybara@google.com>2020-11-16 10:47:30 -0800
commitf2cb2247898dcdcb05d52f1faf9689ecef4c0c41 (patch)
treea614486b8af8a1bb88281887c2f84b98fa6e2f08 /value/src/main/java/com/google/auto
parent97863af8fda6e87250dae9e477f98de62d1ff28b (diff)
downloadauto-f2cb2247898dcdcb05d52f1faf9689ecef4c0c41.tar.gz
The methods returned by `BuilderContext.buildMethod()` and `.toBuilderMethods()` can be inherited.
This change addresses cases like this: ```java interface Parent<BuilderT> { BuilderT toBuilder(); interface Builder<BuilderT, BuiltT> {", BuilderT setThing(String x);", BuiltT build();", } } @AutoValue abstract class Foo implements Parent<Foo.Builder> { @AutoValue.Builder abstract static class Builder implements Parent.Builder<Builder, Foo> {} } ``` Previously, there would be two problems with this. First, inheriting `toBuilder()` like this didn't work at all, because its return type is `BuilderT` and not `Foo.Builder`. Now we correctly interpret the inherited return type in the context of `Foo`. Second, in `BuilderContext.buildMethod()` we were looking for methods returning `Foo`, and again this failed because the inherited `build()` method returns `BuiltT`. The fix in both cases is to use `Types.asMemberOf` to determine the correct return type in context. Fixes https://github.com/google/auto/issues/933. RELNOTES=The methods returned by `BuilderContext.buildMethod()` and `.toBuilderMethods()` can be inherited. PiperOrigin-RevId: 342670816
Diffstat (limited to 'value/src/main/java/com/google/auto')
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java6
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderSpec.java34
2 files changed, 30 insertions, 10 deletions
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 51387b2c..326b22a7 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
@@ -19,6 +19,7 @@ import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_NAME;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
+import static java.util.Comparator.naturalOrder;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
@@ -36,7 +37,6 @@ import com.google.common.collect.Iterables;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -136,7 +136,7 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
AutoValueExtension.IncrementalExtensionType incrementalType =
extensions.stream()
.map(e -> e.incrementalType(processingEnv))
- .min(Comparator.naturalOrder())
+ .min(naturalOrder())
.orElse(AutoValueExtension.IncrementalExtensionType.ISOLATING);
builder.add(OMIT_IDENTIFIERS_OPTION).addAll(optionsFor(incrementalType));
for (AutoValueExtension extension : extensions) {
@@ -207,7 +207,7 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
Optional<BuilderSpec.Builder> builder = builderSpec.getBuilder();
ImmutableSet<ExecutableElement> toBuilderMethods;
if (builder.isPresent()) {
- toBuilderMethods = builder.get().toBuilderMethods(typeUtils(), abstractMethods);
+ toBuilderMethods = builder.get().toBuilderMethods(typeUtils(), type, abstractMethods);
} else {
toBuilderMethods = ImmutableSet.of();
}
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 7e5b17c9..34095202 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
@@ -25,6 +25,7 @@ import static java.util.stream.Collectors.toSet;
import static javax.lang.model.util.ElementFilter.methodsIn;
import static javax.lang.model.util.ElementFilter.typesIn;
+import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.extension.AutoValueExtension;
import com.google.auto.value.processor.AutoValueOrOneOfProcessor.Property;
@@ -49,6 +50,7 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
@@ -142,14 +144,23 @@ class BuilderSpec {
@Override
public Optional<ExecutableElement> buildMethod() {
- return methodsIn(builderTypeElement.getEnclosedElements()).stream()
+ Types typeUtils = processingEnv.getTypeUtils();
+ DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderTypeElement.asType());
+ return MoreElements.getLocalAndInheritedMethods(
+ builderTypeElement, typeUtils, processingEnv.getElementUtils())
+ .stream()
.filter(
m ->
m.getSimpleName().contentEquals("build")
&& !m.getModifiers().contains(Modifier.PRIVATE)
&& !m.getModifiers().contains(Modifier.STATIC)
- && m.getParameters().isEmpty()
- && erasedTypeIs(m.getReturnType(), autoValueClass))
+ && m.getParameters().isEmpty())
+ .filter(
+ m -> {
+ ExecutableType methodMirror =
+ MoreTypes.asExecutable(typeUtils.asMemberOf(builderTypeMirror, m));
+ return erasedTypeIs(methodMirror.getReturnType(), autoValueClass);
+ })
.findFirst();
}
@@ -201,18 +212,27 @@ class BuilderSpec {
* <p>We currently impose that there cannot be more than one such method.
*/
ImmutableSet<ExecutableElement> toBuilderMethods(
- Types typeUtils, Set<ExecutableElement> abstractMethods) {
+ Types typeUtils,
+ TypeElement autoValueType,
+ Set<ExecutableElement> abstractMethods) {
List<String> builderTypeParamNames =
builderTypeElement.getTypeParameters().stream()
.map(e -> e.getSimpleName().toString())
.collect(toList());
+ DeclaredType autoValueTypeMirror = MoreTypes.asDeclared(autoValueType.asType());
ImmutableSet.Builder<ExecutableElement> methods = ImmutableSet.builder();
for (ExecutableElement method : abstractMethods) {
- if (builderTypeElement.equals(typeUtils.asElement(method.getReturnType()))) {
+ if (!method.getParameters().isEmpty()) {
+ continue;
+ }
+ ExecutableType methodMirror =
+ MoreTypes.asExecutable(typeUtils.asMemberOf(autoValueTypeMirror, method));
+ TypeMirror returnTypeMirror = methodMirror.getReturnType();
+ if (builderTypeElement.equals(typeUtils.asElement(returnTypeMirror))) {
methods.add(method);
- DeclaredType returnType = MoreTypes.asDeclared(method.getReturnType());
+ DeclaredType returnType = MoreTypes.asDeclared(returnTypeMirror);
List<String> typeArguments =
returnType.getTypeArguments().stream()
.filter(t -> t.getKind().equals(TypeKind.TYPEVAR))
@@ -452,7 +472,7 @@ class BuilderSpec {
return nullableAnnotation;
}
- public String copy(AutoValueProcessor.Property property) {
+ public String copy(Property property) {
String copy = copier.copy.apply(property.toString());
if (property.isNullable() && !copier.acceptsNull) {
copy = String.format("(%s == null ? null : %s)", property, copy);