framework.src.org.checkerframework.common.reflection.ClassValAnnotatedTypeFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker-qual Show documentation
Show all versions of checker-qual Show documentation
Checker Qual is the set of annotations (qualifiers) and supporting classes
used by the Checker Framework to type check Java source code. Please
see artifact:
org.checkerframework:checker
package org.checkerframework.common.reflection;
import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.reflection.qual.ClassBound;
import org.checkerframework.common.reflection.qual.ClassVal;
import org.checkerframework.common.reflection.qual.ClassValBottom;
import org.checkerframework.common.reflection.qual.ForName;
import org.checkerframework.common.reflection.qual.GetClass;
import org.checkerframework.common.reflection.qual.UnknownClass;
import org.checkerframework.common.value.ValueAnnotatedTypeFactory;
import org.checkerframework.common.value.ValueChecker;
import org.checkerframework.common.value.qual.StringVal;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.TreeAnnotator;
import org.checkerframework.framework.util.AnnotationBuilder;
import org.checkerframework.framework.util.MultiGraphQualifierHierarchy;
import org.checkerframework.framework.util.MultiGraphQualifierHierarchy.MultiGraphFactory;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.ArrayType;
import com.sun.tools.javac.code.Type.UnionClassType;
public class ClassValAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
protected final AnnotationMirror CLASSVAL_TOP = AnnotationUtils
.fromClass(elements, UnknownClass.class);
public ClassValAnnotatedTypeFactory(BaseTypeChecker checker) {
super(checker);
if (this.getClass().equals(ClassValAnnotatedTypeFactory.class)) {
this.postInit();
}
}
@Override
protected Set> createSupportedTypeQualifiers() {
return Collections.unmodifiableSet(
new HashSet>(
Arrays.asList(UnknownClass.class, ClassVal.class, ClassBound.class, ClassValBottom.class)));
}
private AnnotationMirror createClassVal(List values) {
AnnotationBuilder builder = new AnnotationBuilder(processingEnv,
ClassVal.class.getCanonicalName());
builder.setValue("value", values);
return builder.build();
}
private AnnotationMirror createClassBound(List values) {
AnnotationBuilder builder = new AnnotationBuilder(processingEnv,
ClassBound.class.getCanonicalName());
builder.setValue("value", values);
return builder.build();
}
/**
* Returns the list of classnames from @ClassBound
or @ClassVal
if anno is
* @ClassBound
or @ClassVal
, otherwise returns an empty list
*
* @param anno any AnnotationMirror
* @return list of classnames in anno
*/
public static List getClassNamesFromAnnotation(AnnotationMirror anno) {
if (AnnotationUtils.areSameByClass(anno, ClassBound.class)
|| AnnotationUtils.areSameByClass(anno, ClassVal.class)) {
return AnnotationUtils.getElementValueArray(anno, "value",
String.class, true);
}
return new ArrayList<>();
}
@Override
public QualifierHierarchy createQualifierHierarchy(MultiGraphFactory factory) {
return new ClassValQualifierHierarchy(factory);
}
/**
* The qualifier hierarchy for the ClassVal type system
*/
protected class ClassValQualifierHierarchy extends
MultiGraphQualifierHierarchy {
public ClassValQualifierHierarchy(MultiGraphFactory f) {
super(f);
}
/*
* Determines the least upper bound of a1 and a2. If both are ClassVal
* annotations, then the least upper bound is the set of elements
* obtained by combining the values of both annotations.
*/
@Override
public AnnotationMirror leastUpperBound(AnnotationMirror a1,
AnnotationMirror a2) {
if (!AnnotationUtils.areSameIgnoringValues(getTopAnnotation(a1),
getTopAnnotation(a2))) {
return null;
} else if (isSubtype(a1, a2)) {
return a2;
} else if (isSubtype(a2, a1)) {
return a1;
} else {
List a1ClassNames = getClassNamesFromAnnotation(a1);
List a2ClassNames = getClassNamesFromAnnotation(a2);
Set lubClassNames = new TreeSet();
lubClassNames.addAll(a1ClassNames);
lubClassNames.addAll(a2ClassNames);
// If either annotation is a ClassBound, the lub must also be a class bound.
if (AnnotationUtils.areSameByClass(a1, ClassBound.class) ||
AnnotationUtils.areSameByClass(a2, ClassBound.class)) {
return createClassBound(new ArrayList<>(lubClassNames));
} else {
return createClassVal(new ArrayList<>(lubClassNames));
}
}
}
@Override
public AnnotationMirror greatestLowerBound(AnnotationMirror a1, AnnotationMirror a2) {
if (!AnnotationUtils.areSameIgnoringValues(getTopAnnotation(a1), getTopAnnotation(a2))) {
return null;
} else if (isSubtype(a1, a2)) {
return a1;
} else if (isSubtype(a2, a1)) {
return a2;
} else {
List a1ClassNames = getClassNamesFromAnnotation(a1);
List a2ClassNames = getClassNamesFromAnnotation(a2);
Set glbClassNames = new TreeSet();
glbClassNames.addAll(a1ClassNames);
glbClassNames.retainAll(a2ClassNames);
// If either annotation is a ClassVal, the glb must also be a ClassVal.
// For example:
// GLB( @ClassVal(a,b), @ClassBound(a,c)) is @ClassVal(a)
// because @ClassBound(a) is not a subtype of @ClassVal(a,b)
if (AnnotationUtils.areSameByClass(a1, ClassVal.class) ||
AnnotationUtils.areSameByClass(a2, ClassVal.class)) {
return createClassVal(new ArrayList<>(glbClassNames));
} else {
return createClassBound(new ArrayList<>(glbClassNames));
}
}
}
/*
* Computes subtyping as per the subtyping in the qualifier hierarchy
* structure unless both annotations are ClassVal. In this case, rhs is
* a subtype of lhs iff lhs contains at least every element of rhs
*/
@Override
public boolean isSubtype(AnnotationMirror sub, AnnotationMirror sup) {
if (AnnotationUtils.areSame(sub, sup)
|| AnnotationUtils.areSameByClass(sup, UnknownClass.class)
|| AnnotationUtils
.areSameByClass(sub, ClassValBottom.class)) {
return true;
}
if (AnnotationUtils.areSameByClass(sub, UnknownClass.class)
|| AnnotationUtils
.areSameByClass(sup, ClassValBottom.class)) {
return false;
}
if (AnnotationUtils.areSameByClass(sup, ClassVal.class)
&& AnnotationUtils.areSameByClass(sub, ClassBound.class)) {
return false;
}
// if super: ClassVal && sub is ClassVal
// if super: ClassBound && (sub is ClassBound or ClassVal)
List supValues = getClassNamesFromAnnotation(sup);
List subValues = getClassNamesFromAnnotation(sub);
return supValues.containsAll(subValues);
}
}
@Override
protected TreeAnnotator createTreeAnnotator() {
return new ListTreeAnnotator(new ClassValTreeAnnotator(this),
super.createTreeAnnotator());
}
/**
* Implements these type inference rules:
* C.class: @ClassVal(fully qualified name of C)
* Class.forName(name): @ClassVal("name")
* exp.getClass(): @ClassBound(fully qualified classname of exp)
*/
protected class ClassValTreeAnnotator extends TreeAnnotator {
protected ClassValTreeAnnotator(ClassValAnnotatedTypeFactory factory) {
super(factory);
}
@Override
public Void visitMemberSelect(MemberSelectTree tree,
AnnotatedTypeMirror type) {
if (TreeUtils.isClassLiteral(tree)) {
// Create annotations for Class literals
// C.class: @ClassVal(fully qualified name of C)
ExpressionTree etree = tree.getExpression();
Type classType = (Type) InternalUtils.typeOf(etree);
String name = getClassNameFromType(classType);
if (name != null) {
AnnotationMirror newQual = createClassVal(Arrays
.asList(name));
type.replaceAnnotation(newQual);
}
}
return null;
}
@Override
public Void visitMethodInvocation(MethodInvocationTree tree,
AnnotatedTypeMirror type) {
if (isForNameMethodInovaction(tree)) {
// Class.forName(name): @ClassVal("name")
ExpressionTree arg = tree.getArguments().get(0);
List classNames = getStringValues(arg);
if (classNames != null) {
AnnotationMirror newQual = createClassVal(classNames);
type.replaceAnnotation(newQual);
}
} else if (isGetClassMethodInovaction(tree)) {
// exp.getClass(): @ClassBound(fully qualified class name of exp)
Type clType;
if (TreeUtils.getReceiverTree(tree) != null) {
clType = (Type) InternalUtils.typeOf(TreeUtils
.getReceiverTree(tree));
} else { // receiver is null, so it is implicitly "this"
ClassTree classTree = TreeUtils
.enclosingClass(getPath(tree));
clType = (Type) InternalUtils.typeOf(classTree);
}
String className = getClassNameFromType(clType);
AnnotationMirror newQual = createClassBound(Arrays
.asList(className));
type.replaceAnnotation(newQual);
}
return null;
}
private boolean isForNameMethodInovaction(MethodInvocationTree tree) {
return getDeclAnnotation(InternalUtils.symbol(tree),
ForName.class) != null;
}
private boolean isGetClassMethodInovaction(MethodInvocationTree tree) {
return getDeclAnnotation(InternalUtils.symbol(tree),
GetClass.class) != null;
}
private List getStringValues(ExpressionTree arg) {
ValueAnnotatedTypeFactory valueATF = getTypeFactoryOfSubchecker(ValueChecker.class);
AnnotationMirror annotation = valueATF.getAnnotationMirror(arg, StringVal.class);
if (annotation == null) {
return null;
}
return AnnotationUtils.getElementValueArray(annotation, "value",
String.class, true);
}
/**
* Return String representation of class name. This will not return the
* correct name for anonymous classes.
*/
private String getClassNameFromType(Type classType) {
switch (classType.getKind()) {
case ARRAY:
String array = "";
while (classType.getKind() == TypeKind.ARRAY) {
classType = ((ArrayType) classType).getComponentType();
array+="[]";
}
return getClassNameFromType(classType)+array;
case DECLARED:
StringBuilder className = new StringBuilder(TypesUtils
.getQualifiedName((DeclaredType) classType).toString());
if (classType.getEnclosingType() != null) {
while (classType.getEnclosingType().getKind() != TypeKind.NONE) {
classType = classType.getEnclosingType();
int last = className.lastIndexOf(".");
if (last > -1) {
className.replace(last, last + 1, "$");
}
}
}
return className.toString();
case INTERSECTION:
// This could be more precise
return "java.lang.Object";
case NULL:
return "java.lang.Object";
case UNION:
classType = ((UnionClassType) classType).getLub();
return getClassNameFromType(classType);
case TYPEVAR:
case WILDCARD:
classType = classType.getUpperBound();
return getClassNameFromType(classType);
case INT:
return int.class.getCanonicalName();
case LONG:
return long.class.getCanonicalName();
case SHORT:
return short.class.getCanonicalName();
case BYTE:
return byte.class.getCanonicalName();
case CHAR:
return char.class.getCanonicalName();
case DOUBLE:
return double.class.getCanonicalName();
case FLOAT:
return float.class.getCanonicalName();
case BOOLEAN:
return boolean.class.getCanonicalName();
default:
checker.errorAbort("ClassValAnnotatedTypeFactory.getClassname: did not expect " +classType.getKind());
return "java.lang.Object";
}
}
}
}