package org.checkerframework.checker.signedness;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.Tree;
import java.lang.annotation.Annotation;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import org.checkerframework.checker.signedness.qual.UnknownSignedness;
import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.framework.qual.TypeUseLocation;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.TreeAnnotator;
import org.checkerframework.framework.util.defaults.QualifierDefaults;
import org.checkerframework.javacutil.AnnotationUtils;
/** @checker_framework.manual #signedness-checker Signedness Checker */
public class SignednessAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
// private final AnnotationMirror UNSIGNED;
// private final AnnotationMirror SIGNED;
private final AnnotationMirror UNKNOWN_SIGNEDNESS;
// These are commented out until issues with making boxed implicitly signed
// are worked out. (https://github.com/typetools/checker-framework/issues/797)
/*
private final String JAVA_LANG_BYTE = "java.lang.Byte";
private final String JAVA_LANG_SHORT = "java.lang.Short";
private final String JAVA_LANG_INTEGER = "java.lang.Integer";
private final String JAVA_LANG_LONG = "java.lang.Long";
*/
public SignednessAnnotatedTypeFactory(BaseTypeChecker checker) {
super(checker);
// UNSIGNED = AnnotationUtils.fromClass(elements, Unsigned.class);
// SIGNED = AnnotationUtils.fromClass(elements, Signed.class);
UNKNOWN_SIGNEDNESS = AnnotationUtils.fromClass(elements, UnknownSignedness.class);
postInit();
}
@Override
protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() {
return getBundledTypeQualifiersWithoutPolyAll();
}
/** {@inheritDoc} */
@Override
protected void addComputedTypeAnnotations(
Tree tree, AnnotatedTypeMirror type, boolean iUseFlow) {
// When it is possible to default types based on their TypeKinds,
// this method will no longer be needed.
// Currently, it is adding the LOCAL_VARIABLE default for
// bytes, shorts, ints, and longs so that the implicit for
// those types is not applied when they are local variables.
// Only the local variable default is applied first because
// it is the only refinable location (other than fields) that could
// have a primitive type.
addUnknownSignednessToSomeLocals(tree, type);
super.addComputedTypeAnnotations(tree, type, iUseFlow);
}
/**
* If the tree is a local variable and the type is a byte, short, int or long, then it adds the
* UnknownSignedness annotation so that data flow can refine it.
*/
private void addUnknownSignednessToSomeLocals(Tree tree, AnnotatedTypeMirror type) {
switch (type.getKind()) {
case BYTE:
case SHORT:
case INT:
case LONG:
case FLOAT:
case DOUBLE:
case CHAR:
QualifierDefaults defaults = new QualifierDefaults(elements, this);
defaults.addCheckedCodeDefault(UNKNOWN_SIGNEDNESS, TypeUseLocation.LOCAL_VARIABLE);
defaults.annotate(tree, type);
break;
default:
// Nothing for other cases.
}
// This code commented out until issues with making boxed implicitly signed
// are worked out. (https://github.com/typetools/checker-framework/issues/797)
/*switch (TypesUtils.getQualifiedName(type.getUnderlyingType()).toString()) {
case JAVA_LANG_BYTE:
case JAVA_LANG_SHORT:
case JAVA_LANG_INTEGER:
case JAVA_LANG_LONG:
QualifierDefaults defaults = new QualifierDefaults(elements, this);
defaults.addCheckedCodeDefault(UNKNOWN_SIGNEDNESS, TypeUseLocation.LOCAL_VARIABLE);
defaults.annotate(tree, type);
}*/
}
/** {@inheritDoc} */
@Override
protected TreeAnnotator createTreeAnnotator() {
return new ListTreeAnnotator(
new SignednessTreeAnnotator(this), super.createTreeAnnotator());
}
/**
* This TreeAnnotator ensures that booleans expressions are not given Unsigned or Signed
* annotations by {@link PropagationTreeAnnotator}
*/
private class SignednessTreeAnnotator extends TreeAnnotator {
public SignednessTreeAnnotator(AnnotatedTypeFactory atypeFactory) {
super(atypeFactory);
}
/**
* Change the type of booleans to @UnknownSignedness so that the {@link
* PropagationTreeAnnotator} does not change the type of them.
*/
private Void annotateBoolean(AnnotatedTypeMirror type) {
switch (type.getKind()) {
case BOOLEAN:
type.addAnnotation(UNKNOWN_SIGNEDNESS);
break;
default:
// Nothing for other cases.
}
return null;
}
@Override
public Void visitBinary(BinaryTree tree, AnnotatedTypeMirror type) {
return annotateBoolean(type);
}
@Override
public Void visitCompoundAssignment(CompoundAssignmentTree tree, AnnotatedTypeMirror type) {
return annotateBoolean(type);
}
}
}