/* * 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 [email protected] (É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; } } }