org.checkerframework.framework.util.FieldInvariants Maven / Gradle / Ivy
Show all versions of checker Show documentation
package org.checkerframework.framework.util;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.source.DiagMessage;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.javacutil.BugInCF;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.lang.model.element.AnnotationMirror;
/**
* Represents field invariants, which the user states by writing {@code @FieldInvariant}. Think of
* this as a set of (field name, qualifier) pairs.
*
* If a FieldInvariants object is malformed (inconsistent number of fields and qualifiers),
* BaseTypeVisitor will issue an error.
*/
public class FieldInvariants {
/**
* A list of simple field names. A field may appear more than once in this list. This list has
* the same length as {@link #qualifiers}.
*/
private final List fields;
/**
* A list of qualifiers that apply to the field at the same index in {@link #fields}. In a
* well-formed FieldInvariants, has the same length as {@link #fields}.
*/
private final List qualifiers;
/** The type factory associated with this. */
private final AnnotatedTypeFactory atypeFactory;
/**
* Creates a new FieldInvariants object. The result is well-formed if the length of qualifiers
* is either 1 or equal to length of {@code fields}.
*
* @param fields list of fields
* @param qualifiers list of qualifiers, or a single qualifier that applies to all fields
* @param atypeFactory the type factory
*/
public FieldInvariants(
List fields,
List qualifiers,
AnnotatedTypeFactory atypeFactory) {
this(null, fields, qualifiers, atypeFactory);
}
/**
* 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 the 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
* @param atypeFactory the type factory
*/
public FieldInvariants(
@Nullable FieldInvariants other,
List fields,
List qualifiers,
AnnotatedTypeFactory atypeFactory) {
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;
this.atypeFactory = atypeFactory;
}
/** 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;
}
/**
* Returns true if there is a qualifier for each field in {@code fields}.
*
* @return true if there is a qualifier for each field in {@code fields}
*/
public boolean isWellFormed() {
return qualifiers.size() == fields.size();
}
/**
* Returns null if this is stronger than the given FieldInvariants, otherwise returns the error
* message. This is stronger if each of its qualifiers is a subtype of (or equal to) the
* respective qualfier in the given FieldInvariants.
*
* @param superInvar the value to check for being a weaker invariant
* @return null if this is stronger, otherwise returns an error message
*/
public @Nullable DiagMessage isStrongerThan(FieldInvariants superInvar) {
QualifierHierarchy qualHierarchy = atypeFactory.getQualifierHierarchy();
if (!this.fields.containsAll(superInvar.fields)) {
List missingFields = new ArrayList<>(superInvar.fields);
missingFields.removeAll(fields);
return DiagMessage.error(
"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 =
qualHierarchy.findAnnotationInSameHierarchy(subQualifiers, superA);
if (sub == null || !qualHierarchy.isSubtypeQualifiersOnly(sub, superA)) {
return DiagMessage.error(
"field.invariant.not.subtype.superclass", field, sub, superA);
}
}
}
return null;
}
}