• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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