| /* |
| * Copyright 2014 Google LLC |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.google.auto.value.processor; |
| |
| import java.util.List; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.ArrayType; |
| import javax.lang.model.type.DeclaredType; |
| import javax.lang.model.type.ErrorType; |
| import javax.lang.model.type.IntersectionType; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.type.TypeVariable; |
| import javax.lang.model.type.WildcardType; |
| import javax.lang.model.util.SimpleTypeVisitor8; |
| |
| /** |
| * Handling of undefined types. When we see an undefined type, it might genuinely be undefined, or |
| * it might be a type whose source code will be generated later on as part of the same compilation. |
| * If we encounter an undefined type in a place where we need to know the type, we throw {@link |
| * MissingTypeException}. We then catch that and defer processing for the current class until the |
| * next annotation-processing "round". If the missing class has been generated in the meanwhile, we |
| * may now be able to complete processing. After a round has completed without generating any new |
| * source code, if there are still missing types then we report an error. |
| * |
| * @author emcmanus@google.com (Éamonn McManus) |
| */ |
| final class MissingTypes { |
| private MissingTypes() {} |
| |
| /** |
| * Exception thrown in the specific case where processing of a class was abandoned because it |
| * required types that the class references to be present and they were not. This case is handled |
| * specially because it is possible that those types might be generated later during annotation |
| * processing, so we should reattempt the processing of the class in a later annotation processing |
| * round. |
| */ |
| @SuppressWarnings("serial") |
| static class MissingTypeException extends RuntimeException { |
| MissingTypeException(ErrorType missingType) { |
| // Although it is not specified as such, in practice ErrorType.toString() is the type name |
| // that appeared in the source code. Showing it here can help in debugging issues with |
| // deferral. |
| super(missingType == null ? null : missingType.toString()); |
| } |
| } |
| |
| /** |
| * Check that the return type and parameter types of the given method are all defined, and arrange |
| * to defer processing until the next round if not. |
| * |
| * @throws MissingTypeException if the return type or a parameter type of the given method is |
| * undefined |
| */ |
| static void deferIfMissingTypesIn(ExecutableElement method) { |
| MISSING_TYPE_VISITOR.check(method.getReturnType()); |
| for (VariableElement param : method.getParameters()) { |
| MISSING_TYPE_VISITOR.check(param.asType()); |
| } |
| } |
| |
| private static final MissingTypeVisitor MISSING_TYPE_VISITOR = new MissingTypeVisitor(); |
| |
| private static class MissingTypeVisitor extends SimpleTypeVisitor8<Void, TypeMirrorSet> { |
| // Avoid infinite recursion for a type like `Enum<E extends Enum<E>>` by remembering types that |
| // we have already seen on this visit. Recursion has to go through a declared type, such as Enum |
| // in this example, so in principle it should be enough to check only in visitDeclared. However |
| // Eclipse has a quirk where the second E in `Enum<E extends Enum<E>>` is not the same as the |
| // first, and if you ask for its bounds you will get another `Enum<E>` with a third E. So we |
| // also check in visitTypeVariable. TypeMirrorSet does consider that all these E variables are |
| // the same so infinite recursion is avoided. |
| void check(TypeMirror type) { |
| type.accept(this, new TypeMirrorSet()); |
| } |
| |
| @Override |
| public Void visitError(ErrorType t, TypeMirrorSet visiting) { |
| throw new MissingTypeException(t); |
| } |
| |
| @Override |
| public Void visitArray(ArrayType t, TypeMirrorSet visiting) { |
| return t.getComponentType().accept(this, visiting); |
| } |
| |
| @Override |
| public Void visitDeclared(DeclaredType t, TypeMirrorSet visiting) { |
| if (visiting.add(t)) { |
| visitAll(t.getTypeArguments(), visiting); |
| } |
| return null; |
| } |
| |
| @Override |
| public Void visitTypeVariable(TypeVariable t, TypeMirrorSet visiting) { |
| if (visiting.add(t)) { |
| t.getLowerBound().accept(this, visiting); |
| t.getUpperBound().accept(this, visiting); |
| } |
| return null; |
| } |
| |
| @Override |
| public Void visitWildcard(WildcardType t, TypeMirrorSet visiting) { |
| if (t.getSuperBound() != null) { |
| t.getSuperBound().accept(this, visiting); |
| } |
| if (t.getExtendsBound() != null) { |
| t.getExtendsBound().accept(this, visiting); |
| } |
| return null; |
| } |
| |
| @Override |
| public Void visitIntersection(IntersectionType t, TypeMirrorSet visiting) { |
| return visitAll(t.getBounds(), visiting); |
| } |
| |
| private Void visitAll(List<? extends TypeMirror> types, TypeMirrorSet visiting) { |
| for (TypeMirror type : types) { |
| type.accept(this, visiting); |
| } |
| return null; |
| } |
| } |
| } |