package org.checkerframework.framework.util.element; import static com.sun.tools.javac.code.TargetType.CAST; import static com.sun.tools.javac.code.TargetType.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT; import static com.sun.tools.javac.code.TargetType.CONSTRUCTOR_REFERENCE; import static com.sun.tools.javac.code.TargetType.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT; import static com.sun.tools.javac.code.TargetType.EXCEPTION_PARAMETER; import static com.sun.tools.javac.code.TargetType.INSTANCEOF; import static com.sun.tools.javac.code.TargetType.LOCAL_VARIABLE; import static com.sun.tools.javac.code.TargetType.METHOD_FORMAL_PARAMETER; import static com.sun.tools.javac.code.TargetType.METHOD_INVOCATION_TYPE_ARGUMENT; import static com.sun.tools.javac.code.TargetType.METHOD_RECEIVER; import static com.sun.tools.javac.code.TargetType.METHOD_REFERENCE; import static com.sun.tools.javac.code.TargetType.METHOD_REFERENCE_TYPE_ARGUMENT; import static com.sun.tools.javac.code.TargetType.METHOD_RETURN; import static com.sun.tools.javac.code.TargetType.METHOD_TYPE_PARAMETER; import static com.sun.tools.javac.code.TargetType.METHOD_TYPE_PARAMETER_BOUND; import static com.sun.tools.javac.code.TargetType.NEW; import static com.sun.tools.javac.code.TargetType.RESOURCE_VARIABLE; import static com.sun.tools.javac.code.TargetType.THROWS; import static org.checkerframework.framework.util.element.ElementAnnotationUtil.addAnnotationsFromElement; import static org.checkerframework.framework.util.element.ElementAnnotationUtil.annotateViaTypeAnnoPosition; import static org.checkerframework.framework.util.element.ElementAnnotationUtil.partitionByTargetType; import com.sun.source.tree.LambdaExpressionTree; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import com.sun.tools.javac.code.Attribute; import com.sun.tools.javac.code.Attribute.TypeCompound; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.TargetType; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.VariableElement; import org.checkerframework.framework.type.AnnotatedTypeFactory; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.type.ElementAnnotationApplier; import org.checkerframework.javacutil.ErrorReporter; import org.checkerframework.javacutil.Pair; /** Adds annotations to one formal parameter of a method or lambda within a method. */ public class ParamApplier extends IndexedElementAnnotationApplier { public static void apply( AnnotatedTypeMirror type, Element element, AnnotatedTypeFactory typeFactory) { new ParamApplier(type, element, typeFactory).extractAndApply(); } public static int RECEIVER_PARAM_INDEX = Integer.MIN_VALUE; public static boolean accepts(final AnnotatedTypeMirror type, final Element element) { return element.getKind() == ElementKind.PARAMETER; } private final Symbol.MethodSymbol enclosingMethod; private final boolean isLambdaParam; private final Integer lambdaParamIndex; private final LambdaExpressionTree lambdaTree; ParamApplier(AnnotatedTypeMirror type, Element element, AnnotatedTypeFactory typeFactory) { super(type, element); enclosingMethod = getParentMethod(element); if (enclosingMethod.getKind() != ElementKind.INSTANCE_INIT && enclosingMethod.getKind() != ElementKind.STATIC_INIT && enclosingMethod.getParameters().contains(element)) { lambdaTree = null; isLambdaParam = false; lambdaParamIndex = null; } else { Pair<VariableTree, LambdaExpressionTree> paramToEnclosingLambda = ElementAnnotationApplier.getParamAndLambdaTree( (VariableElement) element, typeFactory); if (paramToEnclosingLambda != null) { VariableTree paramDecl = paramToEnclosingLambda.first; lambdaTree = paramToEnclosingLambda.second; isLambdaParam = true; lambdaParamIndex = lambdaTree.getParameters().indexOf(paramDecl); } else { lambdaTree = null; isLambdaParam = false; lambdaParamIndex = null; } } } /** * @return the index of element its parent method's parameter list. Integer.MIN_VALUE if the * element is the receiver parameter. */ @Override public int getElementIndex() { if (isLambdaParam) { return lambdaParamIndex; } if (isReceiver(element)) { return RECEIVER_PARAM_INDEX; } final int paramIndex = enclosingMethod.getParameters().indexOf(element); if (paramIndex == -1) { ErrorReporter.errorAbort( "Could not find parameter Element in parameter list! " + "Parameter( " + element + " ) Parent ( " + enclosingMethod + " ) "); } return paramIndex; } /** @return the parameter index of anno's TypeAnnotationPosition */ @Override public int getTypeCompoundIndex(Attribute.TypeCompound anno) { return anno.getPosition().parameter_index; } /** @return {TargetType.METHOD_FORMAL_PARAMETER, TargetType.METHOD_RECEIVER} */ @Override protected TargetType[] annotatedTargets() { return new TargetType[] {METHOD_FORMAL_PARAMETER, METHOD_RECEIVER}; } /** @return any annotation TargetType that can be found on a method */ @Override protected TargetType[] validTargets() { return new TargetType[] { METHOD_FORMAL_PARAMETER, METHOD_RETURN, THROWS, METHOD_TYPE_PARAMETER, METHOD_TYPE_PARAMETER_BOUND, LOCAL_VARIABLE, RESOURCE_VARIABLE, EXCEPTION_PARAMETER, NEW, CAST, INSTANCEOF, METHOD_INVOCATION_TYPE_ARGUMENT, CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT, METHOD_REFERENCE, CONSTRUCTOR_REFERENCE, METHOD_REFERENCE_TYPE_ARGUMENT, CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT }; } /** @return the TypeCompounds (annotations) of the enclosing method for this parameter */ @Override protected Iterable<Attribute.TypeCompound> getRawTypeAttributes() { return enclosingMethod.getRawTypeAttributes(); } @Override protected Map<TargetClass, List<TypeCompound>> sift( Iterable<Attribute.TypeCompound> typeCompounds) { // this will sift out the annotations that do not have the right position index final Map<TargetClass, List<Attribute.TypeCompound>> targetClassToAnnos = super.sift(typeCompounds); final List<Attribute.TypeCompound> targeted = targetClassToAnnos.get(TargetClass.TARGETED); final List<Attribute.TypeCompound> valid = targetClassToAnnos.get(TargetClass.VALID); // if this is a lambdaParam, filter out from targeted those annos that apply to method formal parameters // if this is a method formal param, filter out from targeted those annos that apply to lambdas int i = 0; while (i < targeted.size()) { final Tree onLambda = targeted.get(i).position.onLambda; if (onLambda == null) { if (!isLambdaParam) { ++i; } else { valid.add(targeted.remove(i)); } } else { if (onLambda.equals(this.lambdaTree)) { ++i; } else { valid.add(targeted.remove(i)); } } } return targetClassToAnnos; } /** * @param targeted type compounds with formal method parameter target types with parameter_index * == getIndex */ @Override protected void handleTargeted(final List<Attribute.TypeCompound> targeted) { final List<TypeCompound> formalParams = new ArrayList<>(); Map<TargetType, List<TypeCompound>> targetToAnnos = partitionByTargetType(targeted, formalParams, METHOD_RECEIVER); if (isReceiver(element)) { annotateViaTypeAnnoPosition(type, targetToAnnos.get(METHOD_RECEIVER)); } else { annotateViaTypeAnnoPosition(type, formalParams); } } /** @return true if element represents the receiver parameter of a method */ private boolean isReceiver(final Element element) { return element.getKind() == ElementKind.PARAMETER && element.getSimpleName().contentEquals("this"); } @Override protected boolean isAccepted() { return accepts(type, element); } /** * Return the enclosing MethodSymbol of the given element, throwing an exception of the symbol's * enclosing element is not a MethodSymbol * * @param methodChildElem some element that is a child of a method typeDeclaration (e.g. a * parameter or return type) * @return the MethodSymbol of the method containing methodChildElem */ public static Symbol.MethodSymbol getParentMethod(final Element methodChildElem) { if (!(methodChildElem.getEnclosingElement() instanceof Symbol.MethodSymbol)) { throw new RuntimeException( "Element is not a direct child of a MethodSymbol. Element ( " + methodChildElem + " parent ( " + methodChildElem.getEnclosingElement() + " ) "); } return (Symbol.MethodSymbol) methodChildElem.getEnclosingElement(); } @Override public void extractAndApply() { addAnnotationsFromElement(type, element.getAnnotationMirrors()); super.extractAndApply(); } }