dagger.internal.codegen.validation.MembersInjectionValidator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dagger-compiler Show documentation
Show all versions of dagger-compiler Show documentation
A fast dependency injector for Android and Java.
/*
* Copyright (C) 2018 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.internal.codegen.validation;
import static androidx.room.compiler.processing.XTypeKt.isArray;
import static com.google.common.base.Preconditions.checkArgument;
import static dagger.internal.codegen.xprocessing.XTypes.asArray;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
import static dagger.internal.codegen.xprocessing.XTypes.isRawParameterizedType;
import androidx.room.compiler.processing.XAnnotation;
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XMethodElement;
import androidx.room.compiler.processing.XType;
import dagger.internal.codegen.binding.InjectionAnnotations;
import dagger.internal.codegen.xprocessing.XTypes;
import javax.inject.Inject;
/**
* Validates members injection requests (members injection methods on components and requests for
* {@code MembersInjector}).
*/
final class MembersInjectionValidator {
private final InjectionAnnotations injectionAnnotations;
@Inject
MembersInjectionValidator(InjectionAnnotations injectionAnnotations) {
this.injectionAnnotations = injectionAnnotations;
}
/** Reports errors if a request for a {@code MembersInjector}) is invalid. */
ValidationReport validateMembersInjectionRequest(
XElement requestElement, XType membersInjectedType) {
ValidationReport.Builder report = ValidationReport.about(requestElement);
checkQualifiers(report, requestElement);
checkMembersInjectedType(report, membersInjectedType);
return report.build();
}
/**
* Reports errors if a members injection method on a component is invalid.
*
* @throws IllegalArgumentException if the method doesn't have exactly one parameter
*/
ValidationReport validateMembersInjectionMethod(
XMethodElement method, XType membersInjectedType) {
checkArgument(
method.getParameters().size() == 1, "expected a method with one parameter: %s", method);
ValidationReport.Builder report = ValidationReport.about(method);
checkQualifiers(report, method);
checkQualifiers(report, method.getParameters().get(0));
checkMembersInjectedType(report, membersInjectedType);
return report.build();
}
private void checkQualifiers(ValidationReport.Builder report, XElement element) {
for (XAnnotation qualifier : injectionAnnotations.getQualifiers(element)) {
report.addError("Cannot inject members into qualified types", element, qualifier);
break; // just report on the first qualifier, in case there is more than one
}
}
private void checkMembersInjectedType(ValidationReport.Builder report, XType type) {
// Only declared types can be members-injected.
if (!isDeclared(type)) {
report.addError("Cannot inject members into " + XTypes.toStableString(type));
return;
}
// If the type is the erasure of a generic type, that means the user referred to
// Foo as just 'Foo', which we don't allow. (This is a judgement call; we
// *could* allow it and instantiate the type bounds, but we don't.)
if (isRawParameterizedType(type)) {
report.addError("Cannot inject members into raw type " + XTypes.toStableString(type));
return;
}
// If the type has arguments, validate that each type argument is declared.
// Otherwise the type argument may be a wildcard (or other type), and we can't
// resolve that to actual types. For array type arguments, validate the type of the array.
if (!type.getTypeArguments().stream().allMatch(this::isResolvableTypeArgument)) {
report.addError(
"Cannot inject members into types with unbounded type arguments: "
+ XTypes.toStableString(type));
}
}
// TODO(dpb): Can this be inverted so it explicitly rejects wildcards or type variables?
// This logic is hard to describe.
private boolean isResolvableTypeArgument(XType type) {
return isDeclared(type)
|| (isArray(type) && isResolvableArrayComponentType(asArray(type).getComponentType()));
}
private boolean isResolvableArrayComponentType(XType type) {
if (isDeclared(type)) {
return type.getTypeArguments().stream().allMatch(this::isResolvableTypeArgument);
} else if (isArray(type)) {
return isResolvableArrayComponentType(asArray(type).getComponentType());
}
return isPrimitive(type);
}
}