org.checkerframework.framework.util.FieldInvariants Maven / Gradle / Ivy
Show all versions of framework-all Show documentation
package org.checkerframework.framework.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import org.checkerframework.framework.source.Result;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.javacutil.BugInCF;
/**
* Represents field invariants, which the user states by writing {@code @FieldInvariant}. Think of
* this as a set of (field, qualifier) pairs.
*
* A FieldInvariants object may be malformed (inconsistent number of fields and qualifiers). In
* this case, the BaseTypeVisitor will issue an error.
*/
public class FieldInvariants {
/**
* A list of simple field names. A field may appear more than once in this list. In a
* well-formed FieldInvariants, has the same length as {@code qualifiers}.
*/
private final List fields;
/**
* A list of qualifiers that apply to the field at the same index in {@code fields}. In a
* well-formed FieldInvariants, has the same length as {@code fields}.
*/
private final List qualifiers;
/**
* Creates a new FieldInvariants object. The result is well-formed if length of qualifiers is
* either 1 or equal to length of {@code fields}.
*
* @param fields list of fields
* @param qualifiers list of qualifiers
*/
public FieldInvariants(List fields, List qualifiers) {
this(null, fields, qualifiers);
}
/**
* Creates a new object with all the invariants in {@code other}, plus those specified by {@code
* fields} and {@code qualifiers}. The result is well-formed if length of qualifiers is either 1
* or equal to length of {@code fields}.
*
* @param other other invariant object, may be null
* @param fields list of fields
* @param qualifiers list of qualifiers
*/
public FieldInvariants(
FieldInvariants other, List fields, List qualifiers) {
if (qualifiers.size() == 1) {
while (fields.size() > qualifiers.size()) {
qualifiers.add(qualifiers.get(0));
}
}
if (other != null) {
fields.addAll(other.fields);
qualifiers.addAll(other.qualifiers);
}
this.fields = Collections.unmodifiableList(fields);
this.qualifiers = qualifiers;
}
/** The simple names of the fields that have a qualifier. May contain duplicates. */
public List getFields() {
return fields;
}
/**
* Returns a list of qualifiers for {@code field}. If {@code field} has no qualifiers, returns
* an empty list.
*
* @param field simple field name
* @return a list of qualifiers for {@code field}, possibly empty
*/
public List getQualifiersFor(CharSequence field) {
if (!isWellFormed()) {
throw new BugInCF("malformed FieldInvariants");
}
String fieldString = field.toString();
int index = fields.indexOf(fieldString);
if (index == -1) {
return Collections.emptyList();
}
List list = new ArrayList<>();
for (int i = 0; i < fields.size(); i++) {
if (fields.get(i).equals(fieldString)) {
list.add(qualifiers.get(i));
}
}
return list;
}
/** @return true if there is a qualifier for each field in {@code fields} */
public boolean isWellFormed() {
return qualifiers.size() == fields.size();
}
/**
* @return null if {@code superInvar} is a super invariant, otherwise returns a Result with the
* error message
*/
public Result isSuperInvariant(FieldInvariants superInvar, AnnotatedTypeFactory factory) {
QualifierHierarchy qualifierHierarchy = factory.getQualifierHierarchy();
if (!this.fields.containsAll(superInvar.fields)) {
List missingFields = new ArrayList<>(superInvar.fields);
missingFields.removeAll(fields);
return Result.failure(
"field.invariant.not.found.superclass", String.join(", ", missingFields));
}
for (String field : superInvar.fields) {
List superQualifiers = superInvar.getQualifiersFor(field);
List subQualifiers = this.getQualifiersFor(field);
for (AnnotationMirror superA : superQualifiers) {
AnnotationMirror sub =
qualifierHierarchy.findAnnotationInSameHierarchy(subQualifiers, superA);
if (sub == null || !qualifierHierarchy.isSubtype(sub, superA)) {
return Result.failure(
"field.invariant.not.subtype.superclass", field, sub, superA);
}
}
}
return null;
}
}