• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google LLC
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 com.google.auto.value.processor;
17 
18 import java.util.List;
19 import javax.lang.model.element.ExecutableElement;
20 import javax.lang.model.element.VariableElement;
21 import javax.lang.model.type.ArrayType;
22 import javax.lang.model.type.DeclaredType;
23 import javax.lang.model.type.ErrorType;
24 import javax.lang.model.type.IntersectionType;
25 import javax.lang.model.type.TypeMirror;
26 import javax.lang.model.type.TypeVariable;
27 import javax.lang.model.type.WildcardType;
28 import javax.lang.model.util.SimpleTypeVisitor8;
29 
30 /**
31  * Handling of undefined types. When we see an undefined type, it might genuinely be undefined, or
32  * it might be a type whose source code will be generated later on as part of the same compilation.
33  * If we encounter an undefined type in a place where we need to know the type, we throw {@link
34  * MissingTypeException}. We then catch that and defer processing for the current class until the
35  * next annotation-processing "round". If the missing class has been generated in the meanwhile, we
36  * may now be able to complete processing. After a round has completed without generating any new
37  * source code, if there are still missing types then we report an error.
38  *
39  * @author emcmanus@google.com (Éamonn McManus)
40  */
41 final class MissingTypes {
MissingTypes()42   private MissingTypes() {}
43 
44   /**
45    * Exception thrown in the specific case where processing of a class was abandoned because it
46    * required types that the class references to be present and they were not. This case is handled
47    * specially because it is possible that those types might be generated later during annotation
48    * processing, so we should reattempt the processing of the class in a later annotation processing
49    * round.
50    */
51   @SuppressWarnings("serial")
52   static class MissingTypeException extends RuntimeException {
MissingTypeException(ErrorType missingType)53     MissingTypeException(ErrorType missingType) {
54       // Although it is not specified as such, in practice ErrorType.toString() is the type name
55       // that appeared in the source code. Showing it here can help in debugging issues with
56       // deferral.
57       super(missingType == null ? null : missingType.toString());
58     }
59   }
60 
61   /**
62    * Check that the return type and parameter types of the given method are all defined, and arrange
63    * to defer processing until the next round if not.
64    *
65    * @throws MissingTypeException if the return type or a parameter type of the given method is
66    *     undefined
67    */
deferIfMissingTypesIn(ExecutableElement method)68   static void deferIfMissingTypesIn(ExecutableElement method) {
69     MISSING_TYPE_VISITOR.check(method.getReturnType());
70     for (VariableElement param : method.getParameters()) {
71       MISSING_TYPE_VISITOR.check(param.asType());
72     }
73   }
74 
75   private static final MissingTypeVisitor MISSING_TYPE_VISITOR = new MissingTypeVisitor();
76 
77   private static class MissingTypeVisitor extends SimpleTypeVisitor8<Void, TypeMirrorSet> {
78     // Avoid infinite recursion for a type like `Enum<E extends Enum<E>>` by remembering types that
79     // we have already seen on this visit. Recursion has to go through a declared type, such as Enum
80     // in this example, so in principle it should be enough to check only in visitDeclared. However
81     // Eclipse has a quirk where the second E in `Enum<E extends Enum<E>>` is not the same as the
82     // first, and if you ask for its bounds you will get another `Enum<E>` with a third E. So we
83     // also check in visitTypeVariable. TypeMirrorSet does consider that all these E variables are
84     // the same so infinite recursion is avoided.
check(TypeMirror type)85     void check(TypeMirror type) {
86       type.accept(this, new TypeMirrorSet());
87     }
88 
89     @Override
visitError(ErrorType t, TypeMirrorSet visiting)90     public Void visitError(ErrorType t, TypeMirrorSet visiting) {
91       throw new MissingTypeException(t);
92     }
93 
94     @Override
visitArray(ArrayType t, TypeMirrorSet visiting)95     public Void visitArray(ArrayType t, TypeMirrorSet visiting) {
96       return t.getComponentType().accept(this, visiting);
97     }
98 
99     @Override
visitDeclared(DeclaredType t, TypeMirrorSet visiting)100     public Void visitDeclared(DeclaredType t, TypeMirrorSet visiting) {
101       if (visiting.add(t)) {
102         visitAll(t.getTypeArguments(), visiting);
103       }
104       return null;
105     }
106 
107     @Override
visitTypeVariable(TypeVariable t, TypeMirrorSet visiting)108     public Void visitTypeVariable(TypeVariable t, TypeMirrorSet visiting) {
109       if (visiting.add(t)) {
110         t.getLowerBound().accept(this, visiting);
111         t.getUpperBound().accept(this, visiting);
112       }
113       return null;
114     }
115 
116     @Override
visitWildcard(WildcardType t, TypeMirrorSet visiting)117     public Void visitWildcard(WildcardType t, TypeMirrorSet visiting) {
118       if (t.getSuperBound() != null) {
119         t.getSuperBound().accept(this, visiting);
120       }
121       if (t.getExtendsBound() != null) {
122         t.getExtendsBound().accept(this, visiting);
123       }
124       return null;
125     }
126 
127     @Override
visitIntersection(IntersectionType t, TypeMirrorSet visiting)128     public Void visitIntersection(IntersectionType t, TypeMirrorSet visiting) {
129       return visitAll(t.getBounds(), visiting);
130     }
131 
visitAll(List<? extends TypeMirror> types, TypeMirrorSet visiting)132     private Void visitAll(List<? extends TypeMirror> types, TypeMirrorSet visiting) {
133       for (TypeMirror type : types) {
134         type.accept(this, visiting);
135       }
136       return null;
137     }
138   }
139 }
140