use of org.checkerframework.javacutil.Pair in project checker-framework by typetools.
the class AnnotationFileParser method processFakeOverride.
/**
* Process a fake override: copy its annotations to the fake overrides part of {@code
* #annotationFileAnnos}.
*
* @param element a real element
* @param decl a fake override of the element
* @param fakeLocation where the fake override was defined
*/
private void processFakeOverride(ExecutableElement element, CallableDeclaration<?> decl, TypeElement fakeLocation) {
// This is a fresh type, which this code may side-effect.
AnnotatedExecutableType methodType = atypeFactory.getAnnotatedType(element);
// Here is a hacky solution that does not use the visitor. It just handles the return type.
// TODO: Walk the type and the declaration, copying annotations from the declaration to the
// element. I think PR #3977 has a visitor that does that, which I should use after it is
// merged.
// The annotations on the method. These include type annotations on the return type.
NodeList<AnnotationExpr> annotations = decl.getAnnotations();
annotate(methodType.getReturnType(), ((MethodDeclaration) decl).getType(), annotations, decl);
List<Pair<TypeMirror, AnnotatedTypeMirror>> l = annotationFileAnnos.fakeOverrides.computeIfAbsent(element, __ -> new ArrayList<>());
l.add(Pair.of(fakeLocation.asType(), methodType));
}
use of org.checkerframework.javacutil.Pair in project checker-framework by typetools.
the class AnnotationFileElementTypes method getFakeOverride.
/**
* Returns the method type of the most specific fake override for the given element, when used as
* a member of the given type.
*
* @param elt element for which annotations are returned
* @param receiverType the type of the class that contains member (or a subtype of it)
* @return the most specific AnnotatedTypeMirror for {@code elt} that is a fake override, or null
* if there are no fake overrides
*/
@Nullable
public AnnotatedExecutableType getFakeOverride(Element elt, AnnotatedTypeMirror receiverType) {
if (parsing) {
throw new BugInCF("parsing while calling getFakeOverride");
}
if (elt.getKind() != ElementKind.METHOD) {
return null;
}
ExecutableElement method = (ExecutableElement) elt;
// This is a list of pairs of (where defined, method type) for fake overrides. The second
// element of each pair is currently always an AnnotatedExecutableType.
List<Pair<TypeMirror, AnnotatedTypeMirror>> candidates = annotationFileAnnos.fakeOverrides.get(method);
if (candidates == null || candidates.isEmpty()) {
return null;
}
TypeMirror receiverTypeMirror = receiverType.getUnderlyingType();
// A list of fake receiver types.
List<TypeMirror> applicableClasses = new ArrayList<>();
List<TypeMirror> applicableInterfaces = new ArrayList<>();
for (Pair<TypeMirror, AnnotatedTypeMirror> candidatePair : candidates) {
TypeMirror fakeLocation = candidatePair.first;
AnnotatedExecutableType candidate = (AnnotatedExecutableType) candidatePair.second;
if (factory.types.isSameType(receiverTypeMirror, fakeLocation)) {
return candidate;
} else if (factory.types.isSubtype(receiverTypeMirror, fakeLocation)) {
TypeElement fakeElement = TypesUtils.getTypeElement(fakeLocation);
switch(fakeElement.getKind()) {
case CLASS:
case ENUM:
applicableClasses.add(fakeLocation);
break;
case INTERFACE:
case ANNOTATION_TYPE:
applicableInterfaces.add(fakeLocation);
break;
default:
throw new BugInCF("What type? %s %s %s", fakeElement.getKind(), fakeElement.getClass(), fakeElement);
}
}
}
if (applicableClasses.isEmpty() && applicableInterfaces.isEmpty()) {
return null;
}
TypeMirror fakeReceiverType = TypesUtils.mostSpecific(!applicableClasses.isEmpty() ? applicableClasses : applicableInterfaces, factory.getProcessingEnv());
if (fakeReceiverType == null) {
StringJoiner message = new StringJoiner(System.lineSeparator());
message.add(String.format("No most specific fake override found for %s with receiver %s." + " These fake overrides are applicable:", elt, receiverTypeMirror));
for (TypeMirror candidate : applicableClasses) {
message.add(" class candidate: " + candidate);
}
for (TypeMirror candidate : applicableInterfaces) {
message.add(" interface candidate: " + candidate);
}
throw new BugInCF(message.toString());
}
for (Pair<TypeMirror, AnnotatedTypeMirror> candidatePair : candidates) {
TypeMirror candidateReceiverType = candidatePair.first;
if (factory.types.isSameType(fakeReceiverType, candidateReceiverType)) {
return (AnnotatedExecutableType) candidatePair.second;
}
}
throw new BugInCF("No match for %s in %s %s %s", fakeReceiverType, candidates, applicableClasses, applicableInterfaces);
}
use of org.checkerframework.javacutil.Pair in project checker-framework by typetools.
the class TADescriptions method runDriver.
protected void runDriver(Object object) throws Exception {
int passed = 0, failed = 0;
Class<?> clazz = object.getClass();
out.println("Tests for " + clazz.getName());
// Find methods
for (Method method : clazz.getMethods()) {
List<Pair<String, TypeAnnotation.Position>> expected = expectedOf(method);
if (expected == null) {
continue;
}
if (method.getReturnType() != String.class) {
throw new IllegalArgumentException("Test method needs to return a string: " + method);
}
String testClass = PersistUtil.testClassOf(method);
try {
String compact = (String) method.invoke(object);
String fullFile = PersistUtil.wrap(compact);
ClassFile cf = PersistUtil.compileAndReturn(fullFile, testClass);
boolean ignoreConstructors = !clazz.getName().equals("Constructors");
List<TypeAnnotation> actual = ReferenceInfoUtil.extendedAnnotationsOf(cf, ignoreConstructors);
String diagnostic = String.join("; ", "Tests for " + clazz.getName(), "compact=" + compact, "fullFile=" + fullFile, "testClass=" + testClass);
ReferenceInfoUtil.compare(expected, actual, cf, diagnostic);
out.println("PASSED: " + method.getName());
++passed;
} catch (Throwable e) {
out.println("FAILED: " + method.getName());
out.println(" " + e);
++failed;
}
}
out.println();
int total = passed + failed;
out.println(total + " total tests: " + passed + " PASSED, " + failed + " FAILED");
out.flush();
if (failed != 0) {
throw new RuntimeException(failed + " tests failed");
}
}
use of org.checkerframework.javacutil.Pair in project checker-framework by typetools.
the class CFAbstractStore method isMonotonicUpdate.
/**
* Return true if fieldAcc is an update of a monotonic qualifier to its target qualifier.
* (e.g. @MonotonicNonNull to @NonNull). Always returns false if {@code sequentialSemantics} is
* true.
*
* @return true if fieldAcc is an update of a monotonic qualifier to its target qualifier
* (e.g. @MonotonicNonNull to @NonNull)
*/
protected boolean isMonotonicUpdate(FieldAccess fieldAcc, V value) {
if (analysis.atypeFactory.getSupportedMonotonicTypeQualifiers().isEmpty()) {
return false;
}
boolean isMonotonic = false;
// TODO: Update the javadoc of this method when the above to-do item is addressed.
if (!sequentialSemantics) {
// only compute if necessary
AnnotatedTypeFactory atypeFactory = this.analysis.atypeFactory;
List<Pair<AnnotationMirror, AnnotationMirror>> fieldAnnotations = atypeFactory.getAnnotationWithMetaAnnotation(fieldAcc.getField(), MonotonicQualifier.class);
for (Pair<AnnotationMirror, AnnotationMirror> fieldAnnotation : fieldAnnotations) {
AnnotationMirror monotonicAnnotation = fieldAnnotation.second;
// permitted for use in the framework
@SuppressWarnings("deprecation") Name annotation = AnnotationUtils.getElementValueClassName(monotonicAnnotation, "value", false);
AnnotationMirror target = AnnotationBuilder.fromName(atypeFactory.getElementUtils(), annotation);
// Make sure the 'target' annotation is present.
if (AnnotationUtils.containsSame(value.getAnnotations(), target)) {
isMonotonic = true;
break;
}
}
}
return isMonotonic;
}
use of org.checkerframework.javacutil.Pair in project checker-framework by typetools.
the class CFAbstractStore method updateForMethodCall.
/* --------------------------------------------------------- */
/* Handling of fields */
/* --------------------------------------------------------- */
/**
* Remove any information that might not be valid any more after a method call, and add
* information guaranteed by the method.
*
* <ol>
* <li>If the method is side-effect-free (as indicated by {@link
* org.checkerframework.dataflow.qual.SideEffectFree} or {@link
* org.checkerframework.dataflow.qual.Pure}), then no information needs to be removed.
* <li>Otherwise, all information about field accesses {@code a.f} needs to be removed, except
* if the method {@code n} cannot modify {@code a.f} (e.g., if {@code a} is a local variable
* or {@code this}, and {@code f} is final).
* <li>Furthermore, if the field has a monotonic annotation, then its information can also be
* kept.
* </ol>
*
* Furthermore, if the method is deterministic, we store its result {@code val} in the store.
*/
public void updateForMethodCall(MethodInvocationNode n, AnnotatedTypeFactory atypeFactory, V val) {
ExecutableElement method = n.getTarget().getMethod();
// case 1: remove information if necessary
if (!(analysis.checker.hasOption("assumeSideEffectFree") || analysis.checker.hasOption("assumePure") || isSideEffectFree(atypeFactory, method))) {
boolean sideEffectsUnrefineAliases = ((GenericAnnotatedTypeFactory) atypeFactory).sideEffectsUnrefineAliases;
// isUnmodifiableByOtherCode. Example: @KeyFor("valueThatCanBeMutated").
if (sideEffectsUnrefineAliases) {
localVariableValues.entrySet().removeIf(e -> !e.getKey().isUnmodifiableByOtherCode());
}
// update this value
if (sideEffectsUnrefineAliases) {
thisValue = null;
}
// update field values
if (sideEffectsUnrefineAliases) {
fieldValues.entrySet().removeIf(e -> !e.getKey().isUnmodifiableByOtherCode());
} else {
Map<FieldAccess, V> newFieldValues = new HashMap<>(CollectionsPlume.mapCapacity(fieldValues));
for (Map.Entry<FieldAccess, V> e : fieldValues.entrySet()) {
FieldAccess fieldAccess = e.getKey();
V otherVal = e.getValue();
// case 3: the field has a monotonic annotation
if (!((GenericAnnotatedTypeFactory<?, ?, ?, ?>) atypeFactory).getSupportedMonotonicTypeQualifiers().isEmpty()) {
List<Pair<AnnotationMirror, AnnotationMirror>> fieldAnnotations = atypeFactory.getAnnotationWithMetaAnnotation(fieldAccess.getField(), MonotonicQualifier.class);
V newOtherVal = null;
for (Pair<AnnotationMirror, AnnotationMirror> fieldAnnotation : fieldAnnotations) {
AnnotationMirror monotonicAnnotation = fieldAnnotation.second;
// permitted for use in the framework
@SuppressWarnings("deprecation") Name annotation = AnnotationUtils.getElementValueClassName(monotonicAnnotation, "value", false);
AnnotationMirror target = AnnotationBuilder.fromName(atypeFactory.getElementUtils(), annotation);
// Make sure the 'target' annotation is present.
if (AnnotationUtils.containsSame(otherVal.getAnnotations(), target)) {
newOtherVal = analysis.createSingleAnnotationValue(target, otherVal.getUnderlyingType()).mostSpecific(newOtherVal, null);
}
}
if (newOtherVal != null) {
// keep information for all hierarchies where we had a
// monotone annotation.
newFieldValues.put(fieldAccess, newOtherVal);
continue;
}
}
// case 2:
if (!fieldAccess.isUnassignableByOtherCode()) {
// remove information completely
continue;
}
// keep information
newFieldValues.put(fieldAccess, otherVal);
}
fieldValues = newFieldValues;
}
// update array values
arrayValues.clear();
// update method values
methodValues.keySet().removeIf(e -> !e.isUnmodifiableByOtherCode());
}
// store information about method call if possible
JavaExpression methodCall = JavaExpression.fromNode(n);
replaceValue(methodCall, val);
}
Aggregations