Skip to content
Permalink
Browse files

@CopyAnnotations.exclude now affects type annotations.

Do not copy a type annotation from an abstract property method of an @autovalue class to the implementation of that method, if there is also a @CopyAnnotations annotation that excludes the type annotation.

Fixes #682.

RELNOTES=In AutoValue, @CopyAnnotations.exclude now affects type annotations.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=227527442
  • Loading branch information...
eamonnmcmanus authored and ronshapiro committed Jan 2, 2019
1 parent 6515eab commit 3ee205b4a1ad9f866988ea785a0b620c142b9a99
@@ -156,6 +156,15 @@ static Nullable nullable() {
return new AutoAnnotation_AutoValueJava8Test_nullable();
}

@Test
public void testNullablePropertyImplementationIsNullable() throws NoSuchMethodException {
Method method =
AutoValue_AutoValueJava8Test_NullableProperties.class.getDeclaredMethod("nullableString");
assertThat(method.getAnnotatedReturnType().getAnnotations())
.asList()
.contains(nullable());
}

@Test
public void testNullablePropertyConstructorParameterIsNullable() throws NoSuchMethodException {
Constructor<?> constructor =
@@ -172,6 +181,44 @@ public void testNullablePropertyConstructorParameterIsNullable() throws NoSuchMe
}
}

@AutoValue
abstract static class NullablePropertiesNotCopied {
@AutoValue.CopyAnnotations(exclude = Nullable.class)
abstract @Nullable String nullableString();

abstract int randomInt();

NullablePropertiesNotCopied create(String notNullableAfterAll, int randomInt) {
return new AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied(
notNullableAfterAll, randomInt);
}
}

@Test
public void testExcludedNullablePropertyImplementation() throws NoSuchMethodException {
Method method = AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied.class
.getDeclaredMethod("nullableString");
assertThat(method.getAnnotatedReturnType().getAnnotations())
.asList()
.doesNotContain(nullable());
}

@Test
public void testExcludedNullablePropertyConstructorParameter() throws NoSuchMethodException {
Constructor<?> constructor =
AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied.class.getDeclaredConstructor(
String.class, int.class);
try {
assertThat(constructor.getAnnotatedParameterTypes()[0].getAnnotations())
.asList()
.doesNotContain(nullable());
} catch (AssertionError e) {
if (javacHandlesTypeAnnotationsCorrectly) {
throw e;
}
}
}

