blob: 21908e7224861a1defb60835deca8a3b07d53f04 [file] [log] [blame]
import java.util.HashMap;
import org.checkerframework.checker.nullness.qual.*;
public class KeyForSubtyping {
HashMap<String, String> mapA = new HashMap<>();
HashMap<String, String> mapB = new HashMap<>();
HashMap<String, String> mapC = new HashMap<>();
public void testSubtypeAssignments(
String not_a_key,
@KeyFor("this.mapA") String a,
@KeyFor("this.mapB") String b,
@KeyFor({"this.mapA", "this.mapB"}) String ab) {
// Try the error cases first, otherwise dataflow will change the inferred annotations on the
// variables such that a line of code can have an effect on a subsequent line of code. We want
// each of these tests to be independent.
// :: error: (assignment)
ab = a;
// :: error: (assignment)
ab = b;
// :: error: (assignment)
a = b;
// :: error: (assignment)
a = not_a_key;
// :: error: (assignment)
b = not_a_key;
// :: error: (assignment)
ab = not_a_key;
// Now try the success cases
a = ab;
b = ab;
not_a_key = ab;
not_a_key = a;
}
public void testDataFlow(
String not_yet_a_key,
@KeyFor("this.mapA") String a,
@KeyFor("this.mapB") String b,
@KeyFor({"this.mapA", "this.mapB"}) String ab) {
// Test that when a valid assignment is made, dataflow transfers the
// KeyFor type qualifier from the right hand side to the left hand side.
// :: error: (argument)
method1(not_yet_a_key);
not_yet_a_key = a;
method1(not_yet_a_key);
method1(a);
// :: error: (argument)
method1(b);
method1(ab);
b = ab;
method1(b);
}
public void testSetOrdering(
@KeyFor({"this.mapC", "this.mapA"}) String ac,
@KeyFor({"this.mapA", "this.mapB", "this.mapC"}) String abc) {
// Test that the order of elements in the set doesn't matter when doing subtyping checks,
// @KeyFor("A, B, C") <: @KeyFor("C, A")
// Try the error case first - see the note in method testSubtypeAssignments
// :: error: (assignment)
abc = ac;
ac = abc;
}
public void testDataflowTransitivity(
@KeyFor({"this.mapA"}) String a,
@KeyFor({"this.mapA", "this.mapB"}) String ab,
@KeyFor({"this.mapA", "this.mapB", "this.mapC"}) String abc) {
ab = abc;
// At this point, dataflow should have refined the type of ab to
// @KeyFor({"this.mapA","this.mapB","this.mapC"})
a = ab;
// At this point, dataflow should have refined the type of a to
// @KeyFor({"this.mapA","this.mapB","this.mapC"})
// This would not succeed without the previous two assignments, but should now because of
// dataflow.
abc = a;
}
private void method1(@KeyFor("this.mapA") String a) {}
private void testWithNullnessAnnotation(
String not_a_key,
@KeyFor("this.mapA") String a,
@KeyFor("this.mapB") String b,
@Nullable @KeyFor({"this.mapA", "this.mapB"}) String ab) {
// These fail only because a @Nullable RHS cannot be assigned to a @NonNull LHS.
// :: error: (assignment)
a = ab;
// :: error: (assignment)
b = ab;
// :: error: (assignment)
not_a_key = ab;
not_a_key = a; // Succeeds because both sides are @NonNull
}
// Test overriding
static class Super {
HashMap<String, String> map1 = new HashMap<>();
HashMap<String, String> map2 = new HashMap<>();
void method1(@KeyFor({"this.map1", "this.map2"}) String s) {}
void method2(@KeyFor("this.map1") String s) {}
}
static class Sub extends Super {
@Override
void method1(@KeyFor("this.map1") String s) {}
@Override
// :: error: (override.param)
void method2(@KeyFor({"this.map1", "this.map2"}) String s) {}
}
}