• 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     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