blob: 5ef4dbf2b38a18558449d86669e07552cab6eb20 [file] [log] [blame]
/*
* 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.common;
import java.util.List;
import java.util.Map;
import java.util.stream.StreamSupport;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementVisitor;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
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.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVisitor;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.AbstractElementVisitor8;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import javax.lang.model.util.SimpleTypeVisitor8;
/**
* A utility class that traverses {@link Element} instances and ensures that all type information
* is present and resolvable.
*
* @author Gregory Kick
*/
public final class SuperficialValidation {
/**
* Returns true if all of the given elements return true from {@link #validateElement(Element)}.
*/
public static boolean validateElements(Iterable<? extends Element> elements) {
return StreamSupport.stream(elements.spliterator(), false)
.allMatch(SuperficialValidation::validateElement);
}
private static final ElementVisitor<Boolean, Void> ELEMENT_VALIDATING_VISITOR =
new AbstractElementVisitor8<Boolean, Void>() {
@Override public Boolean visitPackage(PackageElement e, Void p) {
// don't validate enclosed elements because it will return types in the package
return validateAnnotations(e.getAnnotationMirrors());
}
@Override public Boolean visitType(TypeElement e, Void p) {
return isValidBaseElement(e)
&& validateElements(e.getTypeParameters())
&& validateTypes(e.getInterfaces())
&& validateType(e.getSuperclass());
}
@Override public Boolean visitVariable(VariableElement e, Void p) {
return isValidBaseElement(e);
}
@Override public Boolean visitExecutable(ExecutableElement e, Void p) {
AnnotationValue defaultValue = e.getDefaultValue();
return isValidBaseElement(e)
&& (defaultValue == null || validateAnnotationValue(defaultValue, e.getReturnType()))
&& validateType(e.getReturnType())
&& validateTypes(e.getThrownTypes())
&& validateElements(e.getTypeParameters())
&& validateElements(e.getParameters());
}
@Override public Boolean visitTypeParameter(TypeParameterElement e, Void p) {
return isValidBaseElement(e)
&& validateTypes(e.getBounds());
}
@Override public Boolean visitUnknown(Element e, Void p) {
// just assume that unknown elements are OK
return true;
}
};
/**
* Returns true if all types referenced by the given element are defined. The exact meaning of
* this depends on the kind of element. For packages, it means that all annotations on the package
* are fully defined. For other element kinds, it means that types referenced by the element,
* anything it contains, and any of its annotations element are all defined.
*/
public static boolean validateElement(Element element) {
return element.accept(ELEMENT_VALIDATING_VISITOR, null);
}
private static boolean isValidBaseElement(Element e) {
return validateType(e.asType())
&& validateAnnotations(e.getAnnotationMirrors())
&& validateElements(e.getEnclosedElements());
}
private static boolean validateTypes(Iterable<? extends TypeMirror> types) {
for (TypeMirror type : types) {
if (!validateType(type)) {
return false;
}
}
return true;
}
/*
* This visitor does not test type variables specifically, but it seems that that is not actually
* an issue. Javac turns the whole type parameter into an error type if it can't figure out the
* bounds.
*/
private static final TypeVisitor<Boolean, Void> TYPE_VALIDATING_VISITOR =
new SimpleTypeVisitor8<Boolean, Void>() {
@Override
protected Boolean defaultAction(TypeMirror t, Void p) {
return true;
}
@Override
public Boolean visitArray(ArrayType t, Void p) {
return validateType(t.getComponentType());
}
@Override
public Boolean visitDeclared(DeclaredType t, Void p) {
return validateTypes(t.getTypeArguments());
}
@Override
public Boolean visitError(ErrorType t, Void p) {
return false;
}
@Override
public Boolean visitUnknown(TypeMirror t, Void p) {
// just make the default choice for unknown types
return defaultAction(t, p);
}
@Override
public Boolean visitWildcard(WildcardType t, Void p) {
TypeMirror extendsBound = t.getExtendsBound();
TypeMirror superBound = t.getSuperBound();
return (extendsBound == null || validateType(extendsBound))
&& (superBound == null || validateType(superBound));
}
@Override
public Boolean visitExecutable(ExecutableType t, Void p) {
return validateTypes(t.getParameterTypes())
&& validateType(t.getReturnType())
&& validateTypes(t.getThrownTypes())
&& validateTypes(t.getTypeVariables());
}
};
/**
* Returns true if the given type is fully defined. This means that the type itself is defined, as
* are any types it references, such as any type arguments or type bounds. For an {@link
* ExecutableType}, the parameter and return types must be fully defined, as must types declared
* in a {@code throws} clause or in the bounds of any type parameters.
*/
public static boolean validateType(TypeMirror type) {
return type.accept(TYPE_VALIDATING_VISITOR, null);
}
private static boolean validateAnnotations(
Iterable<? extends AnnotationMirror> annotationMirrors) {
for (AnnotationMirror annotationMirror : annotationMirrors) {
if (!validateAnnotation(annotationMirror)) {
return false;
}
}
return true;
}
private static boolean validateAnnotation(AnnotationMirror annotationMirror) {
return validateType(annotationMirror.getAnnotationType())
&& validateAnnotationValues(annotationMirror.getElementValues());
}
private static boolean validateAnnotationValues(
Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap) {
return valueMap.entrySet().stream()
.allMatch(
valueEntry -> {
TypeMirror expectedType = valueEntry.getKey().getReturnType();
return validateAnnotationValue(valueEntry.getValue(), expectedType);
});
}
private static final AnnotationValueVisitor<Boolean, TypeMirror> VALUE_VALIDATING_VISITOR =
new SimpleAnnotationValueVisitor8<Boolean, TypeMirror>() {
@Override protected Boolean defaultAction(Object o, TypeMirror expectedType) {
return MoreTypes.isTypeOf(o.getClass(), expectedType);
}
@Override public Boolean visitUnknown(AnnotationValue av, TypeMirror expectedType) {
// just take the default action for the unknown
return defaultAction(av, expectedType);
}
@Override public Boolean visitAnnotation(AnnotationMirror a, TypeMirror expectedType) {
return MoreTypes.equivalence().equivalent(a.getAnnotationType(), expectedType)
&& validateAnnotation(a);
}
@Override
public Boolean visitArray(List<? extends AnnotationValue> values, TypeMirror expectedType) {
if (!expectedType.getKind().equals(TypeKind.ARRAY)) {
return false;
}
TypeMirror componentType = MoreTypes.asArray(expectedType).getComponentType();
return values.stream().allMatch(value -> value.accept(this, componentType));
}
@Override
public Boolean visitEnumConstant(VariableElement enumConstant, TypeMirror expectedType) {
return MoreTypes.equivalence().equivalent(enumConstant.asType(), expectedType)
&& validateElement(enumConstant);
}
@Override public Boolean visitType(TypeMirror type, TypeMirror ignored) {
// We could check assignability here, but would require a Types instance. Since this
// isn't really the sort of thing that shows up in a bad AST from upstream compilation
// we ignore the expected type and just validate the type. It might be wrong, but
// it's valid.
return validateType(type);
}
@Override public Boolean visitBoolean(boolean b, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Boolean.TYPE, expectedType);
}
@Override public Boolean visitByte(byte b, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Byte.TYPE, expectedType);
}
@Override public Boolean visitChar(char c, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Character.TYPE, expectedType);
}
@Override public Boolean visitDouble(double d, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Double.TYPE, expectedType);
}
@Override public Boolean visitFloat(float f, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Float.TYPE, expectedType);
}
@Override public Boolean visitInt(int i, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Integer.TYPE, expectedType);
}
@Override public Boolean visitLong(long l, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Long.TYPE, expectedType);
}
@Override public Boolean visitShort(short s, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Short.TYPE, expectedType);
}
};
private static boolean validateAnnotationValue(
AnnotationValue annotationValue, TypeMirror expectedType) {
return annotationValue.accept(VALUE_VALIDATING_VISITOR, expectedType);
}
private SuperficialValidation() {}
}