• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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