1 /* 2 * Copyright (C) 2018 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.validation; 18 19 import static androidx.room.compiler.processing.XElementKt.isField; 20 import static androidx.room.compiler.processing.XElementKt.isTypeElement; 21 import static dagger.internal.codegen.base.FrameworkTypes.isFrameworkType; 22 import static dagger.internal.codegen.base.RequestKinds.extractKeyType; 23 import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedFactoryType; 24 import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedInjectionType; 25 import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType; 26 import static dagger.internal.codegen.xprocessing.XElements.asField; 27 import static dagger.internal.codegen.xprocessing.XElements.asTypeElement; 28 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; 29 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 30 import static dagger.internal.codegen.xprocessing.XTypes.isRawParameterizedType; 31 import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf; 32 import static dagger.internal.codegen.xprocessing.XTypes.isWildcard; 33 34 import androidx.room.compiler.processing.XAnnotation; 35 import androidx.room.compiler.processing.XElement; 36 import androidx.room.compiler.processing.XFieldElement; 37 import androidx.room.compiler.processing.XProcessingEnv; 38 import androidx.room.compiler.processing.XType; 39 import androidx.room.compiler.processing.XTypeElement; 40 import androidx.room.compiler.processing.XVariableElement; 41 import com.google.common.collect.ImmutableSet; 42 import dagger.internal.codegen.base.FrameworkTypes; 43 import dagger.internal.codegen.base.RequestKinds; 44 import dagger.internal.codegen.binding.InjectionAnnotations; 45 import dagger.internal.codegen.javapoet.TypeNames; 46 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 47 import dagger.internal.codegen.model.RequestKind; 48 import dagger.internal.codegen.xprocessing.XTypes; 49 import java.util.Optional; 50 import javax.inject.Inject; 51 52 /** Validation for dependency requests. */ 53 final class DependencyRequestValidator { 54 private final XProcessingEnv processingEnv; 55 private final MembersInjectionValidator membersInjectionValidator; 56 private final InjectionAnnotations injectionAnnotations; 57 private final KotlinMetadataUtil metadataUtil; 58 59 @Inject DependencyRequestValidator( XProcessingEnv processingEnv, MembersInjectionValidator membersInjectionValidator, InjectionAnnotations injectionAnnotations, KotlinMetadataUtil metadataUtil)60 DependencyRequestValidator( 61 XProcessingEnv processingEnv, 62 MembersInjectionValidator membersInjectionValidator, 63 InjectionAnnotations injectionAnnotations, 64 KotlinMetadataUtil metadataUtil) { 65 this.processingEnv = processingEnv; 66 this.membersInjectionValidator = membersInjectionValidator; 67 this.injectionAnnotations = injectionAnnotations; 68 this.metadataUtil = metadataUtil; 69 } 70 71 /** 72 * Adds an error if the given dependency request has more than one qualifier annotation or is a 73 * non-instance request with a wildcard type. 74 */ validateDependencyRequest( ValidationReport.Builder report, XElement requestElement, XType requestType)75 void validateDependencyRequest( 76 ValidationReport.Builder report, XElement requestElement, XType requestType) { 77 if (requestElement.hasAnnotation(TypeNames.ASSISTED)) { 78 // Don't validate assisted parameters. These are not dependency requests. 79 return; 80 } 81 if (missingQualifierMetadata(requestElement)) { 82 report.addError( 83 "Unable to read annotations on an injected Kotlin property. " 84 + "The Dagger compiler must also be applied to any project containing @Inject " 85 + "properties.", 86 requestElement); 87 88 // Skip any further validation if we don't have valid metadata for a type that needs it. 89 return; 90 } 91 92 new Validator(report, requestElement, requestType).validate(); 93 } 94 95 /** 96 * Returns {@code true} if a kotlin inject field is missing metadata about its qualifiers. 97 * 98 * <p>See https://youtrack.jetbrains.com/issue/KT-34684. 99 */ missingQualifierMetadata(XElement requestElement)100 private boolean missingQualifierMetadata(XElement requestElement) { 101 if (isField(requestElement)) { 102 XFieldElement fieldElement = asField(requestElement); 103 // static/top-level injected fields are not supported, 104 // so no need to get qualifier from kotlin metadata 105 if (!fieldElement.isStatic() 106 && isTypeElement(fieldElement.getEnclosingElement()) 107 && metadataUtil.hasMetadata(fieldElement) 108 && metadataUtil.isMissingSyntheticPropertyForAnnotations(fieldElement)) { 109 Optional<XTypeElement> membersInjector = 110 Optional.ofNullable( 111 processingEnv.findTypeElement( 112 membersInjectorNameForType(asTypeElement(fieldElement.getEnclosingElement())))); 113 return !membersInjector.isPresent(); 114 } 115 } 116 return false; 117 } 118 119 private final class Validator { 120 private final ValidationReport.Builder report; 121 private final XElement requestElement; 122 private final XType requestType; 123 private final ImmutableSet<XAnnotation> qualifiers; 124 Validator(ValidationReport.Builder report, XElement requestElement, XType requestType)125 Validator(ValidationReport.Builder report, XElement requestElement, XType requestType) { 126 this.report = report; 127 this.requestElement = requestElement; 128 this.requestType = requestType; 129 this.qualifiers = injectionAnnotations.getQualifiers(requestElement); 130 } 131 validate()132 void validate() { 133 checkQualifiers(); 134 checkType(); 135 } 136 checkQualifiers()137 private void checkQualifiers() { 138 if (qualifiers.size() > 1) { 139 for (XAnnotation qualifier : qualifiers) { 140 report.addError( 141 "A single dependency request may not use more than one @Qualifier", 142 requestElement, 143 qualifier); 144 } 145 } 146 } 147 checkType()148 private void checkType() { 149 if (isFrameworkType(requestType) && isRawParameterizedType(requestType)) { 150 report.addError( 151 "Dagger does not support injecting raw type: " + XTypes.toStableString(requestType), 152 requestElement); 153 // If the requested type is a raw framework type then skip the remaining checks as they 154 // will just be noise. 155 return; 156 } 157 XType keyType = extractKeyType(requestType); 158 if (qualifiers.isEmpty() && isDeclared(keyType)) { 159 XTypeElement typeElement = keyType.getTypeElement(); 160 if (isAssistedInjectionType(typeElement)) { 161 report.addError( 162 "Dagger does not support injecting @AssistedInject type, " 163 + XTypes.toStableString(requestType) 164 + ". Did you mean to inject its assisted factory type instead?", 165 requestElement); 166 } 167 RequestKind requestKind = RequestKinds.getRequestKind(requestType); 168 if (!(requestKind == RequestKind.INSTANCE || requestKind == RequestKind.PROVIDER) 169 && isAssistedFactoryType(typeElement)) { 170 report.addError( 171 "Dagger does not support injecting Lazy<T>, Producer<T>, " 172 + "or Produced<T> when T is an @AssistedFactory-annotated type such as " 173 + XTypes.toStableString(keyType), 174 requestElement); 175 } 176 } 177 if (isWildcard(keyType)) { 178 // TODO(ronshapiro): Explore creating this message using RequestKinds. 179 report.addError( 180 "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, " 181 + "or Produced<T> when T is a wildcard type such as " 182 + XTypes.toStableString(keyType), 183 requestElement); 184 } 185 if (isTypeOf(keyType, TypeNames.MEMBERS_INJECTOR)) { 186 if (keyType.getTypeArguments().isEmpty()) { 187 report.addError("Cannot inject a raw MembersInjector", requestElement); 188 } else { 189 report.addSubreport( 190 membersInjectionValidator.validateMembersInjectionRequest( 191 requestElement, keyType.getTypeArguments().get(0))); 192 } 193 } 194 } 195 } 196 197 /** 198 * Adds an error if the given dependency request is for a {@link dagger.producers.Producer} or 199 * {@link dagger.producers.Produced}. 200 * 201 * <p>Only call this when processing a provision binding. 202 */ 203 // TODO(dpb): Should we disallow Producer entry points in non-production components? checkNotProducer(ValidationReport.Builder report, XVariableElement requestElement)204 void checkNotProducer(ValidationReport.Builder report, XVariableElement requestElement) { 205 XType requestType = requestElement.getType(); 206 if (FrameworkTypes.isProducerType(requestType)) { 207 report.addError( 208 String.format( 209 "%s may only be injected in @Produces methods", 210 getSimpleName(requestType.getTypeElement())), 211 requestElement); 212 } 213 } 214 } 215