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;