| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import org.checkerframework.checker.nullness.qual.KeyFor; |
| import org.checkerframework.checker.nullness.qual.KeyForBottom; |
| import org.checkerframework.checker.nullness.qual.NonNull; |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.checkerframework.dataflow.qual.Pure; |
| import org.checkerframework.framework.qual.Covariant; |
| import org.checkerframework.framework.qual.DefaultQualifier; |
| import org.checkerframework.framework.qual.TypeUseLocation; |
| |
| @DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.IMPLICIT_UPPER_BOUND) |
| public class KeyForChecked { |
| |
| interface KFMap<@KeyForBottom K extends @NonNull Object, V extends @NonNull Object> { |
| @Covariant(0) |
| public static interface Entry< |
| @KeyForBottom K1 extends @Nullable Object, V1 extends @Nullable Object> { |
| K1 getKey(); |
| |
| V1 getValue(); |
| } |
| |
| @Pure |
| boolean containsKey(@Nullable Object a1); |
| |
| @Pure |
| @Nullable V get(@Nullable Object a1); |
| |
| @Nullable V put(K a1, V a2); |
| |
| Set<@KeyFor("this") K> keySet(); |
| |
| Set<KFMap.Entry<@KeyFor("this") K, V>> entrySet(); |
| |
| KFIterator<K> iterator(); |
| } |
| |
| class KFHashMap<@KeyForBottom K2 extends @NonNull Object, V2 extends @NonNull Object> |
| implements KFMap<K2, V2> { |
| @Pure |
| public boolean containsKey(@Nullable Object a1) { |
| return false; |
| } |
| |
| @Pure |
| public @Nullable V2 get(@Nullable Object a1) { |
| return null; |
| } |
| |
| public @Nullable V2 put(K2 a1, V2 a2) { |
| return null; |
| } |
| |
| public Set<@KeyFor("this") K2> keySet() { |
| return new HashSet<@KeyFor("this") K2>(); |
| } |
| |
| public Set<KFMap.Entry<@KeyFor("this") K2, V2>> entrySet() { |
| return new HashSet<KFMap.Entry<@KeyFor("this") K2, V2>>(); |
| } |
| |
| public KFIterator<K2> iterator() { |
| return new KFIterator<K2>(); |
| } |
| } |
| |
| @Covariant(0) |
| class KFIterator<@KeyForBottom E extends @Nullable Object> {} |
| |
| void incorrect1(Object map) { |
| String nonkey = ""; |
| // :: error: (assignment) |
| @KeyFor("map") String key = nonkey; |
| } |
| |
| void correct1(Object map) { |
| String nonkey = ""; |
| @SuppressWarnings("assignment") |
| @KeyFor("map") String key = nonkey; |
| } |
| |
| void incorrect2() { |
| KFMap<String, Object> m = new KFHashMap<>(); |
| m.put("a", new Object()); |
| m.put("b", new Object()); |
| m.put("c", new Object()); |
| |
| Collection<@KeyFor("m") String> coll = m.keySet(); |
| |
| @SuppressWarnings("assignment") |
| @KeyFor("m") String newkey = "new"; |
| |
| coll.add(newkey); |
| // TODO: at this point, the @KeyFor annotation is violated |
| m.put("new", new Object()); |
| } |
| |
| void correct2() { |
| KFMap<String, Object> m = new KFHashMap<>(); |
| m.put("a", new Object()); |
| m.put("b", new Object()); |
| m.put("c", new Object()); |
| |
| Collection<@KeyFor("m") String> coll = m.keySet(); |
| |
| @SuppressWarnings("assignment") |
| @KeyFor("m") String newkey = "new"; |
| |
| m.put(newkey, new Object()); |
| coll.add(newkey); |
| } |
| |
| void iter() { |
| KFMap<String, Object> emap = new KFHashMap<>(); |
| Set<@KeyFor("emap") String> s = emap.keySet(); |
| Iterator<@KeyFor("emap") String> it = emap.keySet().iterator(); |
| Iterator<@KeyFor("emap") String> it2 = s.iterator(); |
| |
| Collection<@KeyFor("emap") String> x = Collections.unmodifiableSet(emap.keySet()); |
| |
| for (@KeyFor("emap") String st : s) {} |
| for (String st : s) {} |
| Object bubu = new Object(); |
| // :: error: (enhancedfor) |
| for (@KeyFor("bubu") String st : s) {} |
| } |
| |
| <T> void dominators(KFMap<T, List<T>> preds) { |
| for (T node : preds.keySet()) {} |
| |
| for (@KeyFor("preds") T node : preds.keySet()) {} |
| } |
| |
| void entrySet() { |
| KFMap<String, Object> emap = new KFHashMap<>(); |
| Set<KFMap.Entry<@KeyFor("emap") String, Object>> es = emap.entrySet(); |
| |
| // KeyFor has to be explicit on the component to Entry sets because |
| // a) it's not clear which map the Entry set may have come from |
| // b) and there is no guarantee the map is still accessible |
| // :: error: (assignment) |
| Set<KFMap.Entry<String, Object>> es2 = emap.entrySet(); |
| } |
| |
| public static <K, V> void mapToString(KFMap<K, V> m) { |
| Set<KFMap.Entry<@KeyFor("m") K, V>> eset = m.entrySet(); |
| |
| for (KFMap.Entry<@KeyFor("m") K, V> entry : m.entrySet()) {} |
| } |
| |
| void testWF(KFMap<String, Object> m) { |
| KFIterator<String> it = m.iterator(); |
| } |
| } |