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