• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.binding;
18 
19 import static androidx.room.compiler.processing.XElementKt.isConstructor;
20 import static androidx.room.compiler.processing.XElementKt.isField;
21 import static androidx.room.compiler.processing.XElementKt.isMethod;
22 import static androidx.room.compiler.processing.XElementKt.isMethodParameter;
23 import static androidx.room.compiler.processing.XElementKt.isTypeElement;
24 import static com.google.common.base.Preconditions.checkNotNull;
25 import static com.google.common.base.Preconditions.checkState;
26 import static com.google.common.collect.Iterables.getOnlyElement;
27 import static dagger.internal.codegen.binding.SourceFiles.factoryNameForElement;
28 import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable;
29 import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
30 import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
31 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
32 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
33 import static dagger.internal.codegen.xprocessing.XElements.asField;
34 import static dagger.internal.codegen.xprocessing.XElements.asMethod;
35 import static dagger.internal.codegen.xprocessing.XElements.asMethodParameter;
36 import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
37 import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
38 
39 import androidx.room.compiler.processing.XAnnotation;
40 import androidx.room.compiler.processing.XConstructorElement;
41 import androidx.room.compiler.processing.XElement;
42 import androidx.room.compiler.processing.XExecutableElement;
43 import androidx.room.compiler.processing.XFieldElement;
44 import androidx.room.compiler.processing.XProcessingEnv;
45 import androidx.room.compiler.processing.XTypeElement;
46 import com.google.common.collect.ImmutableList;
47 import com.google.common.collect.ImmutableSet;
48 import com.squareup.javapoet.ClassName;
49 import dagger.internal.codegen.base.DaggerSuperficialValidation;
50 import dagger.internal.codegen.base.ElementFormatter;
51 import dagger.internal.codegen.compileroption.CompilerOptions;
52 import dagger.internal.codegen.javapoet.TypeNames;
53 import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
54 import dagger.internal.codegen.model.DaggerAnnotation;
55 import dagger.internal.codegen.model.Scope;
56 import dagger.internal.codegen.xprocessing.XAnnotations;
57 import java.util.Optional;
58 import java.util.stream.Stream;
59 import javax.inject.Inject;
60 
61 /** Utilities relating to annotations defined in the {@code javax.inject} package. */
62 public final class InjectionAnnotations {
63   private final XProcessingEnv processingEnv;
64   private final KotlinMetadataUtil kotlinMetadataUtil;
65   private final DaggerSuperficialValidation superficialValidation;
66   private final CompilerOptions compilerOptions;
67 
68   @Inject
InjectionAnnotations( XProcessingEnv processingEnv, KotlinMetadataUtil kotlinMetadataUtil, DaggerSuperficialValidation superficialValidation, CompilerOptions compilerOptions)69   InjectionAnnotations(
70       XProcessingEnv processingEnv,
71       KotlinMetadataUtil kotlinMetadataUtil,
72       DaggerSuperficialValidation superficialValidation,
73       CompilerOptions compilerOptions) {
74     this.processingEnv = processingEnv;
75     this.kotlinMetadataUtil = kotlinMetadataUtil;
76     this.superficialValidation = superficialValidation;
77     this.compilerOptions = compilerOptions;
78   }
79 
80   /**
81    * Returns the scope on the given element if it exists.
82    *
83    * <p>The {@code ScopeMetadata} is used to avoid superficial validation on unnecessary
84    * annotations. If the {@code ScopeMetadata} does not exist, then all annotations must be
85    * superficially validated before we can determine if they are scopes or not.
86    *
87    * @throws IllegalArgumentException if the given element has more than one scope.
88    */
getScope(XElement element)89   public Optional<Scope> getScope(XElement element) {
90     return getScopes(element).stream().collect(toOptional());
91   }
92 
93   /**
94    * Returns the scopes on the given element, or an empty set if none exist.
95    *
96    * <p>Note: Use {@link #getScope(XElement)} if the usage of the scope on the given element has
97    * already been validated and known to be unique. This method should typically only be used in the
98    * process of such validation.
99    *
100    * <p>The {@code ScopeMetadata} is used to avoid superficial validation on unnecessary
101    * annotations. If the {@code ScopeMetadata} does not exist, then all annotations must be
102    * superficially validated before we can determine if they are scopes or not.
103    */
getScopes(XElement element)104   public ImmutableSet<Scope> getScopes(XElement element) {
105     superficialValidation.validateTypeOf(element);
106     ImmutableSet<Scope> scopes =
107         getScopesWithMetadata(element).orElseGet(() -> getScopesWithoutMetadata(element));
108 
109     // Fully validate each scope to ensure its values are also valid.
110     scopes.stream()
111         .map(scope -> scope.scopeAnnotation().xprocessing())
112         .forEach(scope -> superficialValidation.validateAnnotationOf(element, scope));
113     return scopes;
114   }
115 
getScopesWithoutMetadata(XElement element)116   private ImmutableSet<Scope> getScopesWithoutMetadata(XElement element) {
117     // Validate the annotation types before we check for @Scope, otherwise the @Scope
118     // annotation may appear to be missing (b/213880825).
119     superficialValidation.validateAnnotationTypesOf(element);
120     return element.getAllAnnotations().stream()
121         .filter(InjectionAnnotations::hasScopeAnnotation)
122         .map(DaggerAnnotation::from)
123         .map(Scope::scope)
124         .collect(toImmutableSet());
125   }
126 
getScopesWithMetadata(XElement element)127   private Optional<ImmutableSet<Scope>> getScopesWithMetadata(XElement element) {
128     Optional<XAnnotation> scopeMetadata = getScopeMetadata(element);
129     if (!scopeMetadata.isPresent()) {
130       return Optional.empty();
131     }
132     String scopeName = scopeMetadata.get().getAsString("value");
133     if (scopeName.isEmpty()) {
134       return Optional.of(ImmutableSet.of());
135     }
136     ImmutableList<XAnnotation> scopeAnnotations =
137         element.getAllAnnotations().stream()
138             .filter(
139                 annotation ->
140                     scopeName.contentEquals(
141                         annotation.getType().getTypeElement().getQualifiedName()))
142             .collect(toImmutableList());
143     checkState(
144         scopeAnnotations.size() == 1,
145         "Expected %s to have a scope annotation for %s but found: %s",
146         ElementFormatter.elementToString(element),
147         scopeName,
148         scopeAnnotations.stream().map(XAnnotations::toStableString).collect(toImmutableList()));
149     XAnnotation scopeAnnotation = getOnlyElement(scopeAnnotations);
150     // Do superficial validation before we convert to a Scope, otherwise the @Scope annotation may
151     // appear to be missing from the annotation if it's no longer on the classpath.
152     superficialValidation.validateAnnotationTypeOf(element, scopeAnnotation);
153 
154     // If strictSuperficialValidation is disabled, then we fall back to the old behavior where
155     // we may potentially miss a scope rather than report an exception.
156     if (compilerOptions.strictSuperficialValidation()) {
157       return Optional.of(ImmutableSet.of(Scope.scope(DaggerAnnotation.from(scopeAnnotation))));
158     } else {
159       return Scope.isScope(DaggerAnnotation.from(scopeAnnotation))
160           ? Optional.of(ImmutableSet.of(Scope.scope(DaggerAnnotation.from(scopeAnnotation))))
161           : Optional.empty();
162     }
163   }
164 
getScopeMetadata(XElement element)165   private Optional<XAnnotation> getScopeMetadata(XElement element) {
166     return getGeneratedNameForScopeMetadata(element)
167         .flatMap(factoryName -> Optional.ofNullable(processingEnv.findTypeElement(factoryName)))
168         .flatMap(factory -> Optional.ofNullable(factory.getAnnotation(TypeNames.SCOPE_METADATA)));
169   }
170 
getGeneratedNameForScopeMetadata(XElement element)171   private Optional<ClassName> getGeneratedNameForScopeMetadata(XElement element) {
172     // Currently, we only support ScopeMetadata for inject-constructor types and provides methods.
173     if (isTypeElement(element)) {
174       return asTypeElement(element).getConstructors().stream()
175           .filter(InjectionAnnotations::hasInjectOrAssistedInjectAnnotation)
176           .findFirst()
177           .map(SourceFiles::factoryNameForElement);
178     } else if (isMethod(element) && element.hasAnnotation(TypeNames.PROVIDES)) {
179       return Optional.of(factoryNameForElement(asMethod(element)));
180     }
181     return Optional.empty();
182   }
183 
184   /**
185    * Returns the qualifier on the given element if it exists.
186    *
187    * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary
188    * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be
189    * superficially validated before we can determine if they are qualifiers or not.
190    *
191    * @throws IllegalArgumentException if the given element has more than one qualifier.
192    */
getQualifier(XElement element)193   public Optional<XAnnotation> getQualifier(XElement element) {
194     checkNotNull(element);
195     ImmutableSet<XAnnotation> qualifierAnnotations = getQualifiers(element);
196     switch (qualifierAnnotations.size()) {
197       case 0:
198         return Optional.empty();
199       case 1:
200         return Optional.of(getOnlyElement(qualifierAnnotations));
201       default:
202         throw new IllegalArgumentException(
203             element + " was annotated with more than one @Qualifier annotation");
204     }
205   }
206 
207   /*
208    * Returns the qualifiers on the given element, or an empty set if none exist.
209    *
210    * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary
211    * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be
212    * superficially validated before we can determine if they are qualifiers or not.
213    */
getQualifiers(XElement element)214   public ImmutableSet<XAnnotation> getQualifiers(XElement element) {
215     superficialValidation.validateTypeOf(element);
216     ImmutableSet<XAnnotation> qualifiers =
217         getQualifiersWithMetadata(element)
218             .orElseGet(() -> getQualifiersWithoutMetadata(element));
219 
220     if (isField(element)) {
221       XFieldElement field = asField(element);
222       // static/top-level injected fields are not supported,
223       // no need to get qualifier from kotlin metadata
224       if (!field.isStatic()
225           && isTypeElement(field.getEnclosingElement())
226           && hasInjectAnnotation(field)
227           && kotlinMetadataUtil.hasMetadata(field)) {
228         qualifiers =
229             Stream.concat(qualifiers.stream(), getQualifiersForKotlinProperty(field).stream())
230                 .map(DaggerAnnotation::from) // Wrap in DaggerAnnotation to deduplicate
231                 .distinct()
232                 .map(DaggerAnnotation::xprocessing)
233                 .collect(toImmutableSet());
234       }
235     }
236 
237     // Fully validate each qualifier to ensure its values are also valid.
238     qualifiers.forEach(qualifier -> superficialValidation.validateAnnotationOf(element, qualifier));
239 
240     return qualifiers;
241   }
242 
getQualifiersWithoutMetadata(XElement element)243   private ImmutableSet<XAnnotation> getQualifiersWithoutMetadata(XElement element) {
244     // Validate the annotation types before we check for @Qualifier, otherwise the
245     // @Qualifier annotation may appear to be missing (b/213880825).
246     superficialValidation.validateAnnotationTypesOf(element);
247     return element.getAllAnnotations().stream()
248         .filter(InjectionAnnotations::hasQualifierAnnotation)
249         .collect(toImmutableSet());
250   }
251 
getQualifiersWithMetadata(XElement element)252   private Optional<ImmutableSet<XAnnotation>> getQualifiersWithMetadata(XElement element) {
253     Optional<XAnnotation> qualifierMetadata = getQualifierMetadata(element);
254     if (!qualifierMetadata.isPresent()) {
255       return Optional.empty();
256     }
257     ImmutableSet<String> qualifierNames =
258         ImmutableSet.copyOf(qualifierMetadata.get().getAsStringList("value"));
259     if (qualifierNames.isEmpty()) {
260       return Optional.of(ImmutableSet.of());
261     }
262     ImmutableSet<XAnnotation> qualifierAnnotations =
263         element.getAllAnnotations().stream()
264             .filter(
265                 annotation ->
266                     qualifierNames.contains(
267                         annotation.getType().getTypeElement().getQualifiedName()))
268             .collect(toImmutableSet());
269     if (qualifierAnnotations.isEmpty()) {
270       return Optional.of(ImmutableSet.of());
271     }
272     // We should be guaranteed that there's exactly one qualifier since the existance of
273     // @QualifierMetadata means that this element has already been processed and multiple
274     // qualifiers would have been caught already.
275     XAnnotation qualifierAnnotation = getOnlyElement(qualifierAnnotations);
276 
277     // Ensure the annotation type is superficially valid before we check for @Qualifier, otherwise
278     // the @Qualifier marker may appear to be missing from the annotation (b/213880825).
279     superficialValidation.validateAnnotationTypeOf(element, qualifierAnnotation);
280     if (compilerOptions.strictSuperficialValidation()) {
281       return Optional.of(ImmutableSet.of(qualifierAnnotation));
282     } else {
283       // If strictSuperficialValidation is disabled, then we fall back to the old behavior where
284       // we may potentially miss a qualifier rather than report an exception.
285       return hasQualifierAnnotation(qualifierAnnotation)
286           ? Optional.of(ImmutableSet.of(qualifierAnnotation))
287           : Optional.empty();
288     }
289   }
290 
291   /**
292    * Returns {@code QualifierMetadata} annotation.
293    *
294    * <p>Currently, {@code QualifierMetadata} is only associated with inject constructor parameters,
295    * inject fields, inject method parameters, provide methods, and provide method parameters.
296    */
getQualifierMetadata(XElement element)297   private Optional<XAnnotation> getQualifierMetadata(XElement element) {
298     return getGeneratedNameForQualifierMetadata(element)
299         .flatMap(name -> Optional.ofNullable(processingEnv.findTypeElement(name)))
300         .flatMap(type -> Optional.ofNullable(type.getAnnotation(TypeNames.QUALIFIER_METADATA)));
301   }
302 
getGeneratedNameForQualifierMetadata(XElement element)303   private Optional<ClassName> getGeneratedNameForQualifierMetadata(XElement element) {
304     // Currently we only support @QualifierMetadata for @Inject fields, @Inject method parameters,
305     // @Inject constructor parameters, @Provides methods, and @Provides method parameters.
306     if (isField(element) && hasInjectAnnotation(element)) {
307       return Optional.of(membersInjectorNameForType(closestEnclosingTypeElement(element)));
308     } else if (isMethod(element) && element.hasAnnotation(TypeNames.PROVIDES)) {
309       return Optional.of(factoryNameForElement(asMethod(element)));
310     } else if (isMethodParameter(element)) {
311       XExecutableElement executableElement = asMethodParameter(element).getEnclosingElement();
312       if (isConstructor(executableElement)
313           && hasInjectOrAssistedInjectAnnotation(executableElement)) {
314         return Optional.of(factoryNameForElement(executableElement));
315       }
316       if (isMethod(executableElement) && hasInjectAnnotation(executableElement)) {
317         return Optional.of(membersInjectorNameForType(closestEnclosingTypeElement(element)));
318       }
319       if (isMethod(executableElement) && executableElement.hasAnnotation(TypeNames.PROVIDES)) {
320         return Optional.of(factoryNameForElement(executableElement));
321       }
322     }
323     return Optional.empty();
324   }
325 
326   /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */
injectedConstructors(XTypeElement type)327   public static ImmutableSet<XConstructorElement> injectedConstructors(XTypeElement type) {
328     return type.getConstructors().stream()
329         .filter(InjectionAnnotations::hasInjectAnnotation)
330         .collect(toImmutableSet());
331   }
332 
hasQualifierAnnotation(XAnnotation annotation)333   private static boolean hasQualifierAnnotation(XAnnotation annotation) {
334     return annotation
335         .getType()
336         .getTypeElement()
337         .hasAnyAnnotation(TypeNames.QUALIFIER, TypeNames.QUALIFIER_JAVAX);
338   }
339 
hasScopeAnnotation(XAnnotation annotation)340   private static boolean hasScopeAnnotation(XAnnotation annotation) {
341     return annotation
342         .getType()
343         .getTypeElement()
344         .hasAnyAnnotation(TypeNames.SCOPE, TypeNames.SCOPE_JAVAX);
345   }
346 
347   /** Returns true if the given element is annotated with {@link Inject}. */
hasInjectAnnotation(XElement element)348   public static boolean hasInjectAnnotation(XElement element) {
349     return element.hasAnyAnnotation(TypeNames.INJECT, TypeNames.INJECT_JAVAX);
350   }
351 
352   /** Returns true if the given element is annotated with {@link Inject}. */
hasInjectOrAssistedInjectAnnotation(XElement element)353   public static boolean hasInjectOrAssistedInjectAnnotation(XElement element) {
354     return element.hasAnyAnnotation(
355         TypeNames.INJECT, TypeNames.INJECT_JAVAX, TypeNames.ASSISTED_INJECT);
356   }
357 
358   /**
359    * Gets the qualifiers annotation of a Kotlin Property. Finding these annotations involve finding
360    * the synthetic method for annotations as described by the Kotlin metadata or finding the
361    * corresponding MembersInjector method for the field, which also contains the qualifier
362    * annotation.
363    */
getQualifiersForKotlinProperty(XFieldElement field)364   private ImmutableSet<XAnnotation> getQualifiersForKotlinProperty(XFieldElement field) {
365     // TODO(bcorso): Consider moving this to KotlinMetadataUtil
366     if (kotlinMetadataUtil.isMissingSyntheticPropertyForAnnotations(field)) {
367       // If we detect that the synthetic method for annotations is missing, possibly due to the
368       // element being from a compiled class, then find the MembersInjector that was generated
369       // for the enclosing class and extract the qualifier information from it.
370       XTypeElement membersInjector =
371           processingEnv.findTypeElement(
372               membersInjectorNameForType(asTypeElement(field.getEnclosingElement())));
373       if (membersInjector != null) {
374         String memberInjectedFieldSignature = memberInjectedFieldSignatureForVariable(field);
375         // TODO(danysantiago): We have to iterate over all the injection methods for every qualifier
376         //  look up. Making this N^2 when looking through all the injected fields. :(
377         return membersInjector.getDeclaredMethods().stream()
378             .filter(
379                 method ->
380                     Optional.ofNullable(method.getAnnotation(TypeNames.INJECTED_FIELD_SIGNATURE))
381                         .map(annotation -> annotation.getAsString("value"))
382                         .map(memberInjectedFieldSignature::equals)
383                         // If a method is not an @InjectedFieldSignature method then filter it out
384                         .orElse(false))
385             .collect(toOptional())
386             .map(this::getQualifiers)
387             .orElseThrow(
388                 () ->
389                     new IllegalStateException(
390                         String.format(
391                             "No matching InjectedFieldSignature for %1$s. This likely means that "
392                                 + "%1$s was compiled with an older, incompatible version of "
393                                 + "Dagger. Please update all Dagger dependencies to the same "
394                                 + "version.",
395                             memberInjectedFieldSignature)));
396       } else {
397         throw new IllegalStateException(
398             "No MembersInjector found for " + field.getEnclosingElement());
399       }
400     } else {
401       return Stream.concat(
402               kotlinMetadataUtil
403                   .getSyntheticPropertyAnnotations(field, TypeNames.QUALIFIER)
404                   .stream(),
405               kotlinMetadataUtil
406                   .getSyntheticPropertyAnnotations(field, TypeNames.QUALIFIER_JAVAX)
407                   .stream())
408           .collect(toImmutableSet());
409     }
410   }
411 }
412