blob: 2ef1fd6e252dc627c810bfc13fa4c318d5aab7a4 [file] [log] [blame]
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 + ")";
}
}