• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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