1 /* 2 * Copyright (C) 2023 The Dagger Authors. 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 17 package dagger.internal.codegen.binding; 18 19 import static androidx.room.compiler.processing.XElementKt.isMethod; 20 import static androidx.room.compiler.processing.XElementKt.isVariableElement; 21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; 22 import static dagger.internal.codegen.xprocessing.XElements.asMethod; 23 import static dagger.internal.codegen.xprocessing.XElements.asVariable; 24 25 import androidx.room.compiler.processing.XAnnotation; 26 import androidx.room.compiler.processing.XElement; 27 import androidx.room.compiler.processing.XNullability; 28 import androidx.room.compiler.processing.XType; 29 import com.google.auto.value.AutoValue; 30 import com.google.common.collect.ImmutableSet; 31 import com.squareup.javapoet.ClassName; 32 import dagger.internal.codegen.xprocessing.XAnnotations; 33 import java.util.stream.Stream; 34 35 /** 36 * Contains information about the nullability of an element. 37 * 38 * <p>Note that an element can be nullable if either: 39 * 40 * <ul> 41 * <li>The element is annotated with {@code Nullable} or 42 * <li>the associated kotlin type is nullable (i.e. {@code T?} types in Kotlin source). 43 * </ul> 44 */ 45 @AutoValue 46 public abstract class Nullability { 47 /** A constant that can represent any non-null element. */ 48 public static final Nullability NOT_NULLABLE = 49 new AutoValue_Nullability(false, ImmutableSet.of()); 50 of(XElement element)51 public static Nullability of(XElement element) { 52 return new AutoValue_Nullability( 53 /* isKotlinTypeNullable= */ isKotlinTypeNullable(element), 54 /* nullableAnnotations= */ getNullableAnnotations(element)); 55 } 56 getNullableAnnotations(XElement element)57 private static ImmutableSet<ClassName> getNullableAnnotations(XElement element) { 58 return getNullableAnnotations(element.getAllAnnotations().stream(), ImmutableSet.of()); 59 } 60 getNullableAnnotations( Stream<XAnnotation> annotations, ImmutableSet<ClassName> filterSet)61 private static ImmutableSet<ClassName> getNullableAnnotations( 62 Stream<XAnnotation> annotations, 63 ImmutableSet<ClassName> filterSet) { 64 return annotations 65 .map(XAnnotations::getClassName) 66 .filter(annotation -> annotation.simpleName().contentEquals("Nullable")) 67 .filter(annotation -> !filterSet.contains(annotation)) 68 .collect(toImmutableSet()); 69 } 70 71 /** 72 * Returns {@code true} if the element's type is a Kotlin nullable type, e.g. {@code Foo?}. 73 * 74 * <p>Note that this method ignores any {@code @Nullable} type annotations and only looks for 75 * explicit {@code ?} usages on kotlin types. 76 */ isKotlinTypeNullable(XElement element)77 private static boolean isKotlinTypeNullable(XElement element) { 78 if (element.getClosestMemberContainer().isFromJava()) { 79 // Note: Technically, it isn't possible for Java sources to have nullable types like in Kotlin 80 // sources, but for some reason KSP treats certain types as nullable if they have a 81 // specific @Nullable (TYPE_USE target) annotation. Thus, to avoid inconsistencies with KAPT, 82 // just return false if this element is from a java source. 83 return false; 84 } else if (isMethod(element)) { 85 return isKotlinTypeNullable(asMethod(element).getReturnType()); 86 } else if (isVariableElement(element)) { 87 return isKotlinTypeNullable(asVariable(element).getType()); 88 } else { 89 return false; 90 } 91 } 92 isKotlinTypeNullable(XType type)93 private static boolean isKotlinTypeNullable(XType type) { 94 return type.getNullability() == XNullability.NULLABLE; 95 } 96 isKotlinTypeNullable()97 public abstract boolean isKotlinTypeNullable(); 98 nullableAnnotations()99 public abstract ImmutableSet<ClassName> nullableAnnotations(); 100 isNullable()101 public final boolean isNullable() { 102 return isKotlinTypeNullable() || !nullableAnnotations().isEmpty(); 103 } 104 Nullability()105 Nullability() {} 106 } 107