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 */
16package dagger.internal.codegen;
17
18import dagger.Provides;
19import java.util.regex.Matcher;
20import java.util.regex.Pattern;
21import 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 */
29final 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