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