1 /* 2 * Copyright (C) 2016 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.validation; 18 19 import static dagger.internal.codegen.validation.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS; 20 import static dagger.internal.codegen.validation.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING; 21 import static dagger.internal.codegen.validation.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT; 22 import static dagger.internal.codegen.validation.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS; 23 import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive; 24 25 import androidx.room.compiler.processing.XMethodElement; 26 import androidx.room.compiler.processing.XProcessingEnv; 27 import androidx.room.compiler.processing.XType; 28 import androidx.room.compiler.processing.XVariableElement; 29 import com.google.common.collect.ImmutableSet; 30 import dagger.internal.codegen.base.ContributionType; 31 import dagger.internal.codegen.base.DaggerSuperficialValidation; 32 import dagger.internal.codegen.base.SetType; 33 import dagger.internal.codegen.binding.BindsTypeChecker; 34 import dagger.internal.codegen.binding.InjectionAnnotations; 35 import dagger.internal.codegen.javapoet.TypeNames; 36 import javax.inject.Inject; 37 38 /** A validator for {@link dagger.Binds} methods. */ 39 final class BindsMethodValidator extends BindingMethodValidator { 40 private final BindsTypeChecker bindsTypeChecker; 41 private final DaggerSuperficialValidation superficialValidation; 42 43 @Inject BindsMethodValidator( BindsTypeChecker bindsTypeChecker, DaggerSuperficialValidation superficialValidation, XProcessingEnv processingEnv, DependencyRequestValidator dependencyRequestValidator, InjectionAnnotations injectionAnnotations)44 BindsMethodValidator( 45 BindsTypeChecker bindsTypeChecker, 46 DaggerSuperficialValidation superficialValidation, 47 XProcessingEnv processingEnv, 48 49 DependencyRequestValidator dependencyRequestValidator, 50 InjectionAnnotations injectionAnnotations) { 51 super( 52 TypeNames.BINDS, 53 ImmutableSet.of(TypeNames.MODULE, TypeNames.PRODUCER_MODULE), 54 MUST_BE_ABSTRACT, 55 NO_EXCEPTIONS, 56 ALLOWS_MULTIBINDINGS, 57 ALLOWS_SCOPING, 58 processingEnv, 59 dependencyRequestValidator, 60 injectionAnnotations); 61 this.bindsTypeChecker = bindsTypeChecker; 62 this.superficialValidation = superficialValidation; 63 } 64 65 @Override elementValidator(XMethodElement method)66 protected ElementValidator elementValidator(XMethodElement method) { 67 return new Validator(method); 68 } 69 70 private class Validator extends MethodValidator { 71 private final XMethodElement method; 72 Validator(XMethodElement method)73 Validator(XMethodElement method) { 74 super(method); 75 this.method = method; 76 } 77 78 @Override checkParameters()79 protected void checkParameters() { 80 if (method.getParameters().size() != 1) { 81 report.addError( 82 bindingMethods( 83 "must have exactly one parameter, whose type is assignable to the return type")); 84 } else { 85 super.checkParameters(); 86 } 87 } 88 89 @Override checkParameter(XVariableElement parameter)90 protected void checkParameter(XVariableElement parameter) { 91 super.checkParameter(parameter); 92 XType returnType = boxIfNecessary(method.getReturnType()); 93 XType parameterType = parameter.getType(); 94 ContributionType contributionType = ContributionType.fromBindingElement(method); 95 if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(returnType)) { 96 report.addError( 97 "@Binds @ElementsIntoSet methods must return a Set and take a Set parameter"); 98 } 99 100 if (!bindsTypeChecker.isAssignable(parameterType, returnType, contributionType)) { 101 // Validate the type hierarchy of both sides to make sure they're both valid. 102 // If one of the types isn't valid it means we need to delay validation to the next round. 103 // Note: BasicAnnotationProcessor only performs superficial validation on the referenced 104 // types within the module. Thus, we're guaranteed that the types in the @Binds method are 105 // valid, but it says nothing about their supertypes, which are needed for isAssignable. 106 superficialValidation.validateTypeHierarchyOf("return type", method, returnType); 107 superficialValidation.validateTypeHierarchyOf("parameter", parameter, parameterType); 108 // TODO(ronshapiro): clarify this error message for @ElementsIntoSet cases, where the 109 // right-hand-side might not be assignable to the left-hand-side, but still compatible with 110 // Set.addAll(Collection<? extends E>) 111 report.addError("@Binds methods' parameter type must be assignable to the return type"); 112 } 113 } 114 boxIfNecessary(XType maybePrimitive)115 private XType boxIfNecessary(XType maybePrimitive) { 116 return isPrimitive(maybePrimitive) ? maybePrimitive.boxed() : maybePrimitive; 117 } 118 } 119 } 120