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