blob: b00e0942fc9309d5c22961ff2f638b85cf18a856 [file] [log] [blame]
package org.checkerframework.checker.index;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import org.checkerframework.checker.index.qual.LengthOf;
import org.checkerframework.dataflow.cfg.node.MethodAccessNode;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.TreeUtils;
/**
* Given a Tree or other construct, this class has methods to query whether it is a particular
* method call.
*/
public class IndexMethodIdentifier {
/** The {@code java.lang.Math#random()} method. */
private final ExecutableElement mathRandom;
/** The {@code java.util.Random#nextDouble()} method. */
private final ExecutableElement randomNextDouble;
/** The {@code java.util.Random#nextInt()} method. */
private final ExecutableElement randomNextInt;
/** The {@code java.lang.String#length()} method. */
private final ExecutableElement stringLength;
/** The {@code java.lang.Math#min()} methods. */
private final List<ExecutableElement> mathMinMethods;
/** The {@code java.lang.Math#max()} methods. */
private final List<ExecutableElement> mathMaxMethods;
/** The LengthOf.value argument/element. */
private final ExecutableElement lengthOfValueElement;
/** The type factory. */
private final AnnotatedTypeFactory factory;
public IndexMethodIdentifier(AnnotatedTypeFactory factory) {
this.factory = factory;
ProcessingEnvironment processingEnv = factory.getProcessingEnv();
mathRandom = TreeUtils.getMethod("java.lang.Math", "random", 0, processingEnv);
randomNextDouble = TreeUtils.getMethod("java.util.Random", "nextDouble", 0, processingEnv);
randomNextInt = TreeUtils.getMethod("java.util.Random", "nextInt", 1, processingEnv);
stringLength = TreeUtils.getMethod("java.lang.String", "length", 0, processingEnv);
mathMinMethods = TreeUtils.getMethods("java.lang.Math", "min", 2, processingEnv);
mathMaxMethods = TreeUtils.getMethods("java.lang.Math", "max", 2, processingEnv);
lengthOfValueElement = TreeUtils.getMethod(LengthOf.class, "value", 0, processingEnv);
}
/** Returns true iff the argument is an invocation of Math.min. */
public boolean isMathMin(Tree methodTree) {
ProcessingEnvironment processingEnv = factory.getProcessingEnv();
return TreeUtils.isMethodInvocation(methodTree, mathMinMethods, processingEnv);
}
/** Returns true iff the argument is an invocation of Math.max. */
public boolean isMathMax(Tree methodTree) {
ProcessingEnvironment processingEnv = factory.getProcessingEnv();
return TreeUtils.isMethodInvocation(methodTree, mathMaxMethods, processingEnv);
}
/** Returns true iff the argument is an invocation of Math.random(). */
public boolean isMathRandom(Tree tree, ProcessingEnvironment processingEnv) {
return TreeUtils.isMethodInvocation(tree, mathRandom, processingEnv);
}
/** Returns true iff the argument is an invocation of Random.nextDouble(). */
public boolean isRandomNextDouble(Tree tree, ProcessingEnvironment processingEnv) {
return TreeUtils.isMethodInvocation(tree, randomNextDouble, processingEnv);
}
/** Returns true iff the argument is an invocation of Random.nextInt(). */
public boolean isRandomNextInt(Tree tree, ProcessingEnvironment processingEnv) {
return TreeUtils.isMethodInvocation(tree, randomNextInt, processingEnv);
}
/**
* Returns true if {@code tree} is an invocation of a method that returns the length of "this"
*
* @param tree a tree
* @return true if {@code tree} is an invocation of a method that returns the length of {@code
* this}
*/
public boolean isLengthOfMethodInvocation(Tree tree) {
if (tree.getKind() != Kind.METHOD_INVOCATION) {
return false;
}
return isLengthOfMethodInvocation(TreeUtils.elementFromUse((MethodInvocationTree) tree));
}
/**
* Returns true if {@code tree} evaluates to the length of "this". This might be a call to
* String,length, or a method annotated with @LengthOf.
*
* @return true if {@code tree} evaluates to the length of "this"
*/
public boolean isLengthOfMethodInvocation(ExecutableElement ele) {
if (stringLength.equals(ele)) {
// TODO: Why not just annotate String.length with @LengthOf and thus eliminate the
// special case in this method's implementation?
return true;
}
AnnotationMirror lengthOfAnno = factory.getDeclAnnotation(ele, LengthOf.class);
if (lengthOfAnno == null) {
return false;
}
AnnotationValue lengthOfValue = lengthOfAnno.getElementValues().get(lengthOfValueElement);
return AnnotationUtils.annotationValueContains(lengthOfValue, "this");
}
/**
* Returns true if {@code node} is an invocation of a method that returns the length of {@code
* this}
*
* @param node a node
* @return true if {@code node} is an invocation of a method that returns the length of {@code
* this}
*/
public boolean isLengthOfMethodInvocation(Node node) {
if (node instanceof MethodInvocationNode) {
MethodInvocationNode methodInvocationNode = (MethodInvocationNode) node;
MethodAccessNode methodAccessNode = methodInvocationNode.getTarget();
ExecutableElement ele = methodAccessNode.getMethod();
return isLengthOfMethodInvocation(ele);
}
return false;
}
}