• 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 
21 import androidx.room.compiler.processing.XMethodElement;
22 import androidx.room.compiler.processing.XType;
23 import androidx.room.compiler.processing.XTypeElement;
24 import com.google.common.base.Joiner;
25 import com.google.common.collect.ImmutableMap;
26 import dagger.internal.codegen.base.ComponentAnnotation;
27 import dagger.internal.codegen.base.ComponentCreatorAnnotation;
28 import dagger.internal.codegen.base.ComponentKind;
29 import java.util.Set;
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, Set<XMethodElement> additionalMethods)205     public final String factoryMethodReturnsSupertypeWithMissingMethods(
206         XTypeElement component,
207         XTypeElement componentBuilder,
208         XType returnType,
209         XMethodElement buildMethod,
210         Set<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           Joiner.on(", ").join(additionalMethods));
220     }
221 
bindsInstanceNotAllowedOnBothSetterMethodAndParameter()222     public final String bindsInstanceNotAllowedOnBothSetterMethodAndParameter() {
223       return process("@Component.Builder setter methods may not have @BindsInstance on both the "
224           + "method and its parameter; choose one or the other");
225     }
226 
inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter()227     public final String inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter() {
228       return bindsInstanceNotAllowedOnBothSetterMethodAndParameter() + ". Inherited method: %s";
229     }
230   }
231 
232   private static final class BuilderMessages extends ComponentCreatorMessages {
BuilderMessages(Function<String, String> transformation)233     BuilderMessages(Function<String, String> transformation) {
234       super(transformation);
235     }
236 
237     @Override
missingFactoryMethod()238     public String missingFactoryMethod() {
239       return process(
240           "@Component.Builder types must have exactly one no-args method that "
241               + " returns the @Component type");
242     }
243 
244     @Override
multipleSettersForModuleOrDependencyType()245     public String multipleSettersForModuleOrDependencyType() {
246       return process(
247           "@Component.Builder types must not have more than one setter method per module or "
248               + "dependency, but %s is set by %s");
249     }
250 
251     @Override
extraSetters()252     public String extraSetters() {
253       return process(
254           "@Component.Builder has setters for modules or components that aren't required: %s");
255     }
256 
257     @Override
missingSetters()258     public String missingSetters() {
259       return process(
260           "@Component.Builder is missing setters for required modules or components: %s");
261     }
262 
263     @Override
twoFactoryMethods()264     public String twoFactoryMethods() {
265       return process(
266           "@Component.Builder types must have exactly one zero-arg method, and that"
267               + " method must return the @Component type. Already found: %s");
268     }
269 
270     @Override
inheritedTwoFactoryMethods()271     public String inheritedTwoFactoryMethods() {
272       return process(
273           "@Component.Builder types must have exactly one zero-arg method, and that"
274               + " method must return the @Component type. Found %s and %s");
275     }
276 
277     @Override
factoryMethodMustReturnComponentType()278     public String factoryMethodMustReturnComponentType() {
279       return process(
280           "@Component.Builder methods that have no arguments must return the @Component type or a "
281               + "supertype of the @Component");
282     }
283 
284     @Override
factoryMethodMayNotBeAnnotatedWithBindsInstance()285     public String factoryMethodMayNotBeAnnotatedWithBindsInstance() {
286       return process(
287           "@Component.Builder no-arg build methods may not be annotated with @BindsInstance");
288     }
289 
290     @Override
nonBindsInstanceParametersMayNotBePrimitives()291     public String nonBindsInstanceParametersMayNotBePrimitives() {
292       return process(
293           "@Component.Builder methods that are not annotated with @BindsInstance "
294               + "must take either a module or a component dependency, not a primitive");
295     }
296   }
297 
298   private static final class FactoryMessages extends ComponentCreatorMessages {
FactoryMessages(Function<String, String> transformation)299     FactoryMessages(Function<String, String> transformation) {
300       super(transformation.andThen(FACTORY));
301     }
302 
303     @Override
missingFactoryMethod()304     public String missingFactoryMethod() {
305       return process(
306           "@Component.Factory types must have exactly one method that "
307               + "returns the @Component type");
308     }
309 
310     @Override
multipleSettersForModuleOrDependencyType()311     public String multipleSettersForModuleOrDependencyType() {
312       return process(
313           "@Component.Factory methods must not have more than one parameter per module or "
314               + "dependency, but %s is set by %s");
315     }
316 
317     @Override
extraSetters()318     public String extraSetters() {
319       return process(
320           "@Component.Factory method has parameters for modules or components that aren't "
321               + "required: %s");
322     }
323 
324     @Override
missingSetters()325     public String missingSetters() {
326       return process(
327           "@Component.Factory method is missing parameters for required modules or components: %s");
328     }
329 
330     @Override
twoFactoryMethods()331     public String twoFactoryMethods() {
332       return process(
333           "@Component.Factory types must have exactly one abstract method. Already found: %s");
334     }
335 
336     @Override
inheritedTwoFactoryMethods()337     public String inheritedTwoFactoryMethods() {
338       return twoFactoryMethods();
339     }
340 
341     @Override
factoryMethodMustReturnComponentType()342     public String factoryMethodMustReturnComponentType() {
343       return process(
344           "@Component.Factory abstract methods must return the @Component type or a "
345               + "supertype of the @Component");
346     }
347 
348     @Override
factoryMethodMayNotBeAnnotatedWithBindsInstance()349     public String factoryMethodMayNotBeAnnotatedWithBindsInstance() {
350       return process("@Component.Factory method may not be annotated with @BindsInstance");
351     }
352 
353     @Override
nonBindsInstanceParametersMayNotBePrimitives()354     public String nonBindsInstanceParametersMayNotBePrimitives() {
355       return process(
356           "@Component.Factory method parameters that are not annotated with @BindsInstance "
357               + "must be either a module or a component dependency, not a primitive");
358     }
359   }
360 
ErrorMessages()361   private ErrorMessages() {}
362 }
363