• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
21 
22 import com.google.auto.common.MoreElements;
23 import javax.inject.Inject;
24 import javax.lang.model.element.AnnotationMirror;
25 import javax.lang.model.element.Element;
26 import javax.lang.model.element.ExecutableElement;
27 import javax.lang.model.type.ArrayType;
28 import javax.lang.model.type.DeclaredType;
29 import javax.lang.model.type.PrimitiveType;
30 import javax.lang.model.type.TypeMirror;
31 import javax.lang.model.type.TypeVisitor;
32 import javax.lang.model.util.SimpleTypeVisitor8;
33 
34 /**
35  * Validates members injection requests (members injection methods on components and requests for
36  * {@code MembersInjector<Foo>}).
37  */
38 final class MembersInjectionValidator {
39 
40   @Inject
MembersInjectionValidator()41   MembersInjectionValidator() {}
42 
43   /** Reports errors if a request for a {@code MembersInjector<Foo>}) is invalid. */
validateMembersInjectionRequest( Element requestElement, TypeMirror membersInjectedType)44   ValidationReport<Element> validateMembersInjectionRequest(
45       Element requestElement, TypeMirror membersInjectedType) {
46     ValidationReport.Builder<Element> report = ValidationReport.about(requestElement);
47     checkQualifiers(report, requestElement);
48     membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
49     return report.build();
50   }
51 
52   /**
53    * Reports errors if a members injection method on a component is invalid.
54    *
55    * @throws IllegalArgumentException if the method doesn't have exactly one parameter
56    */
validateMembersInjectionMethod( ExecutableElement method, TypeMirror membersInjectedType)57   ValidationReport<ExecutableElement> validateMembersInjectionMethod(
58       ExecutableElement method, TypeMirror membersInjectedType) {
59     checkArgument(
60         method.getParameters().size() == 1, "expected a method with one parameter: %s", method);
61 
62     ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(method);
63     checkQualifiers(report, method);
64     checkQualifiers(report, method.getParameters().get(0));
65     membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
66     return report.build();
67   }
68 
checkQualifiers(ValidationReport.Builder<?> report, Element element)69   private void checkQualifiers(ValidationReport.Builder<?> report, Element element) {
70     for (AnnotationMirror qualifier : getQualifiers(element)) {
71       report.addError("Cannot inject members into qualified types", element, qualifier);
72       break; // just report on the first qualifier, in case there is more than one
73     }
74   }
75 
76   private static final TypeVisitor<Void, ValidationReport.Builder<?>>
77       VALIDATE_MEMBERS_INJECTED_TYPE =
78           new SimpleTypeVisitor8<Void, ValidationReport.Builder<?>>() {
79             // Only declared types can be members-injected.
80             @Override
81             protected Void defaultAction(TypeMirror type, ValidationReport.Builder<?> report) {
82               report.addError("Cannot inject members into " + type);
83               return null;
84             }
85 
86             @Override
87             public Void visitDeclared(DeclaredType type, ValidationReport.Builder<?> report) {
88               if (type.getTypeArguments().isEmpty()) {
89                 // If the type is the erasure of a generic type, that means the user referred to
90                 // Foo<T> as just 'Foo', which we don't allow.  (This is a judgement call; we
91                 // *could* allow it and instantiate the type bounds, but we don't.)
92                 if (!MoreElements.asType(type.asElement()).getTypeParameters().isEmpty()) {
93                   report.addError("Cannot inject members into raw type " + type);
94                 }
95               } else {
96                 // If the type has arguments, validate that each type argument is declared.
97                 // Otherwise the type argument may be a wildcard (or other type), and we can't
98                 // resolve that to actual types.  For array type arguments, validate the type of the
99                 // array.
100                 for (TypeMirror arg : type.getTypeArguments()) {
101                   if (!arg.accept(DECLARED_OR_ARRAY, null)) {
102                     report.addError(
103                         "Cannot inject members into types with unbounded type arguments: " + type);
104                   }
105                 }
106               }
107               return null;
108             }
109           };
110 
111   // TODO(dpb): Can this be inverted so it explicitly rejects wildcards or type variables?
112   // This logic is hard to describe.
113   private static final TypeVisitor<Boolean, Void> DECLARED_OR_ARRAY =
114       new SimpleTypeVisitor8<Boolean, Void>(false) {
115         @Override
116         public Boolean visitArray(ArrayType arrayType, Void p) {
117           return arrayType
118               .getComponentType()
119               .accept(
120                   new SimpleTypeVisitor8<Boolean, Void>(false) {
121                     @Override
122                     public Boolean visitDeclared(DeclaredType declaredType, Void p) {
123                       for (TypeMirror arg : declaredType.getTypeArguments()) {
124                         if (!arg.accept(this, null)) {
125                           return false;
126                         }
127                       }
128                       return true;
129                     }
130 
131                     @Override
132                     public Boolean visitArray(ArrayType arrayType, Void p) {
133                       return arrayType.getComponentType().accept(this, null);
134                     }
135 
136                     @Override
137                     public Boolean visitPrimitive(PrimitiveType primitiveType, Void p) {
138                       return true;
139                     }
140                   },
141                   null);
142         }
143 
144         @Override
145         public Boolean visitDeclared(DeclaredType t, Void p) {
146           return true;
147         }
148       };
149 }
150