@AutoValue
abstract static class NullableNonNullable {
abstract @Nullable String nullableString();
@@ -267,6 +314,7 @@ public void testNestedNullablePropertiesAreCopied() throws Exception {
}

@AutoValue
@SuppressWarnings("AutoValueImmutableFields")
abstract static class PrimitiveArrays {
@SuppressWarnings("mutable")
abstract boolean[] booleans();
@@ -428,6 +476,7 @@ public void testOmitOptionalWithNullableBuilder() {
}

@AutoValue
@SuppressWarnings("AutoValueImmutableFields")
public abstract static class BuilderWithUnprefixedGetters<T extends Comparable<T>> {
public abstract ImmutableList<T> list();

@@ -501,6 +550,7 @@ public void testBuilderWithUnprefixedGetter() {
}

@AutoValue
@SuppressWarnings("AutoValueImmutableFields")
public abstract static class BuilderWithPrefixedGetters<T extends Comparable<T>> {
public abstract ImmutableList<T> getList();

@@ -641,7 +691,7 @@ public void testEqualsNullable() throws ReflectiveOperationException {
* implementation class.
*/
@Test
public void testTypeAnnotationCopiedToImplementation() throws ReflectiveOperationException {
public void testTypeAnnotationCopiedToImplementation() {
@Nullable String nullableString = "blibby";
AnnotatedTypeParameter<@Nullable String> x = AnnotatedTypeParameter.create(nullableString);
Class<?> c = x.getClass();
@@ -674,8 +724,7 @@ public void testTypeAnnotationCopiedToImplementation() throws ReflectiveOperatio
* implementation class.
*/
@Test
public void testTypeAnnotationOnBuilderCopiedToImplementation()
throws ReflectiveOperationException {
public void testTypeAnnotationOnBuilderCopiedToImplementation() {
AnnotatedTypeParameterWithBuilder.Builder<@Nullable String> builder =
AnnotatedTypeParameterWithBuilder.builder();
Class<?> c = builder.getClass();
@@ -124,8 +124,10 @@
* }</pre>
*
* <p>When the <i>type</i> of an {@code @AutoValue} property method has annotations, those are
* part of the type, so they are always copied to the implementation of the method.
* {@code @CopyAnnotations} has no effect here. For example, suppose {@code @Confidential} is a
* part of the type, so by default they are copied to the implementation of the method. But if
* a type annotation is mentioned in {@code exclude} then it is not copied.
*
* <p>For example, suppose {@code @Confidential} is a
* {@link java.lang.annotation.ElementType#TYPE_USE TYPE_USE} annotation:
*
* <pre>
@@ -141,7 +143,15 @@
* }</pre>
*
* Then the implementation of the {@code name()} method will also have return type
* {@code @Confidential String}.
* {@code @Confidential String}. But if {@code name()} were written like this...
*
* <pre>
*
* {@code @AutoValue.CopyAnnotations(exclude = Confidential.class)}
* abstract {@code @}Confidential String name();</pre>
*
* <p>...then the implementation of {@code name()} would have return type {@code String} without
* the annotation.
*
* @author Carmi Grushko
*/
@@ -24,6 +24,7 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.Sets.union;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

@@ -373,7 +374,8 @@ public final boolean process(Set<? extends TypeElement> annotations, RoundEnviro
ImmutableSet.Builder<Property> props = ImmutableSet.builder();
for (ExecutableElement propertyMethod : propertyMethods) {
TypeMirror returnType = returnTypes.get(propertyMethod);
String propertyType = TypeEncoder.encodeWithAnnotations(returnType);
String propertyType =
TypeEncoder.encodeWithAnnotations(returnType, getExcludedAnnotationTypes(propertyMethod));
String propertyName = methodToPropertyName.get(propertyMethod);
String identifier = methodToIdentifier.get(propertyMethod);
ImmutableList<String> fieldAnnotations =
@@ -808,7 +810,7 @@ private boolean isInAutoValuePackage(String className) {
// Only copy annotations from a class if it has @AutoValue.CopyAnnotations.
if (hasAnnotationMirror(type, COPY_ANNOTATIONS_NAME)) {
Set<String> excludedAnnotations =
union(getExcludedClasses(type), getAnnotationsMarkedWithInherited(type));
union(getExcludedAnnotationClassNames(type), getAnnotationsMarkedWithInherited(type));

return copyAnnotations(type, type, excludedAnnotations);
} else {
@@ -826,9 +828,9 @@ private boolean isInAutoValuePackage(String className) {

/**
* Returns the contents of the {@code AutoValue.CopyAnnotations.exclude} element, as a set of
* strings that are fully-qualified class names.
* {@code TypeMirror} where each type is an annotation type.
*/
private Set<String> getExcludedClasses(Element element) {
private Set<TypeMirror> getExcludedAnnotationTypes(Element element) {
Optional<AnnotationMirror> maybeAnnotation =
getAnnotationMirror(element, COPY_ANNOTATIONS_NAME);
if (!maybeAnnotation.isPresent()) {
@@ -840,7 +842,18 @@ private boolean isInAutoValuePackage(String className) {
(List<AnnotationValue>) getAnnotationValue(maybeAnnotation.get(), "exclude").getValue();
return excludedClasses
.stream()
.map(annotationValue -> MoreTypes.asTypeElement((DeclaredType) annotationValue.getValue()))
.map(annotationValue -> (DeclaredType) annotationValue.getValue())
.collect(toCollection(TypeMirrorSet::new));
}

/**
* Returns the contents of the {@code AutoValue.CopyAnnotations.exclude} element, as a set of
* strings that are fully-qualified class names.
*/
private Set<String> getExcludedAnnotationClassNames(Element element) {
return getExcludedAnnotationTypes(element)
.stream()
.map(MoreTypes::asTypeElement)
.map(typeElement -> typeElement.getQualifiedName().toString())
.collect(toSet());
}
@@ -878,7 +891,7 @@ private static String getAnnotationFqName(AnnotationMirror annotation) {
TypeElement type, ExecutableElement method) {
ImmutableSet<String> excludedAnnotations =
ImmutableSet.<String>builder()
.addAll(getExcludedClasses(method))
.addAll(getExcludedAnnotationClassNames(method))
.add(Override.class.getCanonicalName())
.build();

@@ -906,7 +919,7 @@ private static String getAnnotationFqName(AnnotationMirror annotation) {
}
ImmutableSet<String> excludedAnnotations =
ImmutableSet.<String>builder()
.addAll(getExcludedClasses(method))
.addAll(getExcludedAnnotationClassNames(method))
.add(Override.class.getCanonicalName())
.build();

@@ -17,9 +17,11 @@

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.util.stream.Collectors.toList;

import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.OptionalInt;
import java.util.Set;
@@ -69,8 +71,6 @@ private TypeEncoder() {} // There are no instances of this class.
private static final EncodingTypeVisitor ENCODING_TYPE_VISITOR = new EncodingTypeVisitor();
private static final RawEncodingTypeVisitor RAW_ENCODING_TYPE_VISITOR =
new RawEncodingTypeVisitor();
private static final AnnotatedEncodingTypeVisitor ANNOTATED_ENCODING_TYPE_VISITOR =
new AnnotatedEncodingTypeVisitor();

/**
* Returns the encoding for the given type, where class names are marked by special tokens. The
@@ -96,8 +96,20 @@ static String encodeRaw(TypeMirror type) {
* covers the details of annotation encoding.
*/
static String encodeWithAnnotations(TypeMirror type) {
return encodeWithAnnotations(type, ImmutableSet.of());
}

/**
* Encodes the given type and its type annotations. The class comment for {@link TypeEncoder}
* covers the details of annotation encoding.
*
* @param excludedAnnotationTypes annotations not to include in the encoding. For example, if
* {@code com.example.Nullable} is in this set then the encoding will not include this
* {@code @Nullable} annotation.
*/
static String encodeWithAnnotations(TypeMirror type, Set<TypeMirror> excludedAnnotationTypes) {
StringBuilder sb = new StringBuilder();
return ANNOTATED_ENCODING_TYPE_VISITOR.visit2(type, sb).toString();
return new AnnotatedEncodingTypeVisitor(excludedAnnotationTypes).visit2(type, sb).toString();
}

/**
@@ -275,17 +287,38 @@ void appendTypeArguments(DeclaredType type, StringBuilder sb) {}
* `java.util.List`} form.
*/
private static class AnnotatedEncodingTypeVisitor extends EncodingTypeVisitor {
private final Set<TypeMirror> excludedAnnotationTypes;

AnnotatedEncodingTypeVisitor(Set<TypeMirror> excludedAnnotationTypes) {
this.excludedAnnotationTypes = excludedAnnotationTypes;
}

private void appendAnnotationsWithExclusions(
List<? extends AnnotationMirror> annotations, StringBuilder sb) {
// Optimization for the very common cases where there are no annotations or there are no
// exclusions.
if (annotations.isEmpty() || excludedAnnotationTypes.isEmpty()) {
appendAnnotations(annotations, sb);
return;
}
List<AnnotationMirror> includedAnnotations =
annotations.stream()
.filter(a -> !excludedAnnotationTypes.contains(a.getAnnotationType()))
.collect(toList());
appendAnnotations(includedAnnotations, sb);
}

@Override
public StringBuilder visitPrimitive(PrimitiveType type, StringBuilder sb) {
appendAnnotations(type.getAnnotationMirrors(), sb);
appendAnnotationsWithExclusions(type.getAnnotationMirrors(), sb);
// We can't just append type.toString(), because that will also have the annotation, but
// without encoding.
return sb.append(type.getKind().toString().toLowerCase());
}

@Override
public StringBuilder visitTypeVariable(TypeVariable type, StringBuilder sb) {
appendAnnotations(type.getAnnotationMirrors(), sb);
appendAnnotationsWithExclusions(type.getAnnotationMirrors(), sb);
return sb.append(type.asElement().getSimpleName());
}

@@ -300,7 +333,7 @@ public StringBuilder visitArray(ArrayType type, StringBuilder sb) {
List<? extends AnnotationMirror> annotationMirrors = type.getAnnotationMirrors();
if (!annotationMirrors.isEmpty()) {
sb.append(" ");
appendAnnotations(annotationMirrors, sb);
appendAnnotationsWithExclusions(annotationMirrors, sb);
}
return sb.append("[]");
}
@@ -314,7 +347,7 @@ public StringBuilder visitDeclared(DeclaredType type, StringBuilder sb) {
String className = className(type);
// See the class doc comment for an explanation of « and » here.
sb.append("").append(className).append("`");
appendAnnotations(annotationMirrors, sb);
appendAnnotationsWithExclusions(annotationMirrors, sb);
sb.append("").append(className).append("`");
}
appendTypeArguments(type, sb);
@@ -17,7 +17,6 @@

import com.google.auto.common.MoreTypes;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableList;
import java.util.AbstractSet;
import java.util.Collection;
@@ -32,8 +31,7 @@
* @author emcmanus@google.com (Éamonn McManus)
*/
class TypeMirrorSet extends AbstractSet<TypeMirror> {
private final Set<Equivalence.Wrapper<TypeMirror>> wrappers =
new LinkedHashSet<Wrapper<TypeMirror>>();
private final Set<Equivalence.Wrapper<TypeMirror>> wrappers = new LinkedHashSet<>();

TypeMirrorSet() {}

0 comments on commit 3ee205b

Please sign in to comment.
You can’t perform that action at this time.