diff options
author | Éamonn McManus <emcmanus@google.com> | 2020-11-16 10:46:55 -0800 |
---|---|---|
committer | Google Java Core Libraries <java-core-libraries-team+copybara@google.com> | 2020-11-16 10:47:30 -0800 |
commit | f2cb2247898dcdcb05d52f1faf9689ecef4c0c41 (patch) | |
tree | a614486b8af8a1bb88281887c2f84b98fa6e2f08 /value/src/main/java/com/google/auto | |
parent | 97863af8fda6e87250dae9e477f98de62d1ff28b (diff) | |
download | auto-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.java | 6 | ||||
-rw-r--r-- | value/src/main/java/com/google/auto/value/processor/BuilderSpec.java | 34 |
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); |