1 /*
2  * Copyright (C) 2019 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.kotlin;
18 
19 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
20 import static com.google.auto.common.MoreElements.isAnnotationPresent;
21 import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
22 import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT;
23 import static kotlinx.metadata.Flag.Class.IS_DATA;
24 import static kotlinx.metadata.Flag.Class.IS_OBJECT;
25 import static kotlinx.metadata.Flag.IS_PRIVATE;
26 
27 import com.google.common.collect.ImmutableCollection;
28 import com.google.common.collect.ImmutableList;
29 import dagger.internal.codegen.extension.DaggerCollectors;
30 import java.lang.annotation.Annotation;
31 import java.util.Optional;
32 import javax.inject.Inject;
33 import javax.lang.model.element.AnnotationMirror;
34 import javax.lang.model.element.Element;
35 import javax.lang.model.element.ExecutableElement;
36 import javax.lang.model.element.TypeElement;
37 import javax.lang.model.element.VariableElement;
38 import javax.lang.model.util.ElementFilter;
39 import kotlin.Metadata;
40 import kotlin.jvm.JvmStatic;
41 import kotlinx.metadata.Flag;
42 
43 /** Utility class for interacting with Kotlin Metadata. */
44 public final class KotlinMetadataUtil {
45 
46   private final KotlinMetadataFactory metadataFactory;
47 
48   @Inject
KotlinMetadataUtil(KotlinMetadataFactory metadataFactory)49   KotlinMetadataUtil(KotlinMetadataFactory metadataFactory) {
50     this.metadataFactory = metadataFactory;
51   }
52 
53   /**
54    * Returns {@code true} if this element has the Kotlin Metadata annotation or if it is enclosed in
55    * an element that does.
56    */
hasMetadata(Element element)57   public boolean hasMetadata(Element element) {
58     return isAnnotationPresent(closestEnclosingTypeElement(element), Metadata.class);
59   }
60 
61   /**
62    * Returns the synthetic annotations of a Kotlin property.
63    *
64    * <p>Note that this method only looks for additional annotations in the synthetic property
65    * method, if any, of a Kotlin property and not for annotations in its backing field.
66    */
getSyntheticPropertyAnnotations( VariableElement fieldElement, Class<? extends Annotation> annotationType)67   public ImmutableCollection<? extends AnnotationMirror> getSyntheticPropertyAnnotations(
68       VariableElement fieldElement, Class<? extends Annotation> annotationType) {
69     return metadataFactory
70         .create(fieldElement)
71         .getSyntheticAnnotationMethod(fieldElement)
72         .map(methodElement -> getAnnotatedAnnotations(methodElement, annotationType).asList())
73         .orElse(ImmutableList.of());
74   }
75 
76   /**
77    * Returns {@code true} if the synthetic method for annotations is missing. This can occur when
78    * the Kotlin metadata of the property reports that it contains a synthetic method for annotations
79    * but such method is not found since it is synthetic and ignored by the processor.
80    */
isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement)81   public boolean isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement) {
82     return metadataFactory.create(fieldElement).isMissingSyntheticAnnotationMethod(fieldElement);
83   }
84 
85   /** Returns {@code true} if this type element is a Kotlin Object. */
isObjectClass(TypeElement typeElement)86   public boolean isObjectClass(TypeElement typeElement) {
87     return hasMetadata(typeElement)
88         && metadataFactory.create(typeElement).classMetadata().flags(IS_OBJECT);
89   }
90 
91   /** Returns {@code true} if this type element is a Kotlin data class. */
isDataClass(TypeElement typeElement)92   public boolean isDataClass(TypeElement typeElement) {
93     return hasMetadata(typeElement)
94         && metadataFactory.create(typeElement).classMetadata().flags(IS_DATA);
95   }
96 
97   /* Returns {@code true} if this type element is a Kotlin Companion Object. */
isCompanionObjectClass(TypeElement typeElement)98   public boolean isCompanionObjectClass(TypeElement typeElement) {
99     return hasMetadata(typeElement)
100         && metadataFactory.create(typeElement).classMetadata().flags(IS_COMPANION_OBJECT);
101   }
102 
103   /* Returns {@code true} if this type element has a Kotlin Companion Object. */
hasEnclosedCompanionObject(TypeElement typeElement)104   public boolean hasEnclosedCompanionObject(TypeElement typeElement) {
105     return hasMetadata(typeElement)
106         && metadataFactory.create(typeElement).classMetadata().companionObjectName().isPresent();
107   }
108 
109   /* Returns the Companion Object element enclosed by the given type element. */
getEnclosedCompanionObject(TypeElement typeElement)110   public TypeElement getEnclosedCompanionObject(TypeElement typeElement) {
111     return metadataFactory
112         .create(typeElement)
113         .classMetadata()
114         .companionObjectName()
115         .map(
116             companionObjectName ->
117                 ElementFilter.typesIn(typeElement.getEnclosedElements()).stream()
118                     .filter(
119                         innerType -> innerType.getSimpleName().contentEquals(companionObjectName))
120                     .collect(DaggerCollectors.onlyElement()))
121         .get();
122   }
123 
124   /**
125    * Returns {@code true} if the given type element was declared <code>private</code> in its Kotlin
126    * source.
127    */
isVisibilityPrivate(TypeElement typeElement)128   public boolean isVisibilityPrivate(TypeElement typeElement) {
129     return hasMetadata(typeElement)
130         && metadataFactory.create(typeElement).classMetadata().flags(IS_PRIVATE);
131   }
132 
133   /**
134    * Returns {@code true} if the given executable element was declared {@code internal} in its
135    * Kotlin source.
136    */
isVisibilityInternal(ExecutableElement method)137   public boolean isVisibilityInternal(ExecutableElement method) {
138     return hasMetadata(method)
139         && metadataFactory.create(method).getFunctionMetadata(method).flags(Flag.IS_INTERNAL);
140   }
141 
getPropertyGetter(VariableElement fieldElement)142   public Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) {
143     return metadataFactory.create(fieldElement).getPropertyGetter(fieldElement);
144   }
145 
containsConstructorWithDefaultParam(TypeElement typeElement)146   public boolean containsConstructorWithDefaultParam(TypeElement typeElement) {
147     return hasMetadata(typeElement)
148         && metadataFactory.create(typeElement).containsConstructorWithDefaultParam();
149   }
150 
151   /**
152    * Returns {@code true} if the <code>@JvmStatic</code> annotation is present in the given element.
153    */
isJvmStaticPresent(ExecutableElement element)154   public static boolean isJvmStaticPresent(ExecutableElement element) {
155     return isAnnotationPresent(element, JvmStatic.class);
156   }
157 }
158