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 checkQualifiers(report, requestElement); 81 checkType(report, requestElement, requestType); 82 } 83 checkQualifiers(ValidationReport.Builder<?> report, Element requestElement)84 private void checkQualifiers(ValidationReport.Builder<?> report, Element requestElement) { 85 if (requestElement.getKind() == ElementKind.FIELD 86 // static injected fields are not supported, no need to get qualifier from kotlin metadata 87 && !requestElement.getModifiers().contains(STATIC) 88 && metadataUtil.hasMetadata(requestElement) 89 && metadataUtil.isMissingSyntheticPropertyForAnnotations(asVariable(requestElement))) { 90 Optional<TypeElement> membersInjector = 91 Optional.ofNullable( 92 elements.getTypeElement( 93 membersInjectorNameForType(asType(requestElement.getEnclosingElement())))); 94 if (!membersInjector.isPresent()) { 95 report.addError( 96 "Unable to read annotations on an injected Kotlin property. The Dagger compiler must" 97 + " also be applied to any project containing @Inject properties.", 98 requestElement); 99 return; // finish checking qualifiers since current information is unreliable. 100 } 101 } 102 103 ImmutableCollection<? extends AnnotationMirror> qualifiers = 104 injectionAnnotations.getQualifiers(requestElement); 105 if (qualifiers.size() > 1) { 106 for (AnnotationMirror qualifier : qualifiers) { 107 report.addError( 108 "A single dependency request may not use more than one @Qualifier", 109 requestElement, 110 qualifier); 111 } 112 } 113 } 114 checkType( ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType)115 private void checkType( 116 ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) { 117 TypeMirror keyType = extractKeyType(requestType); 118 RequestKind requestKind = RequestKinds.getRequestKind(requestType); 119 if (keyType.getKind() == TypeKind.DECLARED) { 120 TypeElement typeElement = asTypeElement(keyType); 121 if (isAssistedInjectionType(typeElement)) { 122 report.addError( 123 "Dagger does not support injecting @AssistedInject type, " 124 + requestType 125 + ". Did you mean to inject its assisted factory type instead?", 126 requestElement); 127 } 128 if (requestKind != RequestKind.INSTANCE && isAssistedFactoryType(typeElement)) { 129 report.addError( 130 "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, " 131 + "or Produced<T> when T is an @AssistedFactory-annotated type such as " 132 + keyType, 133 requestElement); 134 } 135 } 136 if (keyType.getKind().equals(WILDCARD)) { 137 // TODO(ronshapiro): Explore creating this message using RequestKinds. 138 report.addError( 139 "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, " 140 + "or Produced<T> when T is a wildcard type such as " 141 + keyType, 142 requestElement); 143 } 144 if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) { 145 DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType); 146 if (membersInjectorType.getTypeArguments().isEmpty()) { 147 report.addError("Cannot inject a raw MembersInjector", requestElement); 148 } else { 149 report.addSubreport( 150 membersInjectionValidator.validateMembersInjectionRequest( 151 requestElement, membersInjectorType.getTypeArguments().get(0))); 152 } 153 } 154 } 155 156 /** 157 * Adds an error if the given dependency request is for a {@link dagger.producers.Producer} or 158 * {@link dagger.producers.Produced}. 159 * 160 * <p>Only call this when processing a provision binding. 161 */ 162 // TODO(dpb): Should we disallow Producer entry points in non-production components? checkNotProducer(ValidationReport.Builder<?> report, VariableElement requestElement)163 void checkNotProducer(ValidationReport.Builder<?> report, VariableElement requestElement) { 164 TypeMirror requestType = requestElement.asType(); 165 if (FrameworkTypes.isProducerType(requestType)) { 166 report.addError( 167 String.format( 168 "%s may only be injected in @Produces methods", 169 MoreTypes.asTypeElement(requestType).getSimpleName()), 170 requestElement); 171 } 172 } 173 } 174