Home | History | Annotate | Download | only in codegen
      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 
    104   static String duplicateMapKeysError(String key) {
    105     return "The same map key is bound more than once for " + key;
    106   }
    107 
    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 
    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 
    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 
    256     protected String process(String s) { return s; }
    257 
    258     /** Errors for component builders. */
    259     final String moreThanOne() {
    260       return process("@Component has more than one @Component.Builder: %s");
    261     }
    262 
    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 
    268     final String generics() {
    269       return process("@Component.Builder types must not have any generic types");
    270     }
    271 
    272     final String mustBeInComponent() {
    273       return process("@Component.Builder types must be nested within a @Component");
    274     }
    275 
    276     final String mustBeClassOrInterface() {
    277       return process("@Component.Builder types must be abstract classes or interfaces");
    278     }
    279 
    280     final String isPrivate() {
    281       return process("@Component.Builder types must not be private");
    282     }
    283 
    284     final String mustBeStatic() {
    285       return process("@Component.Builder types must be static");
    286     }
    287 
    288     final String mustBeAbstract() {
    289       return process("@Component.Builder types must be abstract");
    290     }
    291 
    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 
    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 
    302     final String extraSetters() {
    303       return process(
    304           "@Component.Builder has setters for modules or components that aren't required: %s");
    305     }
    306 
    307     final String missingSetters() {
    308       return process(
    309           "@Component.Builder is missing setters for required modules or components: %s");
    310     }
    311 
    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 
    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 
    322     final String buildMustReturnComponentType() {
    323       return process(
    324           "@Component.Builder methods that have no arguments must return the @Component type");
    325     }
    326 
    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 
    333     final String methodsMustTakeOneArg() {
    334       return process("@Component.Builder methods must not have more than one argument");
    335     }
    336 
    337     final String inheritedMethodsMustTakeOneArg() {
    338       return process(
    339           "@Component.Builder methods must not have more than one argument. Inherited method: %s");
    340     }
    341 
    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 
    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 
    352     final String methodsMayNotHaveTypeParameters() {
    353       return process("@Component.Builder methods must not have type parameters");
    354     }
    355 
    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 
    366     @Override protected String process(String s) {
    367       return s.replaceAll("component", "subcomponent").replaceAll("Component", "Subcomponent");
    368     }
    369 
    370     String builderMethodRequiresNoArgs() {
    371       return "Methods returning a @Subcomponent.Builder must have no arguments";
    372     }
    373 
    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 
    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    */
    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.
    437   static String format(AnnotationMirror annotation) {
    438     return stripCommonTypePrefixes(annotation.toString());
    439   }
    440 
    441   private ErrorMessages() {}
    442 }
    443