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.binding; 18 19 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; 20 import static java.util.stream.Collectors.joining; 21 22 import androidx.room.compiler.processing.XMethodElement; 23 import androidx.room.compiler.processing.XType; 24 import androidx.room.compiler.processing.XTypeElement; 25 import com.google.common.collect.ImmutableCollection; 26 import com.google.common.collect.ImmutableMap; 27 import dagger.internal.codegen.base.ComponentAnnotation; 28 import dagger.internal.codegen.base.ComponentCreatorAnnotation; 29 import dagger.internal.codegen.base.ComponentKind; 30 import java.util.function.Function; 31 import java.util.function.UnaryOperator; 32 33 /** The collection of error messages to be reported back to users. */ 34 public final class ErrorMessages { 35 36 private static final UnaryOperator<String> PRODUCTION = 37 s -> 38 s.replace("component", "production component") 39 .replace("Component", "ProductionComponent"); 40 41 private static final UnaryOperator<String> SUBCOMPONENT = 42 s -> s.replace("component", "subcomponent").replace("Component", "Subcomponent"); 43 44 private static final UnaryOperator<String> FACTORY = s -> s.replace("Builder", "Factory"); 45 46 private static final ImmutableMap<ComponentKind, Function<String, String>> 47 COMPONENT_TRANSFORMATIONS = 48 ImmutableMap.of( 49 ComponentKind.COMPONENT, UnaryOperator.identity(), 50 ComponentKind.SUBCOMPONENT, SUBCOMPONENT, 51 ComponentKind.PRODUCTION_COMPONENT, PRODUCTION, 52 ComponentKind.PRODUCTION_SUBCOMPONENT, PRODUCTION.andThen(SUBCOMPONENT)); 53 componentMessagesFor(ComponentKind componentKind)54 public static ComponentMessages componentMessagesFor(ComponentKind componentKind) { 55 return new ComponentMessages(COMPONENT_TRANSFORMATIONS.get(componentKind)); 56 } 57 componentMessagesFor(ComponentAnnotation componentAnnotation)58 public static ComponentMessages componentMessagesFor(ComponentAnnotation componentAnnotation) { 59 return new ComponentMessages( 60 transformation(componentAnnotation.isProduction(), componentAnnotation.isSubcomponent())); 61 } 62 creatorMessagesFor( ComponentCreatorAnnotation creatorAnnotation)63 public static ComponentCreatorMessages creatorMessagesFor( 64 ComponentCreatorAnnotation creatorAnnotation) { 65 Function<String, String> transformation = 66 transformation( 67 creatorAnnotation.isProductionCreatorAnnotation(), 68 creatorAnnotation.isSubcomponentCreatorAnnotation()); 69 switch (creatorAnnotation.creatorKind()) { 70 case BUILDER: 71 return new BuilderMessages(transformation); 72 case FACTORY: 73 return new FactoryMessages(transformation); 74 } 75 throw new AssertionError(creatorAnnotation); 76 } 77 transformation( boolean isProduction, boolean isSubcomponent)78 private static Function<String, String> transformation( 79 boolean isProduction, boolean isSubcomponent) { 80 Function<String, String> transformation = isProduction ? PRODUCTION : UnaryOperator.identity(); 81 return isSubcomponent ? transformation.andThen(SUBCOMPONENT) : transformation; 82 } 83 84 private abstract static class Messages { 85 private final Function<String, String> transformation; 86 Messages(Function<String, String> transformation)87 Messages(Function<String, String> transformation) { 88 this.transformation = transformation; 89 } 90 process(String s)91 protected final String process(String s) { 92 return transformation.apply(s); 93 } 94 } 95 96 /** Errors for components. */ 97 public static final class ComponentMessages extends Messages { ComponentMessages(Function<String, String> transformation)98 ComponentMessages(Function<String, String> transformation) { 99 super(transformation); 100 } 101 moreThanOne()102 public final String moreThanOne() { 103 return process("@Component has more than one @Component.Builder or @Component.Factory: %s"); 104 } 105 } 106 107 /** Errors for component creators. */ 108 public abstract static class ComponentCreatorMessages extends Messages { ComponentCreatorMessages(Function<String, String> transformation)109 ComponentCreatorMessages(Function<String, String> transformation) { 110 super(transformation); 111 } 112 builderMethodRequiresNoArgs()113 public static String builderMethodRequiresNoArgs() { 114 return "Methods returning a @Component.Builder must have no arguments"; 115 } 116 moreThanOneRefToSubcomponent()117 public static String moreThanOneRefToSubcomponent() { 118 return "Only one method can create a given subcomponent. %s is created by: %s"; 119 } 120 invalidConstructor()121 public final String invalidConstructor() { 122 return process("@Component.Builder classes must have exactly one constructor," 123 + " and it must not be private or have any parameters"); 124 } 125 generics()126 public final String generics() { 127 return process("@Component.Builder types must not have any generic types"); 128 } 129 mustBeInComponent()130 public final String mustBeInComponent() { 131 return process("@Component.Builder types must be nested within a @Component"); 132 } 133 mustBeClassOrInterface()134 public final String mustBeClassOrInterface() { 135 return process("@Component.Builder types must be abstract classes or interfaces"); 136 } 137 isPrivate()138 public final String isPrivate() { 139 return process("@Component.Builder types must not be private"); 140 } 141 mustBeStatic()142 public final String mustBeStatic() { 143 return process("@Component.Builder types must be static"); 144 } 145 mustBeAbstract()146 public final String mustBeAbstract() { 147 return process("@Component.Builder types must be abstract"); 148 } 149 missingFactoryMethod()150 public abstract String missingFactoryMethod(); 151 multipleSettersForModuleOrDependencyType()152 public abstract String multipleSettersForModuleOrDependencyType(); 153 extraSetters()154 public abstract String extraSetters(); 155 missingSetters()156 public abstract String missingSetters(); 157 twoFactoryMethods()158 public abstract String twoFactoryMethods(); 159 inheritedTwoFactoryMethods()160 public abstract String inheritedTwoFactoryMethods(); 161 factoryMethodMustReturnComponentType()162 public abstract String factoryMethodMustReturnComponentType(); 163 inheritedFactoryMethodMustReturnComponentType()164 public final String inheritedFactoryMethodMustReturnComponentType() { 165 return factoryMethodMustReturnComponentType() + ". Inherited method: %s"; 166 } 167 factoryMethodMayNotBeAnnotatedWithBindsInstance()168 public abstract String factoryMethodMayNotBeAnnotatedWithBindsInstance(); 169 inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance()170 public final String inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance() { 171 return factoryMethodMayNotBeAnnotatedWithBindsInstance() + ". Inherited method: %s"; 172 } 173 setterMethodsMustTakeOneArg()174 public final String setterMethodsMustTakeOneArg() { 175 return process("@Component.Builder methods must not have more than one argument"); 176 } 177 inheritedSetterMethodsMustTakeOneArg()178 public final String inheritedSetterMethodsMustTakeOneArg() { 179 return setterMethodsMustTakeOneArg() + ". Inherited method: %s"; 180 } 181 setterMethodsMustReturnVoidOrBuilder()182 public final String setterMethodsMustReturnVoidOrBuilder() { 183 return process("@Component.Builder setter methods must return void, the builder," 184 + " or a supertype of the builder"); 185 } 186 inheritedSetterMethodsMustReturnVoidOrBuilder()187 public final String inheritedSetterMethodsMustReturnVoidOrBuilder() { 188 return setterMethodsMustReturnVoidOrBuilder() + ". Inherited method: %s"; 189 } 190 methodsMayNotHaveTypeParameters()191 public final String methodsMayNotHaveTypeParameters() { 192 return process("@Component.Builder methods must not have type parameters"); 193 } 194 inheritedMethodsMayNotHaveTypeParameters()195 public final String inheritedMethodsMayNotHaveTypeParameters() { 196 return methodsMayNotHaveTypeParameters() + ". Inherited method: %s"; 197 } 198 nonBindsInstanceParametersMayNotBePrimitives()199 public abstract String nonBindsInstanceParametersMayNotBePrimitives(); 200 inheritedNonBindsInstanceParametersMayNotBePrimitives()201 public final String inheritedNonBindsInstanceParametersMayNotBePrimitives() { 202 return nonBindsInstanceParametersMayNotBePrimitives() + ". Inherited method: %s"; 203 } 204 factoryMethodReturnsSupertypeWithMissingMethods( XTypeElement component, XTypeElement componentBuilder, XType returnType, XMethodElement buildMethod, ImmutableCollection<XMethodElement> additionalMethods)205 public final String factoryMethodReturnsSupertypeWithMissingMethods( 206 XTypeElement component, 207 XTypeElement componentBuilder, 208 XType returnType, 209 XMethodElement buildMethod, 210 ImmutableCollection<XMethodElement> additionalMethods) { 211 return String.format( 212 "%1$s.%2$s() returns %3$s, but %4$s declares additional component method(s): %5$s. In " 213 + "order to provide type-safe access to these methods, override %2$s() to return " 214 + "%4$s", 215 componentBuilder.getQualifiedName(), 216 getSimpleName(buildMethod), 217 returnType.getTypeName(), 218 component.getQualifiedName(), 219 additionalMethods.stream() 220 .map(method -> getSimpleName(method) + "()") 221 .collect(joining(", "))); 222 } 223 bindsInstanceNotAllowedOnBothSetterMethodAndParameter()224 public final String bindsInstanceNotAllowedOnBothSetterMethodAndParameter() { 225 return process("@Component.Builder setter methods may not have @BindsInstance on both the " 226 + "method and its parameter; choose one or the other"); 227 } 228 inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter()229 public final String inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter() { 230 return bindsInstanceNotAllowedOnBothSetterMethodAndParameter() + ". Inherited method: %s"; 231 } 232 } 233 234 private static final class BuilderMessages extends ComponentCreatorMessages { BuilderMessages(Function<String, String> transformation)235 BuilderMessages(Function<String, String> transformation) { 236 super(transformation); 237 } 238 239 @Override missingFactoryMethod()240 public String missingFactoryMethod() { 241 return process( 242 "@Component.Builder types must have exactly one no-args method that returns the " 243 + "@Component type"); 244 } 245 246 @Override multipleSettersForModuleOrDependencyType()247 public String multipleSettersForModuleOrDependencyType() { 248 return process( 249 "@Component.Builder types must not have more than one setter method per module or " 250 + "dependency, but %s is set by %s"); 251 } 252 253 @Override extraSetters()254 public String extraSetters() { 255 return process( 256 "@Component.Builder has setters for modules or components that aren't required: %s"); 257 } 258 259 @Override missingSetters()260 public String missingSetters() { 261 return process( 262 "@Component.Builder is missing setters for required modules or components: %s"); 263 } 264 265 @Override twoFactoryMethods()266 public String twoFactoryMethods() { 267 return process( 268 "@Component.Builder types must have exactly one zero-arg method, and that" 269 + " method must return the @Component type. Already found: %s"); 270 } 271 272 @Override inheritedTwoFactoryMethods()273 public String inheritedTwoFactoryMethods() { 274 return process( 275 "@Component.Builder types must have exactly one zero-arg method, and that" 276 + " method must return the @Component type. Found %s and %s"); 277 } 278 279 @Override factoryMethodMustReturnComponentType()280 public String factoryMethodMustReturnComponentType() { 281 return process( 282 "@Component.Builder methods that have no arguments must return the @Component type or a " 283 + "supertype of the @Component"); 284 } 285 286 @Override factoryMethodMayNotBeAnnotatedWithBindsInstance()287 public String factoryMethodMayNotBeAnnotatedWithBindsInstance() { 288 return process( 289 "@Component.Builder no-arg build methods may not be annotated with @BindsInstance"); 290 } 291 292 @Override nonBindsInstanceParametersMayNotBePrimitives()293 public String nonBindsInstanceParametersMayNotBePrimitives() { 294 return process( 295 "@Component.Builder methods that are not annotated with @BindsInstance " 296 + "must take either a module or a component dependency, not a primitive"); 297 } 298 } 299 300 private static final class FactoryMessages extends ComponentCreatorMessages { FactoryMessages(Function<String, String> transformation)301 FactoryMessages(Function<String, String> transformation) { 302 super(transformation.andThen(FACTORY)); 303 } 304 305 @Override missingFactoryMethod()306 public String missingFactoryMethod() { 307 return process( 308 "@Component.Factory types must have exactly one method that " 309 + "returns the @Component type"); 310 } 311 312 @Override multipleSettersForModuleOrDependencyType()313 public String multipleSettersForModuleOrDependencyType() { 314 return process( 315 "@Component.Factory methods must not have more than one parameter per module or " 316 + "dependency, but %s is set by %s"); 317 } 318 319 @Override extraSetters()320 public String extraSetters() { 321 return process( 322 "@Component.Factory method has parameters for modules or components that aren't " 323 + "required: %s"); 324 } 325 326 @Override missingSetters()327 public String missingSetters() { 328 return process( 329 "@Component.Factory method is missing parameters for required modules or components: %s"); 330 } 331 332 @Override twoFactoryMethods()333 public String twoFactoryMethods() { 334 return process( 335 "@Component.Factory types must have exactly one abstract method. Already found: %s"); 336 } 337 338 @Override inheritedTwoFactoryMethods()339 public String inheritedTwoFactoryMethods() { 340 return twoFactoryMethods(); 341 } 342 343 @Override factoryMethodMustReturnComponentType()344 public String factoryMethodMustReturnComponentType() { 345 return process( 346 "@Component.Factory abstract methods must return the @Component type or a " 347 + "supertype of the @Component"); 348 } 349 350 @Override factoryMethodMayNotBeAnnotatedWithBindsInstance()351 public String factoryMethodMayNotBeAnnotatedWithBindsInstance() { 352 return process("@Component.Factory method may not be annotated with @BindsInstance"); 353 } 354 355 @Override nonBindsInstanceParametersMayNotBePrimitives()356 public String nonBindsInstanceParametersMayNotBePrimitives() { 357 return process( 358 "@Component.Factory method parameters that are not annotated with @BindsInstance " 359 + "must be either a module or a component dependency, not a primitive"); 360 } 361 } 362 ErrorMessages()363 private ErrorMessages() {} 364 } 365