1 /* 2 * Copyright 2021 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.auto.value.processor; 17 18 import static java.util.stream.Collectors.toList; 19 20 import com.google.auto.common.MoreTypes; 21 import com.google.common.annotations.VisibleForTesting; 22 import com.google.common.base.Strings; 23 import com.google.common.collect.ImmutableList; 24 import com.google.common.collect.ImmutableMap; 25 import java.util.Collection; 26 import java.util.List; 27 import java.util.Objects; 28 import java.util.Optional; 29 import java.util.stream.Stream; 30 import javax.annotation.processing.ProcessingEnvironment; 31 import javax.lang.model.SourceVersion; 32 import javax.lang.model.element.AnnotationMirror; 33 import javax.lang.model.element.AnnotationValue; 34 import javax.lang.model.element.Element; 35 import javax.lang.model.element.ExecutableElement; 36 import javax.lang.model.type.ArrayType; 37 import javax.lang.model.type.DeclaredType; 38 import javax.lang.model.type.IntersectionType; 39 import javax.lang.model.type.TypeMirror; 40 import javax.lang.model.type.TypeVariable; 41 import javax.lang.model.type.WildcardType; 42 import javax.lang.model.util.SimpleTypeVisitor8; 43 44 class Nullables { 45 /** 46 * If set to a non-empty string, defines which {@code @Nullable} type annotation should be used by 47 * default. If set to an empty string, does not insert {@code @Nullable} unless it is referenced 48 * in the {@code @AutoValue} methods. If unset, defaults to {@value #DEFAULT_NULLABLE}. 49 */ 50 static final String NULLABLE_OPTION = "com.google.auto.value.NullableTypeAnnotation"; 51 52 // We write this using .concat in order to hide it from rewriting rules. 53 private static final String DEFAULT_NULLABLE = "org".concat(".jspecify.nullness.Nullable"); 54 55 private final Optional<AnnotationMirror> defaultNullable; 56 Nullables(ProcessingEnvironment processingEnv)57 Nullables(ProcessingEnvironment processingEnv) { 58 // -Afoo without `=` sets "foo" to null in the getOptions() map. 59 String nullableOption = 60 Strings.nullToEmpty( 61 processingEnv.getOptions().getOrDefault(NULLABLE_OPTION, DEFAULT_NULLABLE)); 62 this.defaultNullable = 63 (!nullableOption.isEmpty() 64 && processingEnv.getSourceVersion().ordinal() >= SourceVersion.RELEASE_8.ordinal()) 65 ? Optional.ofNullable(processingEnv.getElementUtils().getTypeElement(nullableOption)) 66 .map(t -> annotationMirrorOf(MoreTypes.asDeclared(t.asType()))) 67 : Optional.empty(); 68 } 69 annotationMirrorOf(DeclaredType annotationType)70 private static AnnotationMirror annotationMirrorOf(DeclaredType annotationType) { 71 return new AnnotationMirror() { 72 @Override 73 public DeclaredType getAnnotationType() { 74 return annotationType; 75 } 76 77 @Override 78 public ImmutableMap<? extends ExecutableElement, ? extends AnnotationValue> 79 getElementValues() { 80 return ImmutableMap.of(); 81 } 82 }; 83 } 84 85 /** 86 * Returns the type of a {@code @Nullable} type-annotation, if one is found anywhere in the 87 * signatures of the given methods. 88 */ 89 @VisibleForTesting 90 static Optional<AnnotationMirror> nullableMentionedInMethods( 91 Collection<ExecutableElement> methods) { 92 return methods.stream() 93 .flatMap( 94 method -> 95 Stream.concat( 96 Stream.of(method.getReturnType()), 97 method.getParameters().stream().map(Element::asType))) 98 .map(Nullables::nullableIn) 99 .filter(Optional::isPresent) 100 .findFirst() 101 .orElse(Optional.empty()); 102 } 103 104 /** 105 * Returns the type of an appropriate {@code @Nullable} type-annotation, given a set of methods 106 * that are known to be in the same compilation as the code being generated. If one of those 107 * methods contains an appropriate {@code @Nullable} annotation on a parameter or return type, 108 * this method will return that. Otherwise, if the <a href="http://jspecify.org">JSpecify</a> 109 * {@code @Nullable} is available, this method will return it. Otherwise, this method will return 110 * an empty {@code Optional}. 111 */ 112 Optional<AnnotationMirror> appropriateNullableGivenMethods( 113 Collection<ExecutableElement> methods) { 114 return nullableMentionedInMethods(methods).map(Optional::of).orElse(defaultNullable); 115 } 116 117 private static Optional<AnnotationMirror> nullableIn(TypeMirror type) { 118 return new NullableFinder().visit(type); 119 } 120 121 private static Optional<AnnotationMirror> nullableIn( 122 List<? extends AnnotationMirror> annotations) { 123 return annotations.stream() 124 .filter(a -> a.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable")) 125 .map(a -> (AnnotationMirror) a) // get rid of the pesky wildcard 126 .findFirst(); 127 } 128 129 private static class NullableFinder extends SimpleTypeVisitor8<Optional<AnnotationMirror>, Void> { 130 private final TypeMirrorSet visiting = new TypeMirrorSet(); 131 132 NullableFinder() { 133 super(Optional.empty()); 134 } 135 136 // Primitives can't be @Nullable so we don't check that. 137 138 @Override 139 public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void unused) { 140 if (!visiting.add(t)) { 141 return Optional.empty(); 142 } 143 return nullableIn(t.getAnnotationMirrors()) 144 .map(Optional::of) 145 .orElseGet(() -> visitAll(t.getTypeArguments())); 146 } 147 148 @Override 149 public Optional<AnnotationMirror> visitTypeVariable(TypeVariable t, Void unused) { 150 if (!visiting.add(t)) { 151 return Optional.empty(); 152 } 153 return nullableIn(t.getAnnotationMirrors()) 154 .map(Optional::of) 155 .orElseGet(() -> visitAll(ImmutableList.of(t.getUpperBound(), t.getLowerBound()))); 156 } 157 158 @Override 159 public Optional<AnnotationMirror> visitArray(ArrayType t, Void unused) { 160 return nullableIn(t.getAnnotationMirrors()) 161 .map(Optional::of) 162 .orElseGet(() -> visit(t.getComponentType())); 163 } 164 165 @Override 166 public Optional<AnnotationMirror> visitWildcard(WildcardType t, Void unused) { 167 return nullableIn(t.getAnnotationMirrors()) 168 .map(Optional::of) 169 .orElseGet( 170 () -> 171 visitAll( 172 Stream.of(t.getExtendsBound(), t.getSuperBound()) 173 .filter(Objects::nonNull) 174 .collect(toList()))); 175 } 176 177 @Override 178 public Optional<AnnotationMirror> visitIntersection(IntersectionType t, Void unused) { 179 return nullableIn(t.getAnnotationMirrors()) 180 .map(Optional::of) 181 .orElseGet(() -> visitAll(t.getBounds())); 182 } 183 184 private Optional<AnnotationMirror> visitAll(List<? extends TypeMirror> types) { 185 return types.stream() 186 .map(this::visit) 187 .filter(Optional::isPresent) 188 .findFirst() 189 .orElse(Optional.empty()); 190 } 191 } 192 } 193