All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.checker.signedness.SignednessVisitor Maven / Gradle / Ivy

package org.checkerframework.checker.signedness;

import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;

import org.checkerframework.checker.interning.InterningVisitor;
import org.checkerframework.checker.interning.qual.EqualsMethod;
import org.checkerframework.checker.signedness.qual.PolySigned;
import org.checkerframework.checker.signedness.qual.Signed;
import org.checkerframework.checker.signedness.qual.Unsigned;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.basetype.BaseTypeVisitor;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedExecutableType;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.javacutil.TypesUtils;
import org.plumelib.util.IPair;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeMirror;

/**
 * The SignednessVisitor enforces the Signedness Checker rules. These rules are described in the
 * Checker Framework Manual.
 *
 * @checker_framework.manual #signedness-checker Signedness Checker
 */
public class SignednessVisitor extends BaseTypeVisitor {

    public SignednessVisitor(BaseTypeChecker checker) {
        super(checker);
    }

    /**
     * Returns true if an annotated type is annotated as {@link Unsigned} or {@link PolySigned}
     *
     * @param type the annotated type to be checked
     * @return true if the annotated type is annotated as {@link Unsigned} or {@link PolySigned}
     */
    private boolean hasUnsignedAnnotation(AnnotatedTypeMirror type) {
        return type.hasAnnotation(Unsigned.class) || type.hasAnnotation(PolySigned.class);
    }

    /**
     * Returns true if an annotated type is annotated as {@link Signed} or {@link PolySigned}
     *
     * @param type the annotated type to be checked
     * @return true if the annotated type is annotated as {@link Signed} or {@link PolySigned}
     */
    private boolean hasSignedAnnotation(AnnotatedTypeMirror type) {
        return type.hasAnnotation(Signed.class) || type.hasAnnotation(PolySigned.class);
    }

