| package org.checkerframework.checker.index; |
| |
| import com.sun.source.tree.Tree; |
| import javax.lang.model.element.AnnotationMirror; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.VariableElement; |
| import org.checkerframework.checker.index.qual.HasSubsequence; |
| import org.checkerframework.dataflow.expression.FieldAccess; |
| import org.checkerframework.dataflow.expression.JavaExpression; |
| import org.checkerframework.framework.source.SourceChecker; |
| import org.checkerframework.framework.util.JavaExpressionParseUtil; |
| import org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException; |
| import org.checkerframework.framework.util.StringToJavaExpression; |
| import org.checkerframework.javacutil.TreeUtils; |
| |
| /** Holds information from {@link HasSubsequence} annotations. */ |
| public class Subsequence { |
| |
| /** Name of the Subsequence. */ |
| public final String array; |
| /** First index of the subsequence in the backing sequence. */ |
| public final String from; |
| /** Last index of the subsequence in the backing sequence. */ |
| public final String to; |
| |
| private Subsequence(String array, String from, String to) { |
| this.array = array; |
| this.from = from; |
| this.to = to; |
| } |
| |
| /** |
| * Returns a Subsequence representing the {@link HasSubsequence} annotation on the declaration of |
| * {@code varTree} or null if there is not such annotation. |
| * |
| * <p>Note that this method does not standardize or viewpoint adapt the arguments to the |
| * annotation, unlike getSubsequenceFromReceiver. |
| * |
| * @param varTree some tree |
| * @param factory an AnnotatedTypeFactory |
| * @return null or a new Subsequence from the declaration of {@code varTree} |
| */ |
| public static Subsequence getSubsequenceFromTree( |
| Tree varTree, BaseAnnotatedTypeFactoryForIndexChecker factory) { |
| |
| if (!(varTree.getKind() == Tree.Kind.IDENTIFIER |
| || varTree.getKind() == Tree.Kind.MEMBER_SELECT |
| || varTree.getKind() == Tree.Kind.VARIABLE)) { |
| return null; |
| } |
| |
| Element element = TreeUtils.elementFromTree(varTree); |
| AnnotationMirror hasSub = factory.getDeclAnnotation(element, HasSubsequence.class); |
| return createSubsequence(hasSub, factory); |
| } |
| |
| /** |
| * Factory method to create a representation of a subsequence. |
| * |
| * @param hasSub {@link HasSubsequence} annotation or null |
| * @param factory the type factory |
| * @return a new Subsequence object representing {@code hasSub} or null |
| */ |
| private static Subsequence createSubsequence( |
| AnnotationMirror hasSub, BaseAnnotatedTypeFactoryForIndexChecker factory) { |
| if (hasSub == null) { |
| return null; |
| } |
| String from = factory.hasSubsequenceFromValue(hasSub); |
| String to = factory.hasSubsequenceToValue(hasSub); |
| String array = factory.hasSubsequenceSubsequenceValue(hasSub); |
| |
| return new Subsequence(array, from, to); |
| } |
| |
| /** |
| * Returns a Subsequence representing the {@link HasSubsequence} annotation on the declaration of |
| * {@code rec} or null if there is not such annotation. |
| * |
| * @param expr some tree |
| * @param factory an AnnotatedTypeFactory |
| * @return null or a new Subsequence from the declaration of {@code varTree} |
| */ |
| public static Subsequence getSubsequenceFromReceiver( |
| JavaExpression expr, BaseAnnotatedTypeFactoryForIndexChecker factory) { |
| if (!(expr instanceof FieldAccess)) { |
| return null; |
| } |
| |
| FieldAccess fa = (FieldAccess) expr; |
| VariableElement element = fa.getField(); |
| AnnotationMirror hasSub = factory.getDeclAnnotation(element, HasSubsequence.class); |
| if (hasSub == null) { |
| return null; |
| } |
| |
| String array = |
| standardizeAndViewpointAdapt( |
| factory.hasSubsequenceSubsequenceValue(hasSub), fa, factory.getChecker()); |
| String from = |
| standardizeAndViewpointAdapt( |
| factory.hasSubsequenceFromValue(hasSub), fa, factory.getChecker()); |
| String to = |
| standardizeAndViewpointAdapt( |
| factory.hasSubsequenceToValue(hasSub), fa, factory.getChecker()); |
| |
| return new Subsequence(array, from, to); |
| } |
| |
| /** |
| * Helper function to standardize and viewpoint-adapt a string at a given field access. Wraps |
| * {@link JavaExpressionParseUtil#parse}. If a parse exception is encountered, this returns its |
| * argument. |
| * |
| * @param s a Java expression string |
| * @param fieldAccess the field access |
| * @param checker used to parse the expression |
| * @return the argument, standardized and viewpoint-adapted |
| */ |
| private static String standardizeAndViewpointAdapt( |
| String s, FieldAccess fieldAccess, SourceChecker checker) { |
| JavaExpression parseResult; |
| try { |
| parseResult = StringToJavaExpression.atFieldDecl(s, fieldAccess.getField(), checker); |
| } catch (JavaExpressionParseException e) { |
| return s; |
| } |
| |
| return parseResult.atFieldAccess(fieldAccess.getReceiver()).toString(); |
| } |
| |
| /** |
| * Returns the additive inverse of the given String. That is, if the result of this method is some |
| * String s', then s + s' == 0 will evaluate to true. Note that this relies on the fact that the |
| * JavaExpression parser cannot parse multiplication, so it naively just changes '-' to '+' and |
| * vice-versa. |
| * |
| * @param s a Java expression string |
| * @return the negated string |
| */ |
| public static String negateString(String s) { |
| String original = s; |
| String result = ""; |
| if (!original.startsWith("-")) { |
| result += '-'; |
| } |
| for (int i = 0; i < original.length(); i++) { |
| char c = original.charAt(i); |
| if (c == '-') { |
| result += '+'; |
| } else if (c == '+') { |
| result += '-'; |
| } else { |
| result += c; |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| return "Subsequence(array=" + this.array + ", from=" + this.from + ", to=" + this.to + ")"; |
| } |
| } |