| package org.checkerframework.framework.util; |
| |
| import com.sun.source.tree.LambdaExpressionTree; |
| import com.sun.source.tree.MemberSelectTree; |
| import com.sun.source.tree.MethodInvocationTree; |
| import com.sun.source.tree.MethodTree; |
| import com.sun.source.tree.NewClassTree; |
| import com.sun.source.tree.VariableTree; |
| import com.sun.source.util.TreePath; |
| import java.util.ArrayList; |
| import java.util.List; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.TypeMirror; |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.checkerframework.dataflow.cfg.node.MethodInvocationNode; |
| import org.checkerframework.dataflow.expression.FormalParameter; |
| import org.checkerframework.dataflow.expression.JavaExpression; |
| import org.checkerframework.dataflow.expression.LocalVariable; |
| import org.checkerframework.dataflow.expression.ThisReference; |
| import org.checkerframework.dataflow.expression.ViewpointAdaptJavaExpression; |
| import org.checkerframework.framework.source.SourceChecker; |
| import org.checkerframework.framework.util.JavaExpressionParseUtil.JavaExpressionParseException; |
| import org.checkerframework.javacutil.BugInCF; |
| import org.checkerframework.javacutil.ElementUtils; |
| import org.checkerframework.javacutil.TreePathUtil; |
| import org.checkerframework.javacutil.TreeUtils; |
| |
| /** |
| * This interface is both a functional interface, see {@link #toJavaExpression(String)}, and also a |
| * collection of static methods that convert a string to a JavaExpression at common locations. |
| * |
| * <p>Some conversion routines merely do parsing. Other conversion routines parse and then transform |
| * the result of parsing into another {@code JavaExpression}; for all the static methods, this |
| * transformation is viewpoint-adaptation. |
| * |
| * <p>To parse a string "at a location" means to parse it as if it were written in an annotation |
| * that is written on that location. |
| */ |
| @FunctionalInterface |
| public interface StringToJavaExpression { |
| |
| /** |
| * Convert a string to a {@link JavaExpression}. Returns {@code null} if no conversion exists. |
| * |
| * <p>Conversion includes parsing {@code stringExpr} to a {@code JavaExpression} and optionally |
| * transforming the result of parsing into another {@code JavaExpression}. An example of |
| * transformation is viewpoint adaptation. |
| * |
| * @param stringExpr a Java expression |
| * @return a {@code JavaExpression} or {@code null} if no conversion from {@code stringExpr} |
| * exists |
| * @throws JavaExpressionParseException if {@code stringExpr} cannot be parsed to a {@code |
| * JavaExpression} |
| */ |
| @Nullable JavaExpression toJavaExpression(String stringExpr) throws JavaExpressionParseException; |
| |
| /** |
| * Parses a string to a {@link JavaExpression} as if it were written at {@code typeElement}. |
| * |
| * @param expression a Java expression to parse |
| * @param typeElement type element at which {@code expression} is parsed |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atTypeDecl( |
| String expression, TypeElement typeElement, SourceChecker checker) |
| throws JavaExpressionParseException { |
| ThisReference thisReference = new ThisReference(typeElement.asType()); |
| List<FormalParameter> parameters = null; |
| return JavaExpressionParseUtil.parse( |
| expression, |
| typeElement.asType(), |
| thisReference, |
| parameters, |
| null, |
| checker.getPathToCompilationUnit(), |
| checker.getProcessingEnvironment()); |
| } |
| |
| /** |
| * Parses a string to a {@link JavaExpression} as if it were written at {@code fieldElement}. |
| * |
| * @param expression a Java expression to parse |
| * @param fieldElement variable element at which {@code expression} is parsed |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atFieldDecl( |
| String expression, VariableElement fieldElement, SourceChecker checker) |
| throws JavaExpressionParseException { |
| TypeMirror enclosingType = ElementUtils.enclosingTypeElement(fieldElement).asType(); |
| ThisReference thisReference; |
| if (ElementUtils.isStatic(fieldElement)) { |
| // Can't use "this" on a static fieldElement |
| thisReference = null; |
| } else { |
| thisReference = new ThisReference(enclosingType); |
| } |
| List<FormalParameter> parameters = null; |
| return JavaExpressionParseUtil.parse( |
| expression, |
| enclosingType, |
| thisReference, |
| parameters, |
| null, |
| checker.getPathToCompilationUnit(), |
| checker.getProcessingEnvironment()); |
| } |
| |
| /** |
| * Parses a string to a {@link JavaExpression} as if it were written at {@code method}. The |
| * returned {@code JavaExpression} uses {@link FormalParameter}s to represent parameters. Use |
| * {@link #atMethodBody(String, MethodTree, SourceChecker)} if parameters should be {@link |
| * LocalVariable}s instead. |
| * |
| * @param expression a Java expression to parse |
| * @param method method element at which {@code expression} is parsed |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atMethodDecl( |
| String expression, ExecutableElement method, SourceChecker checker) |
| throws JavaExpressionParseException { |
| TypeMirror enclosingType = ElementUtils.enclosingTypeElement(method).asType(); |
| ThisReference thisReference; |
| if (ElementUtils.isStatic(method)) { |
| // Can't use "this" on a static method |
| thisReference = null; |
| } else { |
| thisReference = new ThisReference(enclosingType); |
| } |
| List<FormalParameter> parameters = JavaExpression.getFormalParameters(method); |
| return JavaExpressionParseUtil.parse( |
| expression, |
| enclosingType, |
| thisReference, |
| parameters, |
| null, |
| checker.getPathToCompilationUnit(), |
| checker.getProcessingEnvironment()); |
| } |
| |
| /** |
| * Parses a string to a {@link JavaExpression} as if it were written at {@code methodTree}. The |
| * returned {@code JavaExpression} uses {@link LocalVariable}s to represent parameters. Use {@link |
| * #atMethodDecl(String, ExecutableElement, SourceChecker)} if parameters should be {@link |
| * FormalParameter}s instead. |
| * |
| * @param expression a Java expression to parse |
| * @param methodTree method declaration tree at which {@code expression} is parsed |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atMethodBody( |
| String expression, MethodTree methodTree, SourceChecker checker) |
| throws JavaExpressionParseException { |
| ExecutableElement ee = TreeUtils.elementFromDeclaration(methodTree); |
| JavaExpression javaExpr = StringToJavaExpression.atMethodDecl(expression, ee, checker); |
| return javaExpr.atMethodBody(methodTree); |
| } |
| |
| /** |
| * Parses a string as if it were written at the declaration of the invoked method and then |
| * viewpoint-adapts the result to the call site. |
| * |
| * @param expression a Java expression to parse |
| * @param methodInvocationTree method invocation tree |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atMethodInvocation( |
| String expression, MethodInvocationTree methodInvocationTree, SourceChecker checker) |
| throws JavaExpressionParseException { |
| ExecutableElement ee = TreeUtils.elementFromUse(methodInvocationTree); |
| JavaExpression javaExpr = StringToJavaExpression.atMethodDecl(expression, ee, checker); |
| return javaExpr.atMethodInvocation(methodInvocationTree); |
| } |
| |
| /** |
| * Parses a string as if it were written at the declaration of the invoked method and then |
| * viewpoint-adapts the result to the call site. |
| * |
| * @param expression a Java expression to parse |
| * @param methodInvocationNode method invocation node |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atMethodInvocation( |
| String expression, MethodInvocationNode methodInvocationNode, SourceChecker checker) |
| throws JavaExpressionParseException { |
| ExecutableElement ee = TreeUtils.elementFromUse(methodInvocationNode.getTree()); |
| JavaExpression javaExpr = StringToJavaExpression.atMethodDecl(expression, ee, checker); |
| return javaExpr.atMethodInvocation(methodInvocationNode); |
| } |
| |
| /** |
| * Parses a string as if it were written at the declaration of the invoked constructor and then |
| * viewpoint-adapts the result to the call site. |
| * |
| * @param expression a Java expression to parse |
| * @param newClassTree constructor invocation |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atConstructorInvocation( |
| String expression, NewClassTree newClassTree, SourceChecker checker) |
| throws JavaExpressionParseException { |
| ExecutableElement ee = TreeUtils.elementFromUse(newClassTree); |
| JavaExpression javaExpr = StringToJavaExpression.atMethodDecl(expression, ee, checker); |
| return javaExpr.atConstructorInvocation(newClassTree); |
| } |
| |
| /** |
| * uf found Parses a string as if it were written at the declaration of the field and then |
| * viewpoint-adapts the result to the use. |
| * |
| * @param expression a Java expression to parse |
| * @param fieldAccess the field access tree |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atFieldAccess( |
| String expression, MemberSelectTree fieldAccess, SourceChecker checker) |
| throws JavaExpressionParseException { |
| |
| Element ele = TreeUtils.elementFromUse(fieldAccess); |
| if (ele.getKind() != ElementKind.FIELD && ele.getKind() != ElementKind.ENUM_CONSTANT) { |
| throw new BugInCF("Expected a field, but found %s for %s", ele.getKind(), fieldAccess); |
| } |
| VariableElement fieldEle = (VariableElement) ele; |
| JavaExpression receiver = JavaExpression.fromTree(fieldAccess.getExpression()); |
| JavaExpression javaExpr = StringToJavaExpression.atFieldDecl(expression, fieldEle, checker); |
| return javaExpr.atFieldAccess(receiver); |
| } |
| |
| /** |
| * Parses a string as if it were written at one of the parameters of {@code lambdaTree}. |
| * Parameters of the lambda are expressed as {@link LocalVariable}s. |
| * |
| * @param expression a Java expression to parse |
| * @param lambdaTree the lambda tree |
| * @param parentPath path to the parent of {@code lambdaTree}; required because the expression can |
| * reference final local variables of the enclosing method |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atLambdaParameter( |
| String expression, |
| LambdaExpressionTree lambdaTree, |
| TreePath parentPath, |
| SourceChecker checker) |
| throws JavaExpressionParseException { |
| |
| TypeMirror enclosingType = TreeUtils.typeOf(TreePathUtil.enclosingClass(parentPath)); |
| JavaExpression receiver = JavaExpression.getPseudoReceiver(parentPath, enclosingType); |
| // If receiver isn't a ThisReference, then the lambda is in a static context and "this" |
| // cannot be referenced in the expression. |
| ThisReference thisReference = |
| receiver instanceof ThisReference ? (ThisReference) receiver : null; |
| List<JavaExpression> paramsAsLocals = new ArrayList<>(lambdaTree.getParameters().size()); |
| List<FormalParameter> parameters = new ArrayList<>(lambdaTree.getParameters().size()); |
| int oneBasedIndex = 1; |
| for (VariableTree arg : lambdaTree.getParameters()) { |
| LocalVariable param = (LocalVariable) JavaExpression.fromVariableTree(arg); |
| paramsAsLocals.add(param); |
| parameters.add(new FormalParameter(oneBasedIndex, (VariableElement) param.getElement())); |
| oneBasedIndex++; |
| } |
| |
| JavaExpression javaExpr = |
| JavaExpressionParseUtil.parse( |
| expression, |
| enclosingType, |
| thisReference, |
| parameters, |
| parentPath, |
| checker.getPathToCompilationUnit(), |
| checker.getProcessingEnvironment()); |
| return ViewpointAdaptJavaExpression.viewpointAdapt(javaExpr, paramsAsLocals); |
| } |
| |
| /** |
| * Parses a string as if it were written at {@code localVarPath}. |
| * |
| * @param expression a Java expression to parse |
| * @param localVarPath location at which {@code expression} is parsed |
| * @param checker checker used to get the {@link |
| * javax.annotation.processing.ProcessingEnvironment} and current {@link |
| * com.sun.source.tree.CompilationUnitTree} |
| * @return a {@code JavaExpression} for {@code expression} |
| * @throws JavaExpressionParseException if {@code expression} cannot be parsed |
| */ |
| static JavaExpression atPath(String expression, TreePath localVarPath, SourceChecker checker) |
| throws JavaExpressionParseException { |
| |
| TypeMirror enclosingType = TreeUtils.typeOf(TreePathUtil.enclosingClass(localVarPath)); |
| ThisReference thisReference = |
| TreePathUtil.isTreeInStaticScope(localVarPath) ? null : new ThisReference(enclosingType); |
| |
| MethodTree methodTree = TreePathUtil.enclosingMethod(localVarPath); |
| if (methodTree == null) { |
| return JavaExpressionParseUtil.parse( |
| expression, |
| enclosingType, |
| thisReference, |
| null, |
| localVarPath, |
| checker.getPathToCompilationUnit(), |
| checker.getProcessingEnvironment()); |
| } |
| |
| ExecutableElement methodEle = TreeUtils.elementFromDeclaration(methodTree); |
| List<FormalParameter> parameters = JavaExpression.getFormalParameters(methodEle); |
| JavaExpression javaExpr = |
| JavaExpressionParseUtil.parse( |
| expression, |
| enclosingType, |
| thisReference, |
| parameters, |
| localVarPath, |
| checker.getPathToCompilationUnit(), |
| checker.getProcessingEnvironment()); |
| List<JavaExpression> paramsAsLocals = JavaExpression.getParametersAsLocalVariables(methodEle); |
| return ViewpointAdaptJavaExpression.viewpointAdapt(javaExpr, paramsAsLocals); |
| } |
| } |