use of org.checkerframework.framework.type.AnnotatedTypeMirror in project checker-framework by typetools.
the class LockVisitor method visitMethodInvocation.
/**
* When visiting a method invocation, issue an error if the side effect annotation on the called
* method causes the side effect guarantee of the enclosing method to be violated. For example,
* a method annotated with @ReleasesNoLocks may not call a method annotated
* with @MayReleaseLocks. Also check that matching @GuardSatisfied(index) on a method's formal
* receiver/parameters matches those in corresponding locations on the method call site.
*
* @param node the MethodInvocationTree of the method call being visited
*/
@Override
public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
ExecutableElement methodElement = TreeUtils.elementFromUse(node);
SideEffectAnnotation seaOfInvokedMethod = atypeFactory.methodSideEffectAnnotation(methodElement, false);
MethodTree enclosingMethod = TreeUtils.enclosingMethod(atypeFactory.getPath(node));
ExecutableElement enclosingMethodElement = null;
if (enclosingMethod != null) {
enclosingMethodElement = TreeUtils.elementFromDeclaration(enclosingMethod);
}
if (enclosingMethodElement != null) {
SideEffectAnnotation seaOfContainingMethod = atypeFactory.methodSideEffectAnnotation(enclosingMethodElement, false);
if (seaOfInvokedMethod.isWeakerThan(seaOfContainingMethod)) {
checker.report(Result.failure("method.guarantee.violated", seaOfContainingMethod.getNameOfSideEffectAnnotation(), enclosingMethodElement.toString(), methodElement.toString(), seaOfInvokedMethod.getNameOfSideEffectAnnotation()), node);
}
}
if (methodElement != null) {
// Handle releasing of explicit locks. Verify that the lock expression is effectively
// final.
ExpressionTree recvTree = getReceiverTree(node);
ensureReceiverOfExplicitUnlockCallIsEffectivelyFinal(node, methodElement, recvTree);
// Handle acquiring of explicit locks. Verify that the lock expression is effectively
// final.
// If the method causes expression "this" or "#1" to be locked, verify that those
// expressions are effectively final. TODO: generalize to any expression. This is
// currently designed only to support methods in ReentrantLock and
// ReentrantReadWriteLock (which use the "this" expression), as well as Thread.holdsLock
// (which uses the "#1" expression).
AnnotationMirror ensuresLockHeldAnno = atypeFactory.getDeclAnnotation(methodElement, EnsuresLockHeld.class);
List<String> expressions = new ArrayList<>();
if (ensuresLockHeldAnno != null) {
expressions.addAll(AnnotationUtils.getElementValueArray(ensuresLockHeldAnno, "value", String.class, false));
}
AnnotationMirror ensuresLockHeldIfAnno = atypeFactory.getDeclAnnotation(methodElement, EnsuresLockHeldIf.class);
if (ensuresLockHeldIfAnno != null) {
expressions.addAll(AnnotationUtils.getElementValueArray(ensuresLockHeldIfAnno, "expression", String.class, false));
}
for (String expr : expressions) {
if (expr.equals("this")) {
// are also final. So nothing to be checked for them.
if (recvTree != null) {
ensureExpressionIsEffectivelyFinal(recvTree);
}
} else if (expr.equals("#1")) {
ExpressionTree firstParameter = node.getArguments().get(0);
if (firstParameter != null) {
ensureExpressionIsEffectivelyFinal(firstParameter);
}
}
}
}
// Check that matching @GuardSatisfied(index) on a method's formal receiver/parameters
// matches
// those in corresponding locations on the method call site.
Pair<AnnotatedExecutableType, List<AnnotatedTypeMirror>> mfuPair = atypeFactory.methodFromUse(node);
AnnotatedExecutableType invokedMethod = mfuPair.first;
List<AnnotatedTypeMirror> requiredArgs = AnnotatedTypes.expandVarArgs(atypeFactory, invokedMethod, node.getArguments());
// Index on @GuardSatisfied at each location. -1 when no @GuardSatisfied annotation was
// present.
// Note that @GuardSatisfied with no index is normally represented as having index -1.
// We would like to ignore a @GuardSatisfied with no index for these purposes, so if it is
// encountered we leave its index as -1.
// The first element of the array is reserved for the receiver.
int[] guardSatisfiedIndex = // + 1 for the receiver parameter type
new int[requiredArgs.size() + 1];
// Retrieve receiver types from method definition and method call
guardSatisfiedIndex[0] = -1;
AnnotatedTypeMirror methodDefinitionReceiver = null;
AnnotatedTypeMirror methodCallReceiver = null;
ExecutableElement invokedMethodElement = invokedMethod.getElement();
if (!ElementUtils.isStatic(invokedMethodElement) && invokedMethod.getElement().getKind() != ElementKind.CONSTRUCTOR) {
methodDefinitionReceiver = invokedMethod.getReceiverType();
if (methodDefinitionReceiver != null && methodDefinitionReceiver.hasAnnotation(checkerGuardSatisfiedClass)) {
guardSatisfiedIndex[0] = atypeFactory.getGuardSatisfiedIndex(methodDefinitionReceiver);
methodCallReceiver = atypeFactory.getReceiverType(node);
}
}
for (int i = 0; i < requiredArgs.size(); i++) {
guardSatisfiedIndex[i + 1] = -1;
AnnotatedTypeMirror arg = requiredArgs.get(i);
if (arg.hasAnnotation(checkerGuardSatisfiedClass)) {
guardSatisfiedIndex[i + 1] = atypeFactory.getGuardSatisfiedIndex(arg);
}
}
// Combine all of the actual parameters into one list of AnnotationMirrors
ArrayList<AnnotationMirror> passedArgAnnotations = new ArrayList<>(guardSatisfiedIndex.length);
passedArgAnnotations.add(methodCallReceiver == null ? null : methodCallReceiver.getAnnotationInHierarchy(atypeFactory.GUARDEDBYUNKNOWN));
for (ExpressionTree tree : node.getArguments()) {
passedArgAnnotations.add(atypeFactory.getAnnotatedType(tree).getAnnotationInHierarchy(atypeFactory.GUARDEDBYUNKNOWN));
}
for (int i = 0; i < guardSatisfiedIndex.length; i++) {
if (guardSatisfiedIndex[i] != -1) {
for (int j = i + 1; j < guardSatisfiedIndex.length; j++) {
if (guardSatisfiedIndex[i] == guardSatisfiedIndex[j]) {
// The @GuardedBy/@GuardSatisfied/@GuardedByUnknown/@GuardedByBottom
// annotations must be identical on the corresponding actual parameters.
AnnotationMirror arg1Anno = passedArgAnnotations.get(i);
AnnotationMirror arg2Anno = passedArgAnnotations.get(j);
if (arg1Anno != null && arg2Anno != null) {
boolean bothAreGSwithNoIndex = false;
if (AnnotationUtils.areSameByClass(arg1Anno, checkerGuardSatisfiedClass) && AnnotationUtils.areSameByClass(arg2Anno, checkerGuardSatisfiedClass)) {
if (atypeFactory.getGuardSatisfiedIndex(arg1Anno) == -1 && atypeFactory.getGuardSatisfiedIndex(arg2Anno) == -1) {
// Generally speaking, two @GuardSatisfied annotations with no
// index are incomparable.
// TODO: If they come from the same variable, they are
// comparable. Fix and add a test case.
bothAreGSwithNoIndex = true;
}
}
if (bothAreGSwithNoIndex || !(atypeFactory.getQualifierHierarchy().isSubtype(arg1Anno, arg2Anno) || atypeFactory.getQualifierHierarchy().isSubtype(arg2Anno, arg1Anno))) {
// TODO: allow these strings to be localized
String formalParam1 = null;
if (i == 0) {
formalParam1 = "The receiver type";
} else {
formalParam1 = "Parameter #" + // i, not i-1, so the index is 1-based
i;
}
String formalParam2 = // j, not j-1, so the index is 1-based
"parameter #" + j;
checker.report(Result.failure("guardsatisfied.parameters.must.match", formalParam1, formalParam2, invokedMethod.toString(), guardSatisfiedIndex[i], arg1Anno, arg2Anno), node);
}
}
}
}
}
}
return super.visitMethodInvocation(node, p);
}
use of org.checkerframework.framework.type.AnnotatedTypeMirror in project checker-framework by typetools.
the class LockVisitor method visitVariable.
@Override
public Void visitVariable(VariableTree node, Void p) {
// visit a variable declaration
// A user may not annotate a primitive type, a boxed primitive type or a String
// with any qualifier from the @GuardedBy hierarchy.
// They are immutable, so there is no need to guard them.
TypeMirror tm = TreeUtils.typeOf(node);
if (TypesUtils.isBoxedPrimitive(tm) || TypesUtils.isPrimitive(tm) || TypesUtils.isString(tm)) {
AnnotatedTypeMirror atm = atypeFactory.getAnnotatedType(node);
if (atm.hasExplicitAnnotationRelaxed(atypeFactory.GUARDSATISFIED) || atm.hasExplicitAnnotationRelaxed(atypeFactory.GUARDEDBY) || atm.hasExplicitAnnotation(atypeFactory.GUARDEDBYUNKNOWN) || atm.hasExplicitAnnotation(atypeFactory.GUARDEDBYBOTTOM)) {
checker.report(Result.failure("immutable.type.guardedby"), node);
}
}
issueErrorIfMoreThanOneGuardedByAnnotationPresent(node);
return super.visitVariable(node, p);
}
use of org.checkerframework.framework.type.AnnotatedTypeMirror in project checker-framework by typetools.
the class CollectionToArrayHeuristics method setComponentNullness.
/**
* Sets the nullness of the component of the array type.
*
* @param isNonNull indicates which annotation ({@code NonNull} or {@code Nullable}) should be
* inserted
* @param type the array type
*/
private void setComponentNullness(boolean isNonNull, AnnotatedTypeMirror type) {
assert type.getKind() == TypeKind.ARRAY;
AnnotatedTypeMirror compType = ((AnnotatedArrayType) type).getComponentType();
compType.replaceAnnotation(isNonNull ? atypeFactory.NONNULL : atypeFactory.NULLABLE);
}
use of org.checkerframework.framework.type.AnnotatedTypeMirror in project checker-framework by typetools.
the class KeyForPropagator method propagate.
/**
* Propagate annotations from the type arguments of one type to another. Which type is the
* source and destination of the annotations depends on the direction parameter. Only @KeyFor
* annotations are propagated and only if the type to which it would be propagated contains
* an @UnknownKeyFor or contains no key for annotations of any kind. If any of the type
* arguments are wildcards than they are ignored.
*
* <p>Note the primary annotations of subtype/supertype are not used.
*
* <p>Simple Example:
*
* <pre>{@code
* typeOf(subtype) = ArrayList<@KeyFor("a") String>
* typeOf(supertype) = List<@UnknownKeyFor String>
* direction = TO_SUPERTYPE
* }</pre>
*
* The type of supertype after propagate would be: {@code List<@KeyFor("a") String>}
*
* <p>A more complex example would be:
*
* <pre>{@code
* typeOf(subtype) = HashMap<@UnknownKeyFor String, @KeyFor("b") List<@KeyFor("c") String>>
* typeOf(supertype) = Map<@KeyFor("a") String, @KeyFor("b") List<@KeyFor("c") String>>
* direction = TO_SUBTYPE
* }</pre>
*
* The type of subtype after propagate would be: {@code HashMap<@KeyFor("a")
* String, @KeyFor("b") List<@KeyFor("c") String>>}
*/
public void propagate(final AnnotatedDeclaredType subtype, final AnnotatedDeclaredType supertype, PropagationDirection direction, final AnnotatedTypeFactory typeFactory) {
final TypeElement subtypeElement = (TypeElement) subtype.getUnderlyingType().asElement();
final TypeElement supertypeElement = (TypeElement) supertype.getUnderlyingType().asElement();
final Types types = typeFactory.getProcessingEnv().getTypeUtils();
// Note: The right hand side of this or expression will cover raw types
if (subtype.getTypeArguments().isEmpty()) {
return;
}
// In either case, there is no reason to propagate
if (supertype.getTypeArguments().isEmpty()) {
return;
}
Set<Pair<Integer, Integer>> typeParamMappings = TypeArgumentMapper.mapTypeArgumentIndices(subtypeElement, supertypeElement, types);
KeyForPropagationMerger merger = new KeyForPropagationMerger(typeFactory.getProcessingEnv());
final List<AnnotatedTypeMirror> subtypeArgs = subtype.getTypeArguments();
final List<AnnotatedTypeMirror> supertypeArgs = supertype.getTypeArguments();
for (final Pair<Integer, Integer> path : typeParamMappings) {
final AnnotatedTypeMirror subtypeArg = subtypeArgs.get(path.first);
final AnnotatedTypeMirror supertypeArg = supertypeArgs.get(path.second);
if (subtypeArg.getKind() == TypeKind.WILDCARD || supertypeArg.getKind() == TypeKind.WILDCARD) {
continue;
}
switch(direction) {
case TO_SUBTYPE:
merger.visit(supertypeArg, subtypeArg);
break;
case TO_SUPERTYPE:
merger.visit(subtypeArg, supertypeArg);
break;
case BOTH:
// note if they both have an annotation nothing will happen
merger.visit(subtypeArg, supertypeArg);
merger.visit(supertypeArg, subtypeArg);
break;
}
}
}
use of org.checkerframework.framework.type.AnnotatedTypeMirror in project checker-framework by typetools.
the class NullnessAnnotatedTypeFactory method getMethodReturnType.
@Override
public AnnotatedTypeMirror getMethodReturnType(MethodTree m, ReturnTree r) {
AnnotatedTypeMirror result = super.getMethodReturnType(m, r);
replacePolyQualifier(result, r);
return result;
}
Aggregations