Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@
* @see TypeHierarchy#isSubtype
* @see AnnotatedTypeFactory
*/
public class BaseTypeVisitor<Factory extends GenericAnnotatedTypeFactory<?, ?, ?, ?>>

Check warning on line 198 in framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java

View workflow job for this annotation

GitHub Actions / cftests-nonjunit on JDK 21

no @param for <Factory>

Check warning on line 198 in framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java

View workflow job for this annotation

GitHub Actions / cftests-nonjunit on JDK 26

no @param for <Factory>

Check warning on line 198 in framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java

View workflow job for this annotation

GitHub Actions / cftests-nonjunit on JDK 27

no @param for <Factory>

Check warning on line 198 in framework/src/main/java/org/checkerframework/common/basetype/BaseTypeVisitor.java

View workflow job for this annotation

GitHub Actions / cftests-nonjunit on JDK 25

no @param for <Factory>
extends SourceVisitor<Void, Void> {

/** The {@link BaseTypeChecker} for error reporting. */
Expand Down Expand Up @@ -2236,8 +2236,7 @@
List<AnnotatedTypeMirror> typeargs = mType.typeArgs;

List<AnnotatedTypeParameterBounds> paramBounds =
CollectionsPlume.mapList(
AnnotatedTypeVariable::getBounds, invokedMethod.getTypeVariables());
atypeFactory.methodTypeVariableBoundsFromUse(tree, invokedMethod);

ExecutableElement method = invokedMethod.getElement();
CharSequence methodName = ElementUtils.getSimpleDescription(method);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,7 @@
/**
* TypeVariableSubstitutor provides a method to replace type parameters with their arguments.
*/
protected TypeVariableSubstitutor createTypeVariableSubstitutor() {

Check warning on line 1268 in framework/src/main/java/org/checkerframework/framework/type/AnnotatedTypeFactory.java

View workflow job for this annotation

GitHub Actions / cftests-nonjunit on JDK 11

return new TypeVariableSubstitutor();
}

Expand Down Expand Up @@ -2398,6 +2398,52 @@
return res;
}

/**
* Returns the method type-variable bounds adapted to the viewpoint of a method invocation.
*
* @param tree a method invocation
* @param invokedMethod the type of the invoked method
* @return the adapted method type parameter bounds
*/
public List<AnnotatedTypeParameterBounds> methodTypeVariableBoundsFromUse(
MethodInvocationTree tree, AnnotatedExecutableType invokedMethod) {
List<AnnotatedTypeParameterBounds> bounds =
CollectionsPlume.mapList(
AnnotatedTypeVariable::getBounds, invokedMethod.getTypeVariables());

AnnotatedTypeMirror receiverType = getMethodReceiverType(tree);
if (viewpointAdapter != null && receiverType != null) {
viewpointAdapter.viewpointAdaptTypeParameterBounds(receiverType, bounds);
}
return bounds;
}

/**
* Returns the receiver type used to viewpoint-adapt a method invocation.
*
* @param tree a method invocation tree
* @return the receiver type, or null if the invocation has no receiver

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-reference getReceiverType and explain differences!

*/
private @Nullable AnnotatedTypeMirror getMethodReceiverType(MethodInvocationTree tree) {
ExecutableElement methodElt = TreeUtils.elementFromUse(tree);
if (ElementUtils.isStatic(methodElt)) {
return null;
}

AnnotatedTypeMirror receiverType = getReceiverType(tree);
if (receiverType == null
&& (TreeUtils.isSuperConstructorCall(tree)
|| TreeUtils.isThisConstructorCall(tree))) {
// super() and this() calls don't have a receiver, but they should be view-point adapted
// as if "this" is the receiver.
receiverType = getSelfType(tree);
}
if (receiverType != null && receiverType.getKind() == TypeKind.DECLARED) {
receiverType = applyCaptureConversion(receiverType);
}
return receiverType;
}

/**
* Creates and returns an AnnotatedNullType qualified with {@code annotations}.
*
Expand Down Expand Up @@ -2733,17 +2779,7 @@
protected ParameterizedExecutableType methodFromUse(
MethodInvocationTree tree, boolean inferTypeArgs) {
ExecutableElement methodElt = TreeUtils.elementFromUse(tree);
AnnotatedTypeMirror receiverType = getReceiverType(tree);
if (receiverType == null
&& (TreeUtils.isSuperConstructorCall(tree)
|| TreeUtils.isThisConstructorCall(tree))) {
// super() and this() calls don't have a receiver, but they should be view-point adapted
// as if "this" is the receiver.
receiverType = getSelfType(tree);
}
if (receiverType != null && receiverType.getKind() == TypeKind.DECLARED) {
receiverType = applyCaptureConversion(receiverType);
}
AnnotatedTypeMirror receiverType = getMethodReceiverType(tree);

ParameterizedExecutableType result =
methodFromUse(tree, methodElt, receiverType, inferTypeArgs);
Expand Down
72 changes: 72 additions & 0 deletions framework/tests/viewpointtest/MethodTypeVariableBounds.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import viewpointtest.quals.*;

public class MethodTypeVariableBounds {
static class Methods {
<T extends @ReceiverDependentQual Object> void noArg() {}

<T extends @ReceiverDependentQual Object> void withArg(T t) {}
}

void topReceiver(
@Top Methods methods,
@Top Object top,
@A Object a,
@B Object b,
@Bottom Object bottom) {
// @Top viewpoint-adapts @ReceiverDependentQual to @Lost, so only @Bottom is within the
// adapted method type parameter bound.
// :: error: (type.argument.type.incompatible)
methods.noArg();

// :: error: (type.argument.type.incompatible)
methods.<@Top Object>withArg(top);

// :: error: (type.argument.type.incompatible)
methods.<@A Object>withArg(a);

// :: error: (type.argument.type.incompatible)
methods.<@B Object>withArg(b);

methods.<@Bottom Object>withArg(bottom);

// :: error: (type.arguments.not.inferred)
methods.withArg(top);

// :: error: (type.arguments.not.inferred)
methods.withArg(a);

// :: error: (type.arguments.not.inferred)
methods.withArg(b);

methods.withArg(bottom);
}

void aReceiver(
@A Methods methods, @Top Object top, @A Object a, @B Object b, @Bottom Object bottom) {
// @A viewpoint-adapts @ReceiverDependentQual to @A, so @A and @Bottom are within the
// adapted method type parameter bound.
// :: error: (type.argument.type.incompatible)
methods.noArg();

// :: error: (type.argument.type.incompatible)
methods.<@Top Object>withArg(top);

methods.<@A Object>withArg(a);

// :: error: (type.argument.type.incompatible)
methods.<@B Object>withArg(b);

methods.<@Bottom Object>withArg(bottom);

// :: error: (type.arguments.not.inferred)
methods.withArg(top);

// :: error: (type.arguments.not.inferred)
methods.withArg(a);
Comment thread
aosen-xiong marked this conversation as resolved.

// :: error: (type.arguments.not.inferred)
methods.withArg(b);

methods.withArg(bottom);
}
}
Loading