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.base.Strings; 22 import com.google.common.collect.ImmutableList; 23 import com.google.common.collect.ImmutableMap; 24 import java.util.Collection; 25 import java.util.List; 26 import java.util.Objects; 27 import java.util.Optional; 28 import java.util.stream.Stream; 29 import javax.annotation.processing.ProcessingEnvironment; 30 import javax.lang.model.SourceVersion; 31 import javax.lang.model.element.AnnotationMirror; 32 import javax.lang.model.element.AnnotationValue; 33 import javax.lang.model.element.Element; 34 import javax.lang.model.element.ExecutableElement; 35 import javax.lang.model.type.ArrayType; 36 import javax.lang.model.type.DeclaredType; 37 import javax.lang.model.type.IntersectionType; 38 import javax.lang.model.type.TypeMirror; 39 import javax.lang.model.type.TypeVariable; 40 import javax.lang.model.type.WildcardType; 41 import javax.lang.model.util.SimpleTypeVisitor8; 42 43 class Nullables { 44 /** 45 * If set to a non-empty string, defines which {@code @Nullable} type annotation should be used by 46 * default. If set to an empty string, does not insert {@code @Nullable} unless it is referenced 47 * in the {@code @AutoValue} methods. If unset, defaults to {@value #DEFAULT_NULLABLE}. 48 */ 49 static final String NULLABLE_OPTION = "com.google.auto.value.NullableTypeAnnotation"; 50 51 // We write this using .concat in order to hide it from rewriting rules. 52 private static final String DEFAULT_NULLABLE = "org".concat(".jspecify.nullness.Nullable"); 53 54 private final Optional<AnnotationMirror> nullableTypeAnnotation; 55 Nullables(Optional<AnnotationMirror> nullableTypeAnnotation)56 private Nullables(Optional<AnnotationMirror> nullableTypeAnnotation) { 57 this.nullableTypeAnnotation = nullableTypeAnnotation; 58 } 59 60 /** 61 * Make an instance where the default {@code @Nullable} type annotation is discovered by looking 62 * for methods whose parameter or return types have such an annotation. If there are none, use a 63 * default {@code @Nullable} type annotation if it is available. 64 * 65 * @param methods the methods to examine 66 * @param processingEnv the {@link ProcessingEnvironment}, or null if one is unavailable 67 * (typically in tests) 68 */ fromMethods( ProcessingEnvironment processingEnv, Collection<ExecutableElement> methods)69 static Nullables fromMethods( 70 /* @Nullable */ ProcessingEnvironment processingEnv, Collection<ExecutableElement> methods) { 71 Optional<AnnotationMirror> nullableTypeAnnotation = 72 methods.stream() 73 .flatMap( 74 method -> 75 Stream.concat( 76 Stream.of(method.getReturnType()), 77 method.getParameters().stream().map(Element::asType))) 78 .map(Nullables::nullableIn) 79 .filter(Optional::isPresent) 80 .findFirst() 81 .orElseGet(() -> defaultNullableTypeAnnotation(processingEnv)); 82 return new Nullables(nullableTypeAnnotation); 83 } 84 85 /** 86 * Returns a list that is either empty or contains a single element that is an appropriate 87 * {@code @Nullable} type-annotation. 88 */ nullableTypeAnnotations()89 ImmutableList<AnnotationMirror> nullableTypeAnnotations() { 90 return nullableTypeAnnotation.map(ImmutableList::of).orElse(ImmutableList.of()); 91 } 92 defaultNullableTypeAnnotation( ProcessingEnvironment processingEnv)93 private static Optional<AnnotationMirror> defaultNullableTypeAnnotation( 94 /* @Nullable */ ProcessingEnvironment processingEnv) { 95 if (processingEnv == null) { 96 return Optional.empty(); 97 } 98 // -Afoo without `=` sets "foo" to null in the getOptions() map. 99 String nullableOption = 100 Strings.nullToEmpty( 101 processingEnv.getOptions().getOrDefault(NULLABLE_OPTION, DEFAULT_NULLABLE)); 102 return (!nullableOption.isEmpty() 103 && processingEnv.getSourceVersion().ordinal() >= SourceVersion.RELEASE_8.ordinal()) 104 ? Optional.ofNullable(processingEnv.getElementUtils().getTypeElement(nullableOption)) 105 .map(t -> annotationMirrorOf(MoreTypes.asDeclared(t.asType()))) 106 : Optional.empty(); 107 } 108 annotationMirrorOf(DeclaredType annotationType)109 private static AnnotationMirror annotationMirrorOf(DeclaredType annotationType) { 110 return new AnnotationMirror() { 111 @Override 112 public DeclaredType getAnnotationType() { 113 return annotationType; 114 } 115 116 @Override 117 public ImmutableMap<? extends ExecutableElement, ? extends AnnotationValue> 118 getElementValues() { 119 return ImmutableMap.of(); 120 } 121 }; 122 } 123 124 private static Optional<AnnotationMirror> nullableIn(TypeMirror type) { 125 return new NullableFinder().visit(type); 126 } 127 128 private static Optional<AnnotationMirror> nullableIn( 129 List<? extends AnnotationMirror> annotations) { 130 return annotations.stream() 131 .filter(a -> a.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable")) 132 .map(a -> (AnnotationMirror) a) // get rid of the pesky wildcard 133 .findFirst(); 134 } 135 136 private static class NullableFinder extends SimpleTypeVisitor8<Optional<AnnotationMirror>, Void> { 137 private final TypeMirrorSet visiting = new TypeMirrorSet(); 138 139 NullableFinder() { 140 super(Optional.empty()); 141 } 142 143 // Primitives can't be @Nullable so we don't check that. 144 145 @Override 146 public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void unused) { 147 if (!visiting.add(t)) { 148 return Optional.empty(); 149 } 150 return nullableIn(t.getAnnotationMirrors()) 151 .map(Optional::of) 152 .orElseGet(() -> visitAll(t.getTypeArguments())); 153 } 154 155 @Override 156 public Optional<AnnotationMirror> visitTypeVariable(TypeVariable t, Void unused) { 157 if (!visiting.add(t)) { 158 return Optional.empty(); 159 } 160 return nullableIn(t.getAnnotationMirrors()) 161 .map(Optional::of) 162 .orElseGet(() -> visitAll(ImmutableList.of(t.getUpperBound(), t.getLowerBound()))); 163 } 164 165 @Override 166 public Optional<AnnotationMirror> visitArray(ArrayType t, Void unused) { 167 return nullableIn(t.getAnnotationMirrors()) 168 .map(Optional::of) 169 .orElseGet(() -> visit(t.getComponentType())); 170 } 171 172 @Override 173 public Optional<AnnotationMirror> visitWildcard(WildcardType t, Void unused) { 174 return nullableIn(t.getAnnotationMirrors()) 175 .map(Optional::of) 176 .orElseGet( 177 () -> 178 visitAll( 179 Stream.of(t.getExtendsBound(), t.getSuperBound()) 180 .filter(Objects::nonNull) 181 .collect(toList()))); 182 } 183 184 @Override 185 public Optional<AnnotationMirror> visitIntersection(IntersectionType t, Void unused) { 186 return nullableIn(t.getAnnotationMirrors()) 187 .map(Optional::of) 188 .orElseGet(() -> visitAll(t.getBounds())); 189 } 190 191 private Optional<AnnotationMirror> visitAll(List<? extends TypeMirror> types) { 192 return types.stream() 193 .map(this::visit) 194 .filter(Optional::isPresent) 195 .findFirst() 196 .orElse(Optional.empty()); 197 } 198 } 199 } 200