blob: 91aa7437f0e77b495d73deb8d8f3d83056c3b019 [file] [log] [blame]
package org.checkerframework.common.subtyping;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.subtyping.qual.Unqualified;
import org.checkerframework.framework.qual.DefaultFor;
import org.checkerframework.framework.qual.DefaultQualifierInHierarchy;
import org.checkerframework.framework.qual.SubtypeOf;
import org.checkerframework.framework.qual.TypeUseLocation;
import org.checkerframework.framework.type.AnnotationClassLoader;
import org.checkerframework.framework.util.defaults.QualifierDefaults;
import org.checkerframework.javacutil.AnnotationBuilder;
import org.checkerframework.javacutil.UserError;
import org.plumelib.reflection.Signatures;
/** Defines {@link #createSupportedTypeQualifiers}. */
public class SubtypingAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
public SubtypingAnnotatedTypeFactory(BaseTypeChecker checker) {
super(checker);
postInit();
}
@Override
protected AnnotationClassLoader createAnnotationClassLoader() {
return new SubtypingAnnotationClassLoader(checker);
}
@Override
protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() {
// Subtyping Checker doesn't have a qual directory, so we instantiate the loader here to
// load externally declared annotations
loader = createAnnotationClassLoader();
String qualNames = checker.getOption("quals");
String qualDirectories = checker.getOption("qualDirs");
if (qualNames == null && qualDirectories == null) {
throw new UserError("SubtypingChecker: missing required option. Use -Aquals or -AqualDirs.");
}
Set<Class<? extends Annotation>> qualSet = new LinkedHashSet<>();
// load individually named qualifiers
if (qualNames != null) {
for (String qualName : qualNames.split(",")) {
if (!Signatures.isBinaryName(qualName)) {
throw new UserError("Malformed qualifier \"%s\" in -Aquals=%s", qualName, qualNames);
}
Class<? extends Annotation> anno = loader.loadExternalAnnotationClass(qualName);
if (anno == null) {
throw new UserError("Qualifier specified in -Aquals not found: " + qualName);
}
qualSet.add(anno);
}
}
// load directories of qualifiers
if (qualDirectories != null) {
for (String dirName : qualDirectories.split(":")) {
Set<Class<? extends Annotation>> annos =
loader.loadExternalAnnotationClassesFromDirectory(dirName);
if (annos.isEmpty()) {
throw new UserError(
"Directory specified in -AqualsDir contains no qualifiers: " + dirName);
}
qualSet.addAll(annos);
}
}
if (qualSet.isEmpty()) {
throw new UserError("SubtypingChecker: no qualifiers specified via -Aquals or -AqualDirs");
}
// check for subtype meta-annotation
for (Class<? extends Annotation> qual : qualSet) {
Annotation subtypeOfAnnotation = qual.getAnnotation(SubtypeOf.class);
if (subtypeOfAnnotation != null) {
for (Class<? extends Annotation> superqual : qual.getAnnotation(SubtypeOf.class).value()) {
if (!qualSet.contains(superqual)) {
throw new UserError(
"SubtypingChecker: qualifier "
+ qual
+ " was specified via -Aquals but its super-qualifier "
+ superqual
+ " was not");
}
}
}
}
return qualSet;
}
/**
* If necessary, make Unqualified the default qualifier. Keep most logic in sync with super.
*
* @see
* org.checkerframework.framework.type.GenericAnnotatedTypeFactory#addCheckedCodeDefaults(org.checkerframework.framework.util.defaults.QualifierDefaults)
*/
@Override
protected void addCheckedCodeDefaults(QualifierDefaults defs) {
boolean foundOtherwise = false;
// Add defaults from @DefaultFor and @DefaultQualifierInHierarchy
for (Class<? extends Annotation> qual : getSupportedTypeQualifiers()) {
DefaultFor defaultFor = qual.getAnnotation(DefaultFor.class);
if (defaultFor != null) {
final TypeUseLocation[] locations = defaultFor.value();
defs.addCheckedCodeDefaults(AnnotationBuilder.fromClass(elements, qual), locations);
foundOtherwise =
foundOtherwise || Arrays.asList(locations).contains(TypeUseLocation.OTHERWISE);
}
if (qual.getAnnotation(DefaultQualifierInHierarchy.class) != null) {
defs.addCheckedCodeDefault(
AnnotationBuilder.fromClass(elements, qual), TypeUseLocation.OTHERWISE);
foundOtherwise = true;
}
}
// If Unqualified is a supported qualifier, make it the default.
AnnotationMirror unqualified = AnnotationBuilder.fromClass(elements, Unqualified.class);
if (!foundOtherwise && this.isSupportedQualifier(unqualified)) {
defs.addCheckedCodeDefault(unqualified, TypeUseLocation.OTHERWISE);
}
}
}