Skip to content

Commit

Permalink
Treat qualified assisted types as normal bindings.
Browse files Browse the repository at this point in the history
Allows qualified @AssistedInject/@AssistedFactory types to be provided and injected as normal bindings.

Fixes #2370

RELNOTES=Fixes #2370: Qualified @AssistedInject/@AssistedFactory types can now be provided and injected as normal bindings.
PiperOrigin-RevId: 357652329
  • Loading branch information
bcorso authored and Dagger Team committed Feb 18, 2021
1 parent 7368a7d commit 92aa4c0
Show file tree
Hide file tree
Showing 4 changed files with 411 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package dagger.internal.codegen.validation;

import static com.google.auto.common.MoreTypes.asTypeElement;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verifyNotNull;
import static dagger.internal.codegen.base.Scopes.scopesOf;
import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
Expand Down Expand Up @@ -142,10 +141,12 @@ protected String elementsIntoSetRawSetMessage() {
protected abstract class ElementValidator {
protected final E element;
protected final ValidationReport.Builder<E> report;
private final ImmutableCollection<? extends AnnotationMirror> qualifiers;

protected ElementValidator(E element) {
this.element = element;
this.report = ValidationReport.about(element);
qualifiers = injectionAnnotations.getQualifiers(element);
}

/** Checks the element for validity. */
Expand Down Expand Up @@ -185,10 +186,15 @@ protected void checkAdditionalProperties() {}
protected void checkType() {
switch (ContributionType.fromBindingElement(element)) {
case UNIQUE:
/* Validate that a unique binding is not attempting to bind a framework type. This
* validation is only appropriate for unique bindings because multibindings may collect
* framework types. E.g. Set<Provider<Foo>> is perfectly reasonable. */
// Validate that a unique binding is not attempting to bind a framework type. This
// validation is only appropriate for unique bindings because multibindings may collect
// framework types. E.g. Set<Provider<Foo>> is perfectly reasonable.
checkFrameworkType();

// Validate that a unique binding is not attempting to bind an unqualified assisted type.
// This validation is only appropriate for unique bindings because multibindings may
// collect assisted types.
checkAssistedType();
// fall through

case SET:
Expand All @@ -208,22 +214,26 @@ protected void checkKeyType(TypeMirror keyType) {
TypeKind kind = keyType.getKind();
if (kind.equals(VOID)) {
report.addError(bindingElements("must %s a value (not void)", bindingElementTypeVerb()));
} else if (kind == DECLARED) {
checkNotAssistedInject(keyType);
} else if (!(kind.isPrimitive() || kind.equals(ARRAY) || kind.equals(TYPEVAR))) {
} else if (!(kind.isPrimitive()
|| kind.equals(DECLARED)
|| kind.equals(ARRAY)
|| kind.equals(TYPEVAR))) {
report.addError(badTypeMessage());
}
}

/** Adds errors for a method return type. */
private void checkNotAssistedInject(TypeMirror keyType) {
checkState(keyType.getKind() == TypeKind.DECLARED);
TypeElement keyElement = asTypeElement(keyType);
if (isAssistedInjectionType(keyElement)) {
report.addError("Dagger does not support providing @AssistedInject types.", keyElement);
}
if (isAssistedFactoryType(keyElement)) {
report.addError("Dagger does not support providing @AssistedFactory types.", keyElement);
/** Adds errors for unqualified assisted types. */
private void checkAssistedType() {
if (qualifiers.isEmpty()
&& bindingElementType().isPresent()
&& bindingElementType().get().getKind() == DECLARED) {
TypeElement keyElement = asTypeElement(bindingElementType().get());
if (isAssistedInjectionType(keyElement)) {
report.addError("Dagger does not support providing @AssistedInject types.", keyElement);
}
if (isAssistedFactoryType(keyElement)) {
report.addError("Dagger does not support providing @AssistedFactory types.", keyElement);
}
}
}

Expand Down Expand Up @@ -254,8 +264,6 @@ protected final void checkSetValuesType(TypeMirror type) {
* Adds an error if the element has more than one {@linkplain Qualifier qualifier} annotation.
*/
private void checkQualifiers() {
ImmutableCollection<? extends AnnotationMirror> qualifiers =
injectionAnnotations.getQualifiers(element);
if (qualifiers.size() > 1) {
for (AnnotationMirror qualifier : qualifiers) {
report.addError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,21 @@ void validateDependencyRequest(
// Don't validate assisted parameters. These are not dependency requests.
return;
}
checkQualifiers(report, requestElement);
checkType(report, requestElement, requestType);
if (missingQualifierMetadata(requestElement)) {
report.addError(
"Unable to read annotations on an injected Kotlin property. The Dagger compiler must"
+ " also be applied to any project containing @Inject properties.",
requestElement);

// Skip any further validation if we don't have valid metadata for a type that needs it.
return;
}

new Validator(report, requestElement, requestType).validate();
}

private void checkQualifiers(ValidationReport.Builder<?> report, Element requestElement) {
/** Returns {@code true} if a kotlin inject field is missing metadata about its qualifiers. */
private boolean missingQualifierMetadata(Element requestElement) {
if (requestElement.getKind() == ElementKind.FIELD
// static injected fields are not supported, no need to get qualifier from kotlin metadata
&& !requestElement.getModifiers().contains(STATIC)
Expand All @@ -91,64 +101,80 @@ private void checkQualifiers(ValidationReport.Builder<?> report, Element request
Optional.ofNullable(
elements.getTypeElement(
membersInjectorNameForType(asType(requestElement.getEnclosingElement()))));
if (!membersInjector.isPresent()) {
report.addError(
"Unable to read annotations on an injected Kotlin property. The Dagger compiler must"
+ " also be applied to any project containing @Inject properties.",
requestElement);
return; // finish checking qualifiers since current information is unreliable.
}
return !membersInjector.isPresent();
}
return false;
}

ImmutableCollection<? extends AnnotationMirror> qualifiers =
injectionAnnotations.getQualifiers(requestElement);
if (qualifiers.size() > 1) {
for (AnnotationMirror qualifier : qualifiers) {
report.addError(
"A single dependency request may not use more than one @Qualifier",
requestElement,
qualifier);
private final class Validator {
private final ValidationReport.Builder<?> report;
private final Element requestElement;
private final TypeMirror requestType;
private final TypeMirror keyType;
private final RequestKind requestKind;
private final ImmutableCollection<? extends AnnotationMirror> qualifiers;


Validator(ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) {
this.report = report;
this.requestElement = requestElement;
this.requestType = requestType;
this.keyType = extractKeyType(requestType);
this.requestKind = RequestKinds.getRequestKind(requestType);
this.qualifiers = injectionAnnotations.getQualifiers(requestElement);
}

void validate() {
checkQualifiers();
checkType();
}

private void checkQualifiers() {
if (qualifiers.size() > 1) {
for (AnnotationMirror qualifier : qualifiers) {
report.addError(
"A single dependency request may not use more than one @Qualifier",
requestElement,
qualifier);
}
}
}
}

private void checkType(
ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) {
TypeMirror keyType = extractKeyType(requestType);
RequestKind requestKind = RequestKinds.getRequestKind(requestType);
if (keyType.getKind() == TypeKind.DECLARED) {
TypeElement typeElement = asTypeElement(keyType);
if (isAssistedInjectionType(typeElement)) {
report.addError(
"Dagger does not support injecting @AssistedInject type, "
+ requestType
+ ". Did you mean to inject its assisted factory type instead?",
requestElement);
private void checkType() {
if (qualifiers.isEmpty() && keyType.getKind() == TypeKind.DECLARED) {
TypeElement typeElement = asTypeElement(keyType);
if (isAssistedInjectionType(typeElement)) {
report.addError(
"Dagger does not support injecting @AssistedInject type, "
+ requestType
+ ". Did you mean to inject its assisted factory type instead?",
requestElement);
}
if (requestKind != RequestKind.INSTANCE && isAssistedFactoryType(typeElement)) {
report.addError(
"Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, "
+ "or Produced<T> when T is an @AssistedFactory-annotated type such as "
+ keyType,
requestElement);
}
}
if (requestKind != RequestKind.INSTANCE && isAssistedFactoryType(typeElement)) {
if (keyType.getKind().equals(WILDCARD)) {
// TODO(ronshapiro): Explore creating this message using RequestKinds.
report.addError(
"Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, "
+ "or Produced<T> when T is an @AssistedFactory-annotated type such as "
+ "or Produced<T> when T is a wildcard type such as "
+ keyType,
requestElement);
}
}
if (keyType.getKind().equals(WILDCARD)) {
// TODO(ronshapiro): Explore creating this message using RequestKinds.
report.addError(
"Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, "
+ "or Produced<T> when T is a wildcard type such as "
+ keyType,
requestElement);
}
if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) {
DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType);
if (membersInjectorType.getTypeArguments().isEmpty()) {
report.addError("Cannot inject a raw MembersInjector", requestElement);
} else {
report.addSubreport(
membersInjectionValidator.validateMembersInjectionRequest(
requestElement, membersInjectorType.getTypeArguments().get(0)));
if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) {
DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType);
if (membersInjectorType.getTypeArguments().isEmpty()) {
report.addError("Cannot inject a raw MembersInjector", requestElement);
} else {
report.addSubreport(
membersInjectionValidator.validateMembersInjectionRequest(
requestElement, membersInjectorType.getTypeArguments().get(0)));
}
}
}
}
Expand Down

0 comments on commit 92aa4c0

Please sign in to comment.