• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.hilt.processor.internal.kotlin;
18 
19 import static androidx.room.compiler.processing.XElementKt.isField;
20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
22 import static dagger.internal.codegen.xprocessing.XElements.asField;
23 import static dagger.internal.codegen.xprocessing.XElements.isStatic;
24 
25 import androidx.room.compiler.processing.XAnnotation;
26 import androidx.room.compiler.processing.XElement;
27 import androidx.room.compiler.processing.XFieldElement;
28 import androidx.room.compiler.processing.XMethodElement;
29 import androidx.room.compiler.processing.XTypeElement;
30 import com.google.common.base.Equivalence;
31 import com.google.common.collect.ImmutableList;
32 import com.google.common.collect.ImmutableSet;
33 import com.squareup.javapoet.ClassName;
34 import dagger.hilt.processor.internal.ClassNames;
35 import dagger.internal.codegen.xprocessing.XAnnotations;
36 import dagger.internal.codegen.xprocessing.XElements;
37 import javax.inject.Inject;
38 
39 /** Utility class for interacting with Kotlin Metadata. */
40 public final class KotlinMetadataUtil {
41 
42   private final KotlinMetadataFactory metadataFactory;
43 
44   @Inject
KotlinMetadataUtil(KotlinMetadataFactory metadataFactory)45   KotlinMetadataUtil(KotlinMetadataFactory metadataFactory) {
46     this.metadataFactory = metadataFactory;
47   }
48 
49   /**
50    * Returns {@code true} if this element has the Kotlin Metadata annotation or if it is enclosed in
51    * an element that does.
52    */
hasMetadata(XElement element)53   public boolean hasMetadata(XElement element) {
54     return XElements.closestEnclosingTypeElement(element).hasAnnotation(ClassNames.KOTLIN_METADATA);
55   }
56 
57   // TODO(kuanyingchou): Consider replacing it with `XAnnotated.getAnnotationsAnnotatedWith()`
58   //  once b/278077018 is resolved.
59   /**
60    * Returns the annotations on the given {@code element} annotated with {@code annotationName}.
61    *
62    * <p>Note: If the given {@code element} is a non-static field this method will return annotations
63    * on both the backing field and the associated synthetic property (if one exists).
64    */
getAnnotationsAnnotatedWith( XElement element, ClassName annotationName)65   public ImmutableList<XAnnotation> getAnnotationsAnnotatedWith(
66       XElement element, ClassName annotationName) {
67     return getAnnotations(element).stream()
68         .filter(annotation -> annotation.getTypeElement().hasAnnotation(annotationName))
69         .collect(toImmutableList());
70   }
71 
72   /**
73    * Returns the annotations on the given {@code element} that match the {@code annotationName}.
74    *
75    * <p>Note: If the given {@code element} is a non-static field this method will return annotations
76    * on both the backing field and the associated synthetic property (if one exists).
77    */
getAnnotations(XElement element)78   private ImmutableList<XAnnotation> getAnnotations(XElement element) {
79     ImmutableList<XAnnotation> annotations = ImmutableList.copyOf(element.getAllAnnotations());
80     ImmutableList<XAnnotation> syntheticAnnotations = getSyntheticPropertyAnnotations(element);
81     if (syntheticAnnotations.isEmpty()) {
82       return annotations;
83     }
84     // Dedupe any annotation that appears on both the field and the property.
85     // Note: we reduce the number of annotations we have to dedupe by only checking equivalence on
86     // annotations that have the same class name as a synthetic annotation. This avoids hitting
87     // TypeNotPresentException on annotation values with error types unless it has the same class
88     // name as a synthetic annotation.
89     ImmutableSet<ClassName> syntheticAnnotationClassNames =
90         syntheticAnnotations.stream()
91             .map(XAnnotations::getClassName)
92             .collect(toImmutableSet());
93     ImmutableSet<Equivalence.Wrapper<XAnnotation>> annotationEquivalenceWrappers =
94         annotations.stream()
95             .filter(annotation -> syntheticAnnotationClassNames.contains(annotation.getClassName()))
96             .map(XAnnotations.equivalence()::wrap)
97             .collect(toImmutableSet());
98     ImmutableList<XAnnotation> uniqueSyntheticAnnotations =
99         syntheticAnnotations.stream()
100             .map(XAnnotations.equivalence()::wrap)
101             .filter(wrapper -> !annotationEquivalenceWrappers.contains(wrapper))
102             .map(Equivalence.Wrapper::get)
103             .collect(toImmutableList());
104     return uniqueSyntheticAnnotations.isEmpty()
105         ? annotations
106         : ImmutableList.<XAnnotation>builder()
107             .addAll(annotations)
108             .addAll(uniqueSyntheticAnnotations)
109             .build();
110   }
111 
112   /**
113    * Returns the synthetic annotations of a Kotlin property.
114    *
115    * <p>Note that this method only looks for additional annotations in the synthetic property
116    * method, if any, of a Kotlin property and not for annotations in its backing field.
117    */
getSyntheticPropertyAnnotations(XElement element)118   private ImmutableList<XAnnotation> getSyntheticPropertyAnnotations(XElement element) {
119     // Currently, we avoid trying to get annotations from properties on object class's (i.e.
120     // properties with static jvm backing fields) due to issues explained in CL/336150864.
121     if (!isField(element) || isStatic(element)) {
122       return ImmutableList.of();
123     }
124     XFieldElement field = asField(element);
125     return hasMetadata(field)
126         ? metadataFactory
127             .create(field)
128             .getSyntheticAnnotationMethod(field)
129             .map(XMethodElement::getAllAnnotations)
130             .map(ImmutableList::copyOf)
131             .orElse(ImmutableList.<XAnnotation>of())
132         : ImmutableList.of();
133   }
134 
containsConstructorWithDefaultParam(XTypeElement typeElement)135   public boolean containsConstructorWithDefaultParam(XTypeElement typeElement) {
136     return hasMetadata(typeElement)
137         && metadataFactory.create(typeElement).containsConstructorWithDefaultParam();
138   }
139 }
140