    /**
     * Enforces the following rules on binary operations involving Unsigned and Signed types:
     *
     * 
    *
  • Do not allow any Unsigned types or PolySigned types in {@literal {/, %}} operations. *
  • Do not allow signed right shift {@literal {>>}} on an Unsigned type or a PolySigned * type. *
  • Do not allow unsigned right shift {@literal {>>>}} on a Signed type or a PolySigned * type. *
  • Allow any left shift {@literal {<<}}. *
  • Do not allow non-equality comparisons {@literal {<, <=, >, >=}} on Unsigned types or * PolySigned types. *
  • Do not allow the mixing of Signed and Unsigned types. *
*/ @Override public Void visitBinary(BinaryTree tree, Void p) { // Used in diagnostic messages. ExpressionTree leftOp = tree.getLeftOperand(); ExpressionTree rightOp = tree.getRightOperand(); IPair argTypes = atypeFactory.binaryTreeArgTypes(tree); AnnotatedTypeMirror leftOpType = argTypes.first; AnnotatedTypeMirror rightOpType = argTypes.second; Tree.Kind kind = tree.getKind(); switch (kind) { case DIVIDE: case REMAINDER: if (hasUnsignedAnnotation(leftOpType)) { checker.reportError( leftOp, "operation.unsignedlhs", kind, leftOpType, rightOpType); } else if (hasUnsignedAnnotation(rightOpType)) { checker.reportError( rightOp, "operation.unsignedrhs", kind, leftOpType, rightOpType); } break; case RIGHT_SHIFT: if (hasUnsignedAnnotation(leftOpType) && !SignednessShifts.isMaskedShiftEitherSignedness(tree, getCurrentPath()) && !SignednessShifts.isCastedShiftEitherSignedness( tree, getCurrentPath())) { checker.reportError(leftOp, "shift.signed", kind, leftOpType, rightOpType); } break; case UNSIGNED_RIGHT_SHIFT: if (hasSignedAnnotation(leftOpType) && !SignednessShifts.isMaskedShiftEitherSignedness(tree, getCurrentPath()) && !SignednessShifts.isCastedShiftEitherSignedness( tree, getCurrentPath())) { checker.reportError(leftOp, "shift.unsigned", kind, leftOpType, rightOpType); } break; case LEFT_SHIFT: break; case GREATER_THAN: case GREATER_THAN_EQUAL: case LESS_THAN: case LESS_THAN_EQUAL: if (hasUnsignedAnnotation(leftOpType)) { checker.reportError(leftOp, "comparison.unsignedlhs", leftOpType, rightOpType); } else if (hasUnsignedAnnotation(rightOpType)) { checker.reportError(rightOp, "comparison.unsignedrhs", leftOpType, rightOpType); } break; case EQUAL_TO: case NOT_EQUAL_TO: if (!atypeFactory.maybeIntegral(leftOpType) || !atypeFactory.maybeIntegral(rightOpType)) { break; } if (leftOpType.hasEffectiveAnnotation(Unsigned.class) && rightOpType.hasEffectiveAnnotation(Signed.class)) { checker.reportError( tree, "comparison.mixed.unsignedlhs", leftOpType, rightOpType); } else if (leftOpType.hasEffectiveAnnotation(Signed.class) && rightOpType.hasEffectiveAnnotation(Unsigned.class)) { checker.reportError( tree, "comparison.mixed.unsignedrhs", leftOpType, rightOpType); } break; case PLUS: if (TreeUtils.isStringConcatenation(tree)) { // Note that leftOpType.getUnderlyingType() and rightOpType.getUnderlyingType() // are always java.lang.String. Please refer to binaryTreeArgTypes for more // details. // Here, the original types of operands can be something different from string. // For example, "123" + obj is a string concatenation in which the original type // of the right operand is java.lang.Object. TypeMirror leftOpTM = TreeUtils.typeOf(leftOp); AnnotationMirror leftAnno = leftOpType.getEffectiveAnnotationInHierarchy(atypeFactory.SIGNED); TypeMirror rightOpTM = TreeUtils.typeOf(rightOp); AnnotationMirror rightAnno = rightOpType.getEffectiveAnnotationInHierarchy(atypeFactory.SIGNED); if (!TypesUtils.isCharType(leftOpTM) && !qualHierarchy.isSubtypeQualifiersOnly( leftAnno, atypeFactory.SIGNED)) { checker.reportError(leftOp, "unsigned.concat"); } else if (!TypesUtils.isCharType(rightOpTM) && !qualHierarchy.isSubtypeQualifiersOnly( rightAnno, atypeFactory.SIGNED)) { checker.reportError(rightOp, "unsigned.concat"); } break; } // Other plus binary trees should be handled in the default case. // fall through default: if (leftOpType.hasEffectiveAnnotation(Unsigned.class) && rightOpType.hasEffectiveAnnotation(Signed.class)) { checker.reportError( tree, "operation.mixed.unsignedlhs", kind, leftOpType, rightOpType); } else if (leftOpType.hasEffectiveAnnotation(Signed.class) && rightOpType.hasEffectiveAnnotation(Unsigned.class)) { checker.reportError( tree, "operation.mixed.unsignedrhs", kind, leftOpType, rightOpType); } break; } return super.visitBinary(tree, p); } // Ensure that method annotations are not written on methods they don't apply to. // Copied from InterningVisitor @Override public Void visitMethod(MethodTree tree, Void p) { ExecutableElement methElt = TreeUtils.elementFromDeclaration(tree); boolean hasEqualsMethodAnno = atypeFactory.getDeclAnnotation(methElt, EqualsMethod.class) != null; int params = methElt.getParameters().size(); if (hasEqualsMethodAnno && !(params == 1 || params == 2)) { checker.reportError( tree, "invalid.method.annotation", "@EqualsMethod", "1 or 2", methElt, params); } return super.visitMethod(tree, p); } @Override public Void visitMethodInvocation(MethodInvocationTree tree, Void p) { ExecutableElement methElt = TreeUtils.elementFromUse(tree); boolean hasEqualsMethodAnno = atypeFactory.getDeclAnnotation(methElt, EqualsMethod.class) != null; if (hasEqualsMethodAnno || InterningVisitor.isInvocationOfEquals(tree)) { int params = methElt.getParameters().size(); if (!(params == 1 || params == 2)) { checker.reportError( tree, "invalid.method.annotation", "@EqualsMethod", "1 or 2", methElt, params); } else { AnnotatedTypeMirror leftOpType; AnnotatedTypeMirror rightOpType; if (params == 1) { leftOpType = atypeFactory.getReceiverType(tree); rightOpType = atypeFactory.getAnnotatedType(tree.getArguments().get(0)); } else if (params == 2) { leftOpType = atypeFactory.getAnnotatedType(tree.getArguments().get(0)); rightOpType = atypeFactory.getAnnotatedType(tree.getArguments().get(1)); } else { throw new BugInCF("Checked that params is 1 or 2"); } if (!atypeFactory.maybeIntegral(leftOpType) || !atypeFactory.maybeIntegral(rightOpType)) { // nothing to do } else if (leftOpType.hasAnnotation(Unsigned.class) && rightOpType.hasAnnotation(Signed.class)) { checker.reportError( tree, "comparison.mixed.unsignedlhs", leftOpType, rightOpType); } else if (leftOpType.hasAnnotation(Signed.class) && rightOpType.hasAnnotation(Unsigned.class)) { checker.reportError( tree, "comparison.mixed.unsignedrhs", leftOpType, rightOpType); } } // Don't check against the annotated method declaration (which super would do). return null; } return super.visitMethodInvocation(tree, p); } /** * Returns a string representation of {@code kind}, with trailing _ASSIGNMENT stripped off if * any. * * @param kind a tree kind * @return a string representation of {@code kind}, with trailing _ASSIGNMENT stripped off if * any */ private String kindWithoutAssignment(Tree.Kind kind) { String result = kind.toString(); if (result.endsWith("_ASSIGNMENT")) { return result.substring(0, result.length() - "_ASSIGNMENT".length()); } else { return result; } } /** * Enforces the following rules on compound assignments involving Unsigned and Signed types: * *
    *
  • Do not allow any Unsigned types or PolySigned types in {@literal {/=, %=}} assignments. *
  • Do not allow signed right shift {@literal {>>=}} to assign to an Unsigned type or a * PolySigned type. *
  • Do not allow unsigned right shift {@literal {>>>=}} to assign to a Signed type or a * PolySigned type. *
  • Allow any left shift {@literal {<<=}} assignment. *
  • Do not allow mixing of Signed and Unsigned types. *
*/ @Override public Void visitCompoundAssignment(CompoundAssignmentTree tree, Void p) { ExpressionTree var = tree.getVariable(); ExpressionTree expr = tree.getExpression(); IPair argTypes = atypeFactory.compoundAssignmentTreeArgTypes(tree); AnnotatedTypeMirror varType = argTypes.first; AnnotatedTypeMirror exprType = argTypes.second; Tree.Kind kind = tree.getKind(); switch (kind) { case DIVIDE_ASSIGNMENT: case REMAINDER_ASSIGNMENT: if (hasUnsignedAnnotation(varType)) { checker.reportError( var, "compound.assignment.unsigned.variable", kindWithoutAssignment(kind), varType, exprType); } else if (hasUnsignedAnnotation(exprType)) { checker.reportError( expr, "compound.assignment.unsigned.expression", kindWithoutAssignment(kind), varType, exprType); } break; case RIGHT_SHIFT_ASSIGNMENT: if (hasUnsignedAnnotation(varType)) { checker.reportError( var, "compound.assignment.shift.signed", kindWithoutAssignment(kind), varType, exprType); } break; case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: if (hasSignedAnnotation(varType)) { checker.reportError( var, "compound.assignment.shift.unsigned", kindWithoutAssignment(kind), varType, exprType); } break; case LEFT_SHIFT_ASSIGNMENT: break; case PLUS_ASSIGNMENT: if (TreeUtils.isStringCompoundConcatenation(tree)) { // Note that exprType.getUnderlyingType() is always java.lang.String. // Please refer to compoundAssignmentTreeArgTypes for more details. if (TypesUtils.isCharType(TreeUtils.typeOf(expr))) { break; } AnnotationMirror exprAnno = exprType.getEffectiveAnnotationInHierarchy(atypeFactory.SIGNED); if (!qualHierarchy.isSubtypeQualifiersOnly(exprAnno, atypeFactory.SIGNED)) { checker.reportError(tree.getExpression(), "unsigned.concat"); } break; } // Other plus binary trees should be handled in the default case. // fall through default: if (varType.hasAnnotation(Unsigned.class) && exprType.hasAnnotation(Signed.class)) { checker.reportError( expr, "compound.assignment.mixed.unsigned.variable", kindWithoutAssignment(kind), varType, exprType); } else if (varType.hasAnnotation(Signed.class) && exprType.hasAnnotation(Unsigned.class)) { checker.reportError( expr, "compound.assignment.mixed.unsigned.expression", kindWithoutAssignment(kind), varType, exprType); } break; } return super.visitCompoundAssignment(tree, p); } @Override protected boolean isTypeCastSafe(AnnotatedTypeMirror castType, AnnotatedTypeMirror exprType) { if (!atypeFactory.maybeIntegral(castType)) { // If the cast is not a number or a char, then it is legal. return true; } return super.isTypeCastSafe(castType, exprType); } @Override protected AnnotationMirrorSet getExceptionParameterLowerBoundAnnotations() { return new AnnotationMirrorSet(atypeFactory.SIGNED); } @Override protected void checkConstructorResult( AnnotatedExecutableType constructorType, ExecutableElement constructorElement) {} }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy