Skip to content

Commit

Permalink
Understand @qualified annotation in Kotlin properties.
Browse files Browse the repository at this point in the history
Use Kotlin Metadata library to find the synthetic method where Kotlin
property annotations are placed, then treat the annotations in such
method as if they were part of the backing field. This allows for
@qualified annotations with vague targets to be understood by Dagger
when placed in Kotlin properties and specifically without the need of
the user writing "@field:MyQualifier".

Connecting Kotlin Metadata properties with AST elements requires
finding and matching JVM Signatures (also known as descriptors).
DaggerElements now contains methods to generate descriptors for both
an ExecutableElement and a VariableElement.

RELNOTES=Understand @qualifier annotation in Kotlin properties.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=267373331
  • Loading branch information
danysantiago authored and kluever committed Sep 5, 2019
1 parent ab0e6be commit 646e033
Show file tree
Hide file tree
Showing 35 changed files with 949 additions and 75 deletions.
2 changes: 2 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ jarjar_library(
"//java/dagger/internal/codegen/compileroption",
"//java/dagger/internal/codegen/extension",
"//java/dagger/internal/codegen/javapoet",
"//java/dagger/internal/codegen/kotlin",
"//java/dagger/internal/codegen/langmodel",
"//java/dagger/internal/codegen/statistics",
"//java/dagger/internal/codegen/validation",
Expand All @@ -84,6 +85,7 @@ jarjar_library(
"//java/dagger/internal/codegen/compileroption:libcompileroption-src.jar",
"//java/dagger/internal/codegen/extension:libextension-src.jar",
"//java/dagger/internal/codegen/javapoet:libjavapoet-src.jar",
"//java/dagger/internal/codegen/kotlin:libkotlin-src.jar",
"//java/dagger/internal/codegen/langmodel:liblangmodel-src.jar",
"//java/dagger/internal/codegen/statistics:libstatistics-src.jar",
"//java/dagger/internal/codegen/validation:libvalidation-src.jar",
Expand Down
23 changes: 23 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,26 @@ bind(
name = "zlib",
actual = "@bazel_tools//third_party/zlib",
)

RULES_JVM_EXTERNAL_TAG = "2.7"

RULES_JVM_EXTERNAL_SHA = "f04b1466a00a2845106801e0c5cec96841f49ea4e7d1df88dc8e4bf31523df74"

http_archive(
name = "rules_jvm_external",
sha256 = RULES_JVM_EXTERNAL_SHA,
strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)

load("@rules_jvm_external//:defs.bzl", "maven_install")

maven_install(
artifacts = [
"org.jetbrains.kotlin:kotlin-stdlib:1.3.50",
"org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0",
],
repositories = [
"https://repo1.maven.org/maven2",
],
)
1 change: 1 addition & 0 deletions java/dagger/internal/codegen/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ DAGGER_DEPS = [
"//java/dagger/internal/codegen/compileroption",
"//java/dagger/internal/codegen/extension",
"//java/dagger/internal/codegen/javapoet",
"//java/dagger/internal/codegen/kotlin",
"//java/dagger/internal/codegen/langmodel",
"//java/dagger/internal/codegen/statistics",
"//java/dagger/internal/codegen/validation",
Expand Down
5 changes: 5 additions & 0 deletions java/dagger/internal/codegen/ProcessingRoundCacheModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import dagger.internal.codegen.base.ClearableCache;
import dagger.internal.codegen.binding.BindingGraphFactory;
import dagger.internal.codegen.binding.ModuleDescriptor;
import dagger.internal.codegen.kotlin.KotlinMetadataFactory;
import dagger.multibindings.IntoSet;

/**
Expand All @@ -40,4 +41,8 @@ interface ProcessingRoundCacheModule {
@Binds
@IntoSet
ClearableCache componentImplementationFactory(ComponentImplementationFactory cache);

@Binds
@IntoSet
ClearableCache kotlinMetadata(KotlinMetadataFactory cache);
}
1 change: 1 addition & 0 deletions java/dagger/internal/codegen/binding/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ java_library(
"//java/dagger/internal/codegen/compileroption",
"//java/dagger/internal/codegen/extension",
"//java/dagger/internal/codegen/javapoet",
"//java/dagger/internal/codegen/kotlin",
"//java/dagger/internal/codegen/langmodel",
"//java/dagger/model",
"//java/dagger/model:internal-proxies",
Expand Down
8 changes: 5 additions & 3 deletions java/dagger/internal/codegen/binding/BindingFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.base.InjectionAnnotations.getQualifier;
import static dagger.internal.codegen.base.MoreAnnotationMirrors.wrapOptionalInEquivalence;
import static dagger.internal.codegen.base.Scopes.uniqueScopeOf;
import static dagger.internal.codegen.binding.Binding.hasNonDefaultTypeParameters;
Expand Down Expand Up @@ -83,19 +82,22 @@ public final class BindingFactory {
private final DependencyRequestFactory dependencyRequestFactory;
private final InjectionSiteFactory injectionSiteFactory;
private final DaggerElements elements;
private final InjectionAnnotations injectionAnnotations;

@Inject
BindingFactory(
DaggerTypes types,
DaggerElements elements,
KeyFactory keyFactory,
DependencyRequestFactory dependencyRequestFactory,
InjectionSiteFactory injectionSiteFactory) {
InjectionSiteFactory injectionSiteFactory,
InjectionAnnotations injectionAnnotations) {
this.types = types;
this.elements = elements;
this.keyFactory = keyFactory;
this.dependencyRequestFactory = dependencyRequestFactory;
this.injectionSiteFactory = injectionSiteFactory;
this.injectionAnnotations = injectionAnnotations;
}

/**
Expand All @@ -110,7 +112,7 @@ public ProvisionBinding injectionBinding(
ExecutableElement constructorElement, Optional<TypeMirror> resolvedType) {
checkArgument(constructorElement.getKind().equals(CONSTRUCTOR));
checkArgument(isAnnotationPresent(constructorElement, Inject.class));
checkArgument(!getQualifier(constructorElement).isPresent());
checkArgument(!injectionAnnotations.getQualifier(constructorElement).isPresent());

ExecutableType constructorType = MoreTypes.asExecutable(constructorElement.asType());
DeclaredType constructedType =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation;
import static dagger.internal.codegen.base.InjectionAnnotations.getQualifier;
import static dagger.internal.codegen.base.Scopes.productionScope;
import static dagger.internal.codegen.base.Scopes.scopesOf;
import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.creatorAnnotationsFor;
Expand Down Expand Up @@ -58,17 +57,20 @@ public final class ComponentDescriptorFactory {
private final DaggerTypes types;
private final DependencyRequestFactory dependencyRequestFactory;
private final ModuleDescriptor.Factory moduleDescriptorFactory;
private final InjectionAnnotations injectionAnnotations;

@Inject
ComponentDescriptorFactory(
DaggerElements elements,
DaggerTypes types,
DependencyRequestFactory dependencyRequestFactory,
ModuleDescriptor.Factory moduleDescriptorFactory) {
ModuleDescriptor.Factory moduleDescriptorFactory,
InjectionAnnotations injectionAnnotations) {
this.elements = elements;
this.types = types;
this.dependencyRequestFactory = dependencyRequestFactory;
this.moduleDescriptorFactory = moduleDescriptorFactory;
this.injectionAnnotations = injectionAnnotations;
}

/** Returns a descriptor for a root component type. */
Expand Down Expand Up @@ -221,7 +223,8 @@ private ComponentMethodDescriptor getDescriptorForComponentMethod(
MoreTypes.asExecutable(
types.asMemberOf(MoreTypes.asDeclared(componentElement.asType()), componentMethod));
TypeMirror returnType = resolvedComponentMethod.getReturnType();
if (returnType.getKind().equals(DECLARED) && !getQualifier(componentMethod).isPresent()) {
if (returnType.getKind().equals(DECLARED)
&& !injectionAnnotations.getQualifier(componentMethod).isPresent()) {
TypeElement returnTypeElement = asTypeElement(returnType);
if (subcomponentAnnotation(returnTypeElement).isPresent()) {
// It's a subcomponent factory method. There is no dependency request, and there could be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.Lazy;
import dagger.internal.codegen.base.InjectionAnnotations;
import dagger.internal.codegen.base.MapType;
import dagger.internal.codegen.base.OptionalType;
import dagger.internal.codegen.langmodel.DaggerTypes;
Expand Down Expand Up @@ -62,11 +61,14 @@
public final class DependencyRequestFactory {
private final KeyFactory keyFactory;
private final DaggerTypes types;
private final InjectionAnnotations injectionAnnotations;

@Inject
DependencyRequestFactory(KeyFactory keyFactory, DaggerTypes types) {
DependencyRequestFactory(
KeyFactory keyFactory, DaggerTypes types, InjectionAnnotations injectionAnnotations) {
this.keyFactory = keyFactory;
this.types = types;
this.injectionAnnotations = injectionAnnotations;
}

ImmutableSet<DependencyRequest> forRequiredResolvedVariables(
Expand Down Expand Up @@ -134,7 +136,7 @@ DependencyRequest forRequiredResolvedVariable(
VariableElement variableElement, TypeMirror resolvedType) {
checkNotNull(variableElement);
checkNotNull(resolvedType);
Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(variableElement);
Optional<AnnotationMirror> qualifier = injectionAnnotations.getQualifier(variableElement);
return newDependencyRequest(variableElement, resolvedType, qualifier);
}

Expand All @@ -146,7 +148,7 @@ public DependencyRequest forComponentProvisionMethod(
provisionMethod.getParameters().isEmpty(),
"Component provision methods must be empty: %s",
provisionMethod);
Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(provisionMethod);
Optional<AnnotationMirror> qualifier = injectionAnnotations.getQualifier(provisionMethod);
return newDependencyRequest(provisionMethod, provisionMethodType.getReturnType(), qualifier);
}

Expand All @@ -159,7 +161,7 @@ public DependencyRequest forComponentProductionMethod(
"Component production methods must be empty: %s",
productionMethod);
TypeMirror type = productionMethodType.getReturnType();
Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(productionMethod);
Optional<AnnotationMirror> qualifier = injectionAnnotations.getQualifier(productionMethod);
// Only a component production method can be a request for a ListenableFuture, so we
// special-case it here.
if (isTypeOf(ListenableFuture.class, type)) {
Expand All @@ -178,7 +180,7 @@ DependencyRequest forComponentMembersInjectionMethod(
checkNotNull(membersInjectionMethod);
checkNotNull(membersInjectionMethodType);
Optional<AnnotationMirror> qualifier =
InjectionAnnotations.getQualifier(membersInjectionMethod);
injectionAnnotations.getQualifier(membersInjectionMethod);
checkArgument(!qualifier.isPresent());
TypeMirror membersInjectedType = getOnlyElement(membersInjectionMethodType.getParameterTypes());
return DependencyRequest.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,38 @@
* limitations under the License.
*/

package dagger.internal.codegen.base;
package dagger.internal.codegen.binding;

import static com.google.auto.common.MoreElements.isAnnotationPresent;
import static com.google.common.base.Preconditions.checkNotNull;
import static javax.lang.model.util.ElementFilter.constructorsIn;

import com.google.auto.common.AnnotationMirrors;
import com.google.auto.common.MoreElements;
import com.google.auto.common.SuperficialValidation;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Qualifier;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;

/**
* Utilities relating to annotations defined in the {@code javax.inject} package.
*/
/** Utilities relating to annotations defined in the {@code javax.inject} package. */
public final class InjectionAnnotations {
public static Optional<AnnotationMirror> getQualifier(Element e) {
private final KotlinMetadataUtil kotlinMetadataUtil;

@Inject
InjectionAnnotations(KotlinMetadataUtil kotlinMetadataUtil) {
this.kotlinMetadataUtil = kotlinMetadataUtil;
}

public Optional<AnnotationMirror> getQualifier(Element e) {
if (!SuperficialValidation.validateElement(e)) {
throw new TypeNotPresentException(e.toString(), null);
}
Expand All @@ -53,8 +62,18 @@ public static Optional<AnnotationMirror> getQualifier(Element e) {
}
}

public static ImmutableSet<? extends AnnotationMirror> getQualifiers(Element element) {
return AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class);
public ImmutableSet<? extends AnnotationMirror> getQualifiers(Element element) {
ImmutableSet<? extends AnnotationMirror> qualifiers =
AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class);
if (kotlinMetadataUtil.hasMetadata(element) && element.getKind() == ElementKind.FIELD) {
return Sets.union(
qualifiers,
kotlinMetadataUtil.getSyntheticPropertyAnnotations(
MoreElements.asVariable(element), Qualifier.class))
.immutableCopy();
} else {
return qualifiers;
}
}

/** Returns the constructors in {@code type} that are annotated with {@link Inject}. */
Expand All @@ -63,6 +82,4 @@ public static ImmutableSet<ExecutableElement> injectedConstructors(TypeElement t
.filter(constructor -> isAnnotationPresent(constructor, Inject.class))
.toSet();
}

private InjectionAnnotations() {}
}
9 changes: 5 additions & 4 deletions java/dagger/internal/codegen/binding/KeyFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.base.InjectionAnnotations.getQualifier;
import static dagger.internal.codegen.base.RequestKinds.extractKeyType;
import static dagger.internal.codegen.base.RequestKinds.getRequestKind;
import static dagger.internal.codegen.binding.MapKeys.getMapKey;
Expand Down Expand Up @@ -74,12 +73,14 @@
public final class KeyFactory {
private final DaggerTypes types;
private final DaggerElements elements;
private final InjectionAnnotations injectionAnnotations;

// TODO(user): Make this pkg-private. This is used by KeyFactoryTest.
@Inject
public KeyFactory(DaggerTypes types, DaggerElements elements) {
KeyFactory(
DaggerTypes types, DaggerElements elements, InjectionAnnotations injectionAnnotations) {
this.types = checkNotNull(types);
this.elements = checkNotNull(elements);
this.injectionAnnotations = injectionAnnotations;
}

private TypeMirror boxPrimitives(TypeMirror type) {
Expand Down Expand Up @@ -241,7 +242,7 @@ Key forDelegateBinding(DelegateDeclaration delegateDeclaration, Class<?> framewo
}

private Key forMethod(ExecutableElement method, TypeMirror keyType) {
return forQualifiedType(getQualifier(method), keyType);
return forQualifiedType(injectionAnnotations.getQualifier(method), keyType);
}

public Key forInjectConstructorWithResolvedType(TypeMirror type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import static com.google.common.base.Preconditions.checkState;
import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes;
import static dagger.internal.codegen.base.InjectionAnnotations.getQualifier;

import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
Expand All @@ -39,10 +38,12 @@
/** Formats the signature of an {@link ExecutableElement} suitable for use in error messages. */
public final class MethodSignatureFormatter extends Formatter<ExecutableElement> {
private final DaggerTypes types;
private final InjectionAnnotations injectionAnnotations;

@Inject
public MethodSignatureFormatter(DaggerTypes types) {
public MethodSignatureFormatter(DaggerTypes types, InjectionAnnotations injectionAnnotations) {
this.types = types;
this.injectionAnnotations = injectionAnnotations;
}

/**
Expand Down Expand Up @@ -119,9 +120,9 @@ private String format(
return builder.toString();
}

private static void appendParameter(StringBuilder builder, VariableElement parameter,
TypeMirror type) {
getQualifier(parameter)
private void appendParameter(StringBuilder builder, VariableElement parameter, TypeMirror type) {
injectionAnnotations
.getQualifier(parameter)
.ifPresent(
qualifier -> {
builder.append(formatAnnotation(qualifier)).append(' ');
Expand Down
34 changes: 34 additions & 0 deletions java/dagger/internal/codegen/kotlin/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (C) 2019 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.

# Description:
# Sources related to Kotlin metadata.

package(default_visibility = ["//:src"])

java_library(
name = "kotlin",
srcs = glob(["*.java"]),
plugins = ["//java/dagger/internal/codegen/bootstrap"],
tags = ["maven:merged"],
deps = [
"//java/dagger/internal/codegen/base",
"//java/dagger/internal/codegen/langmodel",
"@google_bazel_common//third_party/java/auto:common",
"@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/jsr330_inject",
"@maven//:org_jetbrains_kotlin_kotlin_stdlib",
"@maven//:org_jetbrains_kotlinx_kotlinx_metadata_jvm",
],
)
Loading

0 comments on commit 646e033

Please sign in to comment.