diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java index 0328324..4cb33a0 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java @@ -52,7 +52,7 @@ private static final Main MAIN = new Main(null/*outWriter*/, null/*errWriter*/, false/*systemExit*/, null/*options*/, null/*progress*/); static { -// TESTS_NAMES = new String[] { "test295_warn_options" }; + TESTS_NAMES = new String[] { "test312_warn_options" }; // TESTS_NUMBERS = new int[] { 306 }; // TESTS_RANGE = new int[] { 298, -1 }; } @@ -12384,4 +12384,73 @@ "1 problem (1 warning)", true); } + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=325342 +// -warn option - regression tests to check option nullAnnot +// Null warnings because of annotations +public void test312_warn_options() { + this.runConformTest( + new String[] { + "X.java", + "import static java.lang.annotation.ElementType.CONSTRUCTOR;\n" + + "import static java.lang.annotation.ElementType.METHOD;\n" + + "import static java.lang.annotation.ElementType.PACKAGE;\n" + + "import static java.lang.annotation.ElementType.PARAMETER;\n" + + "import static java.lang.annotation.ElementType.TYPE;\n" + + "import java.lang.annotation.Documented;\n" + + "import java.lang.annotation.Retention;\n" + + "import java.lang.annotation.RetentionPolicy;\n" + + "import java.lang.annotation.Target;\n" + + "public class X {\n" + + " public void test() { Object o = null; o.toString();}\n" + + " @NonNull Object foo(@Nullable Object o, @NonNull Object o2) {\n" + + " if (o.toString() == \"\"){ return null;}\n" + + " if (o2 == null) {}\n" + + " goo(null).toString();\n" + + " Object local = null;\n" + + " o.toString();\n" + + " Zork z;\n" + + " return null;\n" + + " }\n" + + " @Nullable Object goo(@NonNull Object o2) {\n" + + " return new Object();\n" + + " }\n" + + " @NonNullByDefault Object hoo(Object o2) {\n" + + " if (o2 == null){}\n" + + " if (o2 == null){\n" + + " return null;\n" + + " }\n" + + " return new Object();\n" + + " }\n" + + "}\n"+ + "@Documented\n" + + "@Retention(RetentionPolicy.CLASS)\n" + + "@Target({ METHOD, PARAMETER })\n" + + "@interface NonNull{\n" + + "}\n" + + "@Documented\n" + + "@Retention(RetentionPolicy.CLASS)\n" + + "@Target({ METHOD, PARAMETER })\n" + + "@interface Nullable{\n" + + "}\n" + + "@Documented\n" + + "@Retention(RetentionPolicy.CLASS)\n" + + "@Target({ PACKAGE, TYPE, METHOD, CONSTRUCTOR })\n" + + "@interface NonNullByDefault{\n" + + "}" + }, + "\"" + OUTPUT_DIR + File.separator + "X.java\"" +// + " -sourcepath \"" + OUTPUT_DIR + "\"" + + " -1.5" + + " -warn:+nullAnnot(p.NonNull|p.Nullable|p.NonNullByDefault) -proc:none -d \"" + OUTPUT_DIR + "\"", + "", + "----------\n" + + "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 4)\n" + + " if (o == null && o.toString() == \"\"){}\n" + + " ^\n" + + "Potential null pointer access: The field o may be null at this location\n" + + "----------\n" + + "1 problem (1 warning)", + true); +} } diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java index 89562a4..cad8a7a 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java @@ -3409,7 +3409,40 @@ CompilerOptions.OPTION_IncludeFieldsInNullAnalysis, isEnabling ? CompilerOptions.ENABLED : CompilerOptions.DISABLED); return; - } + } else if (token.startsWith("nullAnnot")) { //$NON-NLS-1$ + String unusedWarningExcludingAnnotations = Util.EMPTY_STRING; + int start = token.indexOf('('); + int end = token.indexOf(')'); + String nonNullAnnotName = null, nullableAnnotName = null, nonNullByDefaultAnnotName = null; + if (isEnabling && start >= 0 && end >= 0 && start < end){ + unusedWarningExcludingAnnotations = token.substring(start+1, end).trim(); + int separator1 = unusedWarningExcludingAnnotations.indexOf('|'); + if (separator1 == -1) throw new IllegalArgumentException(this.bind("configure.invalidNullAnnot", token)); //$NON-NLS-1$ + nonNullAnnotName = unusedWarningExcludingAnnotations.substring(0, separator1).trim(); + if (nonNullAnnotName.length() == 0) throw new IllegalArgumentException(this.bind("configure.invalidNullAnnot", token)); //$NON-NLS-1$ + int separator2 = unusedWarningExcludingAnnotations.indexOf('|', separator1 + 1); + if (separator2 == -1) throw new IllegalArgumentException(this.bind("configure.invalidNullAnnot", token)); //$NON-NLS-1$ + nullableAnnotName = unusedWarningExcludingAnnotations.substring(separator1 + 1, separator2).trim(); + if (nullableAnnotName.length() == 0) throw new IllegalArgumentException(this.bind("configure.invalidNullAnnot", token)); //$NON-NLS-1$ + nonNullByDefaultAnnotName = unusedWarningExcludingAnnotations.substring(separator2 + 1).trim(); + if (nonNullByDefaultAnnotName.length() == 0) throw new IllegalArgumentException(this.bind("configure.invalidNullAnnot", token)); //$NON-NLS-1$ + this.options.put( + CompilerOptions.OPTION_NonNullAnnotationName, nonNullAnnotName); + this.options.put( + CompilerOptions.OPTION_NullableAnnotationName, nonNullAnnotName); + this.options.put( + CompilerOptions.OPTION_NonNullByDefaultAnnotationName, nonNullAnnotName); + } + this.options.put( + CompilerOptions.OPTION_AnnotationBasedNullAnalysis, + isEnabling ? CompilerOptions.ENABLED : CompilerOptions.DISABLED); + setSeverity(CompilerOptions.OPTION_ReportNullSpecViolation, severity, isEnabling); + setSeverity(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, severity, isEnabling); + setSeverity(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, severity, isEnabling); + setSeverity(CompilerOptions.OPTION_ReportRedundantNullAnnotation, severity, isEnabling); + return; + } + break; case 'o' : if (token.equals("over-sync") /*|| token.equals("syncOverride")*/) { //$NON-NLS-1$ diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties index e2e6a74..80dc147 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties @@ -103,6 +103,9 @@ configure.differentencodings=Found encoding {0}. Different encodings were specified: {1} configure.differentencoding=Found encoding {0}. A different encoding was specified: {1} +### null annotations +configure.invalidNullAnnot = Token {0} is not in the expected format "nullAnnot( | | )" + ### requestor requestor.error = {0}. ERROR in {1} requestor.warning = {0}. WARNING in {1} @@ -306,6 +309,7 @@ \ nls string literal lacking non-nls tag //$NON-NLS-$\n\ \ noEffectAssign + assignment without effect\n\ \ null potential missing or redundant null check\n\ +\ nullAnnot(qualified annot. names separated by '|') + null analysis with annot.\n\ \ nullDereference + missing null check\n\ \ nullFields + null analysis for fields\n\ \ over-ann missing @Override annotation (superclass)\n\ diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java index 1d6cf3f..8c81a4a 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java @@ -846,11 +846,12 @@ case NullReference : case PotentialNullReference : case RedundantNullCheck : + return "null"; //$NON-NLS-1$ case NullSpecViolation : case PotentialNullSpecViolation : case NullSpecInsufficientInfo : case RedundantNullAnnotation : - return "null"; //$NON-NLS-1$ + return "nullAnnot"; //$NON-NLS-1$ case FallthroughCase : return "fallthrough"; //$NON-NLS-1$ case OverridingMethodWithoutSuperInvocation : @@ -917,6 +918,8 @@ return IrritantSet.NLS; if ("null".equals(warningToken)) //$NON-NLS-1$ return IrritantSet.NULL; + if ("nullAnnot".equals(warningToken)) //$NON-NLS-1$ + return IrritantSet.NULLANNOT; break; case 'r' : if ("rawtypes".equals(warningToken)) //$NON-NLS-1$ diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java index 2de9409..789f641 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -50,6 +50,7 @@ public static final IrritantSet INCOMPLETE_SWITCH = new IrritantSet(CompilerOptions.IncompleteEnumSwitch); public static final IrritantSet NLS = new IrritantSet(CompilerOptions.NonExternalizedString); public static final IrritantSet NULL = new IrritantSet(CompilerOptions.NullReference); + public static final IrritantSet NULLANNOT = new IrritantSet(CompilerOptions.NullSpecViolation); public static final IrritantSet RAW = new IrritantSet(CompilerOptions.RawTypeReference); public static final IrritantSet RESTRICTION = new IrritantSet(CompilerOptions.ForbiddenReference); public static final IrritantSet SERIAL = new IrritantSet(CompilerOptions.MissingSerialVersion); @@ -119,8 +120,9 @@ .set(CompilerOptions.TypeHiding); NULL .set(CompilerOptions.PotentialNullReference) - .set(CompilerOptions.RedundantNullCheck) - .set(CompilerOptions.NullSpecViolation) + .set(CompilerOptions.RedundantNullCheck); + + NULLANNOT .set(CompilerOptions.PotentialNullSpecViolation) .set(CompilerOptions.NullSpecInsufficientInfo) .set(CompilerOptions.RedundantNullAnnotation);