blob: 6d8dfdaa1aac140889f92e607fd35c40dc7f58cc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2020 Stephan Herrmann and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
import java.util.Map;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import junit.framework.Test;
public class NullChecksTests extends AbstractNullAnnotationTest {
public NullChecksTests(String name) {
super(name);
}
// Static initializer to specify tests subset using TESTS_* static variables
// All specified tests which do not belong to the class are skipped...
static {
// TESTS_NAMES = new String[] { "testAssertNonNull1" };
// TESTS_NUMBERS = new int[] { 561 };
// TESTS_RANGE = new int[] { 1, 2049 };
}
public static Test suite() {
return buildMinimalComplianceTestSuite(testClass(), F_1_8);
}
public static Class<NullChecksTests> testClass() {
return NullChecksTests.class;
}
public void testAssertNonNull1() {
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"import static org.eclipse.jdt.annotation.Checks.*;\n" +
"public class X {\n" +
" @SuppressWarnings(\"null\")\n" +
" static @NonNull String hide(String some) {\n" +
" return some;\n" +
" }\n" +
" public static void main(String... args) {\n" +
" @NonNull String myHiddenNull = hide(null);\n" +
" try {\n" +
" assertNonNull(\"foo\", myHiddenNull);\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(npe.getMessage());\n" +
" }\n" +
" try {\n" +
" assertNonNullWithMessage(\"Shouldn't!\", \"foo\", myHiddenNull);\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(npe.getMessage());\n" +
" }\n" +
" }\n" +
"}\n"
},
getCompilerOptions(),
"",
"Value in position 1 must not be null\n" +
"Shouldn\'t!");
}
public void testAssertNonNullElements() {
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"import static org.eclipse.jdt.annotation.Checks.*;\n" +
"import java.util.*;\n" +
"public class X {\n" +
" @SuppressWarnings(\"null\")\n" +
" static @NonNull String hide(String some) {\n" +
" return some;\n" +
" }\n" +
" public static void main(String... args) {\n" +
" @NonNull List<String> myList = new ArrayList<>();\n" +
" myList.add(\"foo\");\n" +
" myList.add(null);\n" +
" try {\n" +
" assertNonNullElements(myList);\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(npe.getMessage());\n" +
" }\n" +
" @NonNull List<@NonNull String> myList2 = new ArrayList<>();\n" +
" myList2.add(\"foo\");\n" +
" myList2.add(hide(null));\n" +
" try {\n" +
" assertNonNullElements(myList2, \"Shouldn't!\");\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(npe.getMessage());\n" +
" }\n" +
" }\n" +
"}\n"
},
getCompilerOptions(),
"",
"Value in position 1 must not be null\n" +
"Shouldn\'t!");
}
public void testRequireNonNull() {
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"import static org.eclipse.jdt.annotation.Checks.*;\n" +
"public class X {\n" +
" @SuppressWarnings(\"null\")\n" +
" static @NonNull String hide(String some) {\n" +
" return some;\n" +
" }\n" +
" static void test(@Nullable String str, @Nullable X x) {\n" +
" @NonNull String nnStr;\n" +
" @NonNull X nnX;\n" +
" try {\n" +
" nnStr = requireNonNull(str);\n" +
" nnX = requireNonNull(null, \"Shouldn\'t!\");\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(npe.getMessage());\n" +
" }\n" +
" }\n" +
" public static void main(String... args) {\n" +
" test(\"foo\", null);\n" +
" }\n" +
"}\n"
},
getCompilerOptions(),
"",
"Shouldn\'t!");
}
public void testRequireNonEmptyString() {
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"import static org.eclipse.jdt.annotation.Checks.*;\n" +
"public class X {\n" +
" @SuppressWarnings(\"null\")\n" +
" static @NonNull String hide(String some) {\n" +
" return some;\n" +
" }\n" +
" static void test(@Nullable String str1, @Nullable String str2) {\n" +
" @NonNull String nnStr;\n" +
" try {\n" +
" nnStr = requireNonEmpty(str1);\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(\"npe:\"+npe.getMessage());\n" +
" }\n" +
" try {\n" +
" nnStr = requireNonEmpty(str2, \"Shouldn't!\");\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(\"npe\"+npe.getMessage());\n" +
" } catch (IllegalArgumentException iae) {\n" +
" System.out.println(iae.getMessage());\n" +
" }\n" +
" }\n" +
" public static void main(String... args) {\n" +
" test(null, \"\");\n" +
" }\n" +
"}\n"
},
getCompilerOptions(),
"",
"npe:null\n" +
"Shouldn\'t!");
}
public void testRequireNonEmptyCollection() {
runConformTestWithLibs(
new String[] {
"X.java",
"import java.util.*;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"import static org.eclipse.jdt.annotation.Checks.*;\n" +
"public class X {\n" +
" static void test(@Nullable Collection<String> strs, @Nullable Collection<String> strs1, Collection<String> strs2) {\n" +
" @NonNull Collection<String> nnStrs;\n" +
" try {\n" +
" nnStrs = requireNonEmpty(strs);\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(\"NPE:\"+npe.getMessage());\n" +
" }\n" +
" try {\n" +
" nnStrs = requireNonEmpty(strs1);\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(\"npe:\"+npe.getMessage());\n" +
" }\n" +
" try {\n" +
" nnStrs = requireNonEmpty(strs2, \"Shouldn't!\");\n" +
" } catch (NullPointerException npe) {\n" +
" System.out.println(\"npe\"+npe.getMessage());\n" +
" } catch (IllegalArgumentException iae) {\n" +
" System.out.println(iae.getMessage());\n" +
" }\n" +
" }\n" +
" public static void main(String... args) {\n" +
" test(Collections.singletonList(\"good\"), null, Collections.emptyList());\n" +
" }\n" +
"}\n"
},
getCompilerOptions(),
"",
"npe:null\n" +
"Shouldn\'t!");
}
public void testIsNull() {
Map<String, String> compilerOptions = getCompilerOptions();
compilerOptions.put(JavaCore.COMPILER_PB_SUPPRESS_OPTIONAL_ERRORS, JavaCore.ENABLED);
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"import static org.eclipse.jdt.annotation.Checks.*;\n" +
"public class X {\n" +
" @SuppressWarnings(\"null\")\n" +
" static <T> @NonNull T hide(T some) {\n" +
" return some;\n" +
" }\n" +
" static void test(@NonNull X x1, @NonNull X x2, @NonNull X x3) {\n" +
" if (isNull(x1))\n" +
" System.out.println(\"IS NULL\");\n" +
" if (isAnyNull(x2, x1))\n" +
" System.out.println(\"IS ANY NULL 1\");\n" +
" if (isAnyNull(x2, x3))\n" +
" System.out.println(\"IS ANY NULL 2\");\n" +
" }\n" +
" public static void main(String... args) {\n" +
" test(hide(null), new X(), new X());\n" +
" }\n" +
"}\n"
},
compilerOptions,
"",
"IS NULL\n" +
"IS ANY NULL 1");
}
public void testAsNullable() {
runConformTestWithLibs(
new String[] {
"X.java",
"import java.util.*;\n" +
"import static org.eclipse.jdt.annotation.Checks.*;\n" +
"public class X {\n" +
" static void test(Optional<X> xopt) {\n" +
" if (xopt != null) {\n" +
" X x = asNullable(xopt);\n" +
" if (x == null)\n" +
" System.out.println(\"NULL\");\n" +
" }\n" +
" }\n" +
" public static void main(String... args) {\n" +
" test(Optional.ofNullable(null));\n" +
" }\n" +
"}\n"
},
getCompilerOptions(),
"",
"NULL");
}
public void testNonNullElse() {
runConformTestWithLibs(
new String[] {
"X.java",
"import java.util.function.*;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"import static org.eclipse.jdt.annotation.Checks.*;\n" +
"public class X {\n" +
" static void test(String str, String noStr, @NonNull Supplier<@NonNull String> prov) {\n" +
" System.out.println(nonNullElse(str, \"ELSE1\"));\n" +
" System.out.println(nonNullElse(noStr, \"ELSE2\"));\n" +
" System.out.println(nonNullElseGet(str, () -> \"ELSE3\"));\n" +
" System.out.println(nonNullElseGet(noStr, prov));\n" +
" }\n" +
" public static void main(String... args) {\n" +
" test(\"good\", null, () -> \"ELSE4\");\n" +
" }\n" +
"}\n"
},
getCompilerOptions(),
"",
"good\n" +
"ELSE2\n" +
"good\n" +
"ELSE4");
}
public void _testIfNonNull() { // FIXME: see https://bugs.eclipse.org/489609 - [1.8][null] null annotation on wildcard is dropped during inference
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"import static org.eclipse.jdt.annotation.Checks.*;\n" +
"public class X {\n" +
" static void test(@Nullable String str) {\n" +
" ifNonNull(str, s -> print(s));\n" +
" }\n" +
" static void print(@NonNull String s) {\n" +
" System.out.print(s);\n" +
" }\n" +
" public static void main(String... args) {\n" +
" test(\"good\");\n" +
" }\n" +
"}\n"
},
getCompilerOptions(),
"",
"good");
}
public void testBooleanNullAssertions() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import java.util.Objects;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" public void demonstrateNotWorkingNullCheck() {\n" +
" Object mayBeNull = null;\n" +
" if (Math.random() > 0.5) {\n" +
" mayBeNull = new Object();\n" +
" }\n" +
" if (Object.class.isInstance(mayBeNull)) {\n" +
" mayBeNull.toString();\n" +
" }\n" +
" }\n" +
" public void negatedNullCheck() {\n" +
" Object mayBeNull = null;\n" +
" if (Math.random() > 0.5) {\n" +
" mayBeNull = new Object();\n" +
" }\n" +
" if (!Objects.nonNull(mayBeNull)) {\n" +
" System.out.println(\"not\");\n" +
" } else {\n" +
" mayBeNull.toString();\n" +
" }\n" +
" if (!(Integer.class.isInstance(mayBeNull) || Long.class.isInstance(mayBeNull))) {\n" +
" mayBeNull.toString(); // still only a potential problem\n" +
" }\n" +
" }\n" +
" public void nullCheckAlgegra() {\n" +
" Object mayBeNull = null;\n" +
" if (Math.random() > 0.5) {\n" +
" mayBeNull = new Object();\n" +
" }\n" +
" if (Math.random() > 0.5 && Object.class.isInstance(mayBeNull)) {\n" +
" mayBeNull.toString();\n" + // both operands are true
" }\n" +
" if (!Object.class.isInstance(mayBeNull) || Math.random() > 0.5) {\n" +
" System.out.println(\"not\");\n" +
" } else {\n" +
" mayBeNull.toString();\n" + // both operands are false
" }\n" +
" if (Object.class.isInstance(mayBeNull) && mayBeNull.equals(\"hi\"))\n" + // second evaluated only when first is true
" System.out.println(\"equal\");\n" +
" if (Objects.isNull(mayBeNull) || mayBeNull.equals(\"hi\"))\n" + // second evaluated only when first is false
" System.out.println(\"equal or null\");\n" +
" }\n" +
" public void objectsUtils() {\n" +
" Object mayBeNull = null;\n" +
" if (Math.random() > 0.5) {\n" +
" mayBeNull = new Object();\n" +
" }\n" +
" String s = Objects.nonNull(mayBeNull) ? mayBeNull.toString(): null;\n" +
" if (Objects.isNull(mayBeNull) || Math.random() > 0.5) {\n" +
" System.out.println(\"not\");\n" +
" } else {\n" +
" mayBeNull.toString();\n" +
" }\n" +
" }\n" +
" public void loops() {\n" +
" Object mayBeNull = null;\n" +
" if (Math.random() > 0.5) {\n" +
" mayBeNull = new Object();\n" +
" }\n" +
" for (; Objects.nonNull(mayBeNull); mayBeNull=next(mayBeNull)) {\n" +
" mayBeNull.toString();\n" + // guarded by the condition
" }\n" +
" mayBeNull.toString(); // can only be null after the loop\n" +
" Object initiallyNN = new Object();\n" +
" while (Objects.nonNull(initiallyNN)) {\n" +
" initiallyNN.toString();\n" + // guarded by the condition
" initiallyNN = next(initiallyNN);\n" +
" }\n" +
" initiallyNN.toString(); // can only be null after the loop\n" +
" }\n" +
" @Nullable Object next(Object o) { return o; }\n" +
"}\n"
},
getCompilerOptions(),
"----------\n" +
"1. ERROR in X.java (at line 24)\n" +
" mayBeNull.toString(); // still only a potential problem\n" +
" ^^^^^^^^^\n" +
"Potential null pointer access: The variable mayBeNull may be null at this location\n" +
"----------\n" +
"2. ERROR in X.java (at line 65)\n" +
" mayBeNull.toString(); // can only be null after the loop\n" +
" ^^^^^^^^^\n" +
"Null pointer access: The variable mayBeNull can only be null at this location\n" +
"----------\n" +
"3. ERROR in X.java (at line 71)\n" +
" initiallyNN.toString(); // can only be null after the loop\n" +
" ^^^^^^^^^^^\n" +
"Null pointer access: The variable initiallyNN can only be null at this location\n" +
"----------\n");
}
public void testBug465085_comment12() {
Map<String, String> options = getCompilerOptions();
options.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.ERROR);
runConformTest(
new String[] {
"Snippet.java",
"import java.util.Collection;\n" +
"\n" +
"public class Snippet {\n" +
" int instanceCount(Collection<?> elements, Class<?> clazz) {\n" +
" int count = 0;\n" +
" for (Object o : elements) { // warning here: \"The value of the local variable o is not used\"\n" +
" if (clazz.isInstance(o)) {\n" +
" count++;\n" +
" }\n" +
" }\n" +
" return count;\n" +
" }\n" +
"}\n"
},
options);
}
}