dagger.hilt.processor.internal.kotlin.KotlinMetadataUtil Maven / Gradle / Ivy
Show all versions of hilt-compiler Show documentation
/*
* Copyright (C) 2022 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dagger.hilt.processor.internal.kotlin;
import static androidx.room.compiler.processing.XElementKt.isField;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.xprocessing.XElements.asField;
import static dagger.internal.codegen.xprocessing.XElements.isStatic;
import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XFieldElement;
import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XTypeElement;
import com.google.common.base.Equivalence;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.ClassName;
import dagger.hilt.processor.internal.ClassNames;
import dagger.internal.codegen.xprocessing.XAnnotations;
import dagger.internal.codegen.xprocessing.XElements;
import javax.inject.Inject;
/** Utility class for interacting with Kotlin Metadata. */
public final class KotlinMetadataUtil {
private final KotlinMetadataFactory metadataFactory;
@Inject
KotlinMetadataUtil(KotlinMetadataFactory metadataFactory) {
this.metadataFactory = metadataFactory;
}
/**
* Returns {@code true} if this element has the Kotlin Metadata annotation or if it is enclosed in
* an element that does.
*/
public boolean hasMetadata(XElement element) {
return XElements.closestEnclosingTypeElement(element).hasAnnotation(ClassNames.KOTLIN_METADATA);
}
// TODO(kuanyingchou): Consider replacing it with `XAnnotated.getAnnotationsAnnotatedWith()`
// once b/278077018 is resolved.
/**
* Returns the annotations on the given {@code element} annotated with {@code annotationName}.
*
* Note: If the given {@code element} is a non-static field this method will return annotations
* on both the backing field and the associated synthetic property (if one exists).
*/
public ImmutableList getAnnotationsAnnotatedWith(
XElement element, ClassName annotationName) {
return getAnnotations(element).stream()
.filter(annotation -> annotation.getTypeElement().hasAnnotation(annotationName))
.collect(toImmutableList());
}
/**
* Returns the annotations on the given {@code element} that match the {@code annotationName}.
*
* Note: If the given {@code element} is a non-static field this method will return annotations
* on both the backing field and the associated synthetic property (if one exists).
*/
private ImmutableList getAnnotations(XElement element) {
ImmutableList annotations = ImmutableList.copyOf(element.getAllAnnotations());
ImmutableList syntheticAnnotations = getSyntheticPropertyAnnotations(element);
if (syntheticAnnotations.isEmpty()) {
return annotations;
}
// Dedupe any annotation that appears on both the field and the property.
// Note: we reduce the number of annotations we have to dedupe by only checking equivalence on
// annotations that have the same class name as a synthetic annotation. This avoids hitting
// TypeNotPresentException on annotation values with error types unless it has the same class
// name as a synthetic annotation.
ImmutableSet syntheticAnnotationClassNames =
syntheticAnnotations.stream()
.map(XAnnotations::getClassName)
.collect(toImmutableSet());
ImmutableSet> annotationEquivalenceWrappers =
annotations.stream()
.filter(annotation -> syntheticAnnotationClassNames.contains(annotation.getClassName()))
.map(XAnnotations.equivalence()::wrap)
.collect(toImmutableSet());
ImmutableList uniqueSyntheticAnnotations =
syntheticAnnotations.stream()
.map(XAnnotations.equivalence()::wrap)
.filter(wrapper -> !annotationEquivalenceWrappers.contains(wrapper))
.map(Equivalence.Wrapper::get)
.collect(toImmutableList());
return uniqueSyntheticAnnotations.isEmpty()
? annotations
: ImmutableList.builder()
.addAll(annotations)
.addAll(uniqueSyntheticAnnotations)
.build();
}
/**
* Returns the synthetic annotations of a Kotlin property.
*
* Note that this method only looks for additional annotations in the synthetic property
* method, if any, of a Kotlin property and not for annotations in its backing field.
*/
private ImmutableList getSyntheticPropertyAnnotations(XElement element) {
// Currently, we avoid trying to get annotations from properties on object class's (i.e.
// properties with static jvm backing fields) due to issues explained in CL/336150864.
if (!isField(element) || isStatic(element)) {
return ImmutableList.of();
}
XFieldElement field = asField(element);
return hasMetadata(field)
? metadataFactory
.create(field)
.getSyntheticAnnotationMethod(field)
.map(XMethodElement::getAllAnnotations)
.map(ImmutableList::copyOf)
.orElse(ImmutableList.of())
: ImmutableList.of();
}
public boolean containsConstructorWithDefaultParam(XTypeElement typeElement) {
return hasMetadata(typeElement)
&& metadataFactory.create(typeElement).containsConstructorWithDefaultParam();
}
}