diff --git a/checker/tests/nullness/Issue4853Nullness.java b/checker/tests/nullness/Issue4853Nullness.java new file mode 100644 index 00000000000..2800a9b9411 --- /dev/null +++ b/checker/tests/nullness/Issue4853Nullness.java @@ -0,0 +1,18 @@ +import org.checkerframework.checker.nullness.qual.Nullable; + +public class Issue4853Nullness { + interface Interface {} + + static class MyClass { + class InnerMyClass implements Interface {} + } + + abstract static class SubMyClass extends MyClass<@Nullable String> { + protected void f() { + // :: error: (argument) + method(new InnerMyClass()); + } + + abstract void method(Interface callback); + } +} diff --git a/framework/src/main/java/org/checkerframework/framework/type/SupertypeFinder.java b/framework/src/main/java/org/checkerframework/framework/type/SupertypeFinder.java index 5c2e3e7ade7..b5c73e76dd2 100644 --- a/framework/src/main/java/org/checkerframework/framework/type/SupertypeFinder.java +++ b/framework/src/main/java/org/checkerframework/framework/type/SupertypeFinder.java @@ -193,29 +193,51 @@ public List visitDeclared(AnnotatedDeclaredType type, Voi supertypes.add(jlaAnnotation); } + Map typeVarToTypeArg = getTypeVarToTypeArg(type); + + List superTypesNew = new ArrayList<>(); + for (AnnotatedDeclaredType dt : supertypes) { + type.atypeFactory.initializeAtm(dt); + superTypesNew.add( + (AnnotatedDeclaredType) + atypeFactory.getTypeVarSubstitutor().substitute(typeVarToTypeArg, dt)); + } + + return superTypesNew; + } + + /** + * Creates a mapping from a type parameter to its corresponding annotated type argument for all + * type parameters of {@code type}, its enclosing types, and all super types of all {@code + * type}'s enclosing types. + * + * @param type a type + * @return a mapping from each type parameter to its corresponding annotated type argument + */ + private Map getTypeVarToTypeArg(AnnotatedDeclaredType type) { Map mapping = new HashMap<>(); AnnotatedDeclaredType enclosing = type; while (enclosing != null) { TypeElement enclosingTypeElement = (TypeElement) enclosing.getUnderlyingType().asElement(); - List typeArgs = enclosing.getTypeArguments(); List typeParams = enclosingTypeElement.getTypeParameters(); + List typeArgs = enclosing.getTypeArguments(); for (int i = 0; i < enclosing.getTypeArguments().size(); ++i) { AnnotatedTypeMirror typArg = typeArgs.get(i); TypeParameterElement ele = typeParams.get(i); mapping.put((TypeVariable) ele.asType(), typArg); } - enclosing = enclosing.getEnclosingType(); - } + @SuppressWarnings("interning:not.interned") // First time through type == enclosing. + boolean notType = enclosing != type; + if (notType) { + for (AnnotatedDeclaredType enclSuper : directSupertypes(enclosing)) { + mapping.putAll(getTypeVarToTypeArg(enclSuper)); + } + } - List superTypesNew = new ArrayList<>(); - for (AnnotatedDeclaredType dt : supertypes) { - type.atypeFactory.initializeAtm(dt); - superTypesNew.add( - (AnnotatedDeclaredType) atypeFactory.getTypeVarSubstitutor().substitute(mapping, dt)); + enclosing = enclosing.getEnclosingType(); } - - return superTypesNew; + return mapping; } private List supertypesFromElement( diff --git a/framework/tests/all-systems/Issue4853.java b/framework/tests/all-systems/Issue4853.java new file mode 100644 index 00000000000..7445725eeb1 --- /dev/null +++ b/framework/tests/all-systems/Issue4853.java @@ -0,0 +1,16 @@ +@SuppressWarnings("all") // Just check for crashes. +public class Issue4853 { + interface Interface {} + + static class MyClass { + class InnerMyClass implements Interface {} + } + + abstract static class SubMyClass extends MyClass { + protected void f() { + method(new InnerMyClass()); + } + + abstract void method(Interface callback); + } +}