blob: bc497e2d4fdc375fc7938eb1e6c5f584d57b082b [file] [log] [blame]
package org.checkerframework.common.returnsreceiver;
import java.lang.annotation.Annotation;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ElementKind;
import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.returnsreceiver.qual.BottomThis;
import org.checkerframework.common.returnsreceiver.qual.This;
import org.checkerframework.common.returnsreceiver.qual.UnknownThis;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.typeannotator.ListTypeAnnotator;
import org.checkerframework.framework.type.typeannotator.TypeAnnotator;
import org.checkerframework.javacutil.AnnotationBuilder;
import org.checkerframework.javacutil.AnnotationUtils;
/** The type factory for the Returns Receiver Checker. */
public class ReturnsReceiverAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
/**
* The {@code @}{@link This} annotation. The field is package visible due to a use in {@link
* ReturnsReceiverVisitor}
*/
final AnnotationMirror THIS_ANNOTATION;
/**
* Create a new {@code ReturnsReceiverAnnotatedTypeFactory}.
*
* @param checker the type-checker associated with this factory
*/
public ReturnsReceiverAnnotatedTypeFactory(BaseTypeChecker checker) {
super(checker);
THIS_ANNOTATION = AnnotationBuilder.fromClass(elements, This.class);
this.postInit();
}
@Override
protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() {
return getBundledTypeQualifiers(BottomThis.class, UnknownThis.class, This.class);
}
@Override
protected TypeAnnotator createTypeAnnotator() {
return new ListTypeAnnotator(
new ReturnsReceiverTypeAnnotator(this), super.createTypeAnnotator());
}
/** A TypeAnnotator to add the {@code @}{@link This} annotation. */
private class ReturnsReceiverTypeAnnotator extends TypeAnnotator {
/**
* Create a new ReturnsReceiverTypeAnnotator.
*
* @param typeFactory the {@link AnnotatedTypeFactory} associated with this {@link
* TypeAnnotator}
*/
public ReturnsReceiverTypeAnnotator(AnnotatedTypeFactory typeFactory) {
super(typeFactory);
}
@Override
public Void visitExecutable(AnnotatedTypeMirror.AnnotatedExecutableType t, Void p) {
// skip constructors, as we never need to add annotations to them
if (t.getElement().getKind() == ElementKind.CONSTRUCTOR) {
return super.visitExecutable(t, p);
}
AnnotatedTypeMirror returnType = t.getReturnType();
// If any FluentAPIGenerator indicates the method returns this,
// add an @This annotation on the return type.
if (FluentAPIGenerator.check(t)) {
if (!returnType.isAnnotatedInHierarchy(THIS_ANNOTATION)) {
returnType.addAnnotation(THIS_ANNOTATION);
}
}
// If return type is annotated with @This, add @This annotation to the receiver type. We
// cannot yet default all receivers to be @This due to
// https://github.com/typetools/checker-framework/issues/2931
AnnotationMirror retAnnotation = returnType.getAnnotationInHierarchy(THIS_ANNOTATION);
if (retAnnotation != null && AnnotationUtils.areSame(retAnnotation, THIS_ANNOTATION)) {
AnnotatedTypeMirror.AnnotatedDeclaredType receiverType = t.getReceiverType();
if (receiverType != null && !receiverType.isAnnotatedInHierarchy(THIS_ANNOTATION)) {
receiverType.addAnnotation(THIS_ANNOTATION);
}
}
return super.visitExecutable(t, p);
}
}
}