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

checker.src.org.checkerframework.checker.regex.RegexQualifiedTypeFactory 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.

There is a newer version: 3.42.0
Show newest version
package org.checkerframework.checker.regex;

import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import org.checkerframework.checker.experimental.regex_qual.Regex;
import org.checkerframework.checker.experimental.regex_qual.RegexQualifierHierarchy;
import org.checkerframework.javacutil.Pair;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.qualframework.base.QualifiedTypeMirror;
import org.checkerframework.qualframework.base.QualifiedTypeMirror.QualifiedDeclaredType;
import org.checkerframework.qualframework.base.QualifiedTypeMirror.QualifiedTypeVariable;
import org.checkerframework.qualframework.base.QualifiedTypeMirror.QualifiedWildcardType;
import org.checkerframework.qualframework.base.QualifierHierarchy;
import org.checkerframework.qualframework.base.SetQualifierVisitor;
import org.checkerframework.qualframework.base.TypeVariableSubstitutor;
import org.checkerframework.qualframework.base.dataflow.QualAnalysis;
import org.checkerframework.qualframework.base.dataflow.QualTransfer;
import org.checkerframework.qualframework.base.dataflow.QualValue;
import org.checkerframework.qualframework.poly.CombiningOperation;
import org.checkerframework.qualframework.poly.PolyQual;
import org.checkerframework.qualframework.poly.PolyQual.GroundQual;
import org.checkerframework.qualframework.poly.PolyQual.QualVar;
import org.checkerframework.qualframework.poly.QualParams;
import org.checkerframework.qualframework.poly.QualifiedParameterTypeVariableSubstitutor;
import org.checkerframework.qualframework.poly.QualifierParameterTreeAnnotator;
import org.checkerframework.qualframework.poly.QualifierParameterTypeFactory;
import org.checkerframework.qualframework.poly.SimpleQualifierParameterAnnotationConverter;
import org.checkerframework.qualframework.poly.Wildcard;
import org.checkerframework.qualframework.util.ExtendedTypeMirror;
import org.checkerframework.qualframework.util.QualifierContext;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * The QualifiedTypeFactory for the Regex-Qual-Param type system.
 *
 *
 */
public class RegexQualifiedTypeFactory extends QualifierParameterTypeFactory {

    private final CombiningOperation lubOp = new CombiningOperation.Lub<>(new RegexQualifierHierarchy());

    /**
     * The Pattern.compile method.
     *
     * @see Pattern#compile(String)
     */
    private final ExecutableElement patternCompile;

    public RegexQualifiedTypeFactory(QualifierContext> checker) {
        super(checker);

        patternCompile = TreeUtils.getMethod("java.util.regex.Pattern", "compile",
                1, getContext().getProcessingEnvironment());
    }

    @Override
    protected QualifierHierarchy createGroundQualifierHierarchy() {
        return new RegexQualifierHierarchy();
    }

    @Override
    protected RegexAnnotationConverter createAnnotationConverter() {
        return new RegexAnnotationConverter();
    }

