1 /* 2 * Copyright (C) 2014 Google, Inc. 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 package dagger.internal.codegen; 17 18 import dagger.Provides; 19 import java.util.regex.Matcher; 20 import java.util.regex.Pattern; 21 import javax.lang.model.element.AnnotationMirror; 22 23 /** 24 * The collection of error messages to be reported back to users. 25 * 26 * @author Gregory Kick 27 * @since 2.0 28 */ 29 final class ErrorMessages { 30 /* 31 * Common constants. 32 */ 33 static final String INDENT = " "; 34 static final int DUPLICATE_SIZE_LIMIT = 10; 35 36 /* 37 * JSR-330 errors 38 * 39 * These are errors that are explicitly outlined in the JSR-330 APIs 40 */ 41 42 /* constructors */ 43 static final String MULTIPLE_INJECT_CONSTRUCTORS = 44 "Types may only contain one @Inject constructor."; 45 46 /* fields */ 47 static final String FINAL_INJECT_FIELD = "@Inject fields may not be final"; 48 49 /* methods */ 50 static final String ABSTRACT_INJECT_METHOD = "Methods with @Inject may not be abstract."; 51 static final String GENERIC_INJECT_METHOD = 52 "Methods with @Inject may not declare type parameters."; 53 54 /* qualifiers */ 55 static final String MULTIPLE_QUALIFIERS = 56 "A single injection site may not use more than one @Qualifier."; 57 58 /* scope */ 59 static final String MULTIPLE_SCOPES = "A single binding may not declare more than one @Scope."; 60 61 /* 62 * Dagger errors 63 * 64 * These are errors that arise due to restrictions imposed by the dagger implementation. 65 */ 66 67 /* constructors */ 68 static final String INJECT_ON_PRIVATE_CONSTRUCTOR = 69 "Dagger does not support injection into private constructors"; 70 static final String INJECT_CONSTRUCTOR_ON_INNER_CLASS = 71 "@Inject constructors are invalid on inner classes"; 72 static final String INJECT_CONSTRUCTOR_ON_ABSTRACT_CLASS = 73 "@Inject is nonsense on the constructor of an abstract class"; 74 static final String QUALIFIER_ON_INJECT_CONSTRUCTOR = 75 "@Qualifier annotations are not allowed on @Inject constructors."; 76 77 /* fields */ 78 static final String PRIVATE_INJECT_FIELD = 79 "Dagger does not support injection into private fields"; 80 81 static final String STATIC_INJECT_FIELD = 82 "Dagger does not support injection into static fields"; 83 84 /* methods */ 85 static final String PRIVATE_INJECT_METHOD = 86 "Dagger does not support injection into private methods"; 87 88 static final String STATIC_INJECT_METHOD = 89 "Dagger does not support injection into static methods"; 90 91 /* all */ 92 static final String INJECT_INTO_PRIVATE_CLASS = 93 "Dagger does not support injection into private classes"; 94 95 /* 96 * Configuration errors 97 * 98 * These are errors that relate specifically to the Dagger configuration API (@Module, @Provides, 99 * etc.) 100 */ 101 static final String DUPLICATE_BINDINGS_FOR_KEY_FORMAT = 102 "%s is bound multiple times:"; 103 duplicateMapKeysError(String key)104 static String duplicateMapKeysError(String key) { 105 return "The same map key is bound more than once for " + key; 106 } 107 inconsistentMapKeyAnnotationsError(String key)108 static String inconsistentMapKeyAnnotationsError(String key) { 109 return key + " uses more than one @MapKey annotation type"; 110 } 111 112 static final String PROVIDES_METHOD_RETURN_TYPE = 113 "@Provides methods must either return a primitive, an array or a declared type."; 114 115 static final String PRODUCES_METHOD_RETURN_TYPE = 116 "@Produces methods must either return a primitive, an array or a declared type, or a" 117 + " ListenableFuture of one of those types."; 118 119 static final String PRODUCES_METHOD_RAW_FUTURE = 120 "@Produces methods cannot return a raw ListenableFuture."; 121 122 static final String BINDING_METHOD_SET_VALUES_RAW_SET = 123 "@%s methods of type set values cannot return a raw Set"; 124 125 static final String PROVIDES_METHOD_SET_VALUES_RETURN_SET = 126 "@Provides methods of type set values must return a Set"; 127 128 static final String PRODUCES_METHOD_SET_VALUES_RETURN_SET = 129 "@Produces methods of type set values must return a Set or ListenableFuture of Set"; 130 131 static final String BINDING_METHOD_MUST_RETURN_A_VALUE = 132 "@%s methods must return a value (not void)."; 133 134 static final String BINDING_METHOD_ABSTRACT = "@%s methods cannot be abstract"; 135 136 static final String BINDING_METHOD_PRIVATE = "@%s methods cannot be private"; 137 138 static final String BINDING_METHOD_TYPE_PARAMETER = 139 "@%s methods may not have type parameters."; 140 141 static final String BINDING_METHOD_NOT_IN_MODULE = 142 "@%s methods can only be present within a @%s"; 143 144 static final String BINDING_METHOD_NOT_MAP_HAS_MAP_KEY = 145 "@%s methods of non map type cannot declare a map key"; 146 147 static final String BINDING_METHOD_WITH_NO_MAP_KEY = 148 "@%s methods of type map must declare a map key"; 149 150 static final String BINDING_METHOD_WITH_MULTIPLE_MAP_KEY = 151 "@%s methods may not have more than one @MapKey-marked annotation"; 152 153 static final String BINDING_METHOD_WITH_SAME_NAME = 154 "Cannot have more than one @%s method with the same name in a single module"; 155 156 static final String MODULES_WITH_TYPE_PARAMS_MUST_BE_ABSTRACT = 157 "Modules with type parameters must be abstract"; 158 159 static final String REFERENCED_MODULES_MUST_NOT_BE_ABSTRACT = 160 "%s is listed as a module, but is an abstract class or interface"; 161 162 static final String REFERENCED_MODULE_NOT_ANNOTATED = 163 "%s is listed as a module, but is not annotated with %s"; 164 165 static final String REFERENCED_MODULE_MUST_NOT_HAVE_TYPE_PARAMS = 166 "%s is listed as a module, but has type parameters"; 167 168 static final String PROVIDES_METHOD_OVERRIDES_ANOTHER = 169 "@%s methods may not override another method. Overrides: %s"; 170 171 static final String METHOD_OVERRIDES_PROVIDES_METHOD = 172 "@%s methods may not be overridden in modules. Overrides: %s"; 173 174 static final String PROVIDES_OR_PRODUCES_METHOD_MULTIPLE_QUALIFIERS = 175 "Cannot use more than one @Qualifier on a @Provides or @Produces method"; 176 177 /* mapKey errors*/ 178 static final String MAPKEY_WITHOUT_MEMBERS = 179 "Map key annotations must have members"; 180 181 static final String UNWRAPPED_MAP_KEY_WITH_TOO_MANY_MEMBERS= 182 "Map key annotations with unwrapped values must have exactly one member"; 183 184 static final String UNWRAPPED_MAP_KEY_WITH_ARRAY_MEMBER = 185 "Map key annotations with unwrapped values cannot use arrays"; 186 187 /* collection binding errors */ 188 static final String MULTIPLE_CONTRIBUTION_TYPES_FORMAT = 189 "More than one binding present of different types %s"; 190 191 static final String MULTIPLE_BINDING_TYPES_FOR_KEY_FORMAT = 192 "%s has incompatible bindings:\n"; 193 194 static final String PROVIDER_ENTRY_POINT_MAY_NOT_DEPEND_ON_PRODUCER_FORMAT = 195 "%s is a provision entry-point, which cannot depend on a production."; 196 197 static final String PROVIDER_MAY_NOT_DEPEND_ON_PRODUCER_FORMAT = 198 "%s is a provision, which cannot depend on a production."; 199 200 static final String REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_FORMAT = 201 "%s cannot be provided without an @Inject constructor or from an @Provides-annotated method."; 202 203 static final String REQUIRES_PROVIDER_FORMAT = 204 "%s cannot be provided without an @Provides-annotated method."; 205 206 static final String REQUIRES_AT_INJECT_CONSTRUCTOR_OR_PROVIDER_OR_PRODUCER_FORMAT = 207 "%s cannot be provided without an @Inject constructor or from an @Provides- or " 208 + "@Produces-annotated method."; 209 210 static final String REQUIRES_PROVIDER_OR_PRODUCER_FORMAT = 211 "%s cannot be provided without an @Provides- or @Produces-annotated method."; 212 213 static final String MEMBERS_INJECTION_DOES_NOT_IMPLY_PROVISION = 214 "This type supports members injection but cannot be implicitly provided."; 215 216 static final String MEMBERS_INJECTION_WITH_RAW_TYPE = 217 "%s has type parameters, cannot members inject the raw type. via:\n%s"; 218 219 static final String MEMBERS_INJECTION_WITH_UNBOUNDED_TYPE = 220 "Type parameters must be bounded for members injection. %s required by %s, via:\n%s"; 221 222 static final String CONTAINS_DEPENDENCY_CYCLE_FORMAT = "%s.%s() contains a dependency cycle:\n%s"; 223 224 static final String MALFORMED_MODULE_METHOD_FORMAT = 225 "Cannot generated a graph because method %s on module %s was malformed"; 226 nullableToNonNullable(String typeName, String bindingString)227 static String nullableToNonNullable(String typeName, String bindingString) { 228 return String.format( 229 "%s is not nullable, but is being provided by %s", 230 typeName, 231 bindingString); 232 } 233 234 static final String CANNOT_RETURN_NULL_FROM_NON_NULLABLE_COMPONENT_METHOD = 235 "Cannot return null from a non-@Nullable component method"; 236 237 static final String CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD = 238 "Cannot return null from a non-@Nullable @Provides method"; 239 builderMsgsFor(ComponentDescriptor.Kind kind)240 static ComponentBuilderMessages builderMsgsFor(ComponentDescriptor.Kind kind) { 241 switch(kind) { 242 case COMPONENT: 243 return ComponentBuilderMessages.INSTANCE; 244 case SUBCOMPONENT: 245 return SubcomponentBuilderMessages.INSTANCE; 246 case PRODUCTION_COMPONENT: 247 return ProductionComponentBuilderMessages.INSTANCE; 248 default: 249 throw new IllegalStateException(kind.toString()); 250 } 251 } 252 253 static class ComponentBuilderMessages { 254 static final ComponentBuilderMessages INSTANCE = new ComponentBuilderMessages(); 255 process(String s)256 protected String process(String s) { return s; } 257 258 /** Errors for component builders. */ moreThanOne()259 final String moreThanOne() { 260 return process("@Component has more than one @Component.Builder: %s"); 261 } 262 cxtorOnlyOneAndNoArgs()263 final String cxtorOnlyOneAndNoArgs() { 264 return process("@Component.Builder classes must have exactly one constructor," 265 + " and it must not have any parameters"); 266 } 267 generics()268 final String generics() { 269 return process("@Component.Builder types must not have any generic types"); 270 } 271 mustBeInComponent()272 final String mustBeInComponent() { 273 return process("@Component.Builder types must be nested within a @Component"); 274 } 275 mustBeClassOrInterface()276 final String mustBeClassOrInterface() { 277 return process("@Component.Builder types must be abstract classes or interfaces"); 278 } 279 isPrivate()280 final String isPrivate() { 281 return process("@Component.Builder types must not be private"); 282 } 283 mustBeStatic()284 final String mustBeStatic() { 285 return process("@Component.Builder types must be static"); 286 } 287 mustBeAbstract()288 final String mustBeAbstract() { 289 return process("@Component.Builder types must be abstract"); 290 } 291 missingBuildMethod()292 final String missingBuildMethod() { 293 return process("@Component.Builder types must have exactly one no-args method that " 294 + " returns the @Component type"); 295 } 296 manyMethodsForType()297 final String manyMethodsForType() { 298 return process("@Component.Builder types must not have more than one setter method per type," 299 + " but %s is set by %s"); 300 } 301 extraSetters()302 final String extraSetters() { 303 return process( 304 "@Component.Builder has setters for modules or components that aren't required: %s"); 305 } 306 missingSetters()307 final String missingSetters() { 308 return process( 309 "@Component.Builder is missing setters for required modules or components: %s"); 310 } 311 twoBuildMethods()312 final String twoBuildMethods() { 313 return process("@Component.Builder types must have exactly one zero-arg method, and that" 314 + " method must return the @Component type. Already found: %s"); 315 } 316 inheritedTwoBuildMethods()317 final String inheritedTwoBuildMethods() { 318 return process("@Component.Builder types must have exactly one zero-arg method, and that" 319 + " method must return the @Component type. Found %s and %s"); 320 } 321 buildMustReturnComponentType()322 final String buildMustReturnComponentType() { 323 return process( 324 "@Component.Builder methods that have no arguments must return the @Component type"); 325 } 326 inheritedBuildMustReturnComponentType()327 final String inheritedBuildMustReturnComponentType() { 328 return process( 329 "@Component.Builder methods that have no arguments must return the @Component type" 330 + " Inherited method: %s"); 331 } 332 methodsMustTakeOneArg()333 final String methodsMustTakeOneArg() { 334 return process("@Component.Builder methods must not have more than one argument"); 335 } 336 inheritedMethodsMustTakeOneArg()337 final String inheritedMethodsMustTakeOneArg() { 338 return process( 339 "@Component.Builder methods must not have more than one argument. Inherited method: %s"); 340 } 341 methodsMustReturnVoidOrBuilder()342 final String methodsMustReturnVoidOrBuilder() { 343 return process("@Component.Builder setter methods must return void, the builder," 344 + " or a supertype of the builder"); 345 } 346 inheritedMethodsMustReturnVoidOrBuilder()347 final String inheritedMethodsMustReturnVoidOrBuilder() { 348 return process("@Component.Builder setter methods must return void, the builder," 349 + "or a supertype of the builder. Inherited method: %s"); 350 } 351 methodsMayNotHaveTypeParameters()352 final String methodsMayNotHaveTypeParameters() { 353 return process("@Component.Builder methods must not have type parameters"); 354 } 355 inheritedMethodsMayNotHaveTypeParameters()356 final String inheritedMethodsMayNotHaveTypeParameters() { 357 return process( 358 "@Component.Builder methods must not have type parameters. Inherited method: %s"); 359 } 360 } 361 362 static final class SubcomponentBuilderMessages extends ComponentBuilderMessages { 363 @SuppressWarnings("hiding") 364 static final SubcomponentBuilderMessages INSTANCE = new SubcomponentBuilderMessages(); 365 process(String s)366 @Override protected String process(String s) { 367 return s.replaceAll("component", "subcomponent").replaceAll("Component", "Subcomponent"); 368 } 369 builderMethodRequiresNoArgs()370 String builderMethodRequiresNoArgs() { 371 return "Methods returning a @Subcomponent.Builder must have no arguments"; 372 } 373 moreThanOneRefToSubcomponent()374 String moreThanOneRefToSubcomponent() { 375 return "Only one method can create a given subcomponent. %s is created by: %s"; 376 } 377 } 378 379 static final class ProductionComponentBuilderMessages extends ComponentBuilderMessages { 380 @SuppressWarnings("hiding") 381 static final ProductionComponentBuilderMessages INSTANCE = 382 new ProductionComponentBuilderMessages(); 383 process(String s)384 @Override protected String process(String s) { 385 return s.replaceAll("component", "production component") 386 .replaceAll("Component", "ProductionComponent"); 387 } 388 } 389 390 /** 391 * A regular expression to match a small list of specific packages deemed to 392 * be unhelpful to display in fully qualified types in error messages. 393 * 394 * Note: This should never be applied to messages themselves. 395 */ 396 private static final Pattern COMMON_PACKAGE_PATTERN = Pattern.compile( 397 "(?:^|[^.a-z_])" // What we want to match on but not capture. 398 + "((?:" // Start a group with a non-capturing or part 399 + "java[.]lang" 400 + "|java[.]util" 401 + "|javax[.]inject" 402 + "|dagger" 403 + "|com[.]google[.]common[.]base" 404 + "|com[.]google[.]common[.]collect" 405 + ")[.])" // Always end with a literal . 406 + "[A-Z]"); // What we want to match on but not capture. 407 408 /** 409 * A method to strip out common packages and a few rare type prefixes 410 * from types' string representation before being used in error messages. 411 * 412 * This type assumes a String value that is a valid fully qualified 413 * (and possibly parameterized) type, and should NOT be used with 414 * arbitrary text, especially prose error messages. 415 * 416 * TODO(cgruber): Tighten these to take type representations (mirrors 417 * and elements) to avoid accidental mis-use by running errors 418 * through this method. 419 */ stripCommonTypePrefixes(String type)420 static String stripCommonTypePrefixes(String type) { 421 // Special case this enum's constants since they will be incredibly common. 422 type = type.replace(Provides.Type.class.getCanonicalName() + ".", ""); 423 424 // Do regex magic to remove common packages we care to shorten. 425 Matcher matcher = COMMON_PACKAGE_PATTERN.matcher(type); 426 StringBuilder result = new StringBuilder(); 427 int index = 0; 428 while (matcher.find()) { 429 result.append(type.subSequence(index, matcher.start(1))); 430 index = matcher.end(1); // Skip the matched pattern content. 431 } 432 result.append(type.subSequence(index, type.length())); 433 return result.toString(); 434 } 435 436 //TODO(cgruber): Extract Formatter and do something less stringy. format(AnnotationMirror annotation)437 static String format(AnnotationMirror annotation) { 438 return stripCommonTypePrefixes(annotation.toString()); 439 } 440 ErrorMessages()441 private ErrorMessages() {} 442 } 443