• 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.validation.TypeHierarchyValidator.validateTypeHierarchy;
24 
25 import com.google.auto.common.MoreTypes;
26 import com.google.common.collect.ImmutableSet;
27 import dagger.Binds;
28 import dagger.Module;
29 import dagger.internal.codegen.base.ContributionType;
30 import dagger.internal.codegen.base.SetType;
31 import dagger.internal.codegen.binding.BindsTypeChecker;
32 import dagger.internal.codegen.binding.InjectionAnnotations;
33 import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
34 import dagger.internal.codegen.langmodel.DaggerElements;
35 import dagger.internal.codegen.langmodel.DaggerTypes;
36 import dagger.producers.ProducerModule;
37 import javax.inject.Inject;
38 import javax.lang.model.element.ExecutableElement;
39 import javax.lang.model.element.VariableElement;
40 import javax.lang.model.type.TypeMirror;
41 
42 /** A validator for {@link Binds} methods. */
43 final class BindsMethodValidator extends BindingMethodValidator {
44   private final DaggerTypes types;
45   private final BindsTypeChecker bindsTypeChecker;
46 
47   @Inject
BindsMethodValidator( DaggerElements elements, DaggerTypes types, KotlinMetadataUtil kotlinMetadataUtil, BindsTypeChecker bindsTypeChecker, DependencyRequestValidator dependencyRequestValidator, InjectionAnnotations injectionAnnotations)48   BindsMethodValidator(
49       DaggerElements elements,
50       DaggerTypes types,
51       KotlinMetadataUtil kotlinMetadataUtil,
52       BindsTypeChecker bindsTypeChecker,
53       DependencyRequestValidator dependencyRequestValidator,
54       InjectionAnnotations injectionAnnotations) {
55     super(
56         elements,
57         types,
58         kotlinMetadataUtil,
59         Binds.class,
60         ImmutableSet.of(Module.class, ProducerModule.class),
61         dependencyRequestValidator,
62         MUST_BE_ABSTRACT,
63         NO_EXCEPTIONS,
64         ALLOWS_MULTIBINDINGS,
65         ALLOWS_SCOPING,
66         injectionAnnotations);
67     this.types = types;
68     this.bindsTypeChecker = bindsTypeChecker;
69   }
70 
71   @Override
elementValidator(ExecutableElement element)72   protected ElementValidator elementValidator(ExecutableElement element) {
73     return new Validator(element);
74   }
75 
76   private class Validator extends MethodValidator {
Validator(ExecutableElement element)77     Validator(ExecutableElement element) {
78       super(element);
79     }
80 
81     @Override
checkParameters()82     protected void checkParameters() {
83       if (element.getParameters().size() != 1) {
84         report.addError(
85             bindingMethods(
86                 "must have exactly one parameter, whose type is assignable to the return type"));
87       } else {
88         super.checkParameters();
89       }
90     }
91 
92     @Override
checkParameter(VariableElement parameter)93     protected void checkParameter(VariableElement parameter) {
94       super.checkParameter(parameter);
95       TypeMirror leftHandSide = boxIfNecessary(element.getReturnType());
96       TypeMirror rightHandSide = parameter.asType();
97       ContributionType contributionType = ContributionType.fromBindingElement(element);
98       if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(leftHandSide)) {
99         report.addError(
100             "@Binds @ElementsIntoSet methods must return a Set and take a Set parameter");
101       }
102 
103       if (!bindsTypeChecker.isAssignable(rightHandSide, leftHandSide, contributionType)) {
104         // Validate the type hierarchy of both sides to make sure they're both valid.
105         // If one of the types isn't valid it means we need to delay validation to the next round.
106         // Note: BasicAnnotationProcessor only performs superficial validation on the referenced
107         // types within the module. Thus, we're guaranteed that the types in the @Binds method are
108         // valid, but it says nothing about their supertypes, which are needed for isAssignable.
109         validateTypeHierarchy(leftHandSide, types);
110         validateTypeHierarchy(rightHandSide, types);
111         // TODO(ronshapiro): clarify this error message for @ElementsIntoSet cases, where the
112         // right-hand-side might not be assignable to the left-hand-side, but still compatible with
113         // Set.addAll(Collection<? extends E>)
114         report.addError("@Binds methods' parameter type must be assignable to the return type");
115       }
116     }
117 
boxIfNecessary(TypeMirror maybePrimitive)118     private TypeMirror boxIfNecessary(TypeMirror maybePrimitive) {
119       if (maybePrimitive.getKind().isPrimitive()) {
120         return types.boxedClass(MoreTypes.asPrimitiveType(maybePrimitive)).asType();
121       }
122       return maybePrimitive;
123     }
124   }
125 }
126