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