• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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;
18 
19 import static com.google.common.collect.Iterables.getOnlyElement;
20 import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
21 import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
22 import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
23 import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.EXCEPTION;
24 
25 import com.google.auto.common.MoreTypes;
26 import com.google.common.util.concurrent.ListenableFuture;
27 import dagger.internal.codegen.langmodel.DaggerElements;
28 import dagger.internal.codegen.langmodel.DaggerTypes;
29 import dagger.multibindings.ElementsIntoSet;
30 import dagger.producers.ProducerModule;
31 import dagger.producers.Produces;
32 import java.util.Optional;
33 import java.util.Set;
34 import javax.inject.Inject;
35 import javax.lang.model.element.ExecutableElement;
36 import javax.lang.model.type.DeclaredType;
37 import javax.lang.model.type.TypeMirror;
38 
39 /** A validator for {@link Produces} methods. */
40 final class ProducesMethodValidator extends BindingMethodValidator {
41 
42   @Inject
ProducesMethodValidator( DaggerElements elements, DaggerTypes types, DependencyRequestValidator dependencyRequestValidator)43   ProducesMethodValidator(
44       DaggerElements elements,
45       DaggerTypes types,
46       DependencyRequestValidator dependencyRequestValidator) {
47     super(
48         elements,
49         types,
50         dependencyRequestValidator,
51         Produces.class,
52         ProducerModule.class,
53         MUST_BE_CONCRETE,
54         EXCEPTION,
55         ALLOWS_MULTIBINDINGS,
56         NO_SCOPING);
57   }
58 
59   @Override
elementsIntoSetNotASetMessage()60   protected String elementsIntoSetNotASetMessage() {
61     return "@Produces methods of type set values must return a Set or ListenableFuture of Set";
62   }
63 
64   @Override
badTypeMessage()65   protected String badTypeMessage() {
66     return "@Produces methods can return only a primitive, an array, a type variable, "
67         + "a declared type, or a ListenableFuture of one of those types";
68   }
69 
70   @Override
elementValidator(ExecutableElement element)71   protected ElementValidator elementValidator(ExecutableElement element) {
72     return new Validator(element);
73   }
74 
75   private class Validator extends MethodValidator {
Validator(ExecutableElement element)76     Validator(ExecutableElement element) {
77       super(element);
78     }
79 
80     @Override
checkAdditionalMethodProperties()81     protected void checkAdditionalMethodProperties() {
82       checkNullable();
83     }
84 
85     /** Adds a warning if a {@link Produces @Produces} method is declared nullable. */
86     // TODO(beder): Properly handle nullable with producer methods.
checkNullable()87     private void checkNullable() {
88       if (ConfigurationAnnotations.getNullableType(element).isPresent()) {
89         report.addWarning("@Nullable on @Produces methods does not do anything");
90       }
91     }
92 
93     /**
94      * {@inheritDoc}
95      *
96      * <p>Allows {@code keyType} to be a {@link ListenableFuture} of an otherwise-valid key type.
97      */
98     @Override
checkKeyType(TypeMirror keyType)99     protected void checkKeyType(TypeMirror keyType) {
100       Optional<TypeMirror> typeToCheck = unwrapListenableFuture(keyType);
101       if (typeToCheck.isPresent()) {
102         super.checkKeyType(typeToCheck.get());
103       }
104     }
105 
106     /**
107      * {@inheritDoc}
108      *
109      * <p>Allows an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method to return
110      * a {@link ListenableFuture} of a {@link Set} as well.
111      */
112     @Override
checkSetValuesType()113     protected void checkSetValuesType() {
114       Optional<TypeMirror> typeToCheck = unwrapListenableFuture(element.getReturnType());
115       if (typeToCheck.isPresent()) {
116         checkSetValuesType(typeToCheck.get());
117       }
118     }
119 
unwrapListenableFuture(TypeMirror type)120     private Optional<TypeMirror> unwrapListenableFuture(TypeMirror type) {
121       if (MoreTypes.isType(type) && MoreTypes.isTypeOf(ListenableFuture.class, type)) {
122         DeclaredType declaredType = MoreTypes.asDeclared(type);
123         if (declaredType.getTypeArguments().isEmpty()) {
124           report.addError("@Produces methods cannot return a raw ListenableFuture");
125           return Optional.empty();
126         } else {
127           return Optional.of((TypeMirror) getOnlyElement(declaredType.getTypeArguments()));
128         }
129       }
130       return Optional.of(type);
131     }
132   }
133 }
134