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.xprocessing; 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.XAnnotated; 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.google.common.collect.Sets; 32 import com.squareup.javapoet.ClassName; 33 import java.util.Optional; 34 35 /** 36 * Contains information about the nullability of an element or type. 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(ImmutableSet.of(), ImmutableSet.of(), false); 50 of(XElement element)51 public static Nullability of(XElement element) { 52 ImmutableSet<ClassName> nonTypeUseNullableAnnotations = getNullableAnnotations(element); 53 Optional<XType> type = getType(element); 54 ImmutableSet<ClassName> typeUseNullableAnnotations = 55 ImmutableSet.of(); 56 boolean isKotlinTypeNullable = 57 // Note: Technically, it isn't possible for Java sources to have nullable types like in 58 // Kotlin sources, but for some reason KSP treats certain types as nullable if they have a 59 // specific @Nullable (TYPE_USE target) annotation. Thus, to avoid inconsistencies with 60 // KAPT, just ignore type nullability for elements in java sources. 61 !element.getClosestMemberContainer().isFromJava() 62 && type.isPresent() 63 && type.get().getNullability() == XNullability.NULLABLE; 64 return new AutoValue_Nullability( 65 nonTypeUseNullableAnnotations, 66 // Filter type use annotations that are also found on the element as non-type use 67 // annotations. This prevents them from being applied twice in some scenarios and just 68 // defaults to using them in the way before Dagger supported type use annotations. 69 Sets.difference(typeUseNullableAnnotations, nonTypeUseNullableAnnotations).immutableCopy(), 70 isKotlinTypeNullable); 71 } 72 getNullableAnnotations(XAnnotated annotated)73 private static ImmutableSet<ClassName> getNullableAnnotations(XAnnotated annotated) { 74 return annotated.getAllAnnotations().stream() 75 .map(XAnnotations::getClassName) 76 .filter(annotation -> annotation.simpleName().contentEquals("Nullable")) 77 .collect(toImmutableSet()); 78 } 79 getType(XElement element)80 private static Optional<XType> getType(XElement element) { 81 if (isMethod(element)) { 82 return Optional.of(asMethod(element).getReturnType()); 83 } else if (isVariableElement(element)) { 84 return Optional.of(asVariable(element).getType()); 85 } 86 return Optional.empty(); 87 } 88 nonTypeUseNullableAnnotations()89 public abstract ImmutableSet<ClassName> nonTypeUseNullableAnnotations(); 90 typeUseNullableAnnotations()91 public abstract ImmutableSet<ClassName> typeUseNullableAnnotations(); 92 93 /** 94 * Returns {@code true} if the element's type is a Kotlin nullable type, e.g. {@code Foo?}. 95 * 96 * <p>Note that this method ignores any {@code @Nullable} type annotations and only looks for 97 * explicit {@code ?} usages on kotlin types. 98 */ isKotlinTypeNullable()99 public abstract boolean isKotlinTypeNullable(); 100 nullableAnnotations()101 public ImmutableSet<ClassName> nullableAnnotations() { 102 return ImmutableSet.<ClassName>builder() 103 .addAll(nonTypeUseNullableAnnotations()) 104 .addAll(typeUseNullableAnnotations()).build(); 105 } 106 isNullable()107 public final boolean isNullable() { 108 return isKotlinTypeNullable() || !nullableAnnotations().isEmpty(); 109 } 110 Nullability()111 Nullability() {} 112 } 113