1 /* 2 * Copyright (C) 2022 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.compat.XConverters.getProcessingEnv; 20 import static androidx.room.compiler.processing.compat.XConverters.toJavac; 21 import static androidx.room.compiler.processing.compat.XConverters.toXProcessing; 22 import static com.google.common.base.Preconditions.checkArgument; 23 import static com.google.common.collect.Iterables.getOnlyElement; 24 25 import androidx.room.compiler.processing.XMethodElement; 26 import androidx.room.compiler.processing.XProcessingEnv; 27 import androidx.room.compiler.processing.XType; 28 import androidx.room.compiler.processing.XTypeElement; 29 import androidx.room.compiler.processing.compat.XConverters; 30 import com.squareup.javapoet.ClassName; 31 import com.squareup.javapoet.TypeName; 32 import java.util.Optional; 33 import javax.lang.model.SourceVersion; 34 import javax.lang.model.type.TypeKind; 35 36 /** A utility class for {@link XProcessingEnvs} helper methods. */ 37 // TODO(bcorso): Consider moving these methods into XProcessing library. 38 public final class XProcessingEnvs { 39 /** Returns {@code true} if the sources are being compiled on a javac and the version is <= 8. */ isPreJava8SourceVersion(XProcessingEnv processingEnv)40 public static boolean isPreJava8SourceVersion(XProcessingEnv processingEnv) { 41 Optional<SourceVersion> javaSourceVersion = javaSourceVersion(processingEnv); 42 return javaSourceVersion.isPresent() 43 && javaSourceVersion.get().compareTo(SourceVersion.RELEASE_8) < 0; 44 } 45 46 /** 47 * Returns an optional containing the java source version if the current sources are being 48 * compiled with javac, or else returns an empty optional. 49 */ javaSourceVersion(XProcessingEnv processingEnv)50 private static Optional<SourceVersion> javaSourceVersion(XProcessingEnv processingEnv) { 51 switch (processingEnv.getBackend()) { 52 case JAVAC: 53 return Optional.of(toJavac(processingEnv).getSourceVersion()); 54 case KSP: 55 return Optional.empty(); 56 } 57 throw new AssertionError("Unexpected backend: " + processingEnv.getBackend()); 58 } 59 60 /** 61 * Returns {@code true} if {@code overrider} overrides {@code overridden} from within {@code type} 62 */ javacOverrides( XMethodElement overrider, XMethodElement overridden, XTypeElement type)63 public static boolean javacOverrides( 64 XMethodElement overrider, 65 XMethodElement overridden, 66 XTypeElement type) { 67 XProcessingEnv processingEnv = getProcessingEnv(type); 68 switch (processingEnv.getBackend()) { 69 case JAVAC: 70 // TODO(bcorso): Investigate why XMethodElement#overrides() throws exception in some cases. 71 return toJavac(processingEnv) 72 .getElementUtils() // ALLOW_TYPES_ELEMENTS 73 .overrides(toJavac(overrider), toJavac(overridden), toJavac(type)); 74 case KSP: 75 // For KSP, just use the standard overrides since the issues above are specific to javac. 76 return overrider.overrides(overridden, type); 77 } 78 throw new AssertionError("Unexpected backend: " + processingEnv.getBackend()); 79 } 80 81 /** Returns a new unbounded wildcard type argument, i.e. {@code <?>}. */ getUnboundedWildcardType(XProcessingEnv processingEnv)82 public static XType getUnboundedWildcardType(XProcessingEnv processingEnv) { 83 switch (processingEnv.getBackend()) { 84 case JAVAC: 85 return toXProcessing( 86 toJavac(processingEnv) 87 .getTypeUtils() // ALLOW_TYPES_ELEMENTS 88 .getWildcardType(null, null), 89 processingEnv); 90 case KSP: 91 // In KSP, the equivalent of an unbounded wildcard type is the star projection. However, 92 // there's currently no way to create a star projection type directly. Instead, we create a 93 // List<T> type, get its star projection, and then grab the type argument from that. 94 return XConverters.toXProcessing( 95 XConverters.toKS(processingEnv.requireType("java.util.List")).starProjection(), 96 processingEnv) 97 .getTypeArguments() 98 .get(0); 99 } 100 throw new AssertionError("Unexpected backend: " + processingEnv.getBackend()); 101 } 102 103 /** 104 * Returns {@code type}'s single type argument. 105 * 106 * <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}. 107 * 108 * @throws IllegalArgumentException if {@code type} is not a declared type or has zero or more 109 * than one type arguments. 110 */ unwrapType(XType type)111 public static XType unwrapType(XType type) { 112 XType unwrapped = unwrapTypeOrDefault(type, null); 113 checkArgument(unwrapped != null, "%s is a raw type", type); 114 return unwrapped; 115 } 116 117 /** 118 * Returns {@code type}'s single type argument, if one exists, or {@link Object} if not. 119 * 120 * <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}. 121 * 122 * @throws IllegalArgumentException if {@code type} is not a declared type or has more than one 123 * type argument. 124 */ unwrapTypeOrObject(XType type, XProcessingEnv processingEnv)125 public static XType unwrapTypeOrObject(XType type, XProcessingEnv processingEnv) { 126 return unwrapTypeOrDefault(type, processingEnv.requireType(TypeName.OBJECT)); 127 } 128 unwrapTypeOrDefault(XType type, XType defaultType)129 private static XType unwrapTypeOrDefault(XType type, XType defaultType) { 130 XTypeElement typeElement = type.getTypeElement(); 131 checkArgument( 132 !typeElement.getType().getTypeArguments().isEmpty(), 133 "%s does not have a type parameter", 134 typeElement.getQualifiedName()); 135 return getOnlyElement(type.getTypeArguments(), defaultType); 136 } 137 138 /** Returns a primitive int {@link XType}. */ getPrimitiveIntType(XProcessingEnv processingEnv)139 public static XType getPrimitiveIntType(XProcessingEnv processingEnv) { 140 return toXProcessing( 141 toJavac(processingEnv) 142 .getTypeUtils() // ALLOW_TYPES_ELEMENTS 143 .getPrimitiveType(TypeKind.INT), 144 processingEnv); 145 } 146 147 /** Returns the type this method is enclosed in. */ wrapType(ClassName wrapper, XType type, XProcessingEnv processingEnv)148 public static XType wrapType(ClassName wrapper, XType type, XProcessingEnv processingEnv) { 149 return processingEnv.getDeclaredType(processingEnv.requireTypeElement(wrapper), type); 150 } 151 XProcessingEnvs()152 private XProcessingEnvs() {} 153 } 154