import org.checkerframework.checker.nullness.qual.*; import org.checkerframework.framework.qual.DefaultQualifier; /** * The component type of newly created arrays is always @Nullable, also for boxed types. This is an * expanded version of the test case for Issue 151: * https://github.com/typetools/checker-framework/issues/151 */ public class ArrayCreationNullable { void testObjectArray(@NonNull Object @NonNull [] p) { @NonNull Object @NonNull [] objs; //:: error: (new.array.type.invalid) objs = new Object[10]; objs[0].toString(); //:: error: (assignment.type.incompatible) objs = new @Nullable Object[10]; objs[0].toString(); //:: error: (new.array.type.invalid) objs = new @NonNull Object[10]; objs[0].toString(); // Allowed. objs = p; objs[0].toString(); } @DefaultQualifier(NonNull.class) void testObjectArray2() { Object[] objs; // Even if the default qualifier is NonNull, array component // types must be Nullable. //:: error: (new.array.type.invalid) objs = new Object[10]; objs[0].toString(); } void testInitializers() { Object[] objs = {1, 2, 3}; objs = new Integer[] {1, 2, 3}; objs = new Object[] {new Object(), "ha"}; @NonNull Object[] objs2 = {}; //:: error: (assignment.type.incompatible) objs2 = new Integer[] {1, null, 3}; //:: error: (assignment.type.incompatible) objs2 = new Object[] {new Object(), "ha", null}; @NonNull Object[] objs3 = new Integer[] {1, 2, 3}; objs3 = new Integer[] {1, 2, 3}; //:: error: (assignment.type.incompatible) objs3 = new Integer[] {1, 2, 3, null}; (new Integer[] {1, 2, 3})[0].toString(); //:: error: (dereference.of.nullable) (new Integer[] {1, 2, 3, null})[0].toString(); // The assignment context is used to infer a @Nullable component type. @Nullable Object[] objs4 = new Integer[] {1, 2, 3}; //:: error: (dereference.of.nullable) objs4[0].toString(); objs4 = new Integer[] {1, 2, 3}; } void testStringArray(@NonNull String @NonNull [] p) { @NonNull String @NonNull [] strs; //:: error: (new.array.type.invalid) strs = new String[10]; strs[0].toString(); //:: error: (assignment.type.incompatible) strs = new @Nullable String[10]; strs[0].toString(); //:: error: (new.array.type.invalid) strs = new @NonNull String[10]; strs[0].toString(); // Allowed. strs = p; strs[0].toString(); } void testIntegerArray(@NonNull Integer @NonNull [] p) { @NonNull Integer @NonNull [] ints; //:: error: (new.array.type.invalid) ints = new Integer[10]; ints[0].toString(); //:: error: (assignment.type.incompatible) ints = new @Nullable Integer[10]; ints[0].toString(); //:: error: (new.array.type.invalid) ints = new @NonNull Integer[10]; ints[0].toString(); // Allowed. ints = p; ints[0].toString(); } // The component type of zero-length arrays can // be non-null - they will always generate // IndexOutOfBoundsExceptions, but are usually just // used for the type, e.g. in List.toArray. void testLengthZero() { @NonNull Object @NonNull [] objs; objs = new Object[0]; } /* Test case for Issue 153. // toArray re-uses the passed array, if it is of appropriate size. // It is only guaranteed to be non-null, if it is at most the same size. void testToArray(java.util.Set<Object> nns) { @NonNull Object [] nna = nns.toArray(new Object[nns.size()]); // Given array is too small -> new one is created. nna = nns.toArray(new Object[nns.size()-2]); // Padding elements will be null. //TODO:: error: (assignment.type.incompatible) nna = nns.toArray(new Object[nns.size() + 2]); @Nullable Object [] nbla = nns.toArray(new Object[nns.size() + 2]); } */ void testMultiDim() { // new double[10][10] has type double @NonNull[] @Nullable[] //:: error: (new.array.type.invalid) double @NonNull [] @NonNull [] daa = new double[10][10]; double @NonNull [] @Nullable [] daa2 = new double[10][10]; // new Object[10][10] has type @Nullable Object @NonNull[] @Nullable[] //:: error: (new.array.type.invalid) @Nullable Object @NonNull [] @NonNull [] oaa = new Object[10][10]; @Nullable Object @NonNull [] @Nullable [] oaa2 = new Object[10][10]; // new Object[10][10] has type @Nullable Object @NonNull[] @Nullable[] //:: error: (new.array.type.invalid) oaa2 = new Object @NonNull [10] @NonNull [10]; @MonotonicNonNull Object @NonNull [] @MonotonicNonNull [] oaa3 = new @MonotonicNonNull Object @NonNull [10] @MonotonicNonNull [10]; oaa3[0] = new @MonotonicNonNull Object[4]; //:: error: (assignment.type.incompatible) oaa3[0] = null; //:: error: (assignment.type.incompatible) :: error: (accessing.nullable) oaa3[0][0] = null; } @PolyNull Object[] testPolyNull(@PolyNull Object[] in) { @PolyNull Object[] out = new @PolyNull Object[in.length]; for (int i = 0; i < in.length; ++i) { if (in[i] == null) { out[i] = null; } else { out[i] = in[i].getClass().toString(); //:: error: (assignment.type.incompatible) out[i] = null; } } return out; } void testMonotonicNonNull() { @MonotonicNonNull Object @NonNull [] loa = new @MonotonicNonNull Object @NonNull [10]; loa = new Object @NonNull [10]; loa[0] = new Object(); @MonotonicNonNull Object @NonNull [] loa2 = new Object @NonNull [10]; //:: error: (dereference.of.nullable) loa2[0].toString(); } @MonotonicNonNull Object @NonNull [] testReturnContext() { return new Object[10]; } //:: error: (new.array.type.invalid) @NonNull Object @NonNull [] oa0 = new Object[10]; // OK @MonotonicNonNull Object @NonNull [] loa0 = new @MonotonicNonNull Object @NonNull [10]; Object[] oa1 = new Object[] {new Object()}; //:: error: (assignment.type.incompatible) Object[] oa2 = new Object[] {new Object(), null}; public static void main(String[] args) { ArrayCreationNullable e = new ArrayCreationNullable(); Integer[] ints = new Integer[] {5, 6}; // This would result in a NPE, if there were no error. e.testIntegerArray(ints); } }