    @Override
    protected QualifierParameterTreeAnnotator createTreeAnnotator() {
        return new QualifierParameterTreeAnnotator(this) {

            /**
             * Create a Regex qualifier based on the contents of string and char literals.
             * Null literals are Regex.BOTTOM.
             */
            @Override
            public QualifiedTypeMirror> visitLiteral(LiteralTree tree, ExtendedTypeMirror type) {
                QualifiedTypeMirror> result = super.visitLiteral(tree, type);

                if (tree.getKind() == Kind.NULL_LITERAL) {
                    return SetQualifierVisitor.apply(result, RegexQualifiedTypeFactory.this.getQualifierHierarchy().getBottom());
                }

                String regexStr = null;
                if (tree.getKind() == Kind.STRING_LITERAL) {
                    regexStr = (String) tree.getValue();
                } else if (tree.getKind() == Kind.CHAR_LITERAL) {
                    regexStr = Character.toString((Character) tree.getValue());
                }

                if (regexStr != null) {
                    Regex regexQual;
                    if (isRegex(regexStr)) {
                        int groupCount = getGroupCount(regexStr);
                        regexQual = new Regex.RegexVal(groupCount);
                    } else {
                        regexQual = new Regex.PartialRegex(regexStr);
                    }
                    QualParams clone = result.getQualifier().clone();
                    clone.setPrimary(new GroundQual<>(regexQual));
                    result = SetQualifierVisitor.apply(result, clone);
                }

                return result;
            }

            /**
             * Handle string compound assignment.
             */
            @Override
            public QualifiedTypeMirror> visitCompoundAssignment(CompoundAssignmentTree tree,
                    ExtendedTypeMirror type) {

                if (TreeUtils.isStringConcatenation(tree) || TreeUtils.isStringCompoundConcatenation(tree)) {

                    QualParams lRegex = getEffectiveQualifier(getQualifiedType(tree.getExpression()));
                    QualParams rRegex = getEffectiveQualifier(getQualifiedType(tree.getVariable()));
                    QualifiedTypeMirror> result =
                            handleBinaryOperation(tree, lRegex, rRegex, type);

                    if (result != null) {
                        return result;
                    }
                }
                return super.visitCompoundAssignment(tree, type);
            }

            /**
             * Add polymorphism to the Pattern.compile and Pattern.matcher methods.
             */
            @Override
            public QualifiedTypeMirror> visitMethodInvocation(MethodInvocationTree tree, ExtendedTypeMirror type) {
                // TODO: Also get this to work with 2 argument Pattern.compile.

                QualifiedTypeMirror> result = super.visitMethodInvocation(tree, type);

                if (TreeUtils.isMethodInvocation(tree, patternCompile,
                        getContext().getProcessingEnvironment())) {

                    ExpressionTree arg0 = tree.getArguments().get(0);
                    if (getEffectiveQualifier(getQualifiedType(arg0)) == RegexQualifiedTypeFactory.this.getQualifierHierarchy().getBottom()) {
                        result = SetQualifierVisitor.apply(result, RegexQualifiedTypeFactory.this.getQualifierHierarchy().getBottom());
                    } else {
                        Regex qual = getEffectiveQualifier(getQualifiedType(arg0)).getPrimary().getMaximum();
                        QualParams clone = result.getQualifier().clone();
                        clone.setPrimary(new GroundQual<>(qual));
                        result = SetQualifierVisitor.apply(result, clone);
                    }
                }
                return result;
            }

            /**
             * Handle concatenation of Regex or PolyRegex String/char literals.
             * Also handles concatenation of partial regular expressions.
             */
            @Override
            public QualifiedTypeMirror> visitBinary(BinaryTree tree, ExtendedTypeMirror type) {

                if (TreeUtils.isStringConcatenation(tree)
                        || (tree instanceof CompoundAssignmentTree
                        && TreeUtils.isStringCompoundConcatenation((CompoundAssignmentTree)tree))) {

                    QualParams lRegex = getEffectiveQualifier(getQualifiedType(tree.getLeftOperand()));
                    QualParams rRegex = getEffectiveQualifier(getQualifiedType(tree.getRightOperand()));
                    QualifiedTypeMirror> result =
                            handleBinaryOperation(tree, lRegex, rRegex, type);
                    if (result != null) {
                        return result;
                    }
                }
                return super.visitBinary(tree, type);
            }

            /**
             * Returns the QualifiedTypeMirror that is the result of the binary operation represented by tree.
             * Handles concatenation of Regex and PolyRegex qualifiers.
             *
             * @param tree a BinaryTree or a CompoundAssignmentTree
             * @param lRegexParam the qualifier of the left hand side of the expression
             * @param rRegexParam the qualifier of the right hand side of the expression
             * @return result if operation is not a string concatenation or compound assignment. Otherwise
             *          a copy of result with the new qualifier applied is returned.
             */
            private QualifiedTypeMirror> handleBinaryOperation(Tree tree, QualParams lRegexParam,
                    QualParams rRegexParam, ExtendedTypeMirror type) {

                if (TreeUtils.isStringConcatenation(tree)
                        || (tree instanceof CompoundAssignmentTree
                            && TreeUtils.isStringCompoundConcatenation((CompoundAssignmentTree)tree))) {

                    PolyQual resultQual = null;

                    PolyQual rPrimary = rRegexParam.getPrimary();
                    PolyQual lPrimary = lRegexParam.getPrimary();

                    Regex rRegex = getQualifierHierarchy().getBottom() == rRegexParam ?
                            new Regex.RegexVal(0) : rPrimary.getMaximum();
                    Regex lRegex = getQualifierHierarchy().getBottom() == lRegexParam ?
                            new Regex.RegexVal(0) : lPrimary.getMaximum();

                    PolyQual polyResult = checkPoly(lPrimary, rPrimary, lRegex, rRegex);
                    if (polyResult != null) {
                        resultQual = polyResult;

                    } else if (lRegex.isRegexVal() && rRegex.isRegexVal()) {
                        // Regex(a) + Regex(b) = Regex(a + b)
                        int resultCount = ((Regex.RegexVal) lRegex).getCount() + ((Regex.RegexVal) rRegex).getCount();
                        resultQual = new GroundQual(new Regex.RegexVal(resultCount));

                    } else if (lRegex.isPartialRegex() && rRegex.isPartialRegex()) {
                        // Partial + Partial == Regex or Partial
                        String concat = ((Regex.PartialRegex) lRegex).getPartialValue() + ((Regex.PartialRegex) rRegex).getPartialValue();
                        if (isRegex(concat)) {
                            int groupCount = getGroupCount(concat);
                            resultQual = new GroundQual(new Regex.RegexVal(groupCount));
                        } else {
                            resultQual = new GroundQual(new Regex.PartialRegex(concat));
                        }

                    } else if (lRegex.isRegexVal() && rRegex.isPartialRegex()) {
                        // Regex + Partial == Partial
                        String concat = "e" + ((Regex.PartialRegex) rRegex).getPartialValue();
                        resultQual = new GroundQual(new Regex.PartialRegex(concat));

                    } else if (rRegex.isRegexVal() && lRegex.isPartialRegex()) {
                        // Partial + Regex == Partial
                        String concat = ((Regex.PartialRegex) lRegex).getPartialValue() + "e";
                        resultQual = new GroundQual(new Regex.PartialRegex(concat));
                    } else if (rRegex == Regex.TOP || lRegex == Regex.TOP) {
                        resultQual = new GroundQual<>(Regex.TOP);
                    } else if (rRegex == Regex.BOTTOM && lRegex == Regex.BOTTOM) {
                        resultQual = new GroundQual<>(Regex.BOTTOM);
                    }

                    if (resultQual != null) {
                        return new QualifiedDeclaredType<>(type, new QualParams<>(resultQual),
                                new ArrayList>>());
                    }
                }

                return null;
            }

        };
    }

    /**
     * Check to see if the result of the operation is polymorphic.
     *
     * @return the polymorphic PolyQual if the result should be polymorphic, otherwise return null
     */
    private PolyQual checkPoly(PolyQual lPrimary, PolyQual rPrimary, Regex lRegex, Regex rRegex) {
        if (isPolyRegex(lPrimary) && isPolyRegex(rPrimary)) {
            return lPrimary;
        } else if (isPolyRegex(lPrimary) && rRegex.isRegexVal()) {
            return lPrimary;
        } else if (isPolyRegex(rPrimary) && lRegex.isRegexVal()) {
            return rPrimary;
        } else {
            return null;
        }
    }

    private boolean isPolyRegex(PolyQual possiblePoly) {
        return possiblePoly instanceof QualVar
                && ((QualVar) possiblePoly).getName().equals(SimpleQualifierParameterAnnotationConverter.POLY_NAME);
    }

    /**
     * Returns the number of groups in the given regex String.
     */
    public static int getGroupCount(/*@org.checkerframework.checker.regex.qual.Regex*/ String regex) {

        return Pattern.compile(regex).matcher("").groupCount();
    }

    /** This method is a copy of RegexUtil.isValidRegex.
     * We cannot directly use RegexUtil, because it uses type annotations
     * which cannot be used in IDEs (yet).
     */
    /*@SuppressWarnings("purity")*/ // the checker cannot prove that the method is pure, but it is
    /*@org.checkerframework.dataflow.qual.Pure*/
    private static boolean isRegex(String s) {
        try {
            Pattern.compile(s);
        } catch (PatternSyntaxException e) {
            return false;
        }
        return true;
    }

    @Override
    public QualAnalysis> createFlowAnalysis(List>>> fieldValues) {
        return new QualAnalysis>(this.getContext()) {
            @Override
            public QualTransfer> createTransferFunction() {
                return new RegexQualifiedTransfer(this);
            }
        };
    }

    @Override
    public TypeVariableSubstitutor> createTypeVariableSubstitutor() {
        return new QualifiedParameterTypeVariableSubstitutor() {
            @Override
            protected Wildcard combineForSubstitution(Wildcard a, Wildcard b) {
                return a.combineWith(b, lubOp, lubOp);
            }

            @Override
            protected PolyQual combineForSubstitution(PolyQual a, PolyQual b) {
                return a.combineWith(b, lubOp);
            }
        };
    }

    public QualParams getEffectiveQualifier(QualifiedTypeMirror> mirror) {
        switch (mirror.getKind()) {
            case TYPEVAR:
                return this.getQualifiedTypeParameterBounds(
                        ((QualifiedTypeVariable>) mirror).
                                getDeclaration().getUnderlyingType()).getUpperBound().getQualifier();
            case WILDCARD:
                return ((QualifiedWildcardType>)mirror).getExtendsBound().getQualifier();

            default:
                return mirror.getQualifier();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy