update jdt.core and tests to I20121031-2000 (4.3 M3 candidate).
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 3c9fb4e..2c864bd 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
@@ -20,6 +20,8 @@
* bug 370639 - [compiler][resource] restore the default for resource leak warnings
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 374605 - Unreasonable warning for enum-based switch statements
+ * bug 375366 - ECJ ignores unusedParameterIncludeDocCommentReference unless enableJavadoc option is set
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
@@ -82,7 +84,7 @@
"}\n";
static {
-// TESTS_NAMES = new String[] { "testBug375409e" };
+// TESTS_NAMES = new String[] { "testBug375366" };
// TESTS_NUMBERS = new int[] { 306 };
// TESTS_RANGE = new int[] { 298, -1 };
}
@@ -1925,6 +1927,7 @@
" <argument value=\"---OUTPUT_DIR_PLACEHOLDER---\"/>\n" +
" </command_line>\n" +
" <options>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnull\" value=\"org.eclipse.jdt.annotation.NonNull\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnullbydefault\" value=\"org.eclipse.jdt.annotation.NonNullByDefault\"/>\n" +
@@ -2036,6 +2039,7 @@
" <option key=\"org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.unusedPrivateMember\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.unusedTypeArgumentsForMethodInvocation\" value=\"warning\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.unusedTypeParameter\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.unusedWarningToken\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.processAnnotations\" value=\"disabled\"/>\n" +
@@ -7827,7 +7831,12 @@
" ^^^^\n" +
"The label next is never explicitly referenced\n" +
"----------\n" +
- "7 problems (7 warnings)",
+ "8. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 14)\n" +
+ " <T> void bar() {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "8 problems (8 warnings)",
true);
}
// -warn option - regression tests
@@ -8132,7 +8141,12 @@
" ^^^^\n" +
"The label next is never explicitly referenced\n" +
"----------\n" +
- "6 problems (6 warnings)",
+ "7. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 14)\n" +
+ " <T> void bar() {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "7 problems (7 warnings)",
true);
}
// -warn option - regression tests
@@ -8192,7 +8206,12 @@
" ^^^^\n" +
"The label next is never explicitly referenced\n" +
"----------\n" +
- "6 problems (6 warnings)",
+ "7. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 14)\n" +
+ " <T> void bar() {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "7 problems (7 warnings)",
true);
}
// -warn option - regression tests
@@ -8252,7 +8271,12 @@
" ^^^^^^\n" +
"Unused type arguments for the non generic method bar() of type X; it should not be parameterized with arguments <String>\n" +
"----------\n" +
- "6 problems (6 warnings)",
+ "7. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 14)\n" +
+ " <T> void bar() {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "7 problems (7 warnings)",
true);
}
// -warn option - regression tests
@@ -8312,7 +8336,12 @@
" ^^^^\n" +
"The label next is never explicitly referenced\n" +
"----------\n" +
- "6 problems (6 warnings)",
+ "7. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 14)\n" +
+ " <T> void bar() {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "7 problems (7 warnings)",
true);
}
// -warn option - regression tests
@@ -8372,7 +8401,12 @@
" ^^^^\n" +
"The label next is never explicitly referenced\n" +
"----------\n" +
- "6 problems (6 warnings)",
+ "7. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 14)\n" +
+ " <T> void bar() {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "7 problems (7 warnings)",
true);
}
// -warn option - regression tests
@@ -8432,7 +8466,12 @@
" ^^^^\n" +
"The label next is never explicitly referenced\n" +
"----------\n" +
- "6 problems (6 warnings)",
+ "7. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 14)\n" +
+ " <T> void bar() {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "7 problems (7 warnings)",
true);
}
// -warn option - regression tests
@@ -8492,7 +8531,12 @@
" ^^^^\n" +
"The label next is never explicitly referenced\n" +
"----------\n" +
- "6 problems (6 warnings)",
+ "7. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 14)\n" +
+ " <T> void bar() {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "7 problems (7 warnings)",
true);
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=211588
@@ -13537,4 +13581,177 @@
"",
true);
}
+// Bug 375366 - ECJ ignores unusedParameterIncludeDocCommentReference unless enableJavadoc option is set
+// when -properties is used process javadoc by default
+public void testBug375366a() throws IOException {
+ createOutputTestDirectory("regression/.settings");
+ Util.createFile(OUTPUT_DIR+"/.settings/org.eclipse.jdt.core.prefs",
+ "eclipse.preferences.version=1\n" +
+ "org.eclipse.jdt.core.compiler.problem.unusedParameter=warning\n");
+ this.runConformTest(
+ new String[] {
+ "bugs/warning/ShowBug.java",
+ "package bugs.warning;\n" +
+ "\n" +
+ "public class ShowBug {\n" +
+ " /**\n" +
+ " * \n" +
+ " * @param unusedParam\n" +
+ " */\n" +
+ " public void foo(Object unusedParam) {\n" +
+ " \n" +
+ " }\n" +
+ "}\n"
+ },
+ "\"" + OUTPUT_DIR + File.separator + "bugs" + File.separator + "warning" + File.separator + "ShowBug.java\""
+ + " -1.5"
+ + " -properties " + OUTPUT_DIR + File.separator +".settings" + File.separator + "org.eclipse.jdt.core.prefs "
+ + " -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "",
+ false /*don't flush output dir*/);
+}
+
+// Bug 375366 - ECJ ignores unusedParameterIncludeDocCommentReference unless enableJavadoc option is set
+// property file explicitly disables javadoc processing
+public void testBug375366b() throws IOException {
+ createOutputTestDirectory("regression/.settings");
+ Util.createFile(OUTPUT_DIR+"/.settings/org.eclipse.jdt.core.prefs",
+ "eclipse.preferences.version=1\n" +
+ "org.eclipse.jdt.core.compiler.problem.unusedParameter=warning\n" +
+ "org.eclipse.jdt.core.compiler.doc.comment.support=disabled\n");
+ this.runTest(
+ true, // compile OK, expecting only warning
+ new String[] {
+ "bugs/warning/ShowBug.java",
+ "package bugs.warning;\n" +
+ "\n" +
+ "public class ShowBug {\n" +
+ " /**\n" +
+ " * \n" +
+ " * @param unusedParam\n" +
+ " */\n" +
+ " public void foo(Object unusedParam) {\n" +
+ " \n" +
+ " }\n" +
+ "}\n"
+ },
+ "\"" + OUTPUT_DIR + File.separator + "bugs" + File.separator + "warning" + File.separator + "ShowBug.java\""
+ + " -1.5"
+ + " -properties " + OUTPUT_DIR + File.separator +".settings" + File.separator + "org.eclipse.jdt.core.prefs "
+ + " -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "----------\n" +
+ "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/bugs/warning/ShowBug.java (at line 8)\n" +
+ " public void foo(Object unusedParam) {\n" +
+ " ^^^^^^^^^^^\n" +
+ "The value of the parameter unusedParam is not used\n" +
+ "----------\n" +
+ "1 problem (1 warning)",
+ false /*don't flush output dir*/,
+ null /* progress */);
+}
+
+// Bug 375366 - ECJ ignores unusedParameterIncludeDocCommentReference unless enableJavadoc option is set
+// property file enables null annotation support
+public void testBug375366c() throws IOException {
+ createOutputTestDirectory("regression/.settings");
+ Util.createFile(OUTPUT_DIR+"/.settings/org.eclipse.jdt.core.prefs",
+ "eclipse.preferences.version=1\n" +
+ "org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled\n");
+ this.runNegativeTest(
+ new String[] {
+ "p/X.java",
+ "package p;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public class X {\n" +
+ " @NonNull Object foo(@Nullable Object o, @NonNull Object o2) {\n" +
+ " return this;\n" +
+ " }\n" +
+ "}\n" +
+ "class Y extends X {\n" +
+ " @Nullable Object foo(Object o, Object o2) { return null; }\n" +
+ "}\n",
+ "org/eclipse/jdt/annotation/NonNull.java",
+ NONNULL_ANNOTATION_CONTENT,
+ "org/eclipse/jdt/annotation/Nullable.java",
+ NULLABLE_ANNOTATION_CONTENT,
+ "org/eclipse/jdt/annotation/NonNullByDefault.java",
+ NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
+ },
+ "\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ + " -sourcepath \"" + OUTPUT_DIR + "\""
+ + " -1.5"
+ + " -properties " + OUTPUT_DIR + File.separator +".settings" + File.separator + "org.eclipse.jdt.core.prefs "
+ + " -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
+ " @Nullable Object foo(Object o, Object o2) { return null; }\n" +
+ " ^^^^^^^^^^^^^^^^\n" +
+ "The return type is incompatible with the @NonNull return from X.foo(Object, Object)\n" +
+ "----------\n" +
+ "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
+ " @Nullable Object foo(Object o, Object o2) { return null; }\n" +
+ " ^^^^^^\n" +
+ "Missing nullable annotation: inherited method from X declares this parameter as @Nullable\n" +
+ "----------\n" +
+ "3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
+ " @Nullable Object foo(Object o, Object o2) { return null; }\n" +
+ " ^^^^^^\n" +
+ "Missing non-null annotation: inherited method from X declares this parameter as @NonNull\n" +
+ "----------\n" +
+ "3 problems (3 errors)",
+ false/*don't flush*/);
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=385780
+public void test385780_warn_option() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X<T> {\n"+
+ "public <S> X() {\n"+
+ "}\n"+
+ "public void ph(int t) {\n"+
+ "}\n"+
+ "}\n"+
+ "interface doNothingInterface<T> {\n"+
+ "}\n"+
+ "class doNothing {\n"+
+ "public <T> void doNothingMethod() {"+
+ "}\n"+
+ "}\n"+
+ "class noerror {\n"+
+ "public <T> void doNothing(T t) {"+
+ "}"+
+ "}\n"
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " -warn:unusedTypeParameter -proc:none -1.7 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "----------\n" +
+ "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 1)\n" +
+ " public class X<T> {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 2)\n" +
+ " public <S> X() {\n" +
+ " ^\n" +
+ "Unused type parameter S\n" +
+ "----------\n" +
+ "3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 7)\n" +
+ " interface doNothingInterface<T> {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "4. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 10)\n" +
+ " public <T> void doNothingMethod() {}\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "4 problems (4 warnings)",
+ true);
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
index d9d4a26..249f0a0 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
@@ -16,6 +16,7 @@
* bug 365662 - [compiler][null] warn on contradictory and redundant null annotations
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 374605 - Unreasonable warning for enum-based switch statements
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
@@ -407,6 +408,8 @@
expectedProblemAttributes.put("ContradictoryNullAnnotations", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL));
expectedProblemAttributes.put("ComparingIdentical", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("ConflictingImport", new ProblemAttributes(CategorizedProblem.CAT_IMPORT));
+ expectedProblemAttributes.put("ConflictingNullAnnotations", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
+ expectedProblemAttributes.put("ConflictingInheritedNullAnnotations", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("ConstructorVarargsArgumentNeedCast", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("CorruptedSignature", new ProblemAttributes(CategorizedProblem.CAT_BUILDPATH));
expectedProblemAttributes.put("DeadCode", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
@@ -924,6 +927,7 @@
expectedProblemAttributes.put("UnusedPrivateMethod", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
expectedProblemAttributes.put("UnusedPrivateType", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
expectedProblemAttributes.put("UnusedTypeArgumentsForConstructorInvocation", new ProblemAttributes(CategorizedProblem.CAT_MEMBER));
+ expectedProblemAttributes.put("UnusedTypeParameter", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
expectedProblemAttributes.put("UnusedTypeArgumentsForMethodInvocation", new ProblemAttributes(CategorizedProblem.CAT_MEMBER));
expectedProblemAttributes.put("UnusedWarningToken", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
expectedProblemAttributes.put("UseAssertAsAnIdentifier", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE));
@@ -941,6 +945,7 @@
expectedProblemAttributes.put("WildcardConstructorInvocation", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("WildcardFieldAssignment", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("WildcardMethodInvocation", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
+ expectedProblemAttributes.put("IllegalArrayOfUnionType", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
//{ObjectTeams: new problems (incomplete list):
//expectedProblemAttributes.put("", new ProblemAttributes(CategorizedProblem.CAT_UNSPECIFIED));
expectedProblemAttributes.put("OTJ_RELATED", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL));
@@ -1193,6 +1198,8 @@
expectedProblemAttributes.put("CodeSnippetMissingMethod", SKIP);
expectedProblemAttributes.put("ComparingIdentical", new ProblemAttributes(JavaCore.COMPILER_PB_COMPARING_IDENTICAL));
expectedProblemAttributes.put("ConflictingImport", SKIP);
+ expectedProblemAttributes.put("ConflictingNullAnnotations", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION));
+ expectedProblemAttributes.put("ConflictingInheritedNullAnnotations", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION));
expectedProblemAttributes.put("ContradictoryNullAnnotations", SKIP);
expectedProblemAttributes.put("ConstructorVarargsArgumentNeedCast", new ProblemAttributes(JavaCore.COMPILER_PB_VARARGS_ARGUMENT_NEED_CAST));
expectedProblemAttributes.put("CorruptedSignature", SKIP);
@@ -1711,6 +1718,7 @@
expectedProblemAttributes.put("UnusedPrivateMethod", new ProblemAttributes(JavaCore.COMPILER_PB_UNUSED_PRIVATE_MEMBER));
expectedProblemAttributes.put("UnusedPrivateType", new ProblemAttributes(JavaCore.COMPILER_PB_UNUSED_PRIVATE_MEMBER));
expectedProblemAttributes.put("UnusedTypeArgumentsForConstructorInvocation", new ProblemAttributes(JavaCore.COMPILER_PB_UNUSED_TYPE_ARGUMENTS_FOR_METHOD_INVOCATION));
+ expectedProblemAttributes.put("UnusedTypeParameter", new ProblemAttributes(JavaCore.COMPILER_PB_UNUSED_TYPE_PARAMETER));
expectedProblemAttributes.put("UnusedTypeArgumentsForMethodInvocation", new ProblemAttributes(JavaCore.COMPILER_PB_UNUSED_TYPE_ARGUMENTS_FOR_METHOD_INVOCATION));
expectedProblemAttributes.put("UnusedWarningToken", new ProblemAttributes(JavaCore.COMPILER_PB_UNUSED_WARNING_TOKEN));
expectedProblemAttributes.put("UseAssertAsAnIdentifier", new ProblemAttributes(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER));
@@ -1728,6 +1736,7 @@
expectedProblemAttributes.put("WildcardConstructorInvocation", SKIP);
expectedProblemAttributes.put("WildcardFieldAssignment", SKIP);
expectedProblemAttributes.put("WildcardMethodInvocation", SKIP);
+ expectedProblemAttributes.put("IllegalArrayOfUnionType", SKIP);
//{ObjectTeams: new constants:
expectedProblemAttributes.put("OTJ_RELATED", SKIP);
expectedProblemAttributes.put("OTCHAP", SKIP);
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java
index 7c02b9d..97e02af 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java
@@ -12,6 +12,7 @@
* bug 349326 - [1.7] new warning for missing try-with-resources
* bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop)
* bug 383690 - [compiler] location of error re uninitialized final field should be aligned
+ * bug 391517 - java.lang.VerifyError on code that runs correctly in Eclipse 3.7 and eclipse 3.6
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
@@ -2603,6 +2604,41 @@
},
"");
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=391517
+// java.lang.VerifyError on code that runs correctly in Eclipse 3.7 and eclipse 3.6
+public void testBug391517() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "import java.io.PrintWriter;\n" +
+ "\n" +
+ "public class X {\n" +
+ "\n" +
+ " private static final int CONSTANT = 0;\n" +
+ "\n" +
+ " public static void main(String[] args) {\n" +
+ " // TODO Auto-generated method stub\n" +
+ "\n" +
+ " }\n" +
+ "\n" +
+ " static void addStackTrace(String prefix) {\n" +
+ " if (CONSTANT == 0) {\n" +
+ " return;\n" +
+ " }\n" +
+ " PrintWriter pw = null;\n" +
+ " new Exception().printStackTrace(pw);\n" +
+ " if (bar() == null) {\n" +
+ " System.out.println();\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " static Object bar() {\n" +
+ " return null;\n" +
+ " }\n" +
+ "}"
+ },
+ "");
+}
public static Class testClass() {
return FlowAnalysisTest.class;
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
index 7eba0a5..4396c65 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
@@ -51,6 +51,7 @@
options.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.IGNORE);
options.put(CompilerOptions.OPTION_ReportUnusedParameter, CompilerOptions.IGNORE);
options.put(CompilerOptions.OPTION_ReportUnusedPrivateMember, CompilerOptions.IGNORE);
+ options.put(CompilerOptions.OPTION_ReportUnusedTypeParameter, CompilerOptions.IGNORE);
return options;
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
index 00813bf..ffc6b67 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
@@ -44,6 +44,7 @@
protected Map getCompilerOptions() {
Map compilerOptions = super.getCompilerOptions();
compilerOptions.put(CompilerOptions.OPTION_ReportMissingOverrideAnnotationForInterfaceMethodImplementation, CompilerOptions.DISABLED);
+ compilerOptions.put(CompilerOptions.OPTION_ReportUnusedTypeParameter,CompilerOptions.IGNORE);
return compilerOptions;
}
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=322531
@@ -2598,4 +2599,54 @@
"C is a raw type. References to generic type C<T> should be parameterized\n" +
"----------\n");
}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=385780
+public void test385780() {
+ Map customOptions = getCompilerOptions();
+ customOptions.put(
+ CompilerOptions.OPTION_ReportUnusedTypeParameter,
+ CompilerOptions.ERROR);
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X<T> {\n"+
+ "public <S> X() {\n"+
+ "}\n"+
+ "public void ph(int t) {\n"+
+ "}\n"+
+ "}\n"+
+ "interface doNothingInterface<T> {\n"+
+ "}\n"+
+ "class doNothing {\n"+
+ "public <T> void doNothingMethod() {"+
+ "}\n"+
+ "}\n"+
+ "class noerror {\n"+
+ "public <T> void doNothing(T t) {"+
+ "}"+
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in X.java (at line 1)\n" +
+ " public class X<T> {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 2)\n" +
+ " public <S> X() {\n" +
+ " ^\n" +
+ "Unused type parameter S\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 7)\n" +
+ " interface doNothingInterface<T> {\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 10)\n" +
+ " public <T> void doNothingMethod() {}\n" +
+ " ^\n" +
+ "Unused type parameter T\n" +
+ "----------\n",
+ null, true, customOptions);
+}
}
\ No newline at end of file
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java
index 26870cf..0786394 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java
@@ -53,7 +53,7 @@
// 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[] { "testBug388630" };
+// TESTS_NAMES = new String[] { "testBug388281_09" };
// TESTS_NUMBERS = new int[] { 561 };
// TESTS_RANGE = new int[] { 1, 2049 };
}
@@ -3771,4 +3771,538 @@
"Potential null pointer access: The variable a may be null at this location\n" +
"----------\n");
}
+
+/* Content of Test388281.jar used in the following tests:
+
+// === package i (explicit annotations): ===
+package i;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+public interface I {
+ @NonNull Object m1(@Nullable Object a1);
+ @Nullable String m2(@NonNull Object a2);
+ Object m1(@Nullable Object o1, Object o2);
+}
+
+// === package i2 with package-info.java (default annot, canceled in one type): ===
+@org.eclipse.jdt.annotation.NonNullByDefault
+package i2;
+
+package i2;
+public interface I2 {
+ Object m1(Object a1);
+ String m2(Object a2);
+}
+
+package i2;
+public interface II extends i.I {
+ String m1(Object o1, Object o2);
+}
+
+package i2;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+@NonNullByDefault(false)
+public interface I2A {
+ Object m1(Object a1);
+ String m2(Object a2);
+}
+
+// === package c (no null annotations): ===
+package c;
+public class C1 implements i.I {
+ public Object m1(Object a1) {
+ System.out.println(a1.toString()); // (1)
+ return null; // (2)
+ }
+ public String m2(Object a2) {
+ System.out.println(a2.toString());
+ return null;
+ }
+ public Object m1(Object o1, Object o2) {
+ return null;
+ }
+}
+
+package c;
+public class C2 implements i2.I2 {
+ public Object m1(Object a1) {
+ return a1;
+ }
+ public String m2(Object a2) {
+ return a2.toString();
+ }
+}
+ */
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
+// Test whether null annotations from a super interface are respected
+// Class and its super interface both read from binary
+public void testBug388281_01() {
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String[] libs = new String[this.LIBS.length + 1];
+ System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
+ libs[this.LIBS.length] = path;
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, JavaCore.ENABLED);
+ runNegativeTest(
+ new String[] {
+ "Client.java",
+ "import c.C1;\n" +
+ "public class Client {\n" +
+ " void test(C1 c) {\n" +
+ " String s = c.m2(null); // (3)\n" +
+ " System.out.println(s.toUpperCase()); // (4)\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in Client.java (at line 4)\n" +
+ " String s = c.m2(null); // (3)\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
+ "----------\n" +
+ "2. ERROR in Client.java (at line 5)\n" +
+ " System.out.println(s.toUpperCase()); // (4)\n" +
+ " ^\n" +
+ "Potential null pointer access: The variable s may be null at this location\n" +
+ "----------\n",
+ libs,
+ true /* shouldFlush*/,
+ options);
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
+// Test whether null annotations from a super interface are respected
+// Class from source, its supers (class + super interface) from binary
+public void testBug388281_02() {
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String[] libs = new String[this.LIBS.length + 1];
+ System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
+ libs[this.LIBS.length] = path;
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, JavaCore.ENABLED);
+ runNegativeTest(
+ new String[] {
+ "ctest/C.java",
+ "package ctest;\n" +
+ "public class C extends c.C1 {\n" +
+ " @Override\n" +
+ " public Object m1(Object a1) {\n" +
+ " System.out.println(a1.toString()); // (1)\n" +
+ " return null; // (2)\n" +
+ " }\n" +
+ " @Override\n" +
+ " public String m2(Object a2) {\n" +
+ " System.out.println(a2.toString());\n" +
+ " return null;\n" +
+ " }\n" +
+ "}\n",
+ "Client.java",
+ "import ctest.C;\n" +
+ "public class Client {\n" +
+ " void test(C c) {\n" +
+ " String s = c.m2(null); // (3)\n" +
+ " System.out.println(s.toUpperCase()); // (4)\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in ctest\\C.java (at line 5)\n" +
+ " System.out.println(a1.toString()); // (1)\n" +
+ " ^^\n" +
+ "Potential null pointer access: The variable a1 may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in ctest\\C.java (at line 6)\n" +
+ " return null; // (2)\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
+ "----------\n" +
+ "----------\n" +
+ "1. ERROR in Client.java (at line 4)\n" +
+ " String s = c.m2(null); // (3)\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
+ "----------\n" +
+ "2. ERROR in Client.java (at line 5)\n" +
+ " System.out.println(s.toUpperCase()); // (4)\n" +
+ " ^\n" +
+ "Potential null pointer access: The variable s may be null at this location\n" +
+ "----------\n",
+ libs,
+ true /* shouldFlush*/,
+ options);
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
+// Test whether null annotations from a super interface trigger an error against the overriding implementation
+// Class from source, its super interface from binary
+public void testBug388281_03() {
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String[] libs = new String[this.LIBS.length + 1];
+ System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
+ libs[this.LIBS.length] = path;
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, JavaCore.ENABLED);
+ runNegativeTest(
+ new String[] {
+ "ctest/C.java",
+ "package ctest;\n" +
+ "public class C implements i.I {\n" +
+ " public Object m1(Object a1) {\n" +
+ " System.out.println(a1.toString()); // (1)\n" +
+ " return null; // (2)\n" +
+ " }\n" +
+ " public String m2(Object a2) {\n" +
+ " System.out.println(a2.toString());\n" +
+ " return null;\n" +
+ " }\n" +
+ " public Object m1(Object a1, Object a2) {\n" +
+ " System.out.println(a1.toString()); // (3)\n" +
+ " return null;\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in ctest\\C.java (at line 4)\n" +
+ " System.out.println(a1.toString()); // (1)\n" +
+ " ^^\n" +
+ "Potential null pointer access: The variable a1 may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in ctest\\C.java (at line 5)\n" +
+ " return null; // (2)\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
+ "----------\n" +
+ "3. ERROR in ctest\\C.java (at line 12)\n" +
+ " System.out.println(a1.toString()); // (3)\n" +
+ " ^^\n" +
+ "Potential null pointer access: The variable a1 may be null at this location\n" +
+ "----------\n",
+ libs,
+ true /* shouldFlush*/,
+ options);
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
+// Do inherit even if one parameter/return is annotated
+// also features some basic overloading
+public void testBug388281_04() {
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, JavaCore.ENABLED);
+ runNegativeTestWithLibs(
+ true /* shouldFlush*/,
+ new String[] {
+ "i/I.java",
+ "package i;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public interface I {\n" +
+ " @NonNull Object m1(@NonNull Object s1, @Nullable String s2);\n" +
+ " @Nullable Object m1(@Nullable String s1, @NonNull Object s2);\n" +
+ "}\n",
+ "ctest/C.java",
+ "package ctest;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public class C implements i.I {\n" +
+ " public Object m1(@Nullable Object o1, String s2) {\n" +
+ " System.out.println(s2.toString()); // (1)\n" +
+ " return null; // (2)\n" +
+ " }\n" +
+ " public @NonNull Object m1(String s1, Object o2) {\n" +
+ " System.out.println(s1.toString()); // (3)\n" +
+ " return new Object();\n" +
+ " }\n" +
+ "}\n"
+ },
+ options,
+ "----------\n" +
+ "1. ERROR in ctest\\C.java (at line 5)\n" +
+ " System.out.println(s2.toString()); // (1)\n" +
+ " ^^\n" +
+ "Potential null pointer access: The variable s2 may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in ctest\\C.java (at line 6)\n" +
+ " return null; // (2)\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
+ "----------\n" +
+ "3. ERROR in ctest\\C.java (at line 9)\n" +
+ " System.out.println(s1.toString()); // (3)\n" +
+ " ^^\n" +
+ "Potential null pointer access: The variable s1 may be null at this location\n" +
+ "----------\n");
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
+// Test whether null annotations from a super interface trigger an error against the overriding implementation
+// Class from source, its super interface from binary
+// Super interface subject to package level @NonNullByDefault
+public void testBug388281_05() {
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String[] libs = new String[this.LIBS.length + 1];
+ System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
+ libs[this.LIBS.length] = path;
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, JavaCore.ENABLED);
+ runNegativeTest(
+ new String[] {
+ "ctest/C.java",
+ "package ctest;\n" +
+ "public class C implements i2.I2 {\n" +
+ " public Object m1(Object a1) {\n" +
+ " System.out.println(a1.toString()); // silent\n" +
+ " return null; // (1)\n" +
+ " }\n" +
+ " public String m2(Object a2) {\n" +
+ " System.out.println(a2.toString());\n" +
+ " return null; // (2)\n" +
+ " }\n" +
+ "}\n",
+ "Client.java",
+ "import ctest.C;\n" +
+ "public class Client {\n" +
+ " void test(C c) {\n" +
+ " String s = c.m2(null); // (3)\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in ctest\\C.java (at line 5)\n" +
+ " return null; // (1)\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
+ "----------\n" +
+ "2. ERROR in ctest\\C.java (at line 9)\n" +
+ " return null; // (2)\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull String\' but the provided value is null\n" +
+ "----------\n" +
+ "----------\n" +
+ "1. ERROR in Client.java (at line 4)\n" +
+ " String s = c.m2(null); // (3)\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
+ "----------\n",
+ libs,
+ true /* shouldFlush*/,
+ options);
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
+// Conflicting annotations from several indirect super interfaces must be detected
+public void testBug388281_06() {
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String[] libs = new String[this.LIBS.length + 1];
+ System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
+ libs[this.LIBS.length] = path;
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, JavaCore.ENABLED);
+ runNegativeTest(
+ new String[] {
+ "ctest/C.java",
+ "package ctest;\n" +
+ "public class C extends c.C2 implements i2.I2A {\n" + // neither super has explicit annotations,
+ // but C2 inherits those from the default applicable at its super interface i2.I2
+ // whereas I2A cancels that same default
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in ctest\\C.java (at line 2)\n" +
+ " public class C extends c.C2 implements i2.I2A {\n" +
+ " ^\n" +
+ "The method m2(Object) from C2 cannot implement the corresponding method from I2A due to incompatible nullness constraints\n" +
+ "----------\n" +
+ "2. ERROR in ctest\\C.java (at line 2)\n" +
+ " public class C extends c.C2 implements i2.I2A {\n" +
+ " ^\n" +
+ "The method m1(Object) from C2 cannot implement the corresponding method from I2A due to incompatible nullness constraints\n" +
+ "----------\n",
+ libs,
+ true /* shouldFlush*/,
+ options);
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
+// report conflict between inheritance and default
+public void testBug388281_07() {
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, JavaCore.ENABLED);
+ runNegativeTestWithLibs(
+ new String[] {
+ "p1/Super.java",
+ "package p1;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public class Super {\n" +
+ " public @Nullable Object m(@Nullable Object arg) {\n" +
+ " return null;" +
+ " }\n" +
+ "}\n",
+ "p2/Sub.java",
+ "package p2;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "@NonNullByDefault\n" +
+ "public class Sub extends p1.Super {\n" +
+ " @Override\n" +
+ " public Object m(Object arg) { // (a)+(b) conflict at arg and return\n" +
+ " System.out.println(arg.toString()); // (1)\n" +
+ " return null;\n" +
+ " }\n" +
+ "}\n",
+ "Client.java",
+ "public class Client {\n" +
+ " void test(p2.Sub s) {\n" +
+ " Object result = s.m(null);\n" +
+ " System.out.println(result.toString()); // (2)\n" +
+ " }\n" +
+ "}\n"
+ },
+ options,
+ "----------\n" +
+ "1. ERROR in p2\\Sub.java (at line 6)\n" +
+ " public Object m(Object arg) { // (a)+(b) conflict at arg and return\n" +
+ " ^^^^^^\n" +
+ "The default \'@NonNull\' conflicts with the inherited \'@Nullable\' annotation in the overridden method from Super \n" +
+ "----------\n" +
+ "2. ERROR in p2\\Sub.java (at line 6)\n" +
+ " public Object m(Object arg) { // (a)+(b) conflict at arg and return\n" +
+ " ^^^\n" +
+ "The default \'@NonNull\' conflicts with the inherited \'@Nullable\' annotation in the overridden method from Super \n" +
+ "----------\n" +
+ "3. ERROR in p2\\Sub.java (at line 7)\n" +
+ " System.out.println(arg.toString()); // (1)\n" +
+ " ^^^\n" +
+ "Potential null pointer access: The variable arg may be null at this location\n" +
+ "----------\n" +
+ "----------\n" +
+ "1. ERROR in Client.java (at line 4)\n" +
+ " System.out.println(result.toString()); // (2)\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The variable result may be null at this location\n" +
+ "----------\n");
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
+// report conflict between inheritance and default - binary types
+public void testBug388281_08() {
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String[] libs = new String[this.LIBS.length + 1];
+ System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
+ libs[this.LIBS.length] = path;
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, JavaCore.ENABLED);
+ runNegativeTest(
+ new String[] {
+ "ctest/Ctest.java",
+ "package ctest;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "@NonNullByDefault\n" +
+ "public class Ctest implements i2.II {\n" +
+ " public Object m1(@Nullable Object a1) { // silent: conflict at a1 avoided\n" +
+ " return new Object();\n" +
+ " }\n" +
+ " public String m2(Object a2) { // (a) conflict at return\n" +
+ " return null;\n" +
+ " }\n" +
+ " public String m1(Object o1, Object o2) { // (b) conflict at o1\n" +
+ " System.out.println(o1.toString()); // (1) inherited @Nullable\n" +
+ " return null; // (2) @NonNullByDefault in i2.II\n" +
+ " }\n" +
+ "}\n",
+ "Client.java",
+ "public class Client {\n" +
+ " void test(ctest.Ctest c) {\n" +
+ " Object result = c.m1(null, null); // (3) 2nd arg @NonNullByDefault from i2.II\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in ctest\\Ctest.java (at line 8)\n" +
+ " public String m2(Object a2) { // (a) conflict at return\n" +
+ " ^^^^^^\n" +
+ "The default \'@NonNull\' conflicts with the inherited \'@Nullable\' annotation in the overridden method from I \n" +
+ "----------\n" +
+ "2. ERROR in ctest\\Ctest.java (at line 11)\n" +
+ " public String m1(Object o1, Object o2) { // (b) conflict at o1\n" +
+ " ^^\n" +
+ "The default \'@NonNull\' conflicts with the inherited \'@Nullable\' annotation in the overridden method from II \n" +
+ "----------\n" +
+ "3. ERROR in ctest\\Ctest.java (at line 12)\n" +
+ " System.out.println(o1.toString()); // (1) inherited @Nullable\n" +
+ " ^^\n" +
+ "Potential null pointer access: The variable o1 may be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in ctest\\Ctest.java (at line 13)\n" +
+ " return null; // (2) @NonNullByDefault in i2.II\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull String\' but the provided value is null\n" +
+ "----------\n" +
+ "----------\n" +
+ "1. ERROR in Client.java (at line 3)\n" +
+ " Object result = c.m1(null, null); // (3) 2nd arg @NonNullByDefault from i2.II\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
+ "----------\n",
+ libs,
+ true, // should flush
+ options);
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
+// difference between inherited abstract & non-abstract methods
+public void testBug388281_09() {
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_INHERIT_NULL_ANNOTATIONS, JavaCore.ENABLED);
+ runNegativeTestWithLibs(
+ new String[] {
+ "p1/Super.java",
+ "package p1;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public abstract class Super {\n" +
+ " public abstract @NonNull Object compatible(@Nullable Object arg);\n" +
+ " public @Nullable Object incompatible(int dummy, @NonNull Object arg) {\n" +
+ " return null;" +
+ " }\n" +
+ "}\n",
+ "p1/I.java",
+ "package p1;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public interface I {\n" +
+ " public @Nullable Object compatible(@NonNull Object arg);\n" +
+ " public @NonNull Object incompatible(int dummy, @Nullable Object arg);\n" +
+ "}\n",
+ "p2/Sub.java",
+ "package p2;\n" +
+ "public class Sub extends p1.Super implements p1.I {\n" +
+ " @Override\n" +
+ " public Object compatible(Object arg) {\n" +
+ " return this;\n" +
+ " }\n" +
+ " @Override\n" +
+ " public Object incompatible(int dummy, Object arg) {\n" +
+ " return null;\n" +
+ " }\n" +
+ "}\n"
+ },
+ options,
+ "----------\n" +
+ "1. ERROR in p2\\Sub.java (at line 4)\n" +
+ " public Object compatible(Object arg) {\n" +
+ " ^^^^^^\n" +
+ "Conflict between inherited null annotations \'@Nullable\' declared in I versus \'@NonNull\' declared in Super \n" +
+ "----------\n" +
+ "2. ERROR in p2\\Sub.java (at line 4)\n" +
+ " public Object compatible(Object arg) {\n" +
+ " ^^^^^^\n" +
+ "Conflict between inherited null annotations \'@NonNull\' declared in I versus \'@Nullable\' declared in Super \n" +
+ "----------\n" +
+ "3. ERROR in p2\\Sub.java (at line 8)\n" +
+ " public Object incompatible(int dummy, Object arg) {\n" +
+ " ^^^^^^\n" +
+ "Conflict between inherited null annotations \'@NonNull\' declared in I versus \'@Nullable\' declared in Super \n" +
+ "----------\n" +
+ "4. ERROR in p2\\Sub.java (at line 8)\n" +
+ " public Object incompatible(int dummy, Object arg) {\n" +
+ " ^^^^^^\n" +
+ "Conflict between inherited null annotations \'@Nullable\' declared in I versus \'@NonNull\' declared in Super \n" +
+ "----------\n");
+}
+
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/StackMapAttributeTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/StackMapAttributeTest.java
index 35d56a5..5e37b6f 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/StackMapAttributeTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/StackMapAttributeTest.java
@@ -1156,12 +1156,11 @@
" [pc: 19, pc: 102] local: x index: 1 type: X\n" +
" [pc: 2, pc: 111] local: i index: 2 type: int\n" +
" [pc: 90, pc: 102] local: diff index: 3 type: java.lang.Object\n" +
- " Stack map table: number of frames 8\n" +
+ " Stack map table: number of frames 7\n" +
" [pc: 5, full, stack: {}, locals: {java.lang.String[], _, int}]\n" +
" [pc: 38, full, stack: {}, locals: {java.lang.String[], X, int}]\n" +
" [pc: 44, same]\n" +
" [pc: 64, same]\n" +
- " [pc: 82, same]\n" +
" [pc: 85, same]\n" +
" [pc: 102, full, stack: {}, locals: {java.lang.String[], _, int}]\n" +
" [pc: 105, same]\n";
@@ -7992,4 +7991,174 @@
assertEquals("Wrong contents", expectedOutput, actualOutput);
}
}
+
+ // from https://bugs.eclipse.org/bugs/show_bug.cgi?id=385593#c1
+ public void test385593_1() throws Exception {
+ this.runConformTest(
+ new String[] {
+ "stackmap/StackMapTableFormatError.java",
+ "package stackmap;\n" +
+ "\n" +
+ "import java.util.Collection;\n" +
+ "import java.util.Collections;\n" +
+ "\n" +
+ "/**\n" +
+ " * If compiled with Eclipse (compiler target >= 1.6) this snippet causes the\n" +
+ " * error \"java.lang.ClassFormatError: StackMapTable format error: bad\n" +
+ " * verification type\" when executed with JaCoCo code coverage. JaCoCo seems to\n" +
+ " * get confused by unexpected stackmap frames generated by ECJ.\n" +
+ " */\n" +
+ "public class StackMapTableFormatError {\n" +
+ "\n" +
+ " public static Object exec(Collection<Object> set, Object a,\n" +
+ " boolean b) {\n" +
+ " for (Object e : set) {\n" +
+ " if (a != null && (e == null || b)) {\n" +
+ " continue;\n" +
+ " }\n" +
+ " return null;\n" +
+ " }\n" +
+ " return null;\n" +
+ " }\n" +
+ " \n" +
+ " public static void main(String[] args) {\n" +
+ " exec(Collections.emptySet(), null, false);\n" +
+ " }\n" +
+ "\n" +
+ "}\n"
+ });
+
+ ClassFileBytesDisassembler disassembler = ToolFactory.createDefaultClassFileBytesDisassembler();
+ byte[] classFileBytes = org.eclipse.jdt.internal.compiler.util.Util.getFileByteContent(new File(OUTPUT_DIR+File.separator+"stackmap"+File.separator+"StackMapTableFormatError.class"));
+ String actualOutput =
+ disassembler.disassemble(
+ classFileBytes,
+ "\n",
+ ClassFileBytesDisassembler.DETAILED);
+
+ String expectedOutput =
+ " // Method descriptor #15 (Ljava/util/Collection;Ljava/lang/Object;Z)Ljava/lang/Object;\n" +
+ " // Signature: (Ljava/util/Collection<Ljava/lang/Object;>;Ljava/lang/Object;Z)Ljava/lang/Object;\n" +
+ " // Stack: 1, Locals: 5\n" +
+ " public static java.lang.Object exec(java.util.Collection set, java.lang.Object a, boolean b);\n" +
+ " 0 aload_0 [set]\n" +
+ " 1 invokeinterface java.util.Collection.iterator() : java.util.Iterator [18] [nargs: 1]\n" +
+ " 6 astore 4\n" +
+ " 8 goto 36\n" +
+ " 11 aload 4\n" +
+ " 13 invokeinterface java.util.Iterator.next() : java.lang.Object [24] [nargs: 1]\n" +
+ " 18 astore_3 [e]\n" +
+ " 19 aload_1 [a]\n" +
+ " 20 ifnull 34\n" +
+ " 23 aload_3 [e]\n" +
+ " 24 ifnull 36\n" +
+ " 27 iload_2 [b]\n" +
+ " 28 ifeq 34\n" +
+ " 31 goto 36\n" +
+ " 34 aconst_null\n" +
+ " 35 areturn\n" +
+ " 36 aload 4\n" +
+ " 38 invokeinterface java.util.Iterator.hasNext() : boolean [30] [nargs: 1]\n" +
+ " 43 ifne 11\n" +
+ " 46 aconst_null\n" +
+ " 47 areturn\n" +
+ " Line numbers:\n" +
+ " [pc: 0, line: 16]\n" +
+ " [pc: 19, line: 17]\n" +
+ " [pc: 31, line: 18]\n" +
+ " [pc: 34, line: 20]\n" +
+ " [pc: 36, line: 16]\n" +
+ " [pc: 46, line: 22]\n" +
+ " Local variable table:\n" +
+ " [pc: 0, pc: 48] local: set index: 0 type: java.util.Collection\n" +
+ " [pc: 0, pc: 48] local: a index: 1 type: java.lang.Object\n" +
+ " [pc: 0, pc: 48] local: b index: 2 type: boolean\n" +
+ " [pc: 19, pc: 36] local: e index: 3 type: java.lang.Object\n" +
+ " Local variable type table:\n" +
+ " [pc: 0, pc: 48] local: set index: 0 type: java.util.Collection<java.lang.Object>\n" +
+ " Stack map table: number of frames 3\n" +
+ " [pc: 11, full, stack: {}, locals: {java.util.Collection, java.lang.Object, int, _, java.util.Iterator}]\n" +
+ " [pc: 34, full, stack: {}, locals: {java.util.Collection, java.lang.Object, int, java.lang.Object, java.util.Iterator}]\n" +
+ " [pc: 36, full, stack: {}, locals: {java.util.Collection, java.lang.Object, int, _, java.util.Iterator}]";
+ int index = actualOutput.indexOf(expectedOutput);
+ if (index == -1 || expectedOutput.length() == 0) {
+ System.out.println(Util.displayString(actualOutput, 2));
+ }
+ if (index == -1) {
+ assertEquals("Wrong contents", expectedOutput, actualOutput);
+ }
+ }
+
+ // from https://bugs.eclipse.org/bugs/show_bug.cgi?id=385593#c11
+ public void test385593_2() throws Exception {
+ this.runConformTest(
+ new String[] {
+ "snippet/X.java",
+ "package snippet;\n" +
+ "\n" +
+ "\n" +
+ "public class X { \n" +
+ " private void foo(boolean delete) { \n" +
+ " \n" +
+ " String s = bar(); \n" +
+ " StringBuffer buffer =new StringBuffer(); \n" +
+ " \n" +
+ " try { \n" +
+ " \n" +
+ " String[] datas = new String[] { \"\" }; \n" +
+ " Object[] data= new Object[] { s }; \n" +
+ " try { \n" +
+ " buffer.append(datas).append(data); \n" +
+ " } catch (Exception e) { \n" +
+ " if (e != null) \n" +
+ " throw e; \n" +
+ " return; \n" +
+ " } \n" +
+ " \n" +
+ " if (delete) \n" +
+ " buffer.delete(0, buffer.length()); \n" +
+ " \n" +
+ " } catch (Exception x) { \n" +
+ " } finally { \n" +
+ " buffer = null; \n" +
+ " } \n" +
+ " } \n" +
+ " \n" +
+ " String bar() { \n" +
+ " return \"\"; \n" +
+ " } \n" +
+ " \n" +
+ " public static void main(String[] args) { \n" +
+ " new X().foo(false); \n" +
+ " System.out.println(\"SUCCESS\"); \n" +
+ " } \n" +
+ "}\n"
+ });
+
+ ClassFileBytesDisassembler disassembler = ToolFactory.createDefaultClassFileBytesDisassembler();
+ byte[] classFileBytes = org.eclipse.jdt.internal.compiler.util.Util.getFileByteContent(new File(OUTPUT_DIR+File.separator+"snippet"+File.separator+"X.class"));
+ String actualOutput =
+ disassembler.disassemble(
+ classFileBytes,
+ "\n",
+ ClassFileBytesDisassembler.DETAILED);
+
+ String expectedOutput =
+ "Stack map table: number of frames 7\n" +
+ " [pc: 49, full, stack: {java.lang.Exception}, locals: {snippet.X, int, java.lang.String, java.lang.StringBuffer, java.lang.String[], java.lang.Object[]}]\n" +
+ " [pc: 59, append: {java.lang.Exception}]\n" +
+ " [pc: 62, chop 1 local(s)]\n" +
+ " [pc: 79, full, stack: {java.lang.Exception}, locals: {snippet.X, int, java.lang.String, java.lang.StringBuffer}]\n" +
+ " [pc: 86, same_locals_1_stack_item, stack: {java.lang.Throwable}]\n" +
+ " [pc: 93, same]\n" +
+ " [pc: 95, same]\n";
+
+ int index = actualOutput.indexOf(expectedOutput);
+ if (index == -1 || expectedOutput.length() == 0) {
+ System.out.println(Util.displayString(actualOutput, 2));
+ }
+ if (index == -1) {
+ assertEquals("Wrong contents", expectedOutput, actualOutput);
+ }
+ }
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TryStatement17Test.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TryStatement17Test.java
index ba8feb8..fafc9d3 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TryStatement17Test.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TryStatement17Test.java
@@ -1167,6 +1167,55 @@
},
"Done");
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=391092
+public void testBug391092() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " public static void main(String [] args) {\n" +
+ " try {\n" +
+ " } catch (NullPointerException | ArrayIndexOutOfBoundsException e []) {\n" +
+ " } catch (ClassCastException [] c) {\n" +
+ " } catch (ArrayStoreException a[]) {\n" +
+ " } catch (ArithmeticException | NegativeArraySizeException b[][] ) {\n" +
+ " } catch (ClassCastException[][] | ClassNotFoundException[] g) {\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in X.java (at line 4)\n" +
+ " } catch (NullPointerException | ArrayIndexOutOfBoundsException e []) {\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Illegal attempt to create arrays of union types\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 5)\n" +
+ " } catch (ClassCastException [] c) {\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^\n" +
+ "No exception of type ClassCastException[] can be thrown; an exception type must be a subclass of Throwable\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 6)\n" +
+ " } catch (ArrayStoreException a[]) {\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "No exception of type ArrayStoreException[] can be thrown; an exception type must be a subclass of Throwable\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 7)\n" +
+ " } catch (ArithmeticException | NegativeArraySizeException b[][] ) {\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Illegal attempt to create arrays of union types\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 8)\n" +
+ " } catch (ClassCastException[][] | ClassNotFoundException[] g) {\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "No exception of type ClassCastException[][] can be thrown; an exception type must be a subclass of Throwable\n" +
+ "----------\n" +
+ "6. ERROR in X.java (at line 8)\n" +
+ " } catch (ClassCastException[][] | ClassNotFoundException[] g) {\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "No exception of type ClassNotFoundException[] can be thrown; an exception type must be a subclass of Throwable\n" +
+ "----------\n");
+ }
public static Class testClass() {
return TryStatement17Test.class;
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/runtime/LocalVMLauncher.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/runtime/LocalVMLauncher.java
index 2e4d114..a7d925f 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/runtime/LocalVMLauncher.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/runtime/LocalVMLauncher.java
@@ -128,12 +128,12 @@
// does not properly handle spaces in arguments on Unix/Linux platforms.
String[] commandLine = getCommandLine();
- // DEBUG
- /*for (int i = 0; i < commandLine.length; i++) {
+ // DEBUG - temporarily enabled for https://bugs.eclipse.org/393149
+ for (int i = 0; i < commandLine.length; i++) {
System.out.print(commandLine[i] + " ");
}
System.out.println();
- */
+
vmProcess= Runtime.getRuntime().exec(commandLine);
} catch (IOException e) {
diff --git a/org.eclipse.jdt.core.tests.compiler/workspace/Test388281.jar b/org.eclipse.jdt.core.tests.compiler/workspace/Test388281.jar
new file mode 100644
index 0000000..7c66b72
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.compiler/workspace/Test388281.jar
Binary files differ
diff --git a/org.eclipse.jdt.core/META-INF/eclipse.inf b/org.eclipse.jdt.core/META-INF/eclipse.inf
index 32d8ee1..43380da 100644
--- a/org.eclipse.jdt.core/META-INF/eclipse.inf
+++ b/org.eclipse.jdt.core/META-INF/eclipse.inf
@@ -1 +1,2 @@
jarprocessor.exclude.children=true
+jarprocessor.exclude.pack=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 0cf2673..b4bd1c9 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
@@ -18,6 +18,7 @@
* bug 359721 - [options] add command line option for new warning token "resource"
* bug 365208 - [compiler][batch] command line options for annotation based null analysis
* bug 374605 - Unreasonable warning for enum-based switch statements
+ * bug 375366 - ECJ ignores unusedParameterIncludeDocCommentReference unless enableJavadoc option is set
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.batch;
@@ -2930,7 +2931,7 @@
for (Iterator iterator = properties.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry entry = (Map.Entry) iterator.next();
final String key = (String) entry.getKey();
- if (key.startsWith("org.eclipse.jdt.core.compiler.problem")) { //$NON-NLS-1$
+ if (key.startsWith("org.eclipse.jdt.core.compiler.")) { //$NON-NLS-1$
this.options.put(key, entry.getValue());
}
//{ObjectTeams: also use OT-options:
@@ -2939,6 +2940,19 @@
}
// SH}
}
+ // when using a properties file mimic relevant defaults from JavaCorePreferenceInitializer:
+ if (!properties.containsKey(CompilerOptions.OPTION_LocalVariableAttribute)) {
+ this.options.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.GENERATE);
+ }
+ if (!properties.containsKey(CompilerOptions.OPTION_PreserveUnusedLocal)) {
+ this.options.put(CompilerOptions.OPTION_PreserveUnusedLocal, CompilerOptions.PRESERVE);
+ }
+ if (!properties.containsKey(CompilerOptions.OPTION_DocCommentSupport)) {
+ this.options.put(CompilerOptions.OPTION_DocCommentSupport, CompilerOptions.ENABLED);
+ }
+ if (!properties.containsKey(CompilerOptions.OPTION_ReportForbiddenReference)) {
+ this.options.put(CompilerOptions.OPTION_ReportForbiddenReference, CompilerOptions.ERROR);
+ }
}
protected void enableAll(int severity) {
String newValue = null;
@@ -3991,10 +4005,14 @@
setSeverity(CompilerOptions.OPTION_ReportUnusedLabel, severity, isEnabling);
setSeverity(CompilerOptions.OPTION_ReportUnusedTypeArgumentsForMethodInvocation, severity, isEnabling);
setSeverity(CompilerOptions.OPTION_ReportRedundantSpecificationOfTypeArguments, severity, isEnabling);
+ setSeverity(CompilerOptions.OPTION_ReportUnusedTypeParameter, severity,isEnabling); //TODO - Enable this when tests are modified at ui addition.
return;
} else if (token.equals("unusedParam")) { //$NON-NLS-1$
setSeverity(CompilerOptions.OPTION_ReportUnusedParameter, severity, isEnabling);
return;
+ } else if (token.equals("unusedTypeParameter")) { //$NON-NLS-1$
+ setSeverity(CompilerOptions.OPTION_ReportUnusedTypeParameter, severity, isEnabling);
+ return;
} else if (token.equals("unusedParamIncludeDoc")) { //$NON-NLS-1$
this.options.put(
CompilerOptions.OPTION_ReportUnusedParameterIncludeDocCommentReference,
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
index f72b0d8..b1b56a1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
@@ -152,6 +152,8 @@
* UninitializedLocalVariableHintMissingDefault
* UninitializedBlankFinalFieldHintMissingDefault
* ShouldReturnValueHintMissingDefault
+ * ConflictingNullAnnotations
+ * ConflictingInheritedNullAnnotations
*******************************************************************************/
package org.eclipse.jdt.core.compiler;
@@ -1298,6 +1300,10 @@
*/
/** @since 3.4 */
int UnusedTypeArgumentsForConstructorInvocation = MethodRelated + 660;
+ /** @since 3.9 */
+ int UnusedTypeParameter = TypeRelated + 661;
+ /** @since 3.9 */
+ int IllegalArrayOfUnionType = TypeRelated + 662;
/**
* Corrupted binaries
@@ -1508,6 +1514,10 @@
int SpecdNonNullLocalVariableComparisonYieldsFalse = Internal + 932;
/** @since 3.8 */
int RequiredNonNullButProvidedSpecdNullable = Internal + 933;
+ /** @since 3.9 */
+ int ConflictingNullAnnotations = MethodRelated + 939;
+ /** @since 3.9 */
+ int ConflictingInheritedNullAnnotations = MethodRelated + 940;
/**
* External problems -- These are problems defined by other plugins
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
index 5a71ec3..b07cdfd 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
@@ -17,8 +17,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
@@ -3300,9 +3303,9 @@
StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
stackMapFrameCodeStream.removeFramePosition(code_length);
if (stackMapFrameCodeStream.hasFramePositions()) {
- ArrayList frames = new ArrayList();
- traverse(isClinit ? null : methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit);
- int numberOfFrames = frames.size();
+ Map frames = new HashMap();
+ List realFrames = traverse(isClinit ? null : methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit);
+ int numberOfFrames = realFrames.size();
if (numberOfFrames > 1) {
int stackMapTableAttributeOffset = localContentsOffset;
// add the stack map table attribute
@@ -3325,10 +3328,10 @@
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
- StackMapFrame currentFrame = (StackMapFrame) frames.get(0);
+ StackMapFrame currentFrame = (StackMapFrame) realFrames.get(0);
for (int j = 1; j < numberOfFrames; j++) {
// select next frame
- currentFrame = (StackMapFrame) frames.get(j);
+ currentFrame = (StackMapFrame) realFrames.get(j);
// generate current frame
// need to find differences between the current frame and the previous frame
int frameOffset = currentFrame.pc;
@@ -3477,9 +3480,9 @@
StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
stackMapFrameCodeStream.removeFramePosition(code_length);
if (stackMapFrameCodeStream.hasFramePositions()) {
- ArrayList frames = new ArrayList();
- traverse(isClinit ? null: methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit);
- int numberOfFrames = frames.size();
+ Map frames = new HashMap();
+ List realFrames = traverse(isClinit ? null: methodBinding, max_locals, this.contents, codeAttributeOffset + 14, code_length, frames, isClinit);
+ int numberOfFrames = realFrames.size();
if (numberOfFrames > 1) {
int stackMapTableAttributeOffset = localContentsOffset;
// add the stack map table attribute
@@ -3502,12 +3505,12 @@
if (localContentsOffset + 2 >= this.contents.length) {
resizeContents(2);
}
- StackMapFrame currentFrame = (StackMapFrame) frames.get(0);
+ StackMapFrame currentFrame = (StackMapFrame) realFrames.get(0);
StackMapFrame prevFrame = null;
for (int j = 1; j < numberOfFrames; j++) {
// select next frame
prevFrame = currentFrame;
- currentFrame = (StackMapFrame) frames.get(j);
+ currentFrame = (StackMapFrame) realFrames.get(j);
// generate current frame
// need to find differences between the current frame and the previous frame
int offsetDelta = currentFrame.getOffsetDelta(prevFrame);
@@ -3944,7 +3947,6 @@
methodSignature.length);
}
-
private final int i4At(byte[] reference, int relativeOffset,
int structOffset) {
int position = relativeOffset + structOffset;
@@ -4375,8 +4377,32 @@
this.methodCountOffset = this.contentsOffset;
this.contentsOffset += 2;
}
+
+ private List filterFakeFrames(Set realJumpTargets, Map frames, int codeLength) {
+ // no more frame to generate
+ // filter out "fake" frames
+ realJumpTargets.remove(new Integer(codeLength));
+ List result = new ArrayList();
+ for (Iterator iterator = realJumpTargets.iterator(); iterator.hasNext(); ) {
+ Integer jumpTarget = (Integer) iterator.next();
+ StackMapFrame frame = (StackMapFrame) frames.get(jumpTarget);
+ if (frame != null) {
+ result.add(frame);
+ }
+ }
+ Collections.sort(result, new Comparator() {
+ public int compare(Object o1, Object o2) {
+ StackMapFrame frame = (StackMapFrame) o1;
+ StackMapFrame frame2 = (StackMapFrame) o2;
+ return frame.pc - frame2.pc;
+ }
+ });
+ return result;
+ }
- public void traverse(MethodBinding methodBinding, int maxLocals, byte[] bytecodes, int codeOffset, int codeLength, ArrayList frames, boolean isClinit) {
+ public List traverse(MethodBinding methodBinding, int maxLocals, byte[] bytecodes, int codeOffset, int codeLength, Map frames, boolean isClinit) {
+ Set realJumpTarget = new HashSet();
+
StackMapFrameCodeStream stackMapFrameCodeStream = (StackMapFrameCodeStream) this.codeStream;
int[] framePositions = stackMapFrameCodeStream.getFramePositions();
int pc = codeOffset;
@@ -4424,7 +4450,14 @@
initializeDefaultLocals(frame, methodBinding, maxLocals, codeLength);
}
frame.pc = -1;
- frames.add(frame.duplicate());
+ add(frames, frame.duplicate());
+ addRealJumpTarget(realJumpTarget, -1);
+ for (int i = 0, max = this.codeStream.exceptionLabelsCounter; i < max; i++) {
+ ExceptionLabel exceptionLabel = this.codeStream.exceptionLabels[i];
+ if (exceptionLabel != null) {
+ addRealJumpTarget(realJumpTarget, exceptionLabel.position);
+ }
+ }
while (true) {
int currentPC = pc - codeOffset;
if (hasStackMarkers && stackMarker.pc == currentPC) {
@@ -4477,8 +4510,7 @@
if (indexInFramePositions < framePositionsLength) {
currentFramePosition = framePositions[indexInFramePositions];
} else {
- // no more frame to generate
- return;
+ currentFramePosition = Integer.MAX_VALUE;
}
} while (currentFramePosition < currentPC);
}
@@ -4489,13 +4521,12 @@
// initialize locals
initializeLocals(isClinit ? true : methodBinding.isStatic(), currentPC, currentFrame);
// insert a new frame
- frames.add(currentFrame);
+ add(frames, currentFrame);
indexInFramePositions++;
if (indexInFramePositions < framePositionsLength) {
currentFramePosition = framePositions[indexInFramePositions];
} else {
- // no more frame to generate
- return;
+ currentFramePosition = Integer.MAX_VALUE;
}
}
byte opcode = (byte) u1At(bytecodes, 0, pc);
@@ -5041,6 +5072,7 @@
case Opcodes.OPC_ifgt:
case Opcodes.OPC_ifle:
frame.numberOfStackItems--;
+ addRealJumpTarget(realJumpTarget, currentPC + i2At(bytecodes, 1, pc));
pc += 3;
break;
case Opcodes.OPC_if_icmpeq:
@@ -5052,23 +5084,32 @@
case Opcodes.OPC_if_acmpeq:
case Opcodes.OPC_if_acmpne:
frame.numberOfStackItems -= 2;
+ addRealJumpTarget(realJumpTarget, currentPC + i2At(bytecodes, 1, pc));
pc += 3;
break;
case Opcodes.OPC_goto:
+ addRealJumpTarget(realJumpTarget, currentPC + i2At(bytecodes, 1, pc));
pc += 3;
+ addRealJumpTarget(realJumpTarget, pc - codeOffset);
break;
case Opcodes.OPC_tableswitch:
pc++;
while (((pc - codeOffset) & 0x03) != 0) {
pc++;
}
+ // default offset
+ addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 0, pc));
pc += 4; // default
int low = i4At(bytecodes, 0, pc);
pc += 4;
int high = i4At(bytecodes, 0, pc);
pc += 4;
int length = high - low + 1;
- pc += (length * 4);
+ for (int i = 0; i < length; i++) {
+ // pair offset
+ addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 0, pc));
+ pc += 4;
+ }
frame.numberOfStackItems--;
break;
case Opcodes.OPC_lookupswitch:
@@ -5076,9 +5117,16 @@
while (((pc - codeOffset) & 0x03) != 0) {
pc++;
}
- pc += 4; // default
+ addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 0, pc));
+ pc += 4; // default offset
int npairs = (int) u4At(bytecodes, 0, pc);
- pc += (4 + npairs * 8);
+ pc += 4; // npair value
+ for (int i = 0; i < npairs; i++) {
+ pc += 4; // case value
+ // pair offset
+ addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 0, pc));
+ pc += 4;
+ }
frame.numberOfStackItems--;
break;
case Opcodes.OPC_ireturn:
@@ -5088,9 +5136,11 @@
case Opcodes.OPC_areturn:
frame.numberOfStackItems--;
pc++;
+ addRealJumpTarget(realJumpTarget, pc - codeOffset);
break;
case Opcodes.OPC_return:
pc++;
+ addRealJumpTarget(realJumpTarget, pc - codeOffset);
break;
case Opcodes.OPC_getstatic:
index = u2At(bytecodes, 1, pc);
@@ -5493,6 +5543,7 @@
case Opcodes.OPC_athrow:
frame.numberOfStackItems--;
pc++;
+ addRealJumpTarget(realJumpTarget, pc - codeOffset);
break;
case Opcodes.OPC_checkcast:
index = u2At(bytecodes, 1, pc);
@@ -5587,10 +5638,13 @@
case Opcodes.OPC_ifnull:
case Opcodes.OPC_ifnonnull:
frame.numberOfStackItems--;
+ addRealJumpTarget(realJumpTarget, currentPC + i2At(bytecodes, 1, pc));
pc += 3;
break;
case Opcodes.OPC_goto_w:
+ addRealJumpTarget(realJumpTarget, currentPC + i4At(bytecodes, 1, pc));
pc += 5;
+ addRealJumpTarget(realJumpTarget, pc - codeOffset); // handle infinite loop
break;
default: // should not occur
this.codeStream.methodDeclaration.scope.problemReporter().abortDueToInternalError(
@@ -5608,8 +5662,15 @@
break;
}
}
+ return filterFakeFrames(realJumpTarget, frames, codeLength);
}
+ private void addRealJumpTarget(Set realJumpTarget, int pc) {
+ realJumpTarget.add(new Integer(pc));
+ }
+ private void add(Map frames, StackMapFrame frame) {
+ frames.put(new Integer(frame.pc), frame);
+ }
private final int u1At(byte[] reference, int relativeOffset,
int structOffset) {
return (reference[relativeOffset + structOffset] & 0xFF);
@@ -5630,6 +5691,11 @@
+ ((reference[position++] & 0xFF) << 8) + (reference[position] & 0xFF));
}
+ private final int i2At(byte[] reference, int relativeOffset, int structOffset) {
+ int position = relativeOffset + structOffset;
+ return (reference[position++] << 8) + (reference[position] & 0xFF);
+ }
+
public char[] utf8At(byte[] reference, int absoluteOffset,
int bytesAvailable) {
int length = bytesAvailable;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
index 8b3104a..e553578 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
@@ -541,6 +541,10 @@
return false;
ReferenceBinding refType = (ReferenceBinding) type;
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=385780
+ if (refType instanceof TypeVariableBinding) {
+ refType.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
+ }
// ignore references insing Javadoc comments
if ((this.bits & ASTNode.InsideJavadoc) == 0 && refType.isOrEnclosedByPrivateType() && !scope.isDefinedInType(refType)) {
// ignore cases where type is used from inside itself
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java
index 0617799..eb924d1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java
@@ -14,6 +14,7 @@
* bug 367203 - [compiler][null] detect assigning null to nonnull argument
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
* bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -226,8 +227,10 @@
argument.createBinding(this.scope, this.binding.parameters[i]);
// createBinding() has resolved annotations, now transfer nullness info from the argument to the method:
if ((argument.binding.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) != 0) {
- if (this.binding.parameterNonNullness == null)
+ if (this.binding.parameterNonNullness == null) {
this.binding.parameterNonNullness = new Boolean[this.arguments.length];
+ this.binding.tagBits |= TagBits.IsNullnessKnown;
+ }
this.binding.parameterNonNullness[i] = Boolean.valueOf((argument.binding.tagBits & TagBits.AnnotationNonNull) != 0);
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
index c1cbe0a..63a7971 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
@@ -214,7 +214,16 @@
if (isRecursive(null /*lazy initialized visited list*/)) {
this.scope.problemReporter().recursiveConstructorInvocation(this.constructorCall);
}
-
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=385780
+ if (this.typeParameters != null &&
+ !this.scope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
+ for (int i = 0, length = this.typeParameters.length; i < length; ++i) {
+ TypeParameter typeParameter = this.typeParameters[i];
+ if ((typeParameter.binding.modifiers & ExtraCompilerModifiers.AccLocallyUsed) == 0) {
+ this.scope.problemReporter().unusedTypeParameter(typeParameter);
+ }
+ }
+ }
try {
ExceptionHandlingFlowContext constructorContext =
new ExceptionHandlingFlowContext(
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index be060ec..8f31fc5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -21,6 +21,7 @@
* bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
* bug 379784 - [compiler] "Method can be static" is not getting reported
* bug 379834 - Wrong "method can be static" in presence of qualified super and different staticness of nested super class.
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -47,6 +48,7 @@
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ImplicitNullAnnotationVerifier;
import org.eclipse.jdt.internal.compiler.lookup.PolymorphicMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
@@ -922,6 +924,14 @@
return null;
}
+ if (compilerOptions.isAnnotationBasedNullAnalysisEnabled && (this.binding.tagBits & TagBits.IsNullnessKnown) == 0) {
+ // not interested in reporting problems against this.binding:
+//{ObjectTeams: added 2nd arg:
+ new ImplicitNullAnnotationVerifier(compilerOptions.inheritNullAnnotations, scope.environment())
+// SH}
+ .checkImplicitNullAnnotations(this.binding, null/*srcMethod*/, false, scope);
+ }
+
if (((this.bits & ASTNode.InsideExpressionStatement) != 0)
&& this.binding.isPolymorphic()) {
// we only set the return type to be void if this method invocation is used inside an expression statement
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
index 882980c..828b79f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
@@ -123,7 +123,17 @@
// may be in a non necessary <clinit> for innerclass with static final constant fields
if (this.binding.isAbstract() || this.binding.isNative())
return;
-
+
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=385780
+ if (this.typeParameters != null &&
+ !this.scope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
+ for (int i = 0, length = this.typeParameters.length; i < length; ++i) {
+ TypeParameter typeParameter = this.typeParameters[i];
+ if ((typeParameter.binding.modifiers & ExtraCompilerModifiers.AccLocallyUsed) == 0) {
+ this.scope.problemReporter().unusedTypeParameter(typeParameter);
+ }
+ }
+ }
ExceptionHandlingFlowContext methodContext =
new ExceptionHandlingFlowContext(
flowContext,
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
index c9c32dd..193b320 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java
@@ -1194,6 +1194,18 @@
this.scope.problemReporter().unusedPrivateType(this);
}
}
+
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=385780
+ if (this.typeParameters != null &&
+ !this.scope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
+ for (int i = 0, length = this.typeParameters.length; i < length; ++i) {
+ TypeParameter typeParameter = this.typeParameters[i];
+ if ((typeParameter.binding.modifiers & ExtraCompilerModifiers.AccLocallyUsed) == 0) {
+ this.scope.problemReporter().unusedTypeParameter(typeParameter);
+ }
+ }
+ }
+
// for local classes we use the flowContext as our parent, but never use an initialization context for this purpose
// see Bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop)
FlowContext parentContext = (flowContext instanceof InitializationFlowContext) ? null : flowContext;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java
index b176b9b..9939437 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java
@@ -7,7 +7,9 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
- * Stephan Herrmann - Contribution for bug 332637 - Dead Code detection removing code that isn't dead
+ * Stephan Herrmann - Contributions for
+ * bug 332637 - Dead Code detection removing code that isn't dead
+ * bug 391517 - java.lang.VerifyError on code that runs correctly in Eclipse 3.7 and eclipse 3.6
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
@@ -27,6 +29,7 @@
this.initsWhenTrue = initsWhenTrue;
this.initsWhenFalse = initsWhenFalse;
+ this.tagBits = initsWhenTrue.tagBits & initsWhenFalse.tagBits & UNREACHABLE;
}
public FlowInfo addInitializationsFrom(FlowInfo otherInits) {
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 03b1cf0..67fdcff 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
@@ -18,6 +18,7 @@
* bug 370639 - [compiler][resource] restore the default for resource leak warnings
* bug 366063 - Compiler should not add synthetic @NonNull annotations
* bug 374605 - Unreasonable warning for enum-based switch statements
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.impl;
@@ -134,6 +135,7 @@
public static final String OPTION_SuppressWarnings = "org.eclipse.jdt.core.compiler.problem.suppressWarnings"; //$NON-NLS-1$
public static final String OPTION_SuppressOptionalErrors = "org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors"; //$NON-NLS-1$
public static final String OPTION_ReportUnhandledWarningToken = "org.eclipse.jdt.core.compiler.problem.unhandledWarningToken"; //$NON-NLS-1$
+ public static final String OPTION_ReportUnusedTypeParameter = "org.eclipse.jdt.core.compiler.problem.unusedTypeParameter"; //$NON-NLS-1$
public static final String OPTION_ReportUnusedWarningToken = "org.eclipse.jdt.core.compiler.problem.unusedWarningToken"; //$NON-NLS-1$
public static final String OPTION_ReportUnusedLabel = "org.eclipse.jdt.core.compiler.problem.unusedLabel"; //$NON-NLS-1$
public static final String OPTION_FatalOptionalError = "org.eclipse.jdt.core.compiler.problem.fatalOptionalError"; //$NON-NLS-1$
@@ -239,6 +241,7 @@
static final char[][] DEFAULT_NONNULL_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.NonNull".toCharArray()); //$NON-NLS-1$
static final char[][] DEFAULT_NONNULLBYDEFAULT_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.NonNullByDefault".toCharArray()); //$NON-NLS-1$
public static final String OPTION_ReportMissingNonNullByDefaultAnnotation = "org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation"; //$NON-NLS-1$
+ public static final String OPTION_InheritNullAnnotations = "org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations"; //$NON-NLS-1$
/**
* Possible values for configurable options
*/
@@ -351,6 +354,7 @@
public static final int RedundantNullAnnotation = IrritantSet.GROUP2 | ASTNode.Bit14;
public static final int MissingNonNullByDefaultAnnotation = IrritantSet.GROUP2 | ASTNode.Bit15;
public static final int MissingDefaultCase = IrritantSet.GROUP2 | ASTNode.Bit16;
+ public static final int UnusedTypeParameter = IrritantSet.GROUP2 | ASTNode.Bit17;
//{ObjectTeams: OT/J specific problems/irritants:
public static final int OTJFlag = IrritantSet.GROUP3;
@@ -535,6 +539,8 @@
String tolerateIllegalAmbiguousVarargs = System.getProperty("tolerateIllegalAmbiguousVarargsInvocation"); //$NON-NLS-1$
tolerateIllegalAmbiguousVarargsInvocation = tolerateIllegalAmbiguousVarargs != null && tolerateIllegalAmbiguousVarargs.equalsIgnoreCase("true"); //$NON-NLS-1$
}
+ /** Should null annotations of overridden methods be inherited? */
+ public boolean inheritNullAnnotations;
// keep in sync with warningTokenToIrritant and warningTokenFromIrritant
public final static String[] warningTokens = {
@@ -729,6 +735,8 @@
return OPTION_ReportMissingJavadocTagDescription;
case UnusedTypeArguments :
return OPTION_ReportUnusedTypeArgumentsForMethodInvocation;
+ case UnusedTypeParameter:
+ return OPTION_ReportUnusedTypeParameter;
case UnusedWarningToken :
return OPTION_ReportUnusedWarningToken;
case RedundantSuperinterface :
@@ -1010,7 +1018,9 @@
OPTION_ReportNullSpecViolation,
OPTION_ReportNullAnnotationInferenceConflict,
OPTION_ReportNullUncheckedConversion,
- OPTION_ReportRedundantNullAnnotation
+ OPTION_ReportRedundantNullAnnotation,
+ OPTION_ReportUnusedTypeParameter,
+ OPTION_InheritNullAnnotations
};
return result;
}
@@ -1067,6 +1077,7 @@
case DeadCode :
case UnusedObjectAllocation :
case RedundantSpecificationOfTypeArguments :
+ case UnusedTypeParameter:
return "unused"; //$NON-NLS-1$
case DiscouragedReference :
case ForbiddenReference :
@@ -1440,6 +1451,8 @@
optionsMap.put(OPTION_NonNullAnnotationName, String.valueOf(CharOperation.concatWith(this.nonNullAnnotationName, '.')));
optionsMap.put(OPTION_NonNullByDefaultAnnotationName, String.valueOf(CharOperation.concatWith(this.nonNullByDefaultAnnotationName, '.')));
optionsMap.put(OPTION_ReportMissingNonNullByDefaultAnnotation, getSeverityString(MissingNonNullByDefaultAnnotation));
+ optionsMap.put(OPTION_ReportUnusedTypeParameter, getSeverityString(UnusedTypeParameter));
+ optionsMap.put(OPTION_InheritNullAnnotations, this.inheritNullAnnotations ? ENABLED : DISABLED);
return optionsMap;
}
@@ -1598,6 +1611,7 @@
this.nonNullAnnotationName = DEFAULT_NONNULL_ANNOTATION_NAME;
this.nonNullByDefaultAnnotationName = DEFAULT_NONNULLBYDEFAULT_ANNOTATION_NAME;
this.intendedDefaultNonNullness = 0;
+ this.inheritNullAnnotations = false;
this.analyseResourceLeaks = true;
@@ -1893,6 +1907,7 @@
if ((optionValue = optionsMap.get(OPTION_ReportUnclosedCloseable)) != null) updateSeverity(UnclosedCloseable, optionValue);
if ((optionValue = optionsMap.get(OPTION_ReportPotentiallyUnclosedCloseable)) != null) updateSeverity(PotentiallyUnclosedCloseable, optionValue);
if ((optionValue = optionsMap.get(OPTION_ReportExplicitlyClosedAutoCloseable)) != null) updateSeverity(ExplicitlyClosedAutoCloseable, optionValue);
+ if ((optionValue = optionsMap.get(OPTION_ReportUnusedTypeParameter)) != null) updateSeverity(UnusedTypeParameter, optionValue);
if (getSeverity(UnclosedCloseable) == ProblemSeverities.Ignore
&& getSeverity(PotentiallyUnclosedCloseable) == ProblemSeverities.Ignore
&& getSeverity(ExplicitlyClosedAutoCloseable) == ProblemSeverities.Ignore) {
@@ -1974,6 +1989,9 @@
this.nonNullByDefaultAnnotationName = CharOperation.splitAndTrimOn('.', ((String)optionValue).toCharArray());
}
if ((optionValue = optionsMap.get(OPTION_ReportMissingNonNullByDefaultAnnotation)) != null) updateSeverity(MissingNonNullByDefaultAnnotation, optionValue);
+ if ((optionValue = optionsMap.get(OPTION_InheritNullAnnotations)) != null) {
+ this.inheritNullAnnotations = ENABLED.equals(optionValue);
+ }
}
// Javadoc options
@@ -2193,6 +2211,7 @@
buf.append("\n\t- resource is not closed: ").append(getSeverityString(UnclosedCloseable)); //$NON-NLS-1$
buf.append("\n\t- resource may not be closed: ").append(getSeverityString(PotentiallyUnclosedCloseable)); //$NON-NLS-1$
buf.append("\n\t- resource should be handled by try-with-resources: ").append(getSeverityString(ExplicitlyClosedAutoCloseable)); //$NON-NLS-1$
+ buf.append("\n\t- Unused Type Parameter: ").append(getSeverityString(UnusedTypeParameter)); //$NON-NLS-1$
//{ObjectTeams
buf.append("\n\t- decapsulation : ").append(this.decapsulation); //$NON-NLS-1$
buf.append("\n\t- report if not exactly one basecall in callin method : ").append(getSeverityString(NotExactlyOneBasecall)); //$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 96bf024..8efc755 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
@@ -217,6 +217,7 @@
.set(CompilerOptions.RedundantSuperinterface)
.set(CompilerOptions.DeadCode)
.set(CompilerOptions.UnusedObjectAllocation)
+ .set(CompilerOptions.UnusedTypeParameter)
.set(CompilerOptions.RedundantSpecificationOfTypeArguments);
STATIC_METHOD
.set(CompilerOptions.MethodCanBePotentiallyStatic);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
index 076125c..d823335 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
@@ -16,6 +16,7 @@
* bug 365387 - [compiler][null] bug 186342: Issues to follow up post review and verification.
* bug 358903 - Filter practically unimportant resource leak warnings
* bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -1584,13 +1585,6 @@
if (nullableAnnotationName == null || nonNullAnnotationName == null || nonNullByDefaultAnnotationName == null)
return; // not well-configured to use null annotations
- int currentDefault = NO_NULL_DEFAULT;
- if ((this.tagBits & TagBits.AnnotationNonNullByDefault) != 0) {
- currentDefault = NONNULL_BY_DEFAULT;
- } else if ((this.tagBits & TagBits.AnnotationNullUnspecifiedByDefault) != 0) {
- currentDefault = NULL_UNSPECIFIED_BY_DEFAULT;
- }
-
// return:
IBinaryAnnotation[] annotations = method.getAnnotations();
boolean explicitNullness = false;
@@ -1602,7 +1596,6 @@
char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';'
if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) {
methodBinding.tagBits |= TagBits.AnnotationNonNullByDefault;
- currentDefault = NONNULL_BY_DEFAULT;
}
if (!explicitNullness && CharOperation.equals(typeName, nonNullAnnotationName)) {
methodBinding.tagBits |= TagBits.AnnotationNonNull;
@@ -1614,19 +1607,13 @@
}
}
}
- if (!explicitNullness
- && (methodBinding.returnType != null && !methodBinding.returnType.isBaseType())
- && currentDefault == NONNULL_BY_DEFAULT) {
- methodBinding.tagBits |= TagBits.AnnotationNonNull;
- }
// parameters:
TypeBinding[] parameters = methodBinding.parameters;
int numVisibleParams = parameters.length;
int numParamAnnotations = method.getAnnotatedParametersCount();
- if (numParamAnnotations > 0 || currentDefault == NONNULL_BY_DEFAULT) {
+ if (numParamAnnotations > 0) {
for (int j = 0; j < numVisibleParams; j++) {
- explicitNullness = false;
if (numParamAnnotations > 0) {
int startIndex = numParamAnnotations - numVisibleParams;
IBinaryAnnotation[] paramAnnotations = method.getParameterAnnotations(j+startIndex);
@@ -1640,25 +1627,16 @@
if (methodBinding.parameterNonNullness == null)
methodBinding.parameterNonNullness = new Boolean[numVisibleParams];
methodBinding.parameterNonNullness[j] = Boolean.TRUE;
- explicitNullness = true;
break;
} else if (CharOperation.equals(typeName, nullableAnnotationName)) {
if (methodBinding.parameterNonNullness == null)
methodBinding.parameterNonNullness = new Boolean[numVisibleParams];
methodBinding.parameterNonNullness[j] = Boolean.FALSE;
- explicitNullness = true;
break;
}
}
}
}
- if (!explicitNullness && currentDefault == NONNULL_BY_DEFAULT) {
- if (methodBinding.parameterNonNullness == null)
- methodBinding.parameterNonNullness = new Boolean[numVisibleParams];
- if (methodBinding.parameters[j]!= null && !methodBinding.parameters[j].isBaseType()) {
- methodBinding.parameterNonNullness[j] = Boolean.TRUE;
- }
- }
}
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
new file mode 100644
index 0000000..f792bbb
--- /dev/null
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
@@ -0,0 +1,495 @@
+/*******************************************************************************
+ * Copyright (c) 2012 GK Software AG 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Stephan Herrmann - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.lookup;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
+import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.WeakenedTypeBinding;
+import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
+import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer;
+
+/**
+ * Extracted slice from MethodVerifier15, which is responsible only for implicit null annotations.
+ * First, if enabled, it detects overridden methods from which null annotations are inherited.
+ * Next, also default nullness is filled into remaining empty slots.
+ * After all implicit annotations have been filled in compatibility is checked and problems are complained.
+ */
+public class ImplicitNullAnnotationVerifier {
+
+ /**
+ * Simple record to store nullness info for one argument or return type
+ * while iterating over a set of overridden methods.
+ */
+ static class InheritedNonNullnessInfo {
+ Boolean inheritedNonNullness;
+ MethodBinding annotationOrigin;
+ boolean complained;
+ }
+
+ // delegate which to ask for recursive analysis of super methods
+ // can be 'this', but is never a MethodVerifier (to avoid infinite recursion).
+ ImplicitNullAnnotationVerifier buddyImplicitNullAnnotationsVerifier;
+ private boolean inheritNullAnnotations;
+ private LookupEnvironment environment;
+
+//{ObjectTeams: added 2nd arg:
+ public ImplicitNullAnnotationVerifier(boolean inheritNullAnnotations, LookupEnvironment environment) {
+ this.environment = environment;
+// SH}
+ this.buddyImplicitNullAnnotationsVerifier = this;
+ this.inheritNullAnnotations = inheritNullAnnotations;
+ }
+
+ // for sub-classes:
+//{ObjectTeams: added 2nd arg:
+ ImplicitNullAnnotationVerifier(CompilerOptions options, LookupEnvironment environment) {
+ this.buddyImplicitNullAnnotationsVerifier = new ImplicitNullAnnotationVerifier(options.inheritNullAnnotations, environment);
+// SH}
+ this.inheritNullAnnotations = options.inheritNullAnnotations;
+ }
+
+ /**
+ * Check and fill in implicit annotations from overridden methods and from default.
+ * Precondition: caller has checked whether annotation-based null analysis is enabled.
+ */
+ public void checkImplicitNullAnnotations(MethodBinding currentMethod, AbstractMethodDeclaration srcMethod, boolean complain, Scope scope) {
+ // check inherited nullness from superclass and superInterfaces
+ try {
+ ReferenceBinding currentType = currentMethod.declaringClass;
+ if (currentType.id == TypeIds.T_JavaLangObject) {
+ return;
+ }
+ boolean needToApplyNonNullDefault = currentMethod.hasNonNullDefault();
+ // compatibility & inheritance do not consider constructors / static methods:
+ boolean isInstanceMethod = !currentMethod.isConstructor() && !currentMethod.isStatic();
+ complain &= isInstanceMethod;
+ if (!needToApplyNonNullDefault
+ && !complain
+ && !(this.inheritNullAnnotations && isInstanceMethod)) {
+ return; // short cut, no work to be done
+ }
+
+ if (isInstanceMethod) {
+ List superMethodList = new ArrayList();
+
+ int paramLen = currentMethod.parameters.length;
+ findAllOverriddenMethods(currentMethod.original(), currentMethod.selector, paramLen,
+ currentType, new HashSet(), superMethodList);
+
+ // prepare interim storage for nullness info so we don't pollute currentMethod before we know its conflict-free:
+ InheritedNonNullnessInfo[] inheritedNonNullnessInfos = new InheritedNonNullnessInfo[paramLen+1]; // index 0 is for the return type
+ for (int i=0; i<paramLen+1; i++) inheritedNonNullnessInfos[i] = new InheritedNonNullnessInfo();
+
+ int length = superMethodList.size();
+ for (int i = length; --i >= 0;) {
+ MethodBinding currentSuper = (MethodBinding) superMethodList.get(i);
+ if ((currentSuper.tagBits & TagBits.IsNullnessKnown) == 0) {
+ // recurse to prepare currentSuper
+ checkImplicitNullAnnotations(currentSuper, null, false, scope); // TODO (stephan) complain=true if currentSuper is source method??
+ }
+ checkNullSpecInheritance(currentMethod, srcMethod, needToApplyNonNullDefault, complain, currentSuper, scope, inheritedNonNullnessInfos);
+ needToApplyNonNullDefault = false;
+ }
+
+ // transfer collected information into currentMethod:
+ InheritedNonNullnessInfo info = inheritedNonNullnessInfos[0];
+ if (!info.complained) {
+ if (info.inheritedNonNullness == Boolean.TRUE) {
+ currentMethod.tagBits |= TagBits.AnnotationNonNull;
+ } else if (info.inheritedNonNullness == Boolean.FALSE) {
+ currentMethod.tagBits |= TagBits.AnnotationNullable;
+ }
+ }
+ for (int i=0; i<paramLen; i++) {
+ info = inheritedNonNullnessInfos[i+1];
+ if (!info.complained && info.inheritedNonNullness != null) {
+ if (currentMethod.parameterNonNullness == null)
+ currentMethod.parameterNonNullness = new Boolean[paramLen];
+ currentMethod.parameterNonNullness[i] = info.inheritedNonNullness;
+ }
+ }
+
+ }
+ if (needToApplyNonNullDefault) {
+ currentMethod.fillInDefaultNonNullness(srcMethod);
+ }
+ } finally {
+ currentMethod.tagBits |= TagBits.IsNullnessKnown;
+ }
+ }
+
+ /*
+ * Recursively traverse the tree of ancestors but whenever we find a matching method prune the super tree.
+ * Collect all matching methods in 'result'.
+ */
+ private void findAllOverriddenMethods(MethodBinding original, char[] selector, int suggestedParameterLength,
+ ReferenceBinding currentType, Set ifcsSeen, List result)
+ {
+ if (currentType.id == TypeIds.T_JavaLangObject)
+ return;
+
+ // superclass:
+ collectOverriddenMethods(original, selector, suggestedParameterLength, currentType.superclass(), ifcsSeen, result);
+
+ // superInterfaces:
+ ReferenceBinding[] superInterfaces = currentType.superInterfaces();
+ int ifcLen = superInterfaces.length;
+ for (int i = 0; i < ifcLen; i++) {
+ ReferenceBinding currentIfc = superInterfaces[i];
+ if (ifcsSeen.add(currentIfc.original())) { // process each interface at most once
+ collectOverriddenMethods(original, selector, suggestedParameterLength, currentIfc, ifcsSeen, result);
+ }
+ }
+ }
+
+ /* collect matching methods from one supertype. */
+ private void collectOverriddenMethods(MethodBinding original, char[] selector, int suggestedParameterLength,
+ ReferenceBinding superType, Set ifcsSeen, List result)
+ {
+ MethodBinding [] ifcMethods = superType.getMethods(selector, suggestedParameterLength);
+ int length = ifcMethods.length;
+ for (int i=0; i<length; i++) {
+ MethodBinding currentMethod = ifcMethods[i];
+ if (currentMethod.isStatic())
+ continue;
+ if (areParametersEqual(original, currentMethod.original())) {
+ result.add(currentMethod);
+ return; // at most one method is overridden from any supertype
+ }
+ }
+ findAllOverriddenMethods(original, selector, suggestedParameterLength, superType, ifcsSeen, result);
+ }
+
+ /**
+ * The main algorithm in this class.
+ * @param currentMethod focus method
+ * @param srcMethod AST of 'currentMethod' if present
+ * @param hasNonNullDefault is a @NonNull default applicable at the site of currentMethod?
+ * @param shouldComplain should we report any errors found?
+ * (see also comment about flows into this method, below).
+ * @param inheritedMethod one overridden method from a super type
+ * @param scope provides context for error reporting etc.
+ * @param inheritedNonNullnessInfos if non-null, this array of non-null elements is used for
+ * interim recording of nullness information from inheritedMethod rather than prematurely updating currentMethod.
+ * Index position 0 is used for the return type, positions i+1 for argument i.
+ */
+ void checkNullSpecInheritance(MethodBinding currentMethod, AbstractMethodDeclaration srcMethod,
+ boolean hasNonNullDefault, boolean shouldComplain,
+ MethodBinding inheritedMethod, Scope scope, InheritedNonNullnessInfo[] inheritedNonNullnessInfos)
+ {
+ // Note that basically two different flows lead into this method:
+ // (1) during MethodVerifyer15.checkMethods() we want to report errors (against srcMethod or against the current type)
+ // In this case this method is directly called from MethodVerifier15 (checkAgainstInheritedMethod / checkConcreteInheritedMethod)
+ // (2) during on-demand invocation we are mainly interested in the side effects of copying inherited null annotations
+ // In this case this method is called via checkImplicitNullAnnotations from
+ // - MessageSend.resolveType(..)
+ // - SourceTypeBinding.createArgumentBindings(..)
+ // - recursive calls within this class
+ // Still we *might* want to complain about problems found (controlled by 'complain')
+
+ if ((inheritedMethod.tagBits & TagBits.IsNullnessKnown) == 0) {
+ // TODO (stephan): even here we may need to report problems? How to discriminate?
+ this.buddyImplicitNullAnnotationsVerifier.checkImplicitNullAnnotations(inheritedMethod, null, false, scope);
+ }
+ long inheritedBits = inheritedMethod.tagBits;
+ long inheritedNullnessBits = inheritedBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable);
+ long currentBits = currentMethod.tagBits;
+ long currentNullnessBits = currentBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable);
+
+ LookupEnvironment environment = scope.environment();
+ boolean shouldInherit = this.inheritNullAnnotations;
+
+ // return type:
+ returnType: {
+ if (currentMethod.returnType == null || currentMethod.returnType.isBaseType())
+ break returnType; // no nullness for primitive types
+ if (currentNullnessBits == 0) {
+ // unspecified, may fill in either from super or from default
+ if (shouldInherit) {
+ if (inheritedNullnessBits != 0) {
+ if (hasNonNullDefault) {
+ // both inheritance and default: check for conflict?
+ if (shouldComplain && inheritedNullnessBits == TagBits.AnnotationNullable)
+ scope.problemReporter().conflictingNullAnnotations(currentMethod, ((MethodDeclaration) srcMethod).returnType, inheritedMethod);
+ // still use the inherited bits to avoid incompatibility
+ }
+ if (inheritedNonNullnessInfos != null && srcMethod != null) {
+ recordDeferredInheritedNullness(scope, ((MethodDeclaration) srcMethod).returnType,
+ inheritedMethod, Boolean.valueOf(inheritedNullnessBits == TagBits.AnnotationNonNull), inheritedNonNullnessInfos[0]);
+ } else {
+ // no need to defer, record this info now:
+ currentMethod.tagBits |= inheritedNullnessBits;
+ }
+ break returnType; // compatible by construction, skip complain phase below
+ }
+ }
+ if (hasNonNullDefault) { // conflict with inheritance already checked
+ currentMethod.tagBits |= (currentNullnessBits = TagBits.AnnotationNonNull);
+ }
+ }
+ if (shouldComplain) {
+ if ((inheritedNullnessBits & TagBits.AnnotationNonNull) != 0
+ && currentNullnessBits != TagBits.AnnotationNonNull)
+ {
+ if (srcMethod != null) {
+ scope.problemReporter().illegalReturnRedefinition(srcMethod, inheritedMethod,
+ environment.getNonNullAnnotationName());
+ } else {
+ scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
+ return;
+ }
+ }
+ }
+ }
+
+ // parameters:
+ Argument[] currentArguments = srcMethod == null ? null : srcMethod.arguments;
+
+ int length = 0;
+ if (currentArguments != null)
+ length = currentArguments.length;
+ else if (inheritedMethod.parameterNonNullness != null)
+ length = inheritedMethod.parameterNonNullness.length;
+ else if (currentMethod.parameterNonNullness != null)
+ length = currentMethod.parameterNonNullness.length;
+
+ for (int i = 0; i < length; i++) {
+ if (currentMethod.parameters[i].isBaseType()) continue;
+
+ Argument currentArgument = currentArguments == null
+ ? null : currentArguments[i];
+ Boolean inheritedNonNullNess = (inheritedMethod.parameterNonNullness == null)
+ ? null : inheritedMethod.parameterNonNullness[i];
+ Boolean currentNonNullNess = (currentMethod.parameterNonNullness == null)
+ ? null : currentMethod.parameterNonNullness[i];
+
+ if (currentNonNullNess == null) {
+ // unspecified, may fill in either from super or from default
+ if (inheritedNonNullNess != null) {
+ if (shouldInherit) {
+ if (hasNonNullDefault) {
+ // both inheritance and default: check for conflict?
+ if (shouldComplain
+ && inheritedNonNullNess == Boolean.FALSE
+ && currentArgument != null)
+ {
+ scope.problemReporter().conflictingNullAnnotations(currentMethod, currentArgument, inheritedMethod);
+ }
+ // still use the inherited info to avoid incompatibility
+ }
+ if (inheritedNonNullnessInfos != null && srcMethod != null) {
+ recordDeferredInheritedNullness(scope, srcMethod.arguments[i].type,
+ inheritedMethod, inheritedNonNullNess, inheritedNonNullnessInfos[i+1]);
+ } else {
+ // no need to defer, record this info now:
+ if (currentMethod.parameterNonNullness == null)
+ currentMethod.parameterNonNullness = new Boolean[length];
+ currentMethod.parameterNonNullness[i] = inheritedNonNullNess;
+ }
+ continue; // compatible by construction, skip complain phase below
+ }
+ }
+ if (hasNonNullDefault) { // conflict with inheritance already checked
+ if (currentMethod.parameterNonNullness == null)
+ currentMethod.parameterNonNullness = new Boolean[length];
+ currentMethod.parameterNonNullness[i] = (currentNonNullNess = Boolean.TRUE);
+ }
+ }
+ if (shouldComplain) {
+ boolean needNonNull = false;
+ char[][] annotationName;
+ if (inheritedNonNullNess == Boolean.TRUE) {
+ needNonNull = true;
+ annotationName = environment.getNonNullAnnotationName();
+ } else {
+ annotationName = environment.getNullableAnnotationName();
+ }
+ if (inheritedNonNullNess != Boolean.TRUE // super parameter is not restricted to @NonNull
+ && currentNonNullNess == Boolean.TRUE) // current parameter is restricted to @NonNull
+ {
+ // incompatible
+ if (currentArgument != null) {
+ scope.problemReporter().illegalRedefinitionToNonNullParameter(
+ currentArgument,
+ inheritedMethod.declaringClass,
+ (inheritedNonNullNess == null) ? null : environment.getNullableAnnotationName());
+ } else {
+ scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
+ }
+ } else if (inheritedNonNullNess != null
+ && currentNonNullNess == null)
+ {
+ // weak conflict (TODO reconsider this case)
+ if (currentArgument != null) {
+ scope.problemReporter().parameterLackingNullAnnotation(
+ currentArgument,
+ inheritedMethod.declaringClass,
+ needNonNull,
+ annotationName);
+ } else {
+ scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
+ }
+ }
+ }
+ }
+ }
+ /* check for conflicting annotations and record here the info 'inheritedNonNullness' found in 'inheritedMethod'. */
+ protected void recordDeferredInheritedNullness(Scope scope, ASTNode location,
+ MethodBinding inheritedMethod, Boolean inheritedNonNullness,
+ InheritedNonNullnessInfo nullnessInfo)
+ {
+ if (nullnessInfo.inheritedNonNullness != null && nullnessInfo.inheritedNonNullness != inheritedNonNullness) {
+ scope.problemReporter().conflictingInheritedNullAnnotations(location,
+ nullnessInfo.inheritedNonNullness.booleanValue(), nullnessInfo.annotationOrigin,
+ inheritedNonNullness.booleanValue(), inheritedMethod);
+ nullnessInfo.complained = true;
+ // leave previous info intact, so subsequent errors are reported against the same first method
+ } else {
+ nullnessInfo.inheritedNonNullness = inheritedNonNullness;
+ nullnessInfo.annotationOrigin = inheritedMethod;
+ }
+ }
+
+ // ==== minimal set of utility methods previously from MethodVerifier15: ====
+
+ boolean areParametersEqual(MethodBinding one, MethodBinding two) {
+//{ObjectTeams: retrench callin methods:
+/* orig:
+ TypeBinding[] oneArgs = one.parameters;
+ TypeBinding[] twoArgs = two.parameters;
+ :giro */
+ TypeBinding[] oneArgs = one.getSourceParameters();
+ TypeBinding[] twoArgs = two.getSourceParameters();
+// SH}
+ if (oneArgs == twoArgs) return true;
+
+ int length = oneArgs.length;
+ if (length != twoArgs.length) return false;
+
+
+ // methods with raw parameters are considered equal to inherited methods
+ // with parameterized parameters for backwards compatibility, need a more complex check
+ int i;
+ foundRAW: for (i = 0; i < length; i++) {
+//{ObjectTeams: added 3. argument:
+ if (!areTypesEqual(oneArgs[i], twoArgs[i], two)) {
+// SH}
+ if (oneArgs[i].leafComponentType().isRawType()) {
+ if (oneArgs[i].dimensions() == twoArgs[i].dimensions() && oneArgs[i].leafComponentType().isEquivalentTo(twoArgs[i].leafComponentType())) {
+ // raw mode does not apply if the method defines its own type variables
+ if (one.typeVariables != Binding.NO_TYPE_VARIABLES)
+ return false;
+ // one parameter type is raw, hence all parameters types must be raw or non generic
+ // otherwise we have a mismatch check backwards
+ for (int j = 0; j < i; j++)
+ if (oneArgs[j].leafComponentType().isParameterizedTypeWithActualArguments())
+ return false;
+ // switch to all raw mode
+ break foundRAW;
+ }
+ }
+ return false;
+ }
+ }
+ // all raw mode for remaining parameters (if any)
+ for (i++; i < length; i++) {
+//{ObjectTeams: added 3. argument:
+ if (!areTypesEqual(oneArgs[i], twoArgs[i], two)) {
+// SH}
+ if (oneArgs[i].leafComponentType().isRawType())
+ if (oneArgs[i].dimensions() == twoArgs[i].dimensions() && oneArgs[i].leafComponentType().isEquivalentTo(twoArgs[i].leafComponentType()))
+ continue;
+ return false;
+ } else if (oneArgs[i].leafComponentType().isParameterizedTypeWithActualArguments()) {
+ return false; // no remaining parameter can be a Parameterized type (if one has been converted then all RAW types must be converted)
+ }
+ }
+ return true;
+ }
+//{ObjectTeams: enable role type comparison
+//added 3. parameter:
+ boolean areTypesEqual(TypeBinding one, TypeBinding two, MethodBinding methodTwo) {
+ if (one == two) return true;
+// different comparison for role types:
+ if (areEqualRoleTypes(one, two, methodTwo.declaringClass, this.environment))
+ return true;
+// SH}
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=329584
+ switch(one.kind()) {
+ case Binding.TYPE:
+ switch (two.kind()) {
+ case Binding.PARAMETERIZED_TYPE:
+ case Binding.RAW_TYPE:
+ if (one == two.erasure())
+ return true;
+ }
+ break;
+ case Binding.RAW_TYPE:
+ case Binding.PARAMETERIZED_TYPE:
+ switch(two.kind()) {
+ case Binding.TYPE:
+ if (one.erasure() == two)
+ return true;
+ }
+ }
+
+ // need to consider X<?> and X<? extends Object> as the same 'type'
+ if (one.isParameterizedType() && two.isParameterizedType())
+ return one.isEquivalentTo(two) && two.isEquivalentTo(one);
+
+ // Can skip this since we resolved each method before comparing it, see computeSubstituteMethod()
+ // if (one instanceof UnresolvedReferenceBinding)
+ // return ((UnresolvedReferenceBinding) one).resolvedType == two;
+ // if (two instanceof UnresolvedReferenceBinding)
+ // return ((UnresolvedReferenceBinding) two).resolvedType == one;
+ return false; // all other type bindings are identical
+ }
+
+//{ObjectTeams: specific check for role types and arrays thereof:
+ public static boolean areEqualRoleTypes(TypeBinding one, TypeBinding two, ReferenceBinding site, LookupEnvironment environment) {
+ if (one instanceof ArrayBinding) {
+ if (! (two instanceof ArrayBinding))
+ return false;
+ ArrayBinding array1 = (ArrayBinding)one;
+ ArrayBinding array2 = (ArrayBinding)two;
+ if (array1.dimensions != array2.dimensions)
+ return false;
+ one = array1.leafComponentType();
+ two = array2.leafComponentType();
+ }
+ if (one instanceof WeakenedTypeBinding)
+ one = ((WeakenedTypeBinding)one).weakenedType;
+ if (two instanceof WeakenedTypeBinding)
+ two = ((WeakenedTypeBinding)two).weakenedType;
+ if (one instanceof RoleTypeBinding) {
+ if (two instanceof UnresolvedReferenceBinding)
+ {
+ // resolve type (incl. type wrapping) without resolving all of the method:
+ // (cf. comment in BinaryTypeBinding.unResolvedMethods()).
+ two = ((UnresolvedReferenceBinding)two).resolve(environment, false/*convertGenericToRaw*/);
+ two = RoleTypeCreator.maybeWrapUnqualifiedRoleType(two, site);
+ }
+ if (two instanceof RoleTypeBinding)
+ return TypeAnalyzer.areRoleTypesEqual((RoleTypeBinding)one, (RoleTypeBinding)two);
+ }
+ return false;
+ }
+// SH}
+}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
index a4c1c30..621866e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
@@ -15,6 +15,7 @@
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
* bug 365662 - [compiler][null] warn on contradictory and redundant null annotations
* bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -857,10 +858,9 @@
/**
* After method verifier has finished, fill in missing @NonNull specification from the applicable default.
*/
-protected void fillInDefaultNonNullness() {
+protected void fillInDefaultNonNullness(AbstractMethodDeclaration sourceMethod) {
if (this.parameterNonNullness == null)
this.parameterNonNullness = new Boolean[this.parameters.length];
- AbstractMethodDeclaration sourceMethod = sourceMethod();
boolean added = false;
int length = this.parameterNonNullness.length;
for (int i = 0; i < length; i++) {
@@ -872,7 +872,7 @@
if (sourceMethod != null) {
sourceMethod.arguments[i].binding.tagBits |= TagBits.AnnotationNonNull;
}
- } else if (this.parameterNonNullness[i].booleanValue()) {
+ } else if (sourceMethod != null && this.parameterNonNullness[i].booleanValue()) {
sourceMethod.scope.problemReporter().nullAnnotationIsRedundant(sourceMethod, i);
}
}
@@ -883,7 +883,7 @@
&& (this.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) == 0)
{
this.tagBits |= TagBits.AnnotationNonNull;
- } else if ((this.tagBits & TagBits.AnnotationNonNull) != 0) {
+ } else if (sourceMethod != null && (this.tagBits & TagBits.AnnotationNonNull) != 0) {
sourceMethod.scope.problemReporter().nullAnnotationIsRedundant(sourceMethod, -1/*signifies method return*/);
}
}
@@ -1756,4 +1756,11 @@
public TypeVariableBinding[] typeVariables() {
return this.typeVariables;
}
+public boolean hasNonNullDefault() {
+ if ((this.tagBits & TagBits.AnnotationNonNullByDefault) != 0)
+ return true;
+ if ((this.tagBits & TagBits.AnnotationNullUnspecifiedByDefault) != 0)
+ return false;
+ return this.declaringClass.hasNonNullDefault();
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java
index 590e4ca..d0701fa 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.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
@@ -11,6 +11,8 @@
* Benjamin Muskalla - Contribution for bug 239066
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
+ * Stephan Herrmann - Contribution for
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -25,13 +27,10 @@
import org.eclipse.jdt.internal.compiler.util.SimpleSet;
import org.eclipse.objectteams.otdt.core.compiler.IOTConstants;
import org.eclipse.objectteams.otdt.core.compiler.OTNameUtils;
-import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
-import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.WeakenedTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.mappings.CalloutImplementor;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.Protections;
-import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.TypeAnalyzer;
/**
@@ -59,7 +58,7 @@
* Why: would report unimplemented methods.
*
*/
-public class MethodVerifier {
+public class MethodVerifier extends ImplicitNullAnnotationVerifier {
SourceTypeBinding type;
HashtableOfObject inheritedMethods;
HashtableOfObject currentMethods;
@@ -83,6 +82,9 @@
- defining an interface as a local type (local types can only be classes)
*/
MethodVerifier(LookupEnvironment environment) {
+//{ObjectTeams: added 2nd arg:
+ super(environment.globalOptions, environment);
+// SH}
this.type = null; // Initialized with the public method verify(SourceTypeBinding)
this.inheritedMethods = null;
this.currentMethods = null;
@@ -94,20 +96,6 @@
boolean areMethodsCompatible(MethodBinding one, MethodBinding two) {
return isParameterSubsignature(one, two) && areReturnTypesCompatible(one, two);
}
-boolean areParametersEqual(MethodBinding one, MethodBinding two) {
- TypeBinding[] oneArgs = one.parameters;
- TypeBinding[] twoArgs = two.parameters;
- if (oneArgs == twoArgs) return true;
-
- int length = oneArgs.length;
- if (length != twoArgs.length) return false;
-
- for (int i = 0; i < length; i++)
-//{ObjectTeams: added 3. argument
- if (!areTypesEqual(oneArgs[i], twoArgs[i], two)) return false;
-// SH}
- return true;
-}
boolean areReturnTypesCompatible(MethodBinding one, MethodBinding two) {
//{ObjectTeams: consider enhanced callin signatures:
/* orig:
@@ -147,56 +135,6 @@
return one.returnType.isCompatibleWith(two.returnType);
}
-
-//{ObjectTeams: enable role type comparison
-// added 3. parameter:
-boolean areTypesEqual(TypeBinding one, TypeBinding two, MethodBinding methodTwo) {
- if (one == two) return true;
-// different comparison for role types:
- if (areEqualRoleTypes(one, two, methodTwo.declaringClass, this.environment))
- return true;
-// SH}
-
- // its possible that an UnresolvedReferenceBinding can be compared to its resolved type
- // when they're both UnresolvedReferenceBindings then they must be identical like all other types
- // all wrappers of UnresolvedReferenceBindings are converted as soon as the type is resolved
- // so its not possible to have 2 arrays where one is UnresolvedX[] and the other is X[]
- if (one instanceof UnresolvedReferenceBinding)
- return ((UnresolvedReferenceBinding) one).resolvedType == two;
- if (two instanceof UnresolvedReferenceBinding)
- return ((UnresolvedReferenceBinding) two).resolvedType == one;
- return false; // all other type bindings are identical
-}
-//{ObjectTeams: specific check for role types and arrays thereof:
-public static boolean areEqualRoleTypes(TypeBinding one, TypeBinding two, ReferenceBinding site, LookupEnvironment environment) {
- if (one instanceof ArrayBinding) {
- if (! (two instanceof ArrayBinding))
- return false;
- ArrayBinding array1 = (ArrayBinding)one;
- ArrayBinding array2 = (ArrayBinding)two;
- if (array1.dimensions != array2.dimensions)
- return false;
- one = array1.leafComponentType();
- two = array2.leafComponentType();
- }
- if (one instanceof WeakenedTypeBinding)
- one = ((WeakenedTypeBinding)one).weakenedType;
- if (two instanceof WeakenedTypeBinding)
- two = ((WeakenedTypeBinding)two).weakenedType;
- if (one instanceof RoleTypeBinding) {
- if (two instanceof UnresolvedReferenceBinding)
- {
- // resolve type (incl. type wrapping) without resolving all of the method:
- // (cf. comment in BinaryTypeBinding.unResolvedMethods()).
- two = ((UnresolvedReferenceBinding)two).resolve(environment, false/*convertGenericToRaw*/);
- two = RoleTypeCreator.maybeWrapUnqualifiedRoleType(two, site);
- }
- if (two instanceof RoleTypeBinding)
- return TypeAnalyzer.areRoleTypesEqual((RoleTypeBinding)one, (RoleTypeBinding)two);
- }
- return false;
-}
-// SH}
boolean canSkipInheritedMethods() {
if (this.type.superclass() != null && this.type.superclass().isAbstract())
return false;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java
index b47d90e..dc232f6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java
@@ -13,6 +13,7 @@
* Stephan Herrmann - Contributions for
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -55,60 +56,6 @@
return isParameterSubsignature(one, two);
}
-boolean areParametersEqual(MethodBinding one, MethodBinding two) {
-//{ObjectTeams: retrench callin methods:
-/* orig:
- TypeBinding[] oneArgs = one.parameters;
- TypeBinding[] twoArgs = two.parameters;
- :giro */
- TypeBinding[] oneArgs = one.getSourceParameters();
- TypeBinding[] twoArgs = two.getSourceParameters();
-// SH}
- if (oneArgs == twoArgs) return true;
-
- int length = oneArgs.length;
- if (length != twoArgs.length) return false;
-
-
- // methods with raw parameters are considered equal to inherited methods
- // with parameterized parameters for backwards compatibility, need a more complex check
- int i;
- foundRAW: for (i = 0; i < length; i++) {
-//{ObjectTeams: added 3. argument:
- if (!areTypesEqual(oneArgs[i], twoArgs[i], two)) {
-// SH}
- if (oneArgs[i].leafComponentType().isRawType()) {
- if (oneArgs[i].dimensions() == twoArgs[i].dimensions() && oneArgs[i].leafComponentType().isEquivalentTo(twoArgs[i].leafComponentType())) {
- // raw mode does not apply if the method defines its own type variables
- if (one.typeVariables != Binding.NO_TYPE_VARIABLES)
- return false;
- // one parameter type is raw, hence all parameters types must be raw or non generic
- // otherwise we have a mismatch check backwards
- for (int j = 0; j < i; j++)
- if (oneArgs[j].leafComponentType().isParameterizedTypeWithActualArguments())
- return false;
- // switch to all raw mode
- break foundRAW;
- }
- }
- return false;
- }
- }
- // all raw mode for remaining parameters (if any)
- for (i++; i < length; i++) {
-//{ObjectTeams: added 3. argument:
- if (!areTypesEqual(oneArgs[i], twoArgs[i], two)) {
-// SH}
- if (oneArgs[i].leafComponentType().isRawType())
- if (oneArgs[i].dimensions() == twoArgs[i].dimensions() && oneArgs[i].leafComponentType().isEquivalentTo(twoArgs[i].leafComponentType()))
- continue;
- return false;
- } else if (oneArgs[i].leafComponentType().isParameterizedTypeWithActualArguments()) {
- return false; // no remaining parameter can be a Parameterized type (if one has been converted then all RAW types must be converted)
- }
- }
- return true;
-}
boolean areReturnTypesCompatible(MethodBinding one, MethodBinding two) {
//{ObjectTeams: consider enhanced callin signatures:
/* orig:
@@ -128,44 +75,6 @@
// SH}
}
}
-//{ObjectTeams: enable role type comparison
-//added 3. parameter:
-boolean areTypesEqual(TypeBinding one, TypeBinding two, MethodBinding methodTwo) {
- if (one == two) return true;
-// different comparison for role types:
- if (areEqualRoleTypes(one, two, methodTwo.declaringClass, this.environment))
- return true;
-// SH}
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=329584
- switch(one.kind()) {
- case Binding.TYPE:
- switch (two.kind()) {
- case Binding.PARAMETERIZED_TYPE:
- case Binding.RAW_TYPE:
- if (one == two.erasure())
- return true;
- }
- break;
- case Binding.RAW_TYPE:
- case Binding.PARAMETERIZED_TYPE:
- switch(two.kind()) {
- case Binding.TYPE:
- if (one.erasure() == two)
- return true;
- }
- }
-
- // need to consider X<?> and X<? extends Object> as the same 'type'
- if (one.isParameterizedType() && two.isParameterizedType())
- return one.isEquivalentTo(two) && two.isEquivalentTo(one);
-
- // Can skip this since we resolved each method before comparing it, see computeSubstituteMethod()
- // if (one instanceof UnresolvedReferenceBinding)
- // return ((UnresolvedReferenceBinding) one).resolvedType == two;
- // if (two instanceof UnresolvedReferenceBinding)
- // return ((UnresolvedReferenceBinding) two).resolvedType == one;
- return false; // all other type bindings are identical
-}
// Given `overridingMethod' which overrides `inheritedMethod' answer whether some subclass method that
// differs in erasure from overridingMethod could override `inheritedMethod'
protected boolean canOverridingMethodDifferInErasure(MethodBinding overridingMethod, MethodBinding inheritedMethod) {
@@ -188,6 +97,11 @@
void checkConcreteInheritedMethod(MethodBinding concreteMethod, MethodBinding[] abstractMethods) {
super.checkConcreteInheritedMethod(concreteMethod, abstractMethods);
boolean analyseNullAnnotations = this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled;
+ // TODO (stephan): unclear if this srcMethod is actually needed
+ AbstractMethodDeclaration srcMethod = null;
+ if (analyseNullAnnotations && this.type.equals(concreteMethod.declaringClass)) // is currentMethod from the current type?
+ srcMethod = concreteMethod.sourceMethod();
+ boolean hasNonNullDefault = concreteMethod.hasNonNullDefault();
for (int i = 0, l = abstractMethods.length; i < l; i++) {
MethodBinding abstractMethod = abstractMethods[i];
if (concreteMethod.isVarargs() != abstractMethod.isVarargs())
@@ -208,8 +122,9 @@
|| this.type.superclass.erasure().findSuperTypeOriginatingFrom(originalInherited.declaringClass) == null)
this.type.addSyntheticBridgeMethod(originalInherited, concreteMethod.original());
}
- if (analyseNullAnnotations && !concreteMethod.isStatic() && !abstractMethod.isStatic())
- checkNullSpecInheritance(concreteMethod, abstractMethod);
+ if (analyseNullAnnotations && !concreteMethod.isStatic() && !abstractMethod.isStatic()) {
+ checkNullSpecInheritance(concreteMethod, srcMethod, hasNonNullDefault, true, abstractMethod, this.type.scope, null);
+ }
}
}
void checkForBridgeMethod(MethodBinding currentMethod, MethodBinding inheritedMethod, MethodBinding[] allInheritedMethods) {
@@ -415,100 +330,37 @@
void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length, MethodBinding[] allInheritedMethods)
{
super.checkAgainstInheritedMethods(currentMethod, methods, length, allInheritedMethods);
- if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
+ CompilerOptions options = this.environment.globalOptions;
+ if (options.isAnnotationBasedNullAnalysisEnabled
+ && (currentMethod.tagBits & TagBits.IsNullnessKnown) == 0)
+ {
+ // if annotations are inherited these have been checked during STB.resolveTypesFor() (for methods explicit in this.type)
+ AbstractMethodDeclaration srcMethod = null;
+ if (this.type.equals(currentMethod.declaringClass)) // is currentMethod from the current type?
+ srcMethod = currentMethod.sourceMethod();
+ boolean hasNonNullDefault = currentMethod.hasNonNullDefault();
for (int i = length; --i >= 0;)
if (!currentMethod.isStatic() && !methods[i].isStatic())
- checkNullSpecInheritance(currentMethod, methods[i]);
+ checkNullSpecInheritance(currentMethod, srcMethod, hasNonNullDefault, true, methods[i], this.type.scope, null);
}
}
-void checkNullSpecInheritance(MethodBinding currentMethod, MethodBinding inheritedMethod) {
- // precondition: caller has checked whether annotation-based null analysis is enabled.
- long inheritedBits = inheritedMethod.tagBits;
- long currentBits = currentMethod.tagBits;
- AbstractMethodDeclaration srcMethod = null;
- if (this.type.equals(currentMethod.declaringClass)) // is currentMethod from the current type?
- srcMethod = currentMethod.sourceMethod();
-
- // return type:
- if ((inheritedBits & TagBits.AnnotationNonNull) != 0) {
- long currentNullBits = currentBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable);
- if (currentNullBits != TagBits.AnnotationNonNull) {
- if (srcMethod != null) {
- this.type.scope.problemReporter().illegalReturnRedefinition(srcMethod, inheritedMethod,
- this.environment.getNonNullAnnotationName());
- } else {
- this.type.scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
- return;
- }
- }
+void checkNullSpecInheritance(MethodBinding currentMethod, AbstractMethodDeclaration srcMethod,
+ boolean hasNonNullDefault, boolean complain, MethodBinding inheritedMethod, Scope scope, InheritedNonNullnessInfo[] inheritedNonNullnessInfos)
+{
+ complain &= !currentMethod.isConstructor();
+ if (!hasNonNullDefault && !complain && !this.environment.globalOptions.inheritNullAnnotations) {
+ // nothing to be done, take the shortcut
+ currentMethod.tagBits |= TagBits.IsNullnessKnown;
+ return;
}
-
- // parameters:
- Argument[] currentArguments = srcMethod == null ? null : srcMethod.arguments;
- if (inheritedMethod.parameterNonNullness != null) {
- // inherited method has null-annotations, check compatibility:
-
- int length = inheritedMethod.parameterNonNullness.length;
- for (int i = 0; i < length; i++) {
- Argument currentArgument = currentArguments == null ? null : currentArguments[i];
-
- Boolean inheritedNonNullNess = inheritedMethod.parameterNonNullness[i];
- Boolean currentNonNullNess = (currentMethod.parameterNonNullness == null)
- ? null : currentMethod.parameterNonNullness[i];
- if (inheritedNonNullNess != null) { // super has a null annotation
- if (currentNonNullNess == null) { // current parameter lacks null annotation
- boolean needNonNull = false;
- char[][] annotationName;
- if (inheritedNonNullNess == Boolean.TRUE) {
- needNonNull = true;
- annotationName = this.environment.getNonNullAnnotationName();
- } else {
- annotationName = this.environment.getNullableAnnotationName();
- }
- if (currentArgument != null) {
- this.type.scope.problemReporter().parameterLackingNullAnnotation(
- currentArgument,
- inheritedMethod.declaringClass,
- needNonNull,
- annotationName);
- continue;
- } else {
- this.type.scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
- break;
- }
- }
- }
- if (inheritedNonNullNess != Boolean.TRUE) { // super parameter is not restricted to @NonNull
- if (currentNonNullNess == Boolean.TRUE) { // current parameter is restricted to @NonNull
- if (currentArgument != null)
- this.type.scope.problemReporter().illegalRedefinitionToNonNullParameter(
- currentArgument,
- inheritedMethod.declaringClass,
- inheritedNonNullNess == null
- ? null
- : this.environment.getNullableAnnotationName());
- else
- this.type.scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
- }
- }
- }
- } else if (currentMethod.parameterNonNullness != null) {
- // super method has no annotations but current has
- for (int i = 0; i < currentMethod.parameterNonNullness.length; i++) {
- if (currentMethod.parameterNonNullness[i] == Boolean.TRUE) { // tightening from unconstrained to @NonNull
- if (currentArguments != null) {
- this.type.scope.problemReporter().illegalRedefinitionToNonNullParameter(
- currentArguments[i],
- inheritedMethod.declaringClass,
- null);
- } else {
- this.type.scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
- break;
- }
- }
- }
+ // in this context currentMethod can be inherited, too. Recurse if needed.
+ if (currentMethod.declaringClass != this.type
+ && (currentMethod.tagBits & TagBits.IsNullnessKnown) == 0)
+ {
+ this.buddyImplicitNullAnnotationsVerifier.checkImplicitNullAnnotations(currentMethod, srcMethod, complain, this.type.scope);
}
+ super.checkNullSpecInheritance(currentMethod, srcMethod, hasNonNullDefault, complain, inheritedMethod, scope, inheritedNonNullnessInfos);
}
void reportRawReferences() {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
index 9dae96f..9f54faf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
@@ -15,6 +15,7 @@
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
* bug 358903 - Filter practically unimportant resource leak warnings
* bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -1307,6 +1308,23 @@
return false;
}
+/**
+ * Answer whether a @NonNullByDefault is applicable at the given method binding.
+ */
+boolean hasNonNullDefault() {
+ // Note, STB overrides for correctly handling local types
+ ReferenceBinding currentType = this;
+ while (currentType != null) {
+ if ((currentType.tagBits & TagBits.AnnotationNonNullByDefault) != 0)
+ return true;
+ if ((currentType.tagBits & TagBits.AnnotationNullUnspecifiedByDefault) != 0)
+ return false;
+ currentType = currentType.enclosingType();
+ }
+ // package
+ return this.getPackage().defaultNullness == NONNULL_BY_DEFAULT;
+}
+
public final boolean hasRestrictedAccess() {
return (this.modifiers & ExtraCompilerModifiers.AccRestrictedAccess) != 0;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
index d889729..7b88083 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
@@ -20,6 +20,7 @@
* bug 366063 - Compiler should not add synthetic @NonNull annotations
* bug 384663 - Package Based Annotation Compilation Error in JDT 3.8/4.2 (works in 3.7.2)
* bug 386356 - Type mismatch error with annotations and generics
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -43,6 +44,7 @@
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.Expression.DecapsulationState;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
@@ -1673,6 +1675,18 @@
this.scope.buildMethods();
}
+private void initializeNullDefault() {
+ // ensure nullness defaults are initialized at all enclosing levels:
+ switch (this.nullnessDefaultInitialized) {
+ case 0:
+ getAnnotationTagBits(); // initialize
+ //$FALL-THROUGH$
+ case 1:
+ getPackage().isViewedAsDeprecated(); // initialize annotations
+ this.nullnessDefaultInitialized = 2;
+ }
+}
+
/**
* Returns true if a type is identical to another one,
* or for generic types, true if compared to its raw type.
@@ -2311,9 +2325,6 @@
// SH}
if (methodType == null) {
foundReturnTypeProblem = true;
- } else if (methodType.isArrayType() && ((ArrayBinding) methodType).leafComponentType == TypeBinding.VOID) {
- methodDecl.scope.problemReporter().returnTypeCannotBeVoidArray((MethodDeclaration) methodDecl);
- foundReturnTypeProblem = true;
} else {
//{ObjectTeams: generalize return of callin method?
if (method.isCallin() && methodType.isBaseType())
@@ -2347,8 +2358,10 @@
RoleTypeCreator.wrapTypesInMethodDeclSignature(method, methodDecl);
}
// SH}
- if (this.scope.compilerOptions().isAnnotationBasedNullAnalysisEnabled)
- createArgumentBindings(method); // need annotations resolved already at this point
+ CompilerOptions compilerOptions = this.scope.compilerOptions();
+ if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) {
+ createArgumentBindings(method, compilerOptions); // need annotations resolved already at this point
+ }
if (foundReturnTypeProblem)
return method; // but its still unresolved with a null return type & is still connected to its method declaration
@@ -2372,22 +2385,18 @@
// SH}
return method;
}
-private void createArgumentBindings(MethodBinding method) {
- // ensure nullness defaults are initialized at all enclosing levels:
- switch (this.nullnessDefaultInitialized) {
- case 0:
- getAnnotationTagBits(); // initialize
- //$FALL-THROUGH$
- case 1:
- getPackage().isViewedAsDeprecated(); // initialize annotations
- this.nullnessDefaultInitialized = 2;
- }
+private void createArgumentBindings(MethodBinding method, CompilerOptions compilerOptions) {
+ initializeNullDefault();
AbstractMethodDeclaration methodDecl = method.sourceMethod();
if (methodDecl != null) {
+ // while creating argument bindings we also collect explicit null annotations:
if (method.parameters != Binding.NO_PARAMETERS)
methodDecl.createArgumentBindings();
- if ((findNonNullDefault(methodDecl.scope, methodDecl.scope.environment()) == NONNULL_BY_DEFAULT)) {
- method.fillInDefaultNonNullness();
+ // add implicit annotations (inherited(?) & default):
+ if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) {
+//{ObjectTeams: added 2nd ctor arg:
+ new ImplicitNullAnnotationVerifier(compilerOptions.inheritNullAnnotations, this.scope.environment()).checkImplicitNullAnnotations(method, methodDecl, true, this.scope);
+// SH}
}
}
}
@@ -2459,16 +2468,11 @@
return true;
}
-/**
- * Answer the nullness default applicable at the given method binding.
- * Possible values: {@link Binding#NO_NULL_DEFAULT}, {@link Binding#NULL_UNSPECIFIED_BY_DEFAULT}, {@link Binding#NONNULL_BY_DEFAULT}.
- * @param currentScope where to start search for lexically enclosing default
- * @param environment gateway to options
- */
-private int findNonNullDefault(Scope currentScope, LookupEnvironment environment) {
+boolean hasNonNullDefault() {
// find the applicable default inside->out:
SourceTypeBinding currentType = null;
+ Scope currentScope = this.scope;
while (currentScope != null) {
switch (currentScope.kind) {
case Scope.METHOD_SCOPE:
@@ -2476,9 +2480,9 @@
if (referenceMethod != null && referenceMethod.binding != null) {
long methodTagBits = referenceMethod.binding.tagBits;
if ((methodTagBits & TagBits.AnnotationNonNullByDefault) != 0)
- return NONNULL_BY_DEFAULT;
+ return true;
if ((methodTagBits & TagBits.AnnotationNullUnspecifiedByDefault) != 0)
- return NULL_UNSPECIFIED_BY_DEFAULT;
+ return false;
}
break;
case Scope.CLASS_SCOPE:
@@ -2486,7 +2490,7 @@
if (currentType != null) {
int foundDefaultNullness = currentType.defaultNullness;
if (foundDefaultNullness != NO_NULL_DEFAULT) {
- return foundDefaultNullness;
+ return foundDefaultNullness == NONNULL_BY_DEFAULT;
}
}
break;
@@ -2496,13 +2500,10 @@
// package
if (currentType != null) {
- int foundDefaultNullness = currentType.getPackage().defaultNullness;
- if (foundDefaultNullness != NO_NULL_DEFAULT) {
- return foundDefaultNullness;
- }
+ return currentType.getPackage().defaultNullness == NONNULL_BY_DEFAULT;
}
- return NO_NULL_DEFAULT;
+ return false;
}
//{ObjectTeams: helper to find args allowing baseclass decapsulation:
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java
index 2eea880..56d09eb 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java
@@ -9,7 +9,9 @@
* IBM Corporation - initial API and implementation
* Fraunhofer FIRST - extended API and implementation
* Technical University Berlin - extended API and implementation
- * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking
+ * Stephan Herrmann - Contributions for
+ * bug 186342 - [compiler][null] Using annotations for null checking
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -66,6 +68,9 @@
long MultiCatchParameter = ASTNode.Bit13; // local
long IsResource = ASTNode.Bit14; // local
+ // have implicit null annotations been collected (inherited(?) & default)?
+ long IsNullnessKnown = ASTNode.Bit13; // method
+
// test bits to see if parts of binary types are faulted
long AreFieldsSorted = ASTNode.Bit13;
long AreFieldsComplete = ASTNode.Bit14; // sorted and all resolved
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
index bed5152..5bff1c6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
@@ -2933,6 +2933,10 @@
if (extendedDimensions > 0) {
type = type.copyDims(type.dimensions() + extendedDimensions);
type.sourceEnd = this.endPosition;
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=391092
+ if (type instanceof UnionTypeReference) {
+ this.problemReporter().illegalArrayOfUnionType(identifierName, type);
+ }
}
this.astLengthPtr--;
int modifierPositions = this.intStack[this.intPtr--];
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
index ddac5d0..7cc57d0 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
@@ -21,6 +21,7 @@
* bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 374605 - Unreasonable warning for enum-based switch statements
+ * bug 388281 - [compiler][null] inheritance of null annotations as an option
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.problem;
@@ -380,6 +381,8 @@
case IProblem.ParameterLackingNonNullAnnotation:
case IProblem.ParameterLackingNullableAnnotation:
case IProblem.CannotImplementIncompatibleNullness:
+ case IProblem.ConflictingNullAnnotations:
+ case IProblem.ConflictingInheritedNullAnnotations:
return CompilerOptions.NullSpecViolation;
case IProblem.RequiredNonNullButProvidedPotentialNull:
@@ -643,6 +646,9 @@
case IProblem.MissingNonNullByDefaultAnnotationOnPackage:
case IProblem.MissingNonNullByDefaultAnnotationOnType:
return CompilerOptions.MissingNonNullByDefaultAnnotation;
+
+ case IProblem.UnusedTypeParameter:
+ return CompilerOptions.UnusedTypeParameter;
}
return 0;
}
@@ -721,6 +727,7 @@
case CompilerOptions.UnusedLabel :
case CompilerOptions.RedundantSuperinterface :
case CompilerOptions.RedundantSpecificationOfTypeArguments :
+ case CompilerOptions.UnusedTypeParameter:
return CategorizedProblem.CAT_UNNECESSARY_CODE;
case CompilerOptions.UsingDeprecatedAPI :
@@ -1430,6 +1437,15 @@
exception.sourceStart,
exception.sourceEnd);
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=391092
+public void illegalArrayOfUnionType(char[] identifierName, TypeReference typeReference) {
+ this.handle(
+ IProblem.IllegalArrayOfUnionType,
+ NoArgument,
+ NoArgument,
+ typeReference.sourceStart,
+ typeReference.sourceEnd);
+}
public void cannotUseQualifiedEnumConstantInCaseLabel(Reference location, FieldBinding field) {
this.handle(
IProblem.IllegalQualifiedEnumConstantLabel,
@@ -7213,14 +7229,6 @@
}
return sourceStart;
}
-public void returnTypeCannotBeVoidArray(MethodDeclaration methodDecl) {
- this.handle(
- IProblem.CannotAllocateVoidArray,
- NoArgument,
- NoArgument,
- methodDecl.returnType.sourceStart,
- methodDecl.returnType.sourceEnd);
-}
public void scannerError(Parser parser, String errorTokenName) {
Scanner scanner = parser.scanner;
@@ -8597,6 +8605,17 @@
typeDecl.sourceStart,
typeDecl.sourceEnd);
}
+public void unusedTypeParameter(TypeParameter typeParameter) {
+ int severity = computeSeverity(IProblem.UnusedTypeParameter);
+ if (severity == ProblemSeverities.Ignore) return;
+ String [] arguments = new String[] {new String(typeParameter.name)};
+ this.handle(
+ IProblem.UnusedTypeParameter,
+ arguments,
+ arguments,
+ typeParameter.sourceStart,
+ typeParameter.sourceEnd);
+}
public void unusedWarningToken(Expression token) {
String[] arguments = new String[] { token.constant.stringValue() };
this.handle(
@@ -12757,6 +12776,44 @@
this.handle(IProblem.ContradictoryNullAnnotations, arguments, shortArguments, annotation.sourceStart, annotation.sourceEnd);
}
+// conflict default <-> inherited
+public void conflictingNullAnnotations(MethodBinding currentMethod, ASTNode location, MethodBinding inheritedMethod)
+{
+ char[][] nonNullAnnotationName = this.options.nonNullAnnotationName;
+ char[][] nullableAnnotationName = this.options.nullableAnnotationName;
+ String[] arguments = {
+ new String(CharOperation.concatWith(nonNullAnnotationName, '.')),
+ new String(CharOperation.concatWith(nullableAnnotationName, '.')),
+ new String(inheritedMethod.declaringClass.readableName())
+ };
+ String[] shortArguments = {
+ new String(nonNullAnnotationName[nonNullAnnotationName.length-1]),
+ new String(nullableAnnotationName[nullableAnnotationName.length-1]),
+ new String(inheritedMethod.declaringClass.shortReadableName())
+ };
+ this.handle(IProblem.ConflictingNullAnnotations, arguments, shortArguments, location.sourceStart, location.sourceEnd);
+}
+
+// conflict between different inheriteds
+public void conflictingInheritedNullAnnotations(ASTNode location, boolean previousIsNonNull, MethodBinding previousInherited, boolean isNonNull, MethodBinding inheritedMethod)
+{
+ char[][] previousAnnotationName = previousIsNonNull ? this.options.nonNullAnnotationName : this.options.nullableAnnotationName;
+ char[][] annotationName = isNonNull ? this.options.nonNullAnnotationName : this.options.nullableAnnotationName;
+ String[] arguments = {
+ new String(CharOperation.concatWith(previousAnnotationName, '.')),
+ new String(previousInherited.declaringClass.readableName()),
+ new String(CharOperation.concatWith(annotationName, '.')),
+ new String(inheritedMethod.declaringClass.readableName())
+ };
+ String[] shortArguments = {
+ new String(previousAnnotationName[previousAnnotationName.length-1]),
+ new String(previousInherited.declaringClass.shortReadableName()),
+ new String(annotationName[annotationName.length-1]),
+ new String(inheritedMethod.declaringClass.shortReadableName())
+ };
+ this.handle(IProblem.ConflictingInheritedNullAnnotations, arguments, shortArguments, location.sourceStart, location.sourceEnd);
+}
+
public void illegalAnnotationForBaseType(TypeReference type, Annotation[] annotations, char[] annotationName, long nullAnnotationTagBit)
{
int typeId = (nullAnnotationTagBit == TagBits.AnnotationNullable)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
index 303a95b..60f8ffa 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
@@ -18,6 +18,7 @@
# bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
# bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
# bug 374605 - Unreasonable warning for enum-based switch statements
+# bug 388281 - [compiler][null] inheritance of null annotations as an option
###############################################################################
0 = {0}
1 = super cannot be used in java.lang.Object
@@ -586,6 +587,10 @@
### MORE GENERICS
660 = Unused type arguments for the non generic constructor {0}({1}) of type {2}; it should not be parameterized with arguments <{3}>
+661 = Unused type parameter {0}
+
+### MORE TYPE RELATED
+662 = Illegal attempt to create arrays of union types
### CORRUPTED BINARIES
700 = The class file {0} contains a signature ''{1}'' ill-formed at position {2}
@@ -692,6 +697,8 @@
931 = Redundant null check: The variable {0} is specified as @{1}
932 = Null comparison always yields false: The variable {0} is specified as @{1}
933 = Null type mismatch: required ''@{0} {1}'' but the provided value is specified as @{2}
+939 = The default ''@{0}'' conflicts with the inherited ''@{1}'' annotation in the overridden method from {2}
+940 = Conflict between inherited null annotations ''@{0}'' declared in {1} versus ''@{2}'' declared in {3}
### ELABORATIONS
## Access restrictions
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
index 227ada5..8e4d268 100644
--- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
+++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/DefaultCodeFormatter.java
@@ -385,6 +385,7 @@
optionsMap.put(CompilerOptions.OPTION_InlineJsr, CompilerOptions.DISABLED);
optionsMap.put(CompilerOptions.OPTION_ReportMethodCanBeStatic, CompilerOptions.IGNORE);
optionsMap.put(CompilerOptions.OPTION_ReportMethodCanBePotentiallyStatic, CompilerOptions.IGNORE);
+ optionsMap.put(CompilerOptions.OPTION_ReportUnusedTypeParameter, CompilerOptions.IGNORE);
this.defaultCompilerOptions = optionsMap;
}
Object sourceOption = this.options.get(CompilerOptions.OPTION_Source);
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
index 64935a2..12ac931 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
@@ -240,13 +240,13 @@
*/
IPackageFragmentRoot[] findPackageFragmentRoots(IClasspathEntry entry);
/**
- * Returns the first type found following this project's classpath
- * with the given fully qualified name or <code>null</code> if none is found.
+ * Returns the first type (excluding secondary types) found following this project's
+ * classpath with the given fully qualified name or <code>null</code> if none is found.
* The fully qualified name is a dot-separated name. For example,
* a class B defined as a member type of a class A in package x.y should have a
* the fully qualified name "x.y.A.B".
*
- * Note that in order to be found, a type name (or its toplevel enclosing
+ * Note that in order to be found, a type name (or its top level enclosing
* type name) must match its corresponding compilation unit name. As a
* consequence, secondary types cannot be found using this functionality.
* To find secondary types use {@link #findType(String, IProgressMonitor)} instead.
@@ -261,8 +261,8 @@
*/
IType findType(String fullyQualifiedName) throws JavaModelException;
/**
- * Same functionality as {@link #findType(String)} but also look for secondary
- * types if given name does not match a compilation unit name.
+ * Same functionality as {@link #findType(String)} but also looks for secondary
+ * types if the given name does not match a compilation unit name.
*
* @param fullyQualifiedName the given fully qualified name
* @param progressMonitor the progress monitor to report progress to,
@@ -276,15 +276,15 @@
*/
IType findType(String fullyQualifiedName, IProgressMonitor progressMonitor) throws JavaModelException;
/**
- * Returns the first type found following this project's classpath
- * with the given fully qualified name or <code>null</code> if none is found.
+ * Returns the first type (excluding secondary types) found following this project's
+ * classpath with the given fully qualified name or <code>null</code> if none is found.
* The fully qualified name is a dot-separated name. For example,
* a class B defined as a member type of a class A in package x.y should have a
* the fully qualified name "x.y.A.B".
* If the returned type is part of a compilation unit, its owner is the given
* owner.
*
- * Note that in order to be found, a type name (or its toplevel enclosing
+ * Note that in order to be found, a type name (or its top level enclosing
* type name) must match its corresponding compilation unit name. As a
* consequence, secondary types cannot be found using this functionality.
* To find secondary types use {@link #findType(String, WorkingCopyOwner, IProgressMonitor)}
@@ -302,7 +302,7 @@
IType findType(String fullyQualifiedName, WorkingCopyOwner owner) throws JavaModelException;
/**
* Same functionality as {@link #findType(String, WorkingCopyOwner)}
- * but also look for secondary types if given name does not match
+ * but also looks for secondary types if the given name does not match
* a compilation unit name.
*
* @param fullyQualifiedName the given fully qualified name
@@ -318,15 +318,15 @@
*/
IType findType(String fullyQualifiedName, WorkingCopyOwner owner, IProgressMonitor progressMonitor) throws JavaModelException;
/**
- * Returns the first type found following this project's classpath
- * with the given package name and type qualified name
+ * Returns the first type (excluding secondary types) found following this
+ * project's classpath with the given package name and type qualified name
* or <code>null</code> if none is found.
* The package name is a dot-separated name.
* The type qualified name is also a dot-separated name. For example,
* a class B defined as a member type of a class A should have the
* type qualified name "A.B".
*
- * Note that in order to be found, a type name (or its toplevel enclosing
+ * Note that in order to be found, a type name (or its top level enclosing
* type name) must match its corresponding compilation unit name. As a
* consequence, secondary types cannot be found using this functionality.
* To find secondary types use {@link #findType(String, String, IProgressMonitor)}
@@ -344,8 +344,8 @@
*/
IType findType(String packageName, String typeQualifiedName) throws JavaModelException;
/**
- * Same functionality as {@link #findType(String, String)} but also look for
- * secondary types if given name does not match a compilation unit name.
+ * Same functionality as {@link #findType(String, String)} but also looks for
+ * secondary types if the given name does not match a compilation unit name.
*
* @param packageName the given package name
* @param typeQualifiedName the given type qualified name
@@ -360,8 +360,8 @@
*/
IType findType(String packageName, String typeQualifiedName, IProgressMonitor progressMonitor) throws JavaModelException;
/**
- * Returns the first type found following this project's classpath
- * with the given package name and type qualified name
+ * Returns the first type (excluding secondary types) found following this
+ * project's classpath with the given package name and type qualified name
* or <code>null</code> if none is found.
* The package name is a dot-separated name.
* The type qualified name is also a dot-separated name. For example,
@@ -370,7 +370,7 @@
* If the returned type is part of a compilation unit, its owner is the given
* owner.
*
- * Note that in order to be found, a type name (or its toplevel enclosing
+ * Note that in order to be found, a type name (or its top level enclosing
* type name) must match its corresponding compilation unit name. As a
* consequence, secondary types cannot be found using this functionality.
* To find secondary types use {@link #findType(String, String, WorkingCopyOwner, IProgressMonitor)}
@@ -390,7 +390,7 @@
IType findType(String packageName, String typeQualifiedName, WorkingCopyOwner owner) throws JavaModelException;
/**
* Same functionality as {@link #findType(String, String, WorkingCopyOwner)}
- * but also look for secondary types if given name does not match a compilation unit name.
+ * but also looks for secondary types if the given name does not match a compilation unit name.
*
* @param packageName the given package name
* @param typeQualifiedName the given type qualified name
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
index 31ae363..b15a906 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
@@ -98,6 +98,7 @@
* COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO
* COMPILER_PB_MISSING_ENUM_CASE_DESPITE_DEFAULT
* COMPILER_PB_SWITCH_MISSING_DEFAULT_CASE
+ * COMPILER_INHERIT_NULL_ANNOTATIONS
*******************************************************************************/
package org.eclipse.jdt.core;
@@ -526,6 +527,21 @@
* @category CompilerOptionID
*/
public static final String COMPILER_PB_SYNTHETIC_ACCESS_EMULATION = PLUGIN_ID + ".compiler.problem.syntheticAccessEmulation"; //$NON-NLS-1$
+
+ /**
+ * Compiler option ID: Reporting Unused Type Parameter.
+ * <p>When enabled, the compiler will issue an error or a warning whenever it encounters an
+ * unused type parameter. </p>
+ * <dl>
+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.problem.unusedTypeParameter"</code></dd>
+ * <dt>Possible values:</dt><dd><code>{ "error", "warning", "ignore" }</code></dd>
+ * <dt>Default:</dt><dd><code>"ignore"</code></dd>
+ * </dl>
+ * @since 3.9
+ * @category CompilerOptionID
+ */
+ public static final String COMPILER_PB_UNUSED_TYPE_PARAMETER = PLUGIN_ID + ".compiler.problem.unusedTypeParameter"; //$NON-NLS-1$
+
/**
* Compiler option ID: Reporting Non-Externalized String Literal.
* <p>When enabled, the compiler will issue an error or a warning for non externalized
@@ -1689,6 +1705,26 @@
*/
public static final String COMPILER_PB_REDUNDANT_NULL_ANNOTATION = PLUGIN_ID + ".compiler.problem.redundantNullAnnotation"; //$NON-NLS-1$
/**
+ * Compiler option ID: Inheritance of null annotations.
+ * <p>When enabled, the compiler will check for each method without any explicit null annotations:
+ * If it overrides a method which has null annotations, it will treat the
+ * current method as if it had the same annotations as the overridden method.</p>
+ * <p>Annotation inheritance will use the <em>effective</em> nullness of the overridden method
+ * after transitively applying inheritance and after applying any default nullness
+ * (see {@link #COMPILER_NONNULL_BY_DEFAULT_ANNOTATION_NAME}) at the site of the overridden method.</p>
+ * <p>If different implicit null annotations (from a nonnull default and/or overridden methods) are applicable
+ * to the same type in a method signature, this is flagged as an error
+ * and an explicit null annotation must be used to disambiguate.</p>
+ * <dl>
+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations"</code></dd>
+ * <dt>Possible values:</dt><dd><code>{ "disabled", "enabled" }</code></dd>
+ * <dt>Default:</dt><dd><code>"disabled"</code></dd>
+ * </dl>
+ * @since 3.9
+ * @category CompilerOptionID
+ */
+ public static final String COMPILER_INHERIT_NULL_ANNOTATIONS = JavaCore.PLUGIN_ID+".compiler.annotation.inheritNullAnnotations"; //$NON-NLS-1$
+ /**
* Compiler option ID: Setting Source Compatibility Mode.
* <p>Specify whether which source level compatibility is used. From 1.4 on, <code>'assert'</code> is a keyword
* reserved for assertion support. Also note, than when toggling to 1.4 mode, the target VM
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaCorePreferenceInitializer.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaCorePreferenceInitializer.java
index 91c6dde..fb926c3 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaCorePreferenceInitializer.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaCorePreferenceInitializer.java
@@ -29,6 +29,7 @@
*/
public void initializeDefaultPreferences() {
// If modified, also modify the method JavaModelManager#getDefaultOptionsNoInitialization()
+ // and also consider updating org.eclipse.jdt.internal.compiler.batch.Main#initializeWarnings(String)
// Get options names set
HashSet optionNames = JavaModelManager.getJavaModelManager().optionNames;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java
index 301d4b6..981e27b 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java
@@ -10,6 +10,8 @@
* Terry Parker <tparker@google.com>
* - Contribution for https://bugs.eclipse.org/bugs/show_bug.cgi?id=372418
* - Another problem with inner classes referenced from jars or class folders: "The type ... cannot be resolved"
+ * Stephan Herrmann - Contribution for
+ * Bug 392727 - Cannot compile project when a java file contains $ in its file name
*******************************************************************************/
package org.eclipse.jdt.internal.core.builder;
@@ -277,16 +279,17 @@
// Only enclosing type names are present in the additional units table, so strip off inner class specifications
// when doing the lookup (https://bugs.eclipse.org/372418).
// Also take care of $ in the name of the class (https://bugs.eclipse.org/377401)
- int index = qualifiedTypeName.indexOf('$');
- if (index > 0) {
- String enclosingTypeName = qualifiedTypeName.substring(0, index);
- SourceFile unit = (SourceFile) this.additionalUnits.get(enclosingTypeName); // doesn't have file extension
- if (unit != null)
- return new NameEnvironmentAnswer(unit, null /*no access restriction*/);
- }
+ // and prefer name with '$' if unit exists rather than failing to search for nested class (https://bugs.eclipse.org/392727)
SourceFile unit = (SourceFile) this.additionalUnits.get(qualifiedTypeName); // doesn't have file extension
if (unit != null)
return new NameEnvironmentAnswer(unit, null /*no access restriction*/);
+ int index = qualifiedTypeName.indexOf('$');
+ if (index > 0) {
+ String enclosingTypeName = qualifiedTypeName.substring(0, index);
+ unit = (SourceFile) this.additionalUnits.get(enclosingTypeName); // doesn't have file extension
+ if (unit != null)
+ return new NameEnvironmentAnswer(unit, null /*no access restriction*/);
+ }
}
String qBinaryFileName = qualifiedTypeName + SUFFIX_STRING_class;