diff --git a/java.hints/src/org/netbeans/modules/java/hints/bugs/NPECheck.java b/java.hints/src/org/netbeans/modules/java/hints/bugs/NPECheck.java --- a/java.hints/src/org/netbeans/modules/java/hints/bugs/NPECheck.java +++ b/java.hints/src/org/netbeans/modules/java/hints/bugs/NPECheck.java @@ -37,6 +37,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedList; @@ -364,36 +365,65 @@ return result; } - private static State getStateFromAnnotations(CompilationInfo info, Element e) { - return getStateFromAnnotations(info, e, State.POSSIBLE_NULL); - } - private static final AnnotationMirrorGetter OVERRIDE_ANNOTATIONS = Lookup.getDefault().lookup(AnnotationMirrorGetter.class); - private static State getStateFromAnnotations(CompilationInfo info, Element e, State def) { - if (e == null) return def; + private static State getStateFromAnnotations(CompilationInfo info, Element e) { + if (e == null) { + return State.POSSIBLE_NULL; + } Iterable mirrors = OVERRIDE_ANNOTATIONS != null ? OVERRIDE_ANNOTATIONS.getAnnotationMirrors(info, e) : null; - if (mirrors == null) mirrors = e.getAnnotationMirrors(); + if (mirrors == null) { + mirrors = e.getAnnotationMirrors(); + } - for (AnnotationMirror am : mirrors) { - String simpleName = ((TypeElement) am.getAnnotationType().asElement()).getSimpleName().toString(); + Set names = getSimpleNames(mirrors); - if ("Nullable".equals(simpleName) || "NullAllowed".equals(simpleName)) { + if (containsAny(names, "Nullable", "NullAllowed", "CheckForNull")) { return State.POSSIBLE_NULL_REPORT; } - if ("CheckForNull".equals(simpleName)) { + if (containsAny(names, "NotNull", "NonNull", "Nonnull")) { + return State.NOT_NULL; + } + + names = getSimpleNames(info.getElements().getPackageOf(e).getAnnotationMirrors()); + + if (e.getKind() == ElementKind.PARAMETER) { + if (names.contains("ParametersAreNonnullByDefault")) { + return State.NOT_NULL; + } else if (names.contains("ParametersAreNullableByDefault")) { return State.POSSIBLE_NULL_REPORT; } - - if ("NotNull".equals(simpleName) || "NonNull".equals(simpleName) || "Nonnull".equals(simpleName)) { + } else if (e.getKind() == ElementKind.METHOD && containsAny(names, "ReturnValuesAreNonnullByDefault", "ReturnTypesAreNonnullByDefault")) { return State.NOT_NULL; } + return State.POSSIBLE_NULL; } - return def; + private static Set getSimpleNames(Iterable mirrors) { + Iterator it = mirrors.iterator(); + if (!it.hasNext()) { + return Collections.emptySet(); + } + Set names = new HashSet(); + while (it.hasNext()) { + names.add(((TypeElement) it.next().getAnnotationType().asElement()).getSimpleName().toString()); + } + return names; + } + + private static boolean containsAny(Set set, String... values) { + if (set.isEmpty()) { + return false; + } + for (String v : values) { + if (set.contains(v)) { + return true; + } + } + return false; } public interface AnnotationMirrorGetter { diff --git a/java.hints/test/unit/src/org/netbeans/modules/java/hints/bugs/NPECheckTest.java b/java.hints/test/unit/src/org/netbeans/modules/java/hints/bugs/NPECheckTest.java --- a/java.hints/test/unit/src/org/netbeans/modules/java/hints/bugs/NPECheckTest.java +++ b/java.hints/test/unit/src/org/netbeans/modules/java/hints/bugs/NPECheckTest.java @@ -28,7 +28,6 @@ package org.netbeans.modules.java.hints.bugs; -import junit.framework.TestSuite; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.java.hints.test.api.HintTest; import org.openide.filesystems.FileUtil; @@ -1476,6 +1475,72 @@ .assertWarnings(); } + public void testParametersAreNonnullByDefault() throws Exception { + createWithAnnotatedTestPackage("ParametersAreNonnullByDefault", + "" + + "package test;\n" + + "class Test {\n" + + " public int getLength(String s) {\n" + + " return s.length();\n" + + " }\n" + + "}") + .run(NPECheck.class) + .assertWarnings(); + } + + public void testParametersAreNonnullByDefaultUnnecessaryCheck() throws Exception { + createWithAnnotatedTestPackage("ParametersAreNonnullByDefault", + "" + + "package test;\n" + + "class Test {\n" + + " public int getLength(String s) {\n" + + " if (s == null) {\n" + + " return 0;\n" + + " }\n" + + " return s.length();\n" + + " }\n" + + "}") + .run(NPECheck.class) + .assertWarnings("3:12-3:21:verifier:ERR_NotNull"); + } + + public void testParametersAreNullableByDefault() throws Exception { + createWithAnnotatedTestPackage("ParametersAreNullableByDefault", + "" + + "package test;\n" + + "class Test {\n" + + " public int getLength(String s) {\n" + + " return s.length();\n" + + " }\n" + + "}") + .run(NPECheck.class) + .assertWarnings("3:17-3:23:verifier:Possibly Dereferencing null"); + } + + public void testReturnValuesAreNonnullByDefault() throws Exception { + createWithAnnotatedTestPackage("ReturnValuesAreNonnullByDefault", + "" + + "package test;\n" + + "class Test {\n" + + " public String returnNull() {\n" + + " return null;\n" + + " }\n" + + "}") + .run(NPECheck.class) + .assertWarnings("3:15-3:19:verifier:ERR_ReturningNullFromNonNull"); + } + + private static HintTest createWithAnnotatedTestPackage(String annotation, String code) throws Exception { + return HintTest.create() + .input(code) + .input("test/package-info.java", + "@" + annotation + + "\n" + + "package test;" + + "\n" + + "@interface " + annotation + " {}\n"); + } + private void performAnalysisTest(String fileName, String code, String... golden) throws Exception { HintTest.create() .input(fileName, code)