blob: bb5973ae0254dff2a1549a70a2f106f31e2b73e2 [file] [log] [blame]
package org.checkerframework.checker.testchecker.wholeprograminference;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.util.Elements;
import org.checkerframework.checker.testchecker.wholeprograminference.qual.DefaultType;
import org.checkerframework.checker.testchecker.wholeprograminference.qual.ImplicitAnno;
import org.checkerframework.checker.testchecker.wholeprograminference.qual.Parent;
import org.checkerframework.checker.testchecker.wholeprograminference.qual.Sibling1;
import org.checkerframework.checker.testchecker.wholeprograminference.qual.Sibling2;
import org.checkerframework.checker.testchecker.wholeprograminference.qual.SiblingWithFields;
import org.checkerframework.checker.testchecker.wholeprograminference.qual.Top;
import org.checkerframework.checker.testchecker.wholeprograminference.qual.WholeProgramInferenceBottom;
import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.framework.qual.LiteralKind;
import org.checkerframework.framework.type.MostlyNoElementQualifierHierarchy;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.type.treeannotator.ListTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.LiteralTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.PropagationTreeAnnotator;
import org.checkerframework.framework.type.treeannotator.TreeAnnotator;
import org.checkerframework.framework.util.QualifierKind;
import org.checkerframework.javacutil.AnnotationBuilder;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.TreeUtils;
/**
* AnnotatedTypeFactory to test whole-program inference using .jaif files.
*
* <p>The used qualifier hierarchy is straightforward and only intended for test purposes.
*/
public class WholeProgramInferenceTestAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
private final AnnotationMirror PARENT =
new AnnotationBuilder(processingEnv, Parent.class).build();
private final AnnotationMirror BOTTOM =
new AnnotationBuilder(processingEnv, WholeProgramInferenceBottom.class).build();
private final AnnotationMirror IMPLICIT_ANNO =
new AnnotationBuilder(processingEnv, ImplicitAnno.class).build();
/** The SiblingWithFields.value field/element. */
private final ExecutableElement siblingWithFieldsValueElement =
TreeUtils.getMethod(SiblingWithFields.class, "value", 0, processingEnv);
/** The SiblingWithFields.value2 field/element. */
private final ExecutableElement siblingWithFieldsValue2Element =
TreeUtils.getMethod(SiblingWithFields.class, "value2", 0, processingEnv);
public WholeProgramInferenceTestAnnotatedTypeFactory(BaseTypeChecker checker) {
super(checker);
postInit();
}
@Override
protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() {
return new HashSet<Class<? extends Annotation>>(
Arrays.asList(
Parent.class,
DefaultType.class,
Top.class,
Sibling1.class,
Sibling2.class,
WholeProgramInferenceBottom.class,
SiblingWithFields.class,
ImplicitAnno.class));
}
@Override
public TreeAnnotator createTreeAnnotator() {
LiteralTreeAnnotator literalTreeAnnotator = new LiteralTreeAnnotator(this);
literalTreeAnnotator.addLiteralKind(LiteralKind.INT, BOTTOM);
literalTreeAnnotator.addStandardLiteralQualifiers();
return new ListTreeAnnotator(new PropagationTreeAnnotator(this), literalTreeAnnotator);
}
@Override
protected QualifierHierarchy createQualifierHierarchy() {
return new WholeProgramInferenceTestQualifierHierarchy(
this.getSupportedTypeQualifiers(), elements);
}
/**
* Using a MultiGraphQualifierHierarchy to enable tests with Annotations that contain fields. @see
* SiblingWithFields.
*/
protected class WholeProgramInferenceTestQualifierHierarchy
extends MostlyNoElementQualifierHierarchy {
private final QualifierKind SIBLING_WITH_FIELDS_KIND;
/**
* Creates a WholeProgramInferenceTestQualifierHierarchy from the given classes.
*
* @param qualifierClasses classes of annotations that are the qualifiers for this hierarchy
* @param elements element utils
*/
protected WholeProgramInferenceTestQualifierHierarchy(
Collection<Class<? extends Annotation>> qualifierClasses, Elements elements) {
super(qualifierClasses, elements);
SIBLING_WITH_FIELDS_KIND = getQualifierKind(SiblingWithFields.class.getCanonicalName());
}
@Override
public AnnotationMirror getBottomAnnotation(AnnotationMirror start) {
return BOTTOM;
}
@Override
public Set<? extends AnnotationMirror> getBottomAnnotations() {
return Collections.singleton(BOTTOM);
}
@Override
protected AnnotationMirror greatestLowerBoundWithElements(
AnnotationMirror a1,
QualifierKind qualifierKind1,
AnnotationMirror a2,
QualifierKind qualifierKind2,
QualifierKind glbKind) {
if (qualifierKind1 == qualifierKind2 && qualifierKind1 == SIBLING_WITH_FIELDS_KIND) {
if (isSubtypeWithElements(a1, qualifierKind1, a2, qualifierKind2)) {
return a1;
} else {
return IMPLICIT_ANNO;
}
} else if (qualifierKind1 == SIBLING_WITH_FIELDS_KIND) {
return a1;
} else if (qualifierKind2 == SIBLING_WITH_FIELDS_KIND) {
return a2;
}
throw new BugInCF("Unexpected qualifiers: %s %s", a1, a2);
}
@Override
protected AnnotationMirror leastUpperBoundWithElements(
AnnotationMirror a1,
QualifierKind qualifierKind1,
AnnotationMirror a2,
QualifierKind qualifierKind2,
QualifierKind lubKind) {
if (qualifierKind1 == qualifierKind2 && qualifierKind1 == SIBLING_WITH_FIELDS_KIND) {
if (isSubtypeWithElements(a1, qualifierKind1, a2, qualifierKind2)) {
return a1;
} else {
return PARENT;
}
} else if (qualifierKind1 == SIBLING_WITH_FIELDS_KIND) {
return a1;
} else if (qualifierKind2 == SIBLING_WITH_FIELDS_KIND) {
return a2;
}
throw new BugInCF("Unexpected qualifiers: %s %s", a1, a2);
}
@Override
protected boolean isSubtypeWithElements(
AnnotationMirror subAnno,
QualifierKind subKind,
AnnotationMirror superAnno,
QualifierKind superKind) {
if (subKind == SIBLING_WITH_FIELDS_KIND && superKind == SIBLING_WITH_FIELDS_KIND) {
List<String> subVal1 =
AnnotationUtils.getElementValueArray(
subAnno, siblingWithFieldsValueElement, String.class, Collections.emptyList());
List<String> supVal1 =
AnnotationUtils.getElementValueArray(
superAnno, siblingWithFieldsValueElement, String.class, Collections.emptyList());
String subVal2 =
AnnotationUtils.getElementValue(
subAnno, siblingWithFieldsValue2Element, String.class, "");
String supVal2 =
AnnotationUtils.getElementValue(
superAnno, siblingWithFieldsValue2Element, String.class, "");
return subVal1.equals(supVal1) && subVal2.equals(supVal2);
}
throw new BugInCF("Unexpected qualifiers: %s %s", subAnno, superAnno);
}
}
}