package org.checkerframework.framework.util; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Tree; import com.sun.tools.javac.code.Attribute.TypeCompound; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.util.List; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.util.Elements; import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.framework.qual.Dependent; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType; import org.checkerframework.framework.type.GeneralAnnotatedTypeFactory; import org.checkerframework.javacutil.AnnotationUtils; import org.checkerframework.javacutil.InternalUtils; import org.checkerframework.javacutil.TreeUtils; public class DependentTypes { private final Elements elements; // TODO: the implementation of this class needs some serious refactoring // and thought (and documentation...). // @Dependent expresses the relationship between two separate // type systems. It is not enough to use the GeneralATF, which doesn'tee // apply defaulting appropriate for the second type system. // This issue is similar to the interaction between @Unused and // the Nullness Checker. private final GeneralAnnotatedTypeFactory atypeFactory; public DependentTypes(BaseTypeChecker checker, GeneralAnnotatedTypeFactory atypeFactory) { this.elements = checker.getProcessingEnvironment().getElementUtils(); this.atypeFactory = atypeFactory; } private AnnotationMirror getResult(AnnotationMirror anno) { Name valName = AnnotationUtils.getElementValueClassName(anno, "result", false); return AnnotationUtils.fromName(elements, valName); } private AnnotationMirror getWhen(AnnotationMirror anno) { Name valName = AnnotationUtils.getElementValueClassName(anno, "when", false); return AnnotationUtils.fromName(elements, valName); } private AnnotationMirror findDependent(Element element) { // TODO: does this work with a .astub file? List<TypeCompound> tas = ((Symbol) element).getRawTypeAttributes(); for (TypeCompound ta : tas) { if (ta.getAnnotationType().toString().equals(Dependent.class.getCanonicalName())) { return ta; } } return null; } public void doSubsitution( Element symbol, AnnotatedTypeMirror type, AnnotatedTypeMirror receiver) { AnnotationMirror dependentInfo = findDependent(symbol); if (dependentInfo == null) { return; } if (receiver == null) { return; } AnnotationMirror ifpresent = getWhen(dependentInfo); if (receiver.hasAnnotation(ifpresent)) { AnnotationMirror then = getResult(dependentInfo); type.replaceAnnotation(then); } } public void handle(Tree tree, AnnotatedTypeMirror type) { if (!TreeUtils.isExpressionTree(tree)) { return; } ExpressionTree expr = (ExpressionTree) tree; Element symbol = null; if (expr instanceof IdentifierTree) { symbol = TreeUtils.elementFromUse(expr); } else if (expr instanceof MemberSelectTree) { symbol = TreeUtils.elementFromUse(expr); } if (symbol == null || (!symbol.getKind().isField() && symbol.getKind() != ElementKind.LOCAL_VARIABLE)) return; AnnotatedTypeMirror receiver; try { // Ugly hack to not crash type checking if the GeneralATF // runs into some problem determining the receiver type. // TODO: remove GeneralATF, see note with field. receiver = atypeFactory.getReceiverType(expr); } catch (Throwable t) { receiver = null; } if (receiver != null) { doSubsitution(symbol, type, receiver); } } public void handleConstructor( NewClassTree tree, AnnotatedTypeMirror ctr, AnnotatedExecutableType type) { ExecutableElement constructorElt = InternalUtils.constructor(tree); for (int i = 0; i < constructorElt.getParameters().size(); ++i) { Element parameter = constructorElt.getParameters().get(i); AnnotatedTypeMirror paramType = type.getParameterTypes().get(i); doSubsitution(parameter, paramType, ctr); } } }