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 com.google.auto.common.MoreElements.asType; 20 import static com.google.auto.common.MoreElements.asVariable; 21 import static com.google.auto.common.MoreTypes.asTypeElement; 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 javax.lang.model.element.Modifier.STATIC; 27 import static javax.lang.model.type.TypeKind.WILDCARD; 28 29 import com.google.auto.common.MoreElements; 30 import com.google.auto.common.MoreTypes; 31 import com.google.common.collect.ImmutableCollection; 32 import dagger.MembersInjector; 33 import dagger.assisted.Assisted; 34 import dagger.internal.codegen.base.FrameworkTypes; 35 import dagger.internal.codegen.base.RequestKinds; 36 import dagger.internal.codegen.binding.InjectionAnnotations; 37 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 38 import dagger.internal.codegen.langmodel.DaggerElements; 39 import dagger.model.RequestKind; 40 import java.util.Optional; 41 import javax.inject.Inject; 42 import javax.lang.model.element.AnnotationMirror; 43 import javax.lang.model.element.Element; 44 import javax.lang.model.element.ElementKind; 45 import javax.lang.model.element.TypeElement; 46 import javax.lang.model.element.VariableElement; 47 import javax.lang.model.type.DeclaredType; 48 import javax.lang.model.type.TypeKind; 49 import javax.lang.model.type.TypeMirror; 50 51 /** Validation for dependency requests. */ 52 final class DependencyRequestValidator { 53 private final MembersInjectionValidator membersInjectionValidator; 54 private final InjectionAnnotations injectionAnnotations; 55 private final KotlinMetadataUtil metadataUtil; 56 private final DaggerElements elements; 57 58 @Inject DependencyRequestValidator( MembersInjectionValidator membersInjectionValidator, InjectionAnnotations injectionAnnotations, KotlinMetadataUtil metadataUtil, DaggerElements elements)59 DependencyRequestValidator( 60 MembersInjectionValidator membersInjectionValidator, 61 InjectionAnnotations injectionAnnotations, 62 KotlinMetadataUtil metadataUtil, 63 DaggerElements elements) { 64 this.membersInjectionValidator = membersInjectionValidator; 65 this.injectionAnnotations = injectionAnnotations; 66 this.metadataUtil = metadataUtil; 67 this.elements = elements; 68 } 69 70 /** 71 * Adds an error if the given dependency request has more than one qualifier annotation or is a 72 * non-instance request with a wildcard type. 73 */ validateDependencyRequest( ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType)74 void validateDependencyRequest( 75 ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) { 76 if (MoreElements.isAnnotationPresent(requestElement, Assisted.class)) { 77 // Don't validate assisted parameters. These are not dependency requests. 78 return; 79 } 80 if (missingQualifierMetadata(requestElement)) { 81 report.addError( 82 "Unable to read annotations on an injected Kotlin property. The Dagger compiler must" 83 + " also be applied to any project containing @Inject properties.", 84 requestElement); 85 86 // Skip any further validation if we don't have valid metadata for a type that needs it. 87 return; 88 } 89 90 new Validator(report, requestElement, requestType).validate(); 91 } 92 93 /** Returns {@code true} if a kotlin inject field is missing metadata about its qualifiers. */ missingQualifierMetadata(Element requestElement)94 private boolean missingQualifierMetadata(Element requestElement) { 95 if (requestElement.getKind() == ElementKind.FIELD 96 // static injected fields are not supported, no need to get qualifier from kotlin metadata 97 && !requestElement.getModifiers().contains(STATIC) 98 && metadataUtil.hasMetadata(requestElement) 99 && metadataUtil.isMissingSyntheticPropertyForAnnotations(asVariable(requestElement))) { 100 Optional<TypeElement> membersInjector = 101 Optional.ofNullable( 102 elements.getTypeElement( 103 membersInjectorNameForType(asType(requestElement.getEnclosingElement())))); 104 return !membersInjector.isPresent(); 105 } 106 return false; 107 } 108 109 private final class Validator { 110 private final ValidationReport.Builder<?> report; 111 private final Element requestElement; 112 private final TypeMirror requestType; 113 private final TypeMirror keyType; 114 private final RequestKind requestKind; 115 private final ImmutableCollection<? extends AnnotationMirror> qualifiers; 116 117 Validator(ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType)118 Validator(ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) { 119 this.report = report; 120 this.requestElement = requestElement; 121 this.requestType = requestType; 122 this.keyType = extractKeyType(requestType); 123 this.requestKind = RequestKinds.getRequestKind(requestType); 124 this.qualifiers = injectionAnnotations.getQualifiers(requestElement); 125 } 126 validate()127 void validate() { 128 checkQualifiers(); 129 checkType(); 130 } 131 checkQualifiers()132 private void checkQualifiers() { 133 if (qualifiers.size() > 1) { 134 for (AnnotationMirror qualifier : qualifiers) { 135 report.addError( 136 "A single dependency request may not use more than one @Qualifier", 137 requestElement, 138 qualifier); 139 } 140 } 141 } 142 checkType()143 private void checkType() { 144 if (qualifiers.isEmpty() && keyType.getKind() == TypeKind.DECLARED) { 145 TypeElement typeElement = asTypeElement(keyType); 146 if (isAssistedInjectionType(typeElement)) { 147 report.addError( 148 "Dagger does not support injecting @AssistedInject type, " 149 + requestType 150 + ". Did you mean to inject its assisted factory type instead?", 151 requestElement); 152 } 153 if (requestKind != RequestKind.INSTANCE && isAssistedFactoryType(typeElement)) { 154 report.addError( 155 "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, " 156 + "or Produced<T> when T is an @AssistedFactory-annotated type such as " 157 + keyType, 158 requestElement); 159 } 160 } 161 if (keyType.getKind().equals(WILDCARD)) { 162 // TODO(ronshapiro): Explore creating this message using RequestKinds. 163 report.addError( 164 "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, " 165 + "or Produced<T> when T is a wildcard type such as " 166 + keyType, 167 requestElement); 168 } 169 if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) { 170 DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType); 171 if (membersInjectorType.getTypeArguments().isEmpty()) { 172 report.addError("Cannot inject a raw MembersInjector", requestElement); 173 } else { 174 report.addSubreport( 175 membersInjectionValidator.validateMembersInjectionRequest( 176 requestElement, membersInjectorType.getTypeArguments().get(0))); 177 } 178 } 179 } 180 } 181 182 /** 183 * Adds an error if the given dependency request is for a {@link dagger.producers.Producer} or 184 * {@link dagger.producers.Produced}. 185 * 186 * <p>Only call this when processing a provision binding. 187 */ 188 // TODO(dpb): Should we disallow Producer entry points in non-production components? checkNotProducer(ValidationReport.Builder<?> report, VariableElement requestElement)189 void checkNotProducer(ValidationReport.Builder<?> report, VariableElement requestElement) { 190 TypeMirror requestType = requestElement.asType(); 191 if (FrameworkTypes.isProducerType(requestType)) { 192 report.addError( 193 String.format( 194 "%s may only be injected in @Produces methods", 195 MoreTypes.asTypeElement(requestType).getSimpleName()), 196 requestElement); 197 } 198 } 199 } 200