in nullaway/src/main/java/com/uber/nullaway/generics/CheckIdenticalNullabilityVisitor.java [26:80]
public Boolean visitClassType(Type.ClassType lhsType, Type rhsType) {
if (rhsType instanceof NullType || rhsType.isPrimitive()) {
return true;
}
if (rhsType.getKind().equals(TypeKind.WILDCARD)) {
// TODO Handle wildcard types
return true;
}
if (lhsType.isIntersection()) {
return handleIntersectionType((Type.IntersectionClassType) lhsType, rhsType);
}
Types types = state.getTypes();
// The base type of rhsType may be a subtype of lhsType's base type. In such cases, we must
// compare lhsType against the supertype of rhsType with a matching base type.
Type rhsTypeAsSuper = types.asSuper(rhsType, lhsType.tsym);
if (rhsTypeAsSuper == null) {
// Surprisingly, this can in fact occur, in cases involving raw types. See, e.g.,
// GenericsTests#issue1082 and https://github.com/uber/NullAway/pull/1086. Bail out.
return true;
}
// bail out of checking raw types for now
if (rhsTypeAsSuper.isRaw() || lhsType.isRaw()) {
return true;
}
List<Type> lhsTypeArguments = lhsType.getTypeArguments();
List<Type> rhsTypeArguments = rhsTypeAsSuper.getTypeArguments();
// This is impossible, considering the fact that standard Java subtyping succeeds before
// running NullAway
if (lhsTypeArguments.size() != rhsTypeArguments.size()) {
throw new RuntimeException(
"Number of types arguments in " + rhsTypeAsSuper + " does not match " + lhsType);
}
for (int i = 0; i < lhsTypeArguments.size(); i++) {
Type lhsTypeArgument = lhsTypeArguments.get(i);
Type rhsTypeArgument = rhsTypeArguments.get(i);
if (lhsTypeArgument.getKind().equals(TypeKind.WILDCARD)
|| rhsTypeArgument.getKind().equals(TypeKind.WILDCARD)) {
// TODO Handle wildcard types
continue;
}
boolean isLHSNullableAnnotated = GenericsChecks.isNullableAnnotated(lhsTypeArgument, config);
boolean isRHSNullableAnnotated = GenericsChecks.isNullableAnnotated(rhsTypeArgument, config);
if (isLHSNullableAnnotated != isRHSNullableAnnotated) {
return false;
}
// nested generics
if (!lhsTypeArgument.accept(this, rhsTypeArgument)) {
return false;
}
}
// If there is an enclosing type (for non-static inner classes), its type argument nullability
// should also match. When there is no enclosing type, getEnclosingType() returns a NoType
// object, which gets handled by the fallback visitType() method
return lhsType.getEnclosingType().accept(this, rhsType.getEnclosingType());
}