• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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;
18 
19 import static com.google.auto.common.MoreElements.isAnnotationPresent;
20 import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
21 
22 import com.google.auto.common.MoreElements;
23 import com.google.common.collect.ImmutableSet;
24 import dagger.assisted.Assisted;
25 import dagger.assisted.AssistedInject;
26 import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
27 import dagger.internal.codegen.binding.InjectionAnnotations;
28 import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
29 import dagger.internal.codegen.langmodel.DaggerElements;
30 import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
31 import dagger.internal.codegen.validation.ValidationReport;
32 import java.lang.annotation.Annotation;
33 import javax.annotation.processing.Messager;
34 import javax.inject.Inject;
35 import javax.lang.model.element.Element;
36 import javax.lang.model.element.ElementKind;
37 import javax.lang.model.element.TypeElement;
38 import javax.lang.model.element.VariableElement;
39 
40 /**
41  * An annotation processor for {@link dagger.assisted.Assisted}-annotated types.
42  *
43  * <p>This processing step should run after {@link AssistedFactoryProcessingStep}.
44  */
45 final class AssistedProcessingStep extends TypeCheckingProcessingStep<VariableElement> {
46   private final KotlinMetadataUtil kotlinMetadataUtil;
47   private final InjectionAnnotations injectionAnnotations;
48   private final DaggerElements elements;
49   private final Messager messager;
50 
51   @Inject
AssistedProcessingStep( KotlinMetadataUtil kotlinMetadataUtil, InjectionAnnotations injectionAnnotations, DaggerElements elements, Messager messager)52   AssistedProcessingStep(
53       KotlinMetadataUtil kotlinMetadataUtil,
54       InjectionAnnotations injectionAnnotations,
55       DaggerElements elements,
56       Messager messager) {
57     super(MoreElements::asVariable);
58     this.kotlinMetadataUtil = kotlinMetadataUtil;
59     this.injectionAnnotations = injectionAnnotations;
60     this.elements = elements;
61     this.messager = messager;
62   }
63 
64   @Override
annotations()65   public ImmutableSet<Class<? extends Annotation>> annotations() {
66     return ImmutableSet.of(Assisted.class);
67   }
68 
69   @Override
process( VariableElement assisted, ImmutableSet<Class<? extends Annotation>> annotations)70   protected void process(
71       VariableElement assisted, ImmutableSet<Class<? extends Annotation>> annotations) {
72     new AssistedValidator().validate(assisted).printMessagesTo(messager);
73   }
74 
75   private final class AssistedValidator {
validate(VariableElement assisted)76     ValidationReport<VariableElement> validate(VariableElement assisted) {
77       ValidationReport.Builder<VariableElement> report = ValidationReport.about(assisted);
78 
79       Element enclosingElement = assisted.getEnclosingElement();
80       if (!isAssistedInjectConstructor(enclosingElement)
81           && !isAssistedFactoryCreateMethod(enclosingElement)
82           // The generated java stubs for kotlin data classes contain a "copy" method that has
83           // the same parameters (and annotations) as the constructor, so just ignore it.
84           && !isKotlinDataClassCopyMethod(enclosingElement)) {
85         report.addError(
86             "@Assisted parameters can only be used within an @AssistedInject-annotated "
87                 + "constructor.",
88             assisted);
89       }
90 
91       injectionAnnotations
92           .getQualifiers(assisted)
93           .forEach(
94               qualifier ->
95                   report.addError(
96                       "Qualifiers cannot be used with @Assisted parameters.", assisted, qualifier));
97 
98       return report.build();
99     }
100   }
101 
isAssistedInjectConstructor(Element element)102   private boolean isAssistedInjectConstructor(Element element) {
103     return element.getKind() == ElementKind.CONSTRUCTOR
104         && isAnnotationPresent(element, AssistedInject.class);
105   }
106 
isAssistedFactoryCreateMethod(Element element)107   private boolean isAssistedFactoryCreateMethod(Element element) {
108     if (element.getKind() == ElementKind.METHOD) {
109       TypeElement enclosingElement = closestEnclosingTypeElement(element);
110       return AssistedInjectionAnnotations.isAssistedFactoryType(enclosingElement)
111           // This assumes we've already validated AssistedFactory and that a valid method exists.
112           && AssistedInjectionAnnotations.assistedFactoryMethod(enclosingElement, elements)
113               .equals(element);
114     }
115     return false;
116   }
117 
isKotlinDataClassCopyMethod(Element element)118   private boolean isKotlinDataClassCopyMethod(Element element) {
119     // Note: This is a best effort. Technically, we could check the return type and parameters of
120     // the copy method to verify it's the one associated with the constructor, but I'd rather keep
121     // this simple to avoid encoding too many details of kapt's stubs. At worst, we'll be allowing
122     // an @Assisted annotation that has no affect, which is already true for many of Dagger's other
123     // annotations.
124     return element.getKind() == ElementKind.METHOD
125         && element.getSimpleName().contentEquals("copy")
126         && kotlinMetadataUtil.isDataClass(closestEnclosingTypeElement(element));
127   }
128 }
129