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

org.checkerframework.checker.regex.RegexTransfer Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java's type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

The newest version!
package org.checkerframework.checker.regex;

import org.checkerframework.dataflow.analysis.ConditionalTransferResult;
import org.checkerframework.dataflow.analysis.RegularTransferResult;
import org.checkerframework.dataflow.analysis.TransferInput;
import org.checkerframework.dataflow.analysis.TransferResult;
import org.checkerframework.dataflow.cfg.node.ClassNameNode;
import org.checkerframework.dataflow.cfg.node.GreaterThanNode;
import org.checkerframework.dataflow.cfg.node.GreaterThanOrEqualNode;
import org.checkerframework.dataflow.cfg.node.IntegerLiteralNode;
import org.checkerframework.dataflow.cfg.node.LessThanNode;
import org.checkerframework.dataflow.cfg.node.LessThanOrEqualNode;
import org.checkerframework.dataflow.cfg.node.MethodAccessNode;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.expression.JavaExpression;
import org.checkerframework.dataflow.util.NodeUtils;
import org.checkerframework.framework.flow.CFAbstractAnalysis;
import org.checkerframework.framework.flow.CFStore;
import org.checkerframework.framework.flow.CFTransfer;
import org.checkerframework.framework.flow.CFValue;
import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.TreeUtils;

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

/** The transfer function for the Regex Checker. */
public class RegexTransfer extends CFTransfer {

    // isRegex and asRegex are tested as signatures (string name plus formal parameters), not
    // ExecutableElement, because they exist in two packages:
    // org.checkerframework.checker.regex.util.RegexUtil.isRegex(String,int)
    // org.plumelib.util.RegexUtil.isRegex(String,int)
    // and org.plumelib.util might not be on the classpath.
    private static final String IS_REGEX_METHOD_NAME = "isRegex";
    private static final String AS_REGEX_METHOD_NAME = "asRegex";

    /** The MatchResult.groupCount() method. */
    private final ExecutableElement matchResultgroupCount;

    /** Create the transfer function for the Regex Checker. */
    public RegexTransfer(CFAbstractAnalysis analysis) {
        super(analysis);
        this.matchResultgroupCount =
                TreeUtils.getMethod(
                        "java.util.regex.MatchResult",
                        "groupCount",
                        0,
                        analysis.getTypeFactory().getProcessingEnv());
    }

    // TODO: These are special cases for isRegex(String, int) and asRegex(String, int).  They should
    // be replaced by adding an @EnsuresQualifierIf annotation that supports specifying attributes.
    @Override
    public TransferResult visitMethodInvocation(
            MethodInvocationNode n, TransferInput in) {
        TransferResult result = super.visitMethodInvocation(n, in);

        // refine result for some helper methods
        MethodAccessNode target = n.getTarget();
        ExecutableElement method = target.getMethod();
        Node receiver = target.getReceiver();
        if (receiver instanceof ClassNameNode) {
            ClassNameNode cnn = (ClassNameNode) receiver;
            String receiverName = cnn.getElement().toString();
            if (isRegexUtil(receiverName)) {
                result = handleRegexUtil(n, method, result);
            }
        }
        return result;
    }

    private TransferResult handleRegexUtil(
            MethodInvocationNode n,
            ExecutableElement method,
            TransferResult result) {
        RegexAnnotatedTypeFactory factory = (RegexAnnotatedTypeFactory) analysis.getTypeFactory();
        if (ElementUtils.matchesElement(method, IS_REGEX_METHOD_NAME, String.class, int.class)) {
            // RegexUtil.isRegex(s, groups) method
            // (No special case is needed for isRegex(String) because of
            // the annotation on that method's definition.)

            CFStore thenStore = result.getRegularStore();
            CFStore elseStore = thenStore.copy();
            ConditionalTransferResult newResult =
                    new ConditionalTransferResult<>(result.getResultValue(), thenStore, elseStore);
            JavaExpression firstParam = JavaExpression.fromNode(n.getArgument(0));

            // add annotation with correct group count (if possible,
            // regex annotation without count otherwise)
            Node count = n.getArgument(1);
            int groupCount;
            if (count instanceof IntegerLiteralNode) {
                IntegerLiteralNode iln = (IntegerLiteralNode) count;
                groupCount = iln.getValue();
            } else {
                groupCount = 0;
            }
            AnnotationMirror regexAnnotation = factory.createRegexAnnotation(groupCount);
            thenStore.insertValue(firstParam, regexAnnotation);
            return newResult;
        } else if (ElementUtils.matchesElement(
                method, AS_REGEX_METHOD_NAME, String.class, int.class)) {
            // RegexUtil.asRegex(s, groups) method
            // (No special case is needed for asRegex(String) because of
            // the annotation on that method's definition.)

            // add annotation with correct group count (if possible,
            // regex annotation without count otherwise)
            AnnotationMirror regexAnnotation;
            Node count = n.getArgument(1);
            int groupCount;
            if (count instanceof IntegerLiteralNode) {
                IntegerLiteralNode iln = (IntegerLiteralNode) count;
                groupCount = iln.getValue();
            } else {
                groupCount = 0;
            }
            regexAnnotation = factory.createRegexAnnotation(groupCount);

            CFValue newResultValue =
                    analysis.createSingleAnnotationValue(
                            regexAnnotation, result.getResultValue().getUnderlyingType());
            return new RegularTransferResult<>(newResultValue, result.getRegularStore());
        }
        return result;
    }

    @Override
    public TransferResult visitLessThan(
            LessThanNode n, TransferInput in) {
        // Look for: constant < mat.groupCount()
        // Make mat be @Regex(constant + 1)
        TransferResult res = super.visitLessThan(n, in);
        return handleMatcherGroupCount(n.getRightOperand(), n.getLeftOperand(), false, res);
    }

    @Override
    public TransferResult visitLessThanOrEqual(
            LessThanOrEqualNode n, TransferInput in) {
        // Look for: constant <= mat.groupCount()
        // Make mat be @Regex(constant)
        TransferResult res = super.visitLessThanOrEqual(n, in);
        return handleMatcherGroupCount(n.getRightOperand(), n.getLeftOperand(), true, res);
    }

    @Override
    public TransferResult visitGreaterThan(
            GreaterThanNode n, TransferInput in) {

        TransferResult res = super.visitGreaterThan(n, in);
        return handleMatcherGroupCount(n.getLeftOperand(), n.getRightOperand(), false, res);
    }

    @Override
    public TransferResult visitGreaterThanOrEqual(
            GreaterThanOrEqualNode n, TransferInput in) {
        // Look for: mat.groupCount() >= constant
        // Make mat be @Regex(constant)
        TransferResult res = super.visitGreaterThanOrEqual(n, in);
        return handleMatcherGroupCount(n.getLeftOperand(), n.getRightOperand(), true, res);
    }

    /**
     * See whether possibleMatcher is a call of groupCount on a Matcher and possibleConstant is a
     * constant. If so, annotate the matcher as constant + 1 if !isAlsoEqual constant if isAlsoEqual
     *
     * @param possibleMatcher the Node that might be a call of Matcher.groupCount()
     * @param possibleConstant the Node that might be a constant
     * @param isAlsoEqual whether the comparison operation is strict or reflexive
     * @param resultIn the TransferResult
     * @return the possibly refined output TransferResult
     */
    private TransferResult handleMatcherGroupCount(
            Node possibleMatcher,
            Node possibleConstant,
            boolean isAlsoEqual,
            TransferResult resultIn) {
        if (!(possibleMatcher instanceof MethodInvocationNode)) {
            return resultIn;
        }
        if (!(possibleConstant instanceof IntegerLiteralNode)) {
            return resultIn;
        }

        if (!NodeUtils.isMethodInvocation(
                possibleMatcher,
                matchResultgroupCount,
                analysis.getTypeFactory().getProcessingEnv())) {
            return resultIn;
        }

        MethodAccessNode methodAccessNode = ((MethodInvocationNode) possibleMatcher).getTarget();
        Node receiver = methodAccessNode.getReceiver();

        JavaExpression matcherReceiver = JavaExpression.fromNode(receiver);

        IntegerLiteralNode iln = (IntegerLiteralNode) possibleConstant;
        int groupCount;
        if (isAlsoEqual) {
            groupCount = iln.getValue();
        } else {
            groupCount = iln.getValue() + 1;
        }

        CFStore thenStore = resultIn.getRegularStore();
        CFStore elseStore = thenStore.copy();
        ConditionalTransferResult newResult =
                new ConditionalTransferResult<>(resultIn.getResultValue(), thenStore, elseStore);
        RegexAnnotatedTypeFactory factory = (RegexAnnotatedTypeFactory) analysis.getTypeFactory();

        AnnotationMirror regexAnnotation = factory.createRegexAnnotation(groupCount);
        thenStore.insertValue(matcherReceiver, regexAnnotation);

        return newResult;
    }

    /**
     * Returns true if the given receiver is a class named "RegexUtil". Examples of such classes are
     * org.checkerframework.checker.regex.util.RegexUtil and org.plumelib.util.RegexUtil, and the
     * user might copy one into their own project.
     *
     * @param receiver some string
     * @return true if the given receiver is a class named "RegexUtil"
     */
    private boolean isRegexUtil(String receiver) {
        return receiver.equals("RegexUtil") || receiver.endsWith(".RegexUtil");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy