Update jdt.core to I20150317-1130
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 49b0fb1..20cc8b1 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
@@ -27,6 +27,7 @@
* Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Bug 440477 - [null] Infrastructure for feeding external annotations into compilation
* Bug 440687 - [compiler][batch][null] improve command line option for external annotations
+ * Bug 408815 - [batch][null] Add CLI option for COMPILER_PB_SYNTACTIC_NULL_ANALYSIS_FOR_FIELDS
* Jesper Steen Moller - Contributions for
* bug 404146 - [1.7][compiler] nested try-catch-finally-blocks leads to unrunnable Java byte code
* bug 407297 - [1.8][compiler] Control generation of parameter names by option
@@ -79,7 +80,7 @@
"import java.lang.annotation.*;\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
- "@Target({ METHOD, PARAMETER })\n" +
+ "@Target({ METHOD, PARAMETER, FIELD })\n" +
"public @interface Nullable{\n" +
"}\n";
private static final String NONNULL_ANNOTATION_CONTENT = "package org.eclipse.jdt.annotation;\n" +
@@ -87,7 +88,7 @@
"import java.lang.annotation.*;\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
- "@Target({ METHOD, PARAMETER })\n" +
+ "@Target({ METHOD, PARAMETER, FIELD })\n" +
"public @interface NonNull{\n" +
"}\n";
@@ -1907,6 +1908,7 @@
" errors and warnings\n" +
" switchDefault + switch statement lacking a default case\n" +
" syncOverride missing synchronized in synchr. method override\n" +
+ " syntacticAnalysis perform syntax-based null analysis for fields\n" +
" syntheticAccess synthetic access for innerclass\n" +
" tasks(<tags separated by |>) tasks identified by tags inside comments\n" +
" typeHiding + type parameter hiding another type\n" +
@@ -12974,6 +12976,39 @@
true);
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=408815
+// -warn option - regression tests to check option syntacticAnalysis
+// Null warnings because of annotations, null spec violations, suppressed by null-check
+public void test316b_warn_options() {
+ this.runConformTest(
+ new String[] {
+ "p/X.java",
+ "package p;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public class X {\n" +
+ " @Nullable Object f;\n" +
+ " @NonNull Object foo() {\n" +
+ " if (this.f != null)\n" +
+ " return this.f;\n" +
+ " return this;\n" +
+ " }\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"
+ + " -warn:+nullAnnot -warn:+null,syntacticAnalysis -proc:none -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "",
+ true);
+}
+
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=374605
// -warn/-error option : enumSwitchPedantic
public void test317_warn_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 c2e237c..30c5d06 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
@@ -5737,5 +5737,42 @@
"class Test2<E extends Test1<E>>{}\n"
});
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=425203, [compiler][1.7][inference] ECJ rejects valid code using bounded wildcards
+public void test425203() {
+ String source =
+ "import java.util.Arrays;\n" +
+ "import java.util.List;\n" +
+ "interface MyFunction<Input, Output> {\n" +
+ " Output apply(Input input);\n" +
+ "}\n" +
+ "public class Test {\n" +
+ " public <Input, Output> List<Output> wrap(MyFunction<? super Input, ? extends Output> function, Input input) {\n" +
+ " return Arrays.asList(function.apply(input));\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " System.out.println(\"Done\");\n" +
+ " }\n" +
+ "}";
+ if (this.complianceLevel < ClassFileConstants.JDK1_7) {
+ this.runNegativeTest(
+ new String[] {
+ "Test.java",
+ source,
+ },
+ "----------\n" +
+ "1. WARNING in Test.java (at line 8)\n" +
+ " return Arrays.asList(function.apply(input));\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Type safety: A generic array of capture#2-of ? extends Output is created for a varargs parameter\n" +
+ "----------\n" +
+ "2. ERROR in Test.java (at line 8)\n" +
+ " return Arrays.asList(function.apply(input));\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Type mismatch: cannot convert from List<capture#2-of ? extends Output> to List<Output>\n" +
+ "----------\n");
+ } else {
+ runConformTest(new String[]{ "Test.java", source }, "Done");
+ }
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_7.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_7.java
index 22caeb9..fa3587a 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_7.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_7.java
@@ -12,6 +12,7 @@
* Bug 424205 - [1.8] Cannot infer type for diamond type with lambda on method invocation
* Bug 429203 - [1.8][compiler] NPE in AllocationExpression.binding
* Bug 456508 - Unexpected RHS PolyTypeBinding for: <code-snippet>
+ * Bug 462083 - [1.8][inference] Java 8 generic return type mismatch with interface involving type parameter.
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
@@ -2981,6 +2982,29 @@
"Type safety: A generic array of QueryAtom<PGroepAccount,PAccount> is created for a varargs parameter\n" +
"----------\n");
}
+public void testBug462083() {
+ runConformTest(
+ new String[] {
+ "Java8InterfaceTest.java",
+ "public abstract class Java8InterfaceTest\n" +
+ "{\n" +
+ " public static interface Entity {}\n" +
+ "\n" +
+ " public static interface Service<T1 extends Entity> {}\n" +
+ "\n" +
+ " public static interface ServiceLocator<T2 extends Entity> {}\n" +
+ "\n" +
+ " public static class ConcreteClass<T3 extends Entity, S extends Service<T3>> implements ServiceLocator<T3> {}\n" +
+ "\n" +
+ " protected abstract <T4 extends Entity> ConcreteClass<T4, ?> getParameterized(T4 entity);\n" +
+ "\n" +
+ " protected <T5 extends Entity> ServiceLocator<T5> getInterface(T5 entity)\n" +
+ " {\n" +
+ " return getParameterized(entity);\n" +
+ " }\n" +
+ "}\n"
+ });
+}
public static Class testClass() {
return GenericsRegressionTest_1_7.class;
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java
index db37afe..699683f 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java
@@ -5045,4 +5045,65 @@
"The method removeEntity(MyEntity) is undefined for the type new MyTickContext(){}\n" +
"----------\n");
}
+public void testBug455945() {
+ runConformTest(
+ new String[] {
+ "Test.java",
+ "import java.util.function.BiFunction;\n" +
+ "import java.util.function.Function;\n" +
+ "import java.util.function.Predicate;\n" +
+ "import java.util.stream.Stream;\n" +
+ "\n" +
+ "public class Test {\n" +
+ "\n" +
+ " static <T> Tuple2<Seq<T>, Seq<T>> splitAtDoesntCompile(Stream<T> stream, long position) {\n" +
+ " return seq(stream)\n" +
+ " .zipWithIndex()\n" +
+ " .partition(t -> t.v2 < position)\n" +
+ " .map((v1, v2) -> tuple(\n" +
+ " v1.map(t -> t.v1),\n" +
+ " v2.map(t -> t.v1)\n" +
+ " ));\n" +
+ " }\n" +
+ "\n" +
+ " static <T> Tuple2<Seq<T>, Seq<T>> splitAtCompiles(Stream<T> stream, long position) {\n" +
+ " return seq(stream)\n" +
+ " .zipWithIndex()\n" +
+ " .partition(t -> t.v2 < position)\n" +
+ " .map((v1, v2) -> Test.<Seq<T>, Seq<T>>tuple(\n" +
+ " v1.map(t -> t.v1),\n" +
+ " v2.map(t -> t.v1)\n" +
+ " ));\n" +
+ " }\n" +
+ "\n" +
+ " static <T> Seq<T> seq(Stream<T> stream) {\n" +
+ " return null;\n" +
+ " }\n" +
+ "\n" +
+ " static <T1, T2> Tuple2<T1, T2> tuple(T1 v1, T2 v2) {\n" +
+ " return null;\n" +
+ " }\n" +
+ "}\n" +
+ "\n" +
+ "interface I<T> {\n" +
+ " T get();\n" +
+ " <U> I<U> map(Function<T, U> f);\n" +
+ "}\n" +
+ "\n" +
+ "interface Seq<T> {\n" +
+ " Seq<Tuple2<T, Long>> zipWithIndex();\n" +
+ " Tuple2<Seq<T>, Seq<T>> partition(Predicate<? super T> predicate);\n" +
+ " <R> Seq<R> map(Function<? super T, ? extends R> mapper);\n" +
+ "}\n" +
+ "\n" +
+ "class Tuple2<T1, T2> {\n" +
+ " T1 v1;\n" +
+ " T2 v2;\n" +
+ " \n" +
+ " <R> R map(BiFunction<T1, T2, R> function) {\n" +
+ " return null;\n" +
+ " }\n" +
+ "}\n"
+ });
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaRegressionTest.java
index 68dbd0d..eddb943 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaRegressionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaRegressionTest.java
@@ -7,6 +7,8 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for
+ * Bug 446691 - [1.8][null][compiler] NullPointerException in SingleNameReference.analyseCode
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
@@ -681,7 +683,105 @@
},
"done");
}
-
+public void testBug446691_comment5() {
+ runConformTest(new String [] {
+ "Test.java",
+ "import java.util.*;\n" +
+ "\n" +
+ "public class Test {\n" +
+ " protected final Integer myInt;\n" +
+ "\n" +
+ " public Test() {\n" +
+ " myInt = Integer.valueOf(0);\n" +
+ " try {\n" +
+ " Optional.empty().orElseThrow(() -> new IllegalArgumentException(myInt.toString()));\n" +
+ " } catch (IllegalArgumentException e) {\n" +
+ " throw new RuntimeException();\n" +
+ " }\n" +
+ " return;\n" +
+ " }\n" +
+ "}\n"
+ });
+}
+public void testBug446691_comment8() {
+ runConformTest(new String [] {
+ "Boom.java",
+ "public class Boom {\n" +
+ " private final String field;\n" +
+ " public Boom(String arg) {\n" +
+ " this.field = arg;\n" +
+ " try {\n" +
+ " java.util.function.Supplier<String> supplier = () -> field;\n" +
+ " } catch (Exception e) {\n" +
+ " \n" +
+ " }\n" +
+ " }\n" +
+ "}\n"
+ });
+}
+public void testBug446691_comment14() {
+ runNegativeTest(new String [] {
+ "test/Main.java",
+ "package test;\n" +
+ "\n" +
+ "import java.util.logging.Logger;\n" +
+ "\n" +
+ "public class Main {\n" +
+ "\n" +
+ " private static final Logger LOG = Logger.getLogger(\"test\");\n" +
+ " private final static String value;\n" +
+ "\n" +
+ " static {\n" +
+ " try {\n" +
+ " LOG.info(() -> String.format(\"Value is: %s\", value));\n" +
+ " } catch (final Exception ex) {\n" +
+ " throw new ExceptionInInitializerError(ex);\n" +
+ " }\n" +
+ " }\n" +
+ "}"
+ },
+ "----------\n" +
+ "1. ERROR in test\\Main.java (at line 8)\n" +
+ " private final static String value;\n" +
+ " ^^^^^\n" +
+ "The blank final field value may not have been initialized\n" +
+ "----------\n" +
+ "2. ERROR in test\\Main.java (at line 12)\n" +
+ " LOG.info(() -> String.format(\"Value is: %s\", value));\n" +
+ " ^^^^^\n" +
+ "The blank final field value may not have been initialized\n" +
+ "----------\n");
+}
+// error in lambda even if field is assigned later
+public void testBug446691_comment14b() {
+ runNegativeTest(new String [] {
+ "test/Main.java",
+ "package test;\n" +
+ "\n" +
+ "import java.util.logging.Logger;\n" +
+ "\n" +
+ "public class Main {\n" +
+ "\n" +
+ " private static final Logger LOG = Logger.getLogger(\"test\");\n" +
+ " private final static String value;\n" +
+ "\n" +
+ " static {\n" +
+ " try {\n" +
+ " LOG.info(() -> String.format(\"Value is: %s\", value));\n" +
+ " } catch (final Exception ex) {\n" +
+ " throw new ExceptionInInitializerError(ex);\n" +
+ " }\n" +
+ " value = \"\";" +
+ " }\n" +
+ "}"
+ },
+ "----------\n" +
+ "1. ERROR in test\\Main.java (at line 12)\n" +
+ " LOG.info(() -> String.format(\"Value is: %s\", value));\n" +
+ " ^^^^^\n" +
+ "The blank final field value may not have been initialized\n" +
+ "----------\n");
+}
public static Class testClass() {
return LambdaRegressionTest.class;
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java
index d3f5e05..5204ab1 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -39,8 +39,8 @@
@SuppressWarnings({ "unchecked", "rawtypes" })
public class MethodVerifyTest extends AbstractComparableTest {
static {
-// TESTS_NAMES = new String[] { "testBug406928" };
-// TESTS_NUMBERS = new int[] { 213 };
+// TESTS_NAMES = new String[] { "test124", "test124b" };
+// TESTS_NUMBERS = new int[] { 124 };
// TESTS_RANGE = new int[] { 190, -1};
}
@@ -8642,6 +8642,35 @@
"}"},
this.complianceLevel <= ClassFileConstants.JDK1_6 ? "ab" : "Stack Overflow");
}
+// Bug 460993: [compiler] Incremental build not always reports the same errors (type cannot be resolved - indirectly referenced)
+public void test124b() {
+ this.runConformTest(
+ new String[] {
+ "A.java",
+ "public class A {\n" +
+ " public Object o = \"\";\n" +
+ " public static void main(String args[]) {\n" +
+ " X.main(args);\n" +
+ " }\n" +
+ "}\n",
+ "X.java",
+ "public class X {\n" +
+ " public static String choose(String one, String two) {\n" +
+ " return one + X.<String>choose(one, two);\n" +
+ " }\n" +
+ " public static <T> T choose(T one, T two) {\n" +
+ " return two;\n" +
+ " }\n" +
+ " public static void main(String args[]) {\n" +
+ " try {\n" +
+ " System.out.println(choose(\"a\", \"b\"));\n" +
+ " } catch (StackOverflowError e) {\n" +
+ " System.out.println(\"Stack Overflow\");\n" +
+ " }\n" +
+ " }\n" +
+ "}"},
+ this.complianceLevel <= ClassFileConstants.JDK1_6 ? "ab" : "Stack Overflow");
+}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=150655
// variant
public void test125() {
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
index 59b69a0..1e3fba1 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
@@ -4860,12 +4860,7 @@
" ^^^^\n" +
"Null type mismatch: required \'@NonNull T\' but the provided value is null\n" +
"----------\n" +
- "2. ERROR in X.java (at line 10)\n" +
- " void test(Inner<Number> inum) {\n" +
- " ^^^^^^\n" +
- "Null constraint mismatch: The type \'Number\' is not a valid substitute for the type parameter \'@NonNull T\'\n" +
- "----------\n" +
- "3. ERROR in X.java (at line 11)\n" +
+ "2. ERROR in X.java (at line 11)\n" +
" @NonNull Number nnn = inum.process(null); // ERR on argument\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Number\' but the provided value is null\n" +
@@ -7574,11 +7569,6 @@
" @NonNull String s = os.orElse(null);\n" +
" ^^^^^^^^^^^^^^^\n" +
"Contradictory null annotations: method was inferred as \'@NonNull @Nullable String orElse(@NonNull @Nullable String)\', but only one of \'@NonNull\' and \'@Nullable\' can be effective at any location\n" +
- "----------\n" +
- "7. ERROR in OTest.java (at line 11)\n" +
- " @NonNull String s = os.orElse(null);\n" +
- " ^^^^^^^^^^^^^^^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is inferred as @Nullable\n" +
"----------\n");
}
public void testBug454182() {
@@ -7622,4 +7612,131 @@
null,
"");
}
+public void testBug437072() {
+ runNegativeTestWithLibs(
+ new String[] {
+ "X.java",
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "import java.util.List;\n" +
+ "public class X {\n" +
+ " @NonNull int[][] ints = new int[3][4];\n" +
+ " @NonNull int[][] test1() { return new int[3][4]; }\n" +
+ " void test2(@NonNull boolean[][] bools) {\n" +
+ " @NonNull boolean[][] bools2 = bools;\n" +
+ " }\n" +
+ " List<@NonNull int[]> intslist;\n" +
+ " List<@NonNull int> intlist;\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in X.java (at line 4)\n" +
+ " @NonNull int[][] ints = new int[3][4];\n" +
+ " ^^^^^^^^\n" +
+ "The nullness annotation @NonNull is not applicable for the primitive type int\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 5)\n" +
+ " @NonNull int[][] test1() { return new int[3][4]; }\n" +
+ " ^^^^^^^^\n" +
+ "The nullness annotation @NonNull is not applicable for the primitive type int\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 6)\n" +
+ " void test2(@NonNull boolean[][] bools) {\n" +
+ " ^^^^^^^^\n" +
+ "The nullness annotation @NonNull is not applicable for the primitive type boolean\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 7)\n" +
+ " @NonNull boolean[][] bools2 = bools;\n" +
+ " ^^^^^^^^\n" +
+ "The nullness annotation @NonNull is not applicable for the primitive type boolean\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 9)\n" +
+ " List<@NonNull int[]> intslist;\n" +
+ " ^^^^^^^^\n" +
+ "The nullness annotation @Nullable is not applicable for the primitive type int\n" +
+ "----------\n" +
+ "6. ERROR in X.java (at line 10)\n" +
+ " List<@NonNull int> intlist;\n" +
+ " ^^^\n" +
+ "Syntax error, insert \"Dimensions\" to complete ReferenceType\n" +
+ "----------\n");
+}
+public void testBug448709() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION, JavaCore.WARNING); // ensure program is runnable
+ runConformTestWithLibs(
+ new String[] {
+ "Test.java",
+ "import java.util.*;\n" +
+ "import java.util.function.*;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "\n" +
+ "public class Test {\n" +
+ "\n" +
+ " /**\n" +
+ " * A null-annotated version of {@link Objects#requireNonNull(Object)}.\n" +
+ " */\n" +
+ " public static final <T> @NonNull T requireNonNull(final @Nullable T obj) {\n" +
+ " if (obj == null) throw new NullPointerException();\n" +
+ " return obj;\n" +
+ " }\n" +
+ "\n" +
+ " /**\n" +
+ " * A null-annotated version of {@link Optional#map(Function)}.\n" +
+ " */\n" +
+ " public static final <T,U> @NonNull Optional<U> map(final @NonNull Optional<T> optional, final Function<@NonNull ? super T,? extends U> mapper) {\n" +
+ " if (!optional.isPresent()) return requireNonNull(Optional.empty());\n" +
+ " final T source = optional.get();\n" +
+ " final U result = mapper.apply(source);\n" +
+ " System.out.println(source+\"->\"+result);\n" +
+ " return requireNonNull(Optional.<U> ofNullable(result));\n" +
+ " }\n" +
+ "\n" +
+ " /**\n" +
+ " * A method with a {@link NonNull} {@link DefaultLocation#PARAMETER} and {@link DefaultLocation#RETURN_TYPE}.\n" +
+ " */\n" +
+ " public static final @NonNull Integer testMethod(final @NonNull String s) {\n" +
+ " final Integer r = Integer.valueOf(s);\n" +
+ " if (r == null) throw new NullPointerException();\n" +
+ " return r+1;\n" +
+ " }\n" +
+ "\n" +
+ " public static void main(final String[] args) {\n" +
+ " final @NonNull Optional<@Nullable String> optNullableString = requireNonNull(Optional.ofNullable(\"1\"));\n" +
+ "\n" +
+ " final Function<@NonNull String,@NonNull Integer> testMethodRef = Test::testMethod;\n" +
+ " map(optNullableString, testMethodRef);\n" +
+ "\n" +
+ " map(optNullableString, Test::testMethod); // Error: Null type mismatch at parameter 1: required '@NonNull String' but provided '@Nullable String' via method descriptor Function<String,Integer>.apply(String)\n" +
+ "\n" +
+ " map(optNullableString, (s) -> Test.testMethod(s));\n" +
+ " }\n" +
+ "\n" +
+ "}\n"
+ },
+ compilerOptions,
+ "----------\n" +
+ "1. WARNING in Test.java (at line 39)\n" +
+ " map(optNullableString, testMethodRef);\n" +
+ " ^^^^^^^^^^^^^\n" +
+ "Contradictory null annotations: method was inferred as \'@NonNull Optional<@NonNull Integer> map(@NonNull Optional<@Nullable String>, Function<@NonNull ? super @Nullable String,? extends @NonNull Integer>)\', but only one of \'@NonNull\' and \'@Nullable\' can be effective at any location\n" +
+ "----------\n" +
+ "2. WARNING in Test.java (at line 41)\n" +
+ " map(optNullableString, Test::testMethod); // Error: Null type mismatch at parameter 1: required \'@NonNull String\' but provided \'@Nullable String\' via method descriptor Function<String,Integer>.apply(String)\n" +
+ " ^^^^^^^^^^^^^^^^\n" +
+ "Contradictory null annotations: method was inferred as \'@NonNull Optional<@NonNull Integer> map(@NonNull Optional<@Nullable String>, Function<@NonNull ? super @Nullable String,? extends @NonNull Integer>)\', but only one of \'@NonNull\' and \'@Nullable\' can be effective at any location\n" +
+ "----------\n" +
+ "3. WARNING in Test.java (at line 43)\n" +
+ " map(optNullableString, (s) -> Test.testMethod(s));\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Contradictory null annotations: method was inferred as \'@NonNull Optional<@NonNull Integer> map(@NonNull Optional<@Nullable String>, Function<@NonNull ? super @Nullable String,? extends @NonNull Integer>)\', but only one of \'@NonNull\' and \'@Nullable\' can be effective at any location\n" +
+ "----------\n" +
+ "4. WARNING in Test.java (at line 43)\n" +
+ " map(optNullableString, (s) -> Test.testMethod(s));\n" +
+ " ^\n" +
+ "Null type mismatch (type annotations): required \'@NonNull String\' but this expression has type \'@Nullable String\'\n" +
+ "----------\n",
+ "1->2\n" +
+ "1->2\n" +
+ "1->2");
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/dom/StandAloneASTParserTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/dom/StandAloneASTParserTest.java
index dfa4957..9c65b46 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/dom/StandAloneASTParserTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/dom/StandAloneASTParserTest.java
@@ -7,6 +7,8 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for
+ * Bug 461250 - ArrayIndexOutOfBoundsException in SourceTypeBinding.fields
*******************************************************************************/
package org.eclipse.jdt.core.tests.dom;
@@ -16,6 +18,7 @@
import java.io.IOException;
import java.io.Writer;
import java.util.List;
+import java.util.Map;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
@@ -406,4 +409,24 @@
fileY.delete();
}
}
+
+ public void testBug461250() {
+ String source =
+ "class QH<T> implements QR.Q {\n" +
+ " QR.Q q;\n" +
+ " @V(v = A, d = \"\") Map p;\n" +
+ "}\n";
+ Map options = JavaCore.getOptions();
+ JavaCore.setComplianceOptions(JavaCore.VERSION_1_7, options);
+ ASTParser parser = ASTParser.newParser(AST.JLS8);
+ parser.setCompilerOptions(options);
+ parser.setKind(ASTParser.K_COMPILATION_UNIT);
+ parser.setSource(source.toCharArray());
+ parser.setResolveBindings(true);
+ String[] emptyStringArray = new String[0];
+ parser.setEnvironment(emptyStringArray, emptyStringArray, emptyStringArray, true /* includeRunningVMBootclasspath */);
+ parser.setUnitName("dontCare");
+ ASTNode ast = parser.createAST(null);
+ assertTrue("should have parsed a CUD", ast instanceof CompilationUnit);
+ }
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter18Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter18Test.java
index 88fc382..9d4bd69 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter18Test.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter18Test.java
@@ -4938,7 +4938,7 @@
*
* @throws JavaModelException
*/
-public void testBug425601_001() throws JavaModelException {
+public void _testBug425601_001() throws JavaModelException {
this.workingCopy = getWorkingCopy("/Converter18/src/testBug425601_001/Outer.java",
true/* resolve */);
String contents = "package testBug425601_001;\n" +
@@ -4983,7 +4983,7 @@
*
* @throws JavaModelException
*/
-public void testBug425601_002() throws JavaModelException {
+public void _testBug425601_002() throws JavaModelException {
this.workingCopy = getWorkingCopy("/Converter18/src/testBug425601_002/Outer.java",
true/* resolve */);
String contents = "package testBug425601_002;\n" +
@@ -5109,4 +5109,32 @@
assertTrue("Test Failed", false);
}
}
+/**
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=443232
+ * @bug Bug 443232 IAE in ASTNode.setSourceRange with broken code
+ * @throws JavaModelException
+ */
+public void testBug443232() throws JavaModelException {
+ String contents =
+ "package test443232;\n" +
+ "public class E21 {\n" +
+ " {private int[] nums;\n" +
+ " void foo() {\n" +
+ " nums\n" +
+ " }\n" +
+ "}";
+ this.workingCopy = getWorkingCopy("/Converter18/src/test443232/E21.java", contents, false/*computeProblems*/);
+ IJavaProject javaProject = this.workingCopy.getJavaProject();
+
+ final ASTParser parser = ASTParser.newParser(AST.JLS8);
+ parser.setResolveBindings(false);
+ parser.setProject(javaProject);
+ parser.setIgnoreMethodBodies(false);
+ ASTRequestor requestor = new ASTRequestor() {};
+ try {
+ parser.createASTs(new ICompilationUnit[] {this.workingCopy}, new String[0], requestor, null);
+ } catch (IllegalArgumentException e) {
+ assertTrue("Test Failed", false);
+ }
+}
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations17Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations17Test.java
index 674f204..a9eef33 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations17Test.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations17Test.java
@@ -10,8 +10,7 @@
*******************************************************************************/
package org.eclipse.jdt.core.tests.model;
-import junit.framework.Test;
-
+import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder;
@@ -19,12 +18,24 @@
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.util.ExternalAnnotationUtil;
+import org.eclipse.jdt.core.util.ExternalAnnotationUtil.MergeStrategy;
import org.osgi.framework.Bundle;
+import junit.framework.Test;
+
public class ExternalAnnotations17Test extends ExternalAnnotations18Test {
@@ -163,4 +174,204 @@
}, new int[] { 8 });
}
+ // ===== Full round trip: detect problem - annotated - detect problem change =====
+
+ public void testAnnotateFieldOfNested() throws Exception {
+ myCreateJavaProject("TestLibs");
+ addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] {
+ "/UnannotatedLib/libs/Lib1.java",
+ "package libs;\n" +
+ "\n" +
+ "public interface Lib1 {\n" +
+ " public static interface Nested {\n" +
+ " String one = \"1\";\n" +
+ " }\n" +
+ "}\n"
+ }, null);
+
+ // acquire source AST:
+ IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null);
+ String test1Content = "package tests;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "\n" +
+ "import libs.Lib1;\n" +
+ "\n" +
+ "public class Test1 {\n" +
+ " @NonNull String test0() {\n" +
+ " return Lib1.Nested.one;\n" +
+ " }\n" +
+ "}\n";
+ ICompilationUnit cu = fragment.createCompilationUnit("Test1.java", test1Content,
+ true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor());
+ ASTParser parser = ASTParser.newParser(AST.JLS8);
+ parser.setSource(cu);
+ parser.setResolveBindings(true);
+ parser.setStatementsRecovery(false);
+ parser.setBindingsRecovery(false);
+ CompilationUnit unit = (CompilationUnit) parser.createAST(null);
+ IProblem[] problems = unit.getProblems();
+ assertProblems(problems, new String[] {
+ "Pb(912) Null type safety: The expression of type 'String' needs unchecked conversion to conform to '@NonNull String'",
+ }, new int[] { 8 });
+
+ // find type binding:
+ int start = test1Content.indexOf("one");
+ ASTNode name = NodeFinder.perform(unit, start, 0);
+ assertTrue("should be simple name", name.getNodeType() == ASTNode.SIMPLE_NAME);
+ IVariableBinding fieldBinding = (IVariableBinding) ((SimpleName)name).resolveBinding();
+
+ // find annotation file (not yet existing):
+ IFile annotationFile = ExternalAnnotationUtil.getAnnotationFile(this.project, fieldBinding.getDeclaringClass(), null);
+ assertFalse("file should not exist", annotationFile.exists());
+ assertEquals("file path", "/TestLibs/annots/libs/Lib1$Nested.eea", annotationFile.getFullPath().toString());
+
+ // annotate:
+ String originalSignature = ExternalAnnotationUtil.extractGenericTypeSignature(fieldBinding.getVariableDeclaration().getType());
+ ExternalAnnotationUtil.annotateMember("libs/Lib1$Nested", annotationFile,
+ "one",
+ originalSignature,
+ "L1java/lang/String;",
+ MergeStrategy.OVERWRITE_ANNOTATIONS, null);
+ assertTrue("file should exist", annotationFile.exists());
+
+ // check that the error is gone:
+ CompilationUnit reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor());
+ assertNoProblems(reconciled.getProblems());
+ }
+
+
+ public void testAnnotateFieldWithParameterizedType() throws Exception {
+ myCreateJavaProject("TestLibs");
+ addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] {
+ "/UnannotatedLib/libs/Lib1.java",
+ "package libs;\n" +
+ "\n" +
+ "public class Lib1<T> {\n" +
+ " public Lib1<T> one;\n" +
+ "}\n"
+ }, null);
+
+ // acquire source AST:
+ IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null);
+ String test1Content = "package tests;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "\n" +
+ "import libs.Lib1;\n" +
+ "\n" +
+ "public class Test1 {\n" +
+ " @NonNull Lib1<String> test0(Lib1<String> stringLib) {\n" +
+ " return stringLib.one;\n" +
+ " }\n" +
+ "}\n";
+ ICompilationUnit cu = fragment.createCompilationUnit("Test1.java", test1Content,
+ true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor());
+ ASTParser parser = ASTParser.newParser(AST.JLS8);
+ parser.setSource(cu);
+ parser.setResolveBindings(true);
+ parser.setStatementsRecovery(false);
+ parser.setBindingsRecovery(false);
+ CompilationUnit unit = (CompilationUnit) parser.createAST(null);
+ IProblem[] problems = unit.getProblems();
+ assertProblems(problems, new String[] {
+ "Pb(912) Null type safety: The expression of type 'Lib1<String>' needs unchecked conversion to conform to '@NonNull Lib1<String>'",
+ }, new int[] { 8 });
+
+ // find type binding:
+ int start = test1Content.indexOf("one");
+ ASTNode name = NodeFinder.perform(unit, start, 0);
+ assertTrue("should be simple name", name.getNodeType() == ASTNode.SIMPLE_NAME);
+ IVariableBinding fieldBinding = (IVariableBinding) ((SimpleName)name).resolveBinding();
+
+ // find annotation file (not yet existing):
+ IFile annotationFile = ExternalAnnotationUtil.getAnnotationFile(this.project, fieldBinding.getDeclaringClass(), null);
+ assertFalse("file should not exist", annotationFile.exists());
+ assertEquals("file path", "/TestLibs/annots/libs/Lib1.eea", annotationFile.getFullPath().toString());
+
+ // annotate:
+ String originalSignature = ExternalAnnotationUtil.extractGenericTypeSignature(fieldBinding.getVariableDeclaration().getType());
+ ExternalAnnotationUtil.annotateMember("libs/Lib1", annotationFile,
+ "one",
+ originalSignature,
+ "L0libs/Lib1<TT;>;",
+ MergeStrategy.OVERWRITE_ANNOTATIONS, null);
+ assertTrue("file should exist", annotationFile.exists());
+
+ // check that the error is even worse now:
+ CompilationUnit reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor());
+ problems = reconciled.getProblems();
+ assertProblems(problems, new String[] {
+ "Pb(933) Null type mismatch: required '@NonNull Lib1<String>' but the provided value is specified as @Nullable",
+ }, new int[] { 8 });
+ }
+
+ public void testAnnotateMethodReturn() throws Exception {
+ myCreateJavaProject("TestLibs");
+ String lib1Content =
+ "package libs;\n" +
+ "\n" +
+ "public interface Lib1<T> {\n" +
+ " public Lib1<T> getLib();\n" +
+ "}\n";
+ addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] {
+ "/UnannotatedLib/libs/Lib1.java",
+ lib1Content
+ }, null);
+
+ // type check sources:
+ IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null);
+ ICompilationUnit cu = fragment.createCompilationUnit("Test1.java",
+ "package tests;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "\n" +
+ "import libs.Lib1;\n" +
+ "\n" +
+ "public class Test1 {\n" +
+ " @NonNull Lib1<String> test0(Lib1<String> stringLib) {\n" +
+ " return stringLib.getLib();\n" +
+ " }\n" +
+ "}\n",
+ true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor());
+ CompilationUnit reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor());
+ IProblem[] problems = reconciled.getProblems();
+ assertProblems(problems, new String[] {
+ "Pb(912) Null type safety: The expression of type 'Lib1<String>' needs unchecked conversion to conform to '@NonNull Lib1<String>'",
+ }, new int[] { 8 });
+
+ // acquire library AST:
+ IType type = this.project.findType("libs.Lib1");
+ ICompilationUnit libWorkingCopy = type.getClassFile().getWorkingCopy(this.wcOwner, null);
+ ASTParser parser = ASTParser.newParser(AST.JLS8);
+ parser.setSource(libWorkingCopy);
+ parser.setResolveBindings(true);
+ parser.setStatementsRecovery(false);
+ parser.setBindingsRecovery(false);
+ CompilationUnit unit = (CompilationUnit) parser.createAST(null);
+ libWorkingCopy.discardWorkingCopy();
+
+ // find method binding:
+ int start = lib1Content.indexOf("getLib");
+ ASTNode name = NodeFinder.perform(unit, start, 0);
+ assertTrue("should be simple name", name.getNodeType() == ASTNode.SIMPLE_NAME);
+ ASTNode method = name.getParent();
+ IMethodBinding methodBinding = ((MethodDeclaration)method).resolveBinding();
+
+ // find annotation file (not yet existing):
+ IFile annotationFile = ExternalAnnotationUtil.getAnnotationFile(this.project, methodBinding.getDeclaringClass(), null);
+ assertFalse("file should not exist", annotationFile.exists());
+ assertEquals("file path", "/TestLibs/annots/libs/Lib1.eea", annotationFile.getFullPath().toString());
+
+ // annotate:
+ String originalSignature = ExternalAnnotationUtil.extractGenericSignature(methodBinding);
+ ExternalAnnotationUtil.annotateMember("libs/Lib1", annotationFile,
+ "getLib",
+ originalSignature,
+ "()L1libs/Lib1<TT;>;",
+ MergeStrategy.OVERWRITE_ANNOTATIONS, null);
+ assertTrue("file should exist", annotationFile.exists());
+
+ // check that the error has gone:
+ reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor());
+ assertNoProblems(reconciled.getProblems());
+ }
+
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java
index 1a89994..059530b 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java
@@ -17,6 +17,7 @@
import junit.framework.Test;
+import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder;
@@ -33,12 +34,22 @@
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.NodeFinder;
+import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.tests.util.AbstractCompilerTest;
import org.eclipse.jdt.core.tests.util.Util;
+import org.eclipse.jdt.core.util.ExternalAnnotationUtil;
+import org.eclipse.jdt.core.util.ExternalAnnotationUtil.MergeStrategy;
import org.eclipse.jdt.internal.core.ClasspathAttribute;
import org.osgi.framework.Bundle;
@@ -216,7 +227,7 @@
IClasspathAttribute[] extraAttributes = new IClasspathAttribute[] { new ClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, externalAnnotationPath) };
IClasspathEntry entry = JavaCore.newLibraryEntry(
new Path(jarPath),
- null/*src attach*/,
+ new Path('/'+javaProject.getProject().getName()+"/src.zip"),
null/*src attach root*/,
null/*access rules*/,
extraAttributes,
@@ -794,4 +805,146 @@
}
}
+ // ===== Full round trip: detect problem - annotated - detect problem change =====
+
+ public void testAnnotateFieldWithParameterizedType() throws Exception {
+ myCreateJavaProject("TestLibs");
+ String lib1Content =
+ "package libs;\n" +
+ "\n" +
+ "public abstract class Lib1<T> {\n" +
+ " public Lib1<T> one;\n" +
+ " public abstract T get();\n" +
+ "}\n";
+ addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] {
+ "/UnannotatedLib/libs/Lib1.java",
+ lib1Content
+ }, null);
+
+ // type check sources:
+ IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null);
+ ICompilationUnit cu = fragment.createCompilationUnit("Test1.java",
+ "package tests;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "\n" +
+ "import libs.Lib1;\n" +
+ "\n" +
+ "public class Test1 {\n" +
+ " @NonNull String test0(Lib1<String> stringLib) {\n" +
+ " return stringLib.one.get();\n" +
+ " }\n" +
+ "}\n",
+ true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor());
+ CompilationUnit reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor());
+ assertProblems(reconciled.getProblems(), new String[] {
+ "Pb(955) Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to '@NonNull String'",
+ }, new int[] { 8 });
+
+ // acquire library AST:
+ IType type = this.project.findType("libs.Lib1");
+ ICompilationUnit libWorkingCopy = type.getClassFile().getWorkingCopy(this.wcOwner, null);
+ ASTParser parser = ASTParser.newParser(AST.JLS8);
+ parser.setSource(libWorkingCopy);
+ parser.setResolveBindings(true);
+ parser.setStatementsRecovery(false);
+ parser.setBindingsRecovery(false);
+ CompilationUnit unit = (CompilationUnit) parser.createAST(null);
+ libWorkingCopy.discardWorkingCopy();
+
+ // find type binding:
+ int start = lib1Content.indexOf("one");
+ ASTNode name = NodeFinder.perform(unit, start, 0);
+ assertTrue("should be simple name", name.getNodeType() == ASTNode.SIMPLE_NAME);
+ IVariableBinding fieldBinding = (IVariableBinding) ((SimpleName)name).resolveBinding();
+
+ // find annotation file (not yet existing):
+ IFile annotationFile = ExternalAnnotationUtil.getAnnotationFile(this.project, fieldBinding.getDeclaringClass(), null);
+ assertFalse("file should not exist", annotationFile.exists());
+ assertEquals("file path", "/TestLibs/annots/libs/Lib1.eea", annotationFile.getFullPath().toString());
+
+ // annotate:
+ String originalSignature = ExternalAnnotationUtil.extractGenericTypeSignature(fieldBinding.getVariableDeclaration().getType());
+ ExternalAnnotationUtil.annotateMember("libs/Lib1", annotationFile,
+ "one",
+ originalSignature,
+ "Llibs/Lib1<T0T;>;",
+ MergeStrategy.OVERWRITE_ANNOTATIONS, null);
+ assertTrue("file should exist", annotationFile.exists());
+
+ // check that the error is even worse now:
+ reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor());
+ assertProblems(reconciled.getProblems(), new String[] {
+ "Pb(953) Null type mismatch (type annotations): required '@NonNull String' but this expression has type '@Nullable String'",
+ }, new int[] { 8 });
+ }
+
+
+ public void testAnnotateMethodParameter() throws Exception {
+ myCreateJavaProject("TestLibs");
+ String lib1Content =
+ "package libs;\n" +
+ "\n" +
+ "public abstract class Lib1<T,U> {\n" +
+ " public abstract void take(Lib1<X,U> lx);\n" +
+ " public static class X {}\n" +
+ "}\n";
+ addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] {
+ "/UnannotatedLib/libs/Lib1.java",
+ lib1Content
+ }, null);
+
+ // type check sources:
+ IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null);
+ ICompilationUnit cu = fragment.createCompilationUnit("Test1.java",
+ "package tests;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "import libs.Lib1;\n" +
+ "\n" +
+ "public class Test1 {\n" +
+ " void test0(Lib1<Lib1.X,@Nullable String> xLib1, Lib1<Lib1.@Nullable X,@NonNull String> xLib2) {\n" +
+ " xLib1.take(xLib2);\n" +
+ " }\n" +
+ "}\n",
+ true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor());
+ CompilationUnit reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor());
+ assertProblems(reconciled.getProblems(), new String[] {
+ "Pb(953) Null type mismatch (type annotations): required 'Lib1<Lib1.X,@Nullable String>' but this expression has type 'Lib1<Lib1.@Nullable X,@NonNull String>'",
+ }, new int[] { 7 });
+
+ // acquire library AST:
+ IType type = this.project.findType("libs.Lib1");
+ ICompilationUnit libWorkingCopy = type.getClassFile().getWorkingCopy(this.wcOwner, null);
+ ASTParser parser = ASTParser.newParser(AST.JLS8);
+ parser.setSource(libWorkingCopy);
+ parser.setResolveBindings(true);
+ parser.setStatementsRecovery(false);
+ parser.setBindingsRecovery(false);
+ CompilationUnit unit = (CompilationUnit) parser.createAST(null);
+ libWorkingCopy.discardWorkingCopy();
+
+ // find type binding:
+ int start = lib1Content.indexOf("take");
+ ASTNode name = NodeFinder.perform(unit, start, 0);
+ assertTrue("should be simple name", name.getNodeType() == ASTNode.SIMPLE_NAME);
+ ASTNode method = name.getParent();
+ IMethodBinding methodBinding = ((MethodDeclaration)method).resolveBinding();
+
+ // find annotation file (not yet existing):
+ IFile annotationFile = ExternalAnnotationUtil.getAnnotationFile(this.project, methodBinding.getDeclaringClass(), null);
+ assertFalse("file should not exist", annotationFile.exists());
+ assertEquals("file path", "/TestLibs/annots/libs/Lib1.eea", annotationFile.getFullPath().toString());
+
+ // annotate:
+ String originalSignature = ExternalAnnotationUtil.extractGenericSignature(methodBinding);
+ ExternalAnnotationUtil.annotateMember("libs/Lib1", annotationFile,
+ "take",
+ originalSignature,
+ "(Llibs/Lib1<L0libs/Lib1$X;T1U;>;)V", // <- two annotations: @Nullable X and @NonNull U
+ MergeStrategy.OVERWRITE_ANNOTATIONS, null);
+ assertTrue("file should exist", annotationFile.exists());
+
+ // check that the error is even worse now:
+ reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor());
+ assertNoProblems(reconciled.getProblems());
+ }
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchBugs8Tests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchBugs8Tests.java
index 9df7ed8..f780f76 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchBugs8Tests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchBugs8Tests.java
@@ -4638,6 +4638,47 @@
}
}
+//https://bugs.eclipse.org/bugs/show_bug.cgi?id=461025
+//[1.8][search] Constructor reference not found in search
+public void testBug461025_001() throws CoreException {
+this.workingCopies = new ICompilationUnit[1];
+this.workingCopies[0] = getWorkingCopy("/JavaSearchBugs/src/X.java",
+ "@FunctionalInterface\n" +
+ "interface Y<T> {\n" +
+ " T get();\n" +
+ "}\n" +
+ "\n" +
+ "public class X {\n" +
+ " public X() {}\n" +
+ " public X(int i) {}\n" +
+ "\n" +
+ " private void m1() {\n" +
+ " Y<X> s1 = X::new;\n" +
+ "\n" +
+ " Y<X> s2 = new Y<X>() {\n" +
+ "\n" +
+ " @Override\n" +
+ " public X get() {\n" +
+ " return null;\n" +
+ " }\n" +
+ " };\n" +
+ " }\n" +
+ "}\n");
+SearchPattern pattern = SearchPattern.createPattern(
+ "*",
+ CONSTRUCTOR,
+ METHOD_REFERENCE_EXPRESSION,
+ EXACT_RULE);
+new SearchEngine(this.workingCopies).search(pattern,
+new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()},
+getJavaSearchWorkingCopiesScope(),
+this.resultCollector,
+null);
+assertSearchResults(
+ "src/X.java void X.m1() [X::new] EXACT_MATCH"
+);
+}
+
// Add new tests in JavaSearchBugs8Tests
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewriteTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewriteTest.java
index c3883db..abc8f0c 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewriteTest.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewriteTest.java
@@ -18,8 +18,7 @@
import java.util.Iterator;
import java.util.List;
-import junit.framework.Test;
-
+import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Platform;
@@ -49,6 +48,8 @@
import org.eclipse.text.edits.TextEdit;
import org.osgi.service.prefs.BackingStoreException;
+import junit.framework.Test;
+
@SuppressWarnings({"rawtypes", "unchecked"})
public class ImportRewriteTest extends AbstractJavaModelTests {
@@ -107,6 +108,40 @@
}
/**
+ * Expects that imports can be added for types from an unnamed (default) package, and that such
+ * imports are not reduced into an on-demand import. Imports of types from an unnamed package
+ * were legal in versions of Java prior to 1.4.
+ *
+ * Addresses https://bugs.eclipse.org/461863 ("addImport creates .ypename for unqualified type
+ * from default package").
+ */
+ public void testAddImportsFromUnnamedPackage() throws Exception {
+ ICompilationUnit cu = createCompilationUnit("pack1", "C");
+
+ String[] order = new String[] { "java" };
+
+ ImportRewrite imports = newImportsRewrite(cu, order, 2, 2, false);
+ imports.setUseContextToFilterImplicitImports(true);
+ imports.addImport("java.util.ArrayDeque");
+ imports.addImport("java.util.ArrayList");
+ imports.addImport("Bar");
+ imports.addImport("Foo");
+
+ apply(imports);
+
+ StringBuffer expected = new StringBuffer();
+ expected.append("package pack1;\n");
+ expected.append("\n");
+ expected.append("import java.util.*;\n");
+ expected.append("\n");
+ expected.append("import Bar;\n");
+ expected.append("import Foo;\n");
+ expected.append("\n");
+ expected.append("public class C {}");
+ assertEqualString(cu.getSource(), expected.toString());
+ }
+
+ /**
* Addresses https://bugs.eclipse.org/412929 ("Adding a type results in adding a package and
* later does not honor order").
*/
@@ -1411,6 +1446,61 @@
assertEqualString(cuWithoutFiltering.getSource(), expectedWithoutFiltering.toString());
}
+ /**
+ * Addresses https://bugs.eclipse.org/460484 ("ImportRewrite throws SIOOBE when trying to add
+ * import").
+ */
+ public void testAddAdjacentImportWithCommonPrefixButLongerInitialSegment() throws Exception {
+ StringBuffer contents = new StringBuffer();
+ contents.append("package pack1;\n");
+ contents.append("\n");
+ contents.append("import a.FromA;\n");
+ contents.append("import b.FromB;\n");
+ contents.append("\n");
+ contents.append("public class Clazz {}\n");
+ ICompilationUnit cu = createCompilationUnit("pack1", "Clazz", contents.toString());
+
+ ImportRewrite rewrite = newImportsRewrite(cu, new String[] {}, 999, 999, true);
+ rewrite.setUseContextToFilterImplicitImports(true);
+ // Expect that no exception is thrown when "ab" is compared with "a".
+ rewrite.addImport("ab.FromAb");
+ apply(rewrite);
+
+ StringBuffer expected = new StringBuffer();
+ expected.append("package pack1;\n");
+ expected.append("\n");
+ expected.append("import a.FromA;\n");
+ expected.append("import ab.FromAb;\n");
+ expected.append("import b.FromB;\n");
+ expected.append("\n");
+ expected.append("public class Clazz {}\n");
+ assertEqualString(cu.getSource(), expected.toString());
+ }
+
+ // https://bugs.eclipse.org/459320
+ public void testAddImportToCuNotOnClasspath() throws Exception {
+ StringBuffer contents = new StringBuffer();
+ contents.append("package pack1;\n");
+ contents.append("\n");
+ contents.append("public class Clazz {}\n");
+
+ createFolder("/P/alt-src/pack1/");
+ IFile clazz = createFile("/P/alt-src/pack1/Clazz.java", contents.toString());
+ ICompilationUnit cu = (ICompilationUnit) JavaCore.create(clazz);
+ cu.becomeWorkingCopy(null);
+
+ try {
+ ImportRewrite rewrite = newImportsRewrite(cu, new String[] {}, 999, 999, true);
+ rewrite.setUseContextToFilterImplicitImports(true);
+ rewrite.addImport("pack1.AnotherClass");
+ apply(rewrite);
+
+ assertEqualString(cu.getSource(), contents.toString());
+ } finally {
+ cu.discardWorkingCopy();
+ }
+ }
+
public void testAddImports1() throws Exception {
IPackageFragment pack1= this.sourceFolder.createPackageFragment("pack1", false, null);
diff --git a/org.eclipse.jdt.core/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.core/.settings/org.eclipse.jdt.core.prefs
index d1fb84b..c02097a 100644
--- a/org.eclipse.jdt.core/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jdt.core/.settings/org.eclipse.jdt.core.prefs
@@ -54,7 +54,7 @@
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
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 dffb480..05f3e17 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
@@ -23,6 +23,7 @@
* bug 381443 - [compiler][null] Allow parameter widening from @NonNull to unannotated
* Bug 440477 - [null] Infrastructure for feeding external annotations into compilation
* Bug 440687 - [compiler][batch][null] improve command line option for external annotations
+ * Bug 408815 - [batch][null] Add CLI option for COMPILER_PB_SYNTACTIC_NULL_ANALYSIS_FOR_FIELDS
* Jesper S Moller - Contributions for
* bug 407297 - [1.8][compiler] Control generation of parameter names by option
* Mat Booth - Contribution for bug 405176
@@ -3994,6 +3995,11 @@
} else if (token.equals("switchDefault")) { //$NON-NLS-1$
setSeverity(CompilerOptions.OPTION_ReportMissingDefaultCase, severity, isEnabling);
return;
+ } else if (token.equals("syntacticAnalysis")) { //$NON-NLS-1$
+ this.options.put(
+ CompilerOptions.OPTION_SyntacticNullAnalysisForFields,
+ isEnabling ? CompilerOptions.ENABLED : CompilerOptions.DISABLED);
+ return;
}
break;
case 't' :
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
index 275e4e4..217d1b0 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2000, 2014 IBM Corporation and others.
+# Copyright (c) 2000, 2015 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
@@ -16,6 +16,7 @@
# bug 374605 - Unreasonable warning for enum-based switch statements
# bug 388281 - [compiler][null] inheritance of null annotations as an option
# bug 440687 - [compiler][batch][null] improve command line option for external annotations
+# Bug 408815 - [batch][null] Add CLI option for COMPILER_PB_SYNTACTIC_NULL_ANALYSIS_FOR_FIELDS
# Alan Moraes <alan@kelon.org> - Contribution for bug 383644
# Jesper S Moller - Contribution for bug 407297 - [1.8][compiler] Control generation of parameter names by option
###############################################################################
@@ -26,7 +27,7 @@
compiler.name = Eclipse Compiler for Java(TM)
#Format: compiler.version = (The place holders will be automatically filled. Do not remove or alter it)
compiler.version = bundle_qualifier, bundle_version
-compiler.copyright = Copyright IBM Corp 2000, 2013. All rights reserved.
+compiler.copyright = Copyright IBM Corp 2000, 2015. All rights reserved.
###{ObjectTeams:
otdtc.name = Extension for Object Teams
@@ -419,6 +420,7 @@
\ errors and warnings\n\
\ switchDefault + switch statement lacking a default case\n\
\ syncOverride missing synchronized in synchr. method override\n\
+\ syntacticAnalysis perform syntax-based null analysis for fields\n\
\ syntheticAccess synthetic access for innerclass\n\
\ tasks(<tags separated by |>) tasks identified by tags inside comments\n\
\ typeHiding + type parameter hiding another type\n\
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 32caf1f..498026a 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -35,6 +35,7 @@
* Bug 441693 - [1.8][null] Bogus warning for type argument annotated with @NonNull
* Bug 434483 - [1.8][compiler][inference] Type inference not picked up with method reference
* Bug 446442 - [1.8] merge null annotations from super methods
+ * Bug 437072 - [compiler][null] Null analysis emits possibly incorrect warning for new int[][] despite @NonNullByDefault
* Jesper S Moller - Contributions for
* bug 382721 - [1.8][compiler] Effectively final variables needs special treatment
* bug 412153 - [1.8][compiler] Check validity of annotations which may be repeatable
@@ -1115,10 +1116,15 @@
scope.problemReporter().contradictoryNullAnnotations(se8NullAnnotation);
}
se8Annotations = Binding.NO_ANNOTATIONS;
+ se8nullBits = 0;
}
existingType = existingType.withoutToplevelNullAnnotation();
}
TypeBinding oldLeafType = (unionRef == null) ? existingType.leafComponentType() : unionRef.resolvedType;
+ if (se8nullBits != 0 && oldLeafType.isBaseType()) {
+ scope.problemReporter().illegalAnnotationForBaseType(typeRef, new Annotation[] { se8NullAnnotation }, se8nullBits);
+ return existingType;
+ }
AnnotationBinding [][] goodies = new AnnotationBinding[typeRef.getAnnotatableLevels()][];
goodies[0] = se8Annotations; // @T X.Y.Z local; ==> @T should annotate X
TypeBinding newLeafType = scope.environment().createAnnotatedType(oldLeafType, goodies);
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 0209dee..5eea550 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
@@ -840,7 +840,8 @@
resolveAnnotations(this.scope, this.annotations, this.binding);
long sourceLevel = this.scope.compilerOptions().sourceLevel;
- validateNullAnnotations(this.scope.environment().usesNullTypeAnnotations());
+ if (sourceLevel < ClassFileConstants.JDK1_8) // otherwise already checked via Argument.createBinding
+ validateNullAnnotations(this.scope.environment().usesNullTypeAnnotations());
resolveStatements();
// check @Deprecated annotation presence
@@ -1017,7 +1018,7 @@
this.scope.validateNullAnnotation(this.binding.parameters[i].tagBits, this.arguments[i].type, this.arguments[i].annotations);
// TODO(stephan) remove once we're sure:
// this.binding.parameters[i] = this.binding.parameters[i].unannotated();
- }
+ }
}
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
index 1b775ba..035fdf5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
@@ -412,8 +412,7 @@
if (haveDescriptor && !argumentsHaveErrors && blockScope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
if (!argumentsTypeElided) {
- AbstractMethodDeclaration.createArgumentBindings(this.arguments, this.binding, this.scope);
- validateNullAnnotations();
+ AbstractMethodDeclaration.createArgumentBindings(this.arguments, this.binding, this.scope); // includes validation
// no application of null-ness default, hence also no warning regarding redundant null annotation
mergeParameterNullAnnotations(blockScope);
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
index 57bc2c3..26b6a48 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
@@ -421,32 +421,65 @@
return 0; // OK by tagBits
}
+ static class SearchContradictions extends TypeBindingVisitor {
+ ReferenceBinding typeWithContradiction;
+ @Override
+ public boolean visit(ReferenceBinding referenceBinding) {
+ if ((referenceBinding.tagBits & TagBits.AnnotationNullMASK) == TagBits.AnnotationNullMASK) {
+ this.typeWithContradiction = referenceBinding;
+ return false;
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(TypeVariableBinding typeVariable) {
+ if (!visit((ReferenceBinding)typeVariable))
+ return false;
+ long allNullBits = typeVariable.tagBits & TagBits.AnnotationNullMASK;
+ if (typeVariable.firstBound != null)
+ allNullBits = typeVariable.firstBound.tagBits & TagBits.AnnotationNullMASK;
+ for (TypeBinding otherBound : typeVariable.otherUpperBounds())
+ allNullBits |= otherBound.tagBits & TagBits.AnnotationNullMASK;
+ if (allNullBits == TagBits.AnnotationNullMASK) {
+ this.typeWithContradiction = typeVariable;
+ return false;
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(RawTypeBinding rawType) {
+ return visit((ReferenceBinding)rawType);
+ }
+ @Override
+ public boolean visit(WildcardBinding wildcardBinding) {
+ long allNullBits = wildcardBinding.tagBits & TagBits.AnnotationNullMASK;
+ switch (wildcardBinding.boundKind) {
+ case Wildcard.EXTENDS:
+ allNullBits |= wildcardBinding.bound.tagBits & TagBits.AnnotationNonNull;
+ break;
+ case Wildcard.SUPER:
+ allNullBits |= wildcardBinding.bound.tagBits & TagBits.AnnotationNullable;
+ break;
+ }
+ if (allNullBits == TagBits.AnnotationNullMASK) {
+ this.typeWithContradiction = wildcardBinding;
+ return false;
+ }
+ return true;
+ }
+ @Override
+ public boolean visit(ParameterizedTypeBinding parameterizedTypeBinding) {
+ if (!visit((ReferenceBinding) parameterizedTypeBinding))
+ return false;
+ return super.visit(parameterizedTypeBinding);
+ }
+ }
+
/**
* After a method has substituted type parameters, check if this resulted in any contradictory null annotations.
* Problems are either reported directly (if scope != null) or by returning a ProblemMethodBinding.
*/
- public static MethodBinding checkForContradictions(
- final MethodBinding method, final Object location, final Scope scope) {
-
- class SearchContradictions extends TypeBindingVisitor {
- ReferenceBinding typeWithContradiction;
- @Override
- public boolean visit(ReferenceBinding referenceBinding) {
- if ((referenceBinding.tagBits & TagBits.AnnotationNullMASK) == TagBits.AnnotationNullMASK) {
- this.typeWithContradiction = referenceBinding;
- return false;
- }
- return true;
- }
- @Override
- public boolean visit(TypeVariableBinding typeVariable) {
- return visit((ReferenceBinding)typeVariable);
- }
- @Override
- public boolean visit(RawTypeBinding rawType) {
- return visit((ReferenceBinding)rawType);
- }
- }
+ public static MethodBinding checkForContradictions(MethodBinding method, Object location, Scope scope) {
int start = 0, end = 0;
if (location instanceof InvocationSite) {
@@ -484,6 +517,12 @@
return method;
}
+ public static boolean hasContradictions(TypeBinding type) {
+ SearchContradictions searchContradiction = new SearchContradictions();
+ TypeBindingVisitor.visit(searchContradiction, type);
+ return searchContradiction.typeWithContradiction != null;
+ }
+
public static TypeBinding strongerType(TypeBinding type1, TypeBinding type2, LookupEnvironment environment) {
if ((type1.tagBits & TagBits.AnnotationNonNull) != 0)
return mergeTypeAnnotations(type1, type2, true, environment);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
index 8ddad99..a7adb88 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
@@ -33,6 +33,7 @@
* Bug 441734 - [1.8][inference] Generic method with nested parameterized type argument fails on method reference
* Bug 438945 - [1.8] NullPointerException InferenceContext18.checkExpression in java 8 with generics, primitives, and overloading
* Bug 452788 - [1.8][compiler] Type not correctly inferred in lambda expression
+ * Bug 448709 - [1.8][null] ensure we don't infer types that violate null constraints on a type parameter's bound
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contribution for
* Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
*******************************************************************************/
@@ -697,39 +698,41 @@
scope.problemReporter().unhandledException(methodExceptions[i], this);
}
if (scope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
- // TODO: simplify by using this.freeParameters?
- int len;
- int expectedlen = this.binding.parameters.length;
- int providedLen = this.descriptor.parameters.length;
- if (this.receiverPrecedesParameters)
- providedLen--; // one parameter is 'consumed' as the receiver
- boolean isVarArgs = false;
- if (this.binding.isVarargs()) {
- isVarArgs = (providedLen == expectedlen)
- ? !this.descriptor.parameters[expectedlen-1].isCompatibleWith(this.binding.parameters[expectedlen-1])
- : true;
- len = providedLen; // binding parameters will be padded from InferenceContext18.getParameter()
- } else {
- len = Math.min(expectedlen, providedLen);
- }
- for (int i = 0; i < len; i++) {
- TypeBinding descriptorParameter = this.descriptor.parameters[i + (this.receiverPrecedesParameters ? 1 : 0)];
- TypeBinding bindingParameter = InferenceContext18.getParameter(this.binding.parameters, i, isVarArgs);
- NullAnnotationMatching annotationStatus = NullAnnotationMatching.analyse(bindingParameter, descriptorParameter, FlowInfo.UNKNOWN);
- if (annotationStatus.isAnyMismatch()) {
- // immediate reporting:
- scope.problemReporter().referenceExpressionArgumentNullityMismatch(this, bindingParameter, descriptorParameter, this.descriptor, i, annotationStatus);
- }
- }
- if (!this.binding.isConstructor() && (this.descriptor.returnType.tagBits & TagBits.AnnotationNonNull) != 0) {
- // since constructors never return null we don't have to check those anyway.
- if ((this.binding.returnType.tagBits & TagBits.AnnotationNonNull) == 0) {
- char[][] providedAnnotationName = ((this.binding.returnType.tagBits & TagBits.AnnotationNullable) != 0) ?
- scope.environment().getNullableAnnotationName() : null;
- scope.problemReporter().illegalReturnRedefinition(this, this.descriptor,
- scope.environment().getNonNullAnnotationName(),
- providedAnnotationName, this.binding.returnType);
- }
+ if (this.expectedType == null || !NullAnnotationMatching.hasContradictions(this.expectedType)) { // otherwise assume it has been reported and we can do nothing here
+ // TODO: simplify by using this.freeParameters?
+ int len;
+ int expectedlen = this.binding.parameters.length;
+ int providedLen = this.descriptor.parameters.length;
+ if (this.receiverPrecedesParameters)
+ providedLen--; // one parameter is 'consumed' as the receiver
+ boolean isVarArgs = false;
+ if (this.binding.isVarargs()) {
+ isVarArgs = (providedLen == expectedlen)
+ ? !this.descriptor.parameters[expectedlen-1].isCompatibleWith(this.binding.parameters[expectedlen-1])
+ : true;
+ len = providedLen; // binding parameters will be padded from InferenceContext18.getParameter()
+ } else {
+ len = Math.min(expectedlen, providedLen);
+ }
+ for (int i = 0; i < len; i++) {
+ TypeBinding descriptorParameter = this.descriptor.parameters[i + (this.receiverPrecedesParameters ? 1 : 0)];
+ TypeBinding bindingParameter = InferenceContext18.getParameter(this.binding.parameters, i, isVarArgs);
+ NullAnnotationMatching annotationStatus = NullAnnotationMatching.analyse(bindingParameter, descriptorParameter, FlowInfo.UNKNOWN);
+ if (annotationStatus.isAnyMismatch()) {
+ // immediate reporting:
+ scope.problemReporter().referenceExpressionArgumentNullityMismatch(this, bindingParameter, descriptorParameter, this.descriptor, i, annotationStatus);
+ }
+ }
+ if (!this.binding.isConstructor() && (this.descriptor.returnType.tagBits & TagBits.AnnotationNonNull) != 0) {
+ // since constructors never return null we don't have to check those anyway.
+ if ((this.binding.returnType.tagBits & TagBits.AnnotationNonNull) == 0) {
+ char[][] providedAnnotationName = ((this.binding.returnType.tagBits & TagBits.AnnotationNullable) != 0) ?
+ scope.environment().getNullableAnnotationName() : null;
+ scope.problemReporter().illegalReturnRedefinition(this, this.descriptor,
+ scope.environment().getNonNullAnnotationName(),
+ providedAnnotationName, this.binding.returnType);
+ }
+ }
}
}
this.freeParameters = null; // not used after method lookup
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java
index c0391b6..c146eff 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -24,6 +24,7 @@
* Bug 438458 - [1.8][null] clean up handling of null type annotations wrt type variables
* Bug 435570 - [1.8][null] @NonNullByDefault illegally tries to affect "throws E"
* Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
+ * Bug 437072 - [compiler][null] Null analysis emits possibly incorrect warning for new int[][] despite @NonNullByDefault
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
* Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
* Bug 409236 - [1.8][compiler] Type annotations on intersection cast types dropped by code generator
@@ -842,6 +843,8 @@
scope.problemReporter().nullityMismatchTypeArgument(variable, this.resolvedType, this);
}
}
+ if (this.resolvedType.leafComponentType().isBaseType() && hasNullTypeAnnotation())
+ scope.problemReporter().illegalAnnotationForBaseType(this, this.annotations[0], this.resolvedType.tagBits & TagBits.AnnotationNullMASK);
}
/** Retrieve the null annotation that has been translated to the given nullTagBits. */
public Annotation findAnnotation(long nullTagBits) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java
index bc64451..6b61bcf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java
@@ -10,9 +10,6 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.classfmt;
-import static org.eclipse.jdt.core.util.ExternalAnnotationUtil.NONNULL;
-import static org.eclipse.jdt.core.util.ExternalAnnotationUtil.NULLABLE;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -35,6 +32,18 @@
public static final String CLASS_PREFIX = "class "; //$NON-NLS-1$
public static final String SUPER_PREFIX = "super "; //$NON-NLS-1$
+ /** Representation of a 'nullable' annotation, independent of the concrete annotation name used in Java sources. */
+ public static final char NULLABLE = '0';
+
+ /** Representation of a 'nonnull' annotation, independent of the concrete annotation name used in Java sources. */
+ public static final char NONNULL = '1';
+
+ /**
+ * Represents absence of a null annotation. Useful for removing an existing null annotation.
+ * This character is used only internally, it is not part of the Eclipse External Annotation file format.
+ */
+ public static final char NO_ANNOTATION = '@';
+
static final String ANNOTATION_FILE_SUFFIX = ".eea"; //$NON-NLS-1$
private static final String TYPE_PARAMETER_PREFIX = " <"; //$NON-NLS-1$
@@ -60,15 +69,9 @@
private void initialize(InputStream input) throws IOException {
LineNumberReader reader = new LineNumberReader(new InputStreamReader(input));
try {
- String line = reader.readLine().trim();
- if (line.startsWith(CLASS_PREFIX)) {
- line = line.substring(CLASS_PREFIX.length());
- } else {
- throw new IOException("missing class header in annotation file"); //$NON-NLS-1$
- }
- if (!trimTail(line).equals(this.typeName)) {
- throw new IOException("mismatching class name in annotation file, expected "+this.typeName+", but header said "+line); //$NON-NLS-1$ //$NON-NLS-2$
- }
+ assertClassHeader(reader.readLine(), this.typeName);
+
+ String line;
if ((line = reader.readLine()) == null) {
return;
}
@@ -81,7 +84,9 @@
return;
}
}
+ String pendingLine;
do {
+ pendingLine = null;
line = line.trim();
if (line.isEmpty()) continue;
String rawSig = null, annotSig = null;
@@ -102,8 +107,11 @@
line = reader.readLine();
if (line == null || line.isEmpty())
continue; // skip since optional line with annotations is missing
- if (line.charAt(0) == ' ')
- annotSig = line.substring(1);
+ if (line.charAt(0) != ' ') {
+ pendingLine = line; // push back what appears to be the next selector, not a signature
+ continue;
+ }
+ annotSig = line.substring(1);
} catch (Exception ex) {
// continue to escalate below
}
@@ -126,14 +134,38 @@
this.fieldAnnotationSources = new HashMap<String, String>();
this.fieldAnnotationSources.put(selector+':'+rawSig, annotSig);
}
- } while ((line = reader.readLine()) != null);
+ } while (((line = pendingLine) != null) || (line = reader.readLine()) != null);
} finally {
reader.close();
}
}
+ /**
+ * Assert that the given line is a class header for 'typeName' (slash-separated qualified name).
+ */
+ public static void assertClassHeader(String line, String typeName) throws IOException {
+ if (line.startsWith(CLASS_PREFIX)) {
+ line = line.substring(CLASS_PREFIX.length());
+ } else {
+ throw new IOException("missing class header in annotation file"); //$NON-NLS-1$
+ }
+ if (!trimTail(line).equals(typeName)) {
+ throw new IOException("mismatching class name in annotation file, expected "+typeName+", but header said "+line); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ /**
+ * Extract the signature from a line of an external annotation file.
+ * Answers null if line is not in the expected format.
+ */
+ public static String extractSignature(String line) {
+ if (line == null || line.isEmpty() || line.charAt(0) != ' ')
+ return null;
+ return trimTail(line.substring(1));
+ }
+
/** Lines may contain arbitrary trailing data, separated by white space. */
- protected String trimTail(String line) {
+ protected static String trimTail(String line) {
int tail = line.indexOf(' ');
if (tail == -1)
tail = line.indexOf('\t');
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java
index d4d99fd..c6dcc0a 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -20,6 +20,7 @@
* Bug 453483 - [compiler][null][loop] Improve null analysis for loops
* Bug 455723 - Nonnull argument not correctly inferred in loop
* Bug 415790 - [compiler][resource]Incorrect potential resource leak warning in for loop with close in try/catch
+ * Bug 446691 - [1.8][null][compiler] NullPointerException in SingleNameReference.analyseCode
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
@@ -510,7 +511,7 @@
current = initializationContext.initializationParent;
} else if (current instanceof ExceptionHandlingFlowContext) {
ExceptionHandlingFlowContext exceptionContext = (ExceptionHandlingFlowContext) current;
- current = exceptionContext.initializationParent == null ? exceptionContext.getLocalParent() : exceptionContext.initializationParent;
+ current = exceptionContext.initializationParent == null ? exceptionContext.parent : exceptionContext.initializationParent;
} else {
current = current.getLocalParent();
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java
index 2b1e8b9..68770ad 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java
@@ -317,7 +317,7 @@
for (int i = 0; i < length; i++) {
TypeVariableBinding typeParameter = typeParameters[i];
InferenceVariable variable = variables[i];
- TypeBound[] someBounds = typeParameter.getTypeBounds(variable, new InferenceSubstitution(context.environment, context.inferenceVariables));
+ TypeBound[] someBounds = typeParameter.getTypeBounds(variable, new InferenceSubstitution(context));
boolean hasProperBound = false;
if (someBounds.length > 0)
hasProperBound = addBounds(someBounds, context.environment);
@@ -610,7 +610,7 @@
// construct theta = [P1:=alpha1,...]
final InferenceVariable[] alphas = new InferenceVariable[gAlpha.arguments.length];
System.arraycopy(gAlpha.arguments, 0, alphas, 0, alphas.length);
- InferenceSubstitution theta = new InferenceSubstitution(context.environment, alphas) {
+ InferenceSubstitution theta = new InferenceSubstitution(context.environment, alphas, context.currentInvocation) {
@Override
protected TypeBinding getP(int i) {
return parameters[i];
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
index be2c03d..c9a37dc 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
@@ -240,7 +240,7 @@
* Substitute any type variables mentioned in 'type' by the corresponding inference variable, if one exists.
*/
public TypeBinding substitute(TypeBinding type) {
- InferenceSubstitution inferenceSubstitution = new InferenceSubstitution(this.environment, this.inferenceVariables);
+ InferenceSubstitution inferenceSubstitution = new InferenceSubstitution(this);
return inferenceSubstitution.substitute(inferenceSubstitution, type);
}
@@ -392,7 +392,7 @@
}
// 4. bullet: assemble C:
Set<ConstraintFormula> c = new HashSet<ConstraintFormula>();
- if (!addConstraintsToC(this.invocationArguments, c, method, this.inferenceKind, false))
+ if (!addConstraintsToC(this.invocationArguments, c, method, this.inferenceKind, false, invocationSite))
return null;
// 5. bullet: determine B4 from C
while (!c.isEmpty()) {
@@ -444,7 +444,9 @@
}
}
- private boolean addConstraintsToC(Expression[] exprs, Set<ConstraintFormula> c, MethodBinding method, int inferenceKindForMethod, boolean interleaved) throws InferenceFailureException {
+ private boolean addConstraintsToC(Expression[] exprs, Set<ConstraintFormula> c, MethodBinding method, int inferenceKindForMethod, boolean interleaved, InvocationSite site)
+ throws InferenceFailureException
+ {
TypeBinding[] fs;
if (exprs != null) {
int k = exprs.length;
@@ -464,7 +466,8 @@
}
for (int i = 0; i < k; i++) {
TypeBinding fsi = fs[Math.min(i, p-1)];
- TypeBinding substF = substitute(fsi);
+ InferenceSubstitution inferenceSubstitution = new InferenceSubstitution(this.environment, this.inferenceVariables, site);
+ TypeBinding substF = inferenceSubstitution.substitute(inferenceSubstitution,fsi);
if (!addConstraintsToC_OneExpr(exprs[i], c, fsi, substF, method, interleaved))
return false;
}
@@ -472,8 +475,9 @@
return true;
}
- private boolean addConstraintsToC_OneExpr(Expression expri, Set<ConstraintFormula> c, TypeBinding fsi, TypeBinding substF, MethodBinding method, boolean interleaved) throws InferenceFailureException {
-
+ private boolean addConstraintsToC_OneExpr(Expression expri, Set<ConstraintFormula> c, TypeBinding fsi, TypeBinding substF, MethodBinding method, boolean interleaved)
+ throws InferenceFailureException
+ {
// For all i (1 ≤ i ≤ k), if ei is not pertinent to applicability, the set contains ⟨ei → θ Fi⟩.
if (!expri.isPertinentToApplicability(fsi, method)) {
c.add(new ConstraintExpressionFormula(expri, substF, ReductionResult.COMPATIBLE, ARGUMENT_CONSTRAINTS_ARE_SOFT));
@@ -536,7 +540,7 @@
resumeSuspendedInference(prevInvocation);
}
}
- return addConstraintsToC(arguments, c, innerMethod.genericMethod(), applicabilityKind, interleaved);
+ return addConstraintsToC(arguments, c, innerMethod.genericMethod(), applicabilityKind, interleaved, invocation);
} else if (expri instanceof ConditionalExpression) {
ConditionalExpression ce = (ConditionalExpression) expri;
return addConstraintsToC_OneExpr(ce.valueIfTrue, c, fsi, substF, method, interleaved)
@@ -1623,4 +1627,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java
index 1214f5d..c76e3ba 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java
@@ -19,12 +19,18 @@
private LookupEnvironment environment;
private InferenceVariable[] variables;
+ private InvocationSite site;
- public InferenceSubstitution(LookupEnvironment environment, InferenceVariable[] variables) {
+ public InferenceSubstitution(LookupEnvironment environment, InferenceVariable[] variables, InvocationSite site) {
this.environment = environment;
this.variables = variables;
+ this.site = site;
}
-
+
+ public InferenceSubstitution(InferenceContext18 context) {
+ this(context.environment, context.inferenceVariables, context.currentInvocation);
+ }
+
/**
* Override method {@link Scope.Substitutor#substitute(Substitution, TypeBinding)},
* to add substitution of types other than type variables.
@@ -32,7 +38,7 @@
public TypeBinding substitute(Substitution substitution, TypeBinding originalType) {
for (int i = 0; i < this.variables.length; i++) {
InferenceVariable variable = this.variables[i];
- if (TypeBinding.equalsEquals(getP(i), originalType)) {
+ if (this.site == variable.site && TypeBinding.equalsEquals(getP(i), originalType)) {
if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled && originalType.hasNullTypeAnnotations())
return this.environment.createAnnotatedType(variable.withoutToplevelNullAnnotation(), originalType.getTypeAnnotations());
return variable;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
index d28535f..96f24e5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
@@ -125,6 +125,35 @@
methodSubstitute = methodSubstitute.inferFromExpectedType(scope, inferenceContext);
if (methodSubstitute == null)
return null;
+ } else if (compilerOptions.sourceLevel == ClassFileConstants.JDK1_7) {
+ // bug 425203 - consider additional constraints to conform to buggy javac behavior
+ if (methodSubstitute.returnType != TypeBinding.VOID) {
+ TypeBinding expectedType = invocationSite.invocationTargetType();
+ // In case of a method like <T> List<T> foo(T arg), solution based on return type
+ // should not be preferred vs solution based on parameter types, so do not attempt
+ // to use return type based inference in this case
+ if (expectedType != null && !originalMethod.returnType.mentionsAny(originalMethod.parameters, -1)) {
+ TypeBinding uncaptured = methodSubstitute.returnType.uncapture(scope);
+ if (!methodSubstitute.returnType.isCompatibleWith(expectedType) &&
+ expectedType.isCompatibleWith(uncaptured)) {
+ InferenceContext oldContext = inferenceContext;
+ inferenceContext = new InferenceContext(originalMethod);
+ // Include additional constraint pertaining to the expected type
+ originalMethod.returnType.collectSubstitutes(scope, expectedType, inferenceContext, TypeConstants.CONSTRAINT_EXTENDS);
+ ParameterizedGenericMethodBinding substitute = inferFromArgumentTypes(scope, originalMethod, arguments, parameters, inferenceContext);
+ if (substitute != null && substitute.returnType.isCompatibleWith(expectedType)) {
+ // Do not use the new solution if it results in incompatibilities in parameter types
+ if ((scope.parameterCompatibilityLevel(substitute, arguments, false)) > Scope.NOT_COMPATIBLE) {
+ methodSubstitute = substitute;
+ } else {
+ inferenceContext = oldContext;
+ }
+ } else {
+ inferenceContext = oldContext;
+ }
+ }
+ }
+ }
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
index 740687d..032efd5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -50,6 +50,8 @@
* Bug 452194 - Code no longer compiles in 4.4.1, but with confusing error
* Bug 452788 - [1.8][compiler] Type not correctly inferred in lambda expression
* Bug 456236 - [1.8][null] Cannot infer type when constructor argument is annotated with @Nullable
+ * Bug 437072 - [compiler][null] Null analysis emits possibly incorrect warning for new int[][] despite @NonNullByDefault
+ * Bug 462083 - [1.8][inference] Java 8 generic return type mismatch with interface involving type parameter.
* Jesper S Moller - Contributions for
* Bug 378674 - "The method can be declared as static" is wrong
* Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335
@@ -620,7 +622,15 @@
}
}
break;
-
+
+ case Binding.INTERSECTION_TYPE18:
+ IntersectionTypeBinding18 intersection = (IntersectionTypeBinding18) originalType;
+ ReferenceBinding[] types = intersection.getIntersectingTypes();
+ TypeBinding[] substitutes = substitute(substitution, types);
+ ReferenceBinding[] refSubsts = new ReferenceBinding[substitutes.length];
+ System.arraycopy(substitutes, 0, refSubsts, 0, substitutes.length);
+ return substitution.environment().createIntersectionType18(refSubsts);
+
case Binding.TYPE:
if (!originalType.isMemberType()) break;
ReferenceBinding originalReferenceType = (ReferenceBinding) originalType;
@@ -1319,7 +1329,7 @@
// in >= 1.5 mode, ensure the exactMatch did not match raw types
if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5)
for (int i = argumentTypes.length; --i >= 0;)
- if (isPossibleSubtypeOfRawType(argumentTypes[i]))
+ if (isSubtypeOfRawType(argumentTypes[i]))
return null;
// must find both methods for this case: <S extends A> void foo() {} and <N extends B> N foo() { return null; }
// or find an inherited method when the exact match is to a bridge method
@@ -3090,7 +3100,8 @@
// retrieve an exact visible match (if possible)
MethodBinding methodBinding = findExactMethod(currentType, selector, argumentTypes, invocationSite);
- if (methodBinding != null) return methodBinding;
+ if (methodBinding != null && methodBinding.isValidBinding())
+ return methodBinding;
methodBinding = findMethod(currentType, selector, argumentTypes, invocationSite, false);
//{ObjectTeams: callout to private role method goes through the role class-part:
@@ -4021,7 +4032,7 @@
return false;
}
- public boolean isPossibleSubtypeOfRawType(TypeBinding paramType) {
+ public boolean isSubtypeOfRawType(TypeBinding paramType) {
TypeBinding t = paramType.leafComponentType();
if (t.isBaseType()) return false;
@@ -4030,7 +4041,6 @@
int nextPosition = 0;
do {
if (currentType.isRawType()) return true;
- if (!currentType.isHierarchyConnected()) return true; // do not fault in super types right now, so assume one is a raw type
ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
@@ -5496,12 +5506,23 @@
}
public boolean validateNullAnnotation(long tagBits, TypeReference typeRef, Annotation[] annotations) {
- long nullAnnotationTagBit = tagBits & (TagBits.AnnotationNullMASK);
+ if (typeRef == null)
+ return true;
+ TypeBinding type = typeRef.resolvedType;
+
+ boolean usesNullTypeAnnotations = this.environment().usesNullTypeAnnotations();
+ long nullAnnotationTagBit;
+ if (usesNullTypeAnnotations) {
+ type = type.leafComponentType(); // if it's an array, the annotation applies to the leaf component type
+ nullAnnotationTagBit = type.tagBits & TagBits.AnnotationNullMASK;
+ } else {
+ nullAnnotationTagBit = tagBits & (TagBits.AnnotationNullMASK);
+ }
+
if (nullAnnotationTagBit != 0) {
- TypeBinding type = typeRef.resolvedType;
if (type != null && type.isBaseType()) {
// type annotations are *always* illegal for 'void' (already reported)
- if (!(typeRef.resolvedType.id == TypeIds.T_void && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8))
+ if (!(typeRef.resolvedType.id == TypeIds.T_void && usesNullTypeAnnotations))
problemReporter().illegalAnnotationForBaseType(typeRef, annotations, nullAnnotationTagBit);
return false;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SignatureWrapper.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SignatureWrapper.java
index 19514c0..7e61a2d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SignatureWrapper.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SignatureWrapper.java
@@ -13,6 +13,7 @@
package org.eclipse.jdt.internal.compiler.lookup;
import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider;
public class SignatureWrapper {
public char[] signature;
@@ -20,6 +21,7 @@
public int end;
public int bracket;
private boolean use15specifics;
+ private boolean useExternalAnnotations;
public SignatureWrapper(char[] signature, boolean use15specifics) {
this.signature = signature;
@@ -27,6 +29,13 @@
this.end = this.bracket = -1;
this.use15specifics = use15specifics;
}
+ public SignatureWrapper(char[] signature, boolean use15specifics, boolean useExternalAnnotations) {
+ this.signature = signature;
+ this.start = 0;
+ this.end = this.bracket = -1;
+ this.use15specifics = use15specifics;
+ this.useExternalAnnotations = useExternalAnnotations;
+ }
public SignatureWrapper(char [] signature) {
this(signature, true);
}
@@ -35,8 +44,27 @@
}
public int computeEnd() {
int index = this.start;
- while (this.signature[index] == '[')
- index++;
+ if (this.useExternalAnnotations) {
+ // in addition to '[' tokens accept null annotations after the first '['
+ skipDimensions: while(true) {
+ switch (this.signature[index]) {
+ case ExternalAnnotationProvider.NONNULL :
+ case ExternalAnnotationProvider.NULLABLE :
+ case ExternalAnnotationProvider.NO_ANNOTATION :
+ if (index == this.start)
+ break skipDimensions;
+ //$FALL-THROUGH$
+ case '[':
+ index++;
+ break;
+ default:
+ break skipDimensions;
+ }
+ }
+ } else {
+ while (this.signature[index] == '[')
+ index++;
+ }
switch (this.signature[index]) {
case 'L' :
case 'T' :
@@ -50,7 +78,7 @@
this.end = this.signature.length + 1;
break;
default :
- this.end = this.start;
+ this.end = index;
}
if (this.use15specifics || this.end != this.bracket) {
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 b866bfa..c108d8b 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
@@ -38,6 +38,7 @@
* Bug 441693 - [1.8][null] Bogus warning for type argument annotated with @NonNull
* Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
* Bug 457210 - [1.8][compiler][null] Wrong Nullness errors given on full build build but not on incremental build?
+ * Bug 461250 - ArrayIndexOutOfBoundsException in SourceTypeBinding.fields
* Jesper S Moller <jesper@selskabet.org> - Contributions for
* Bug 412153 - [1.8][compiler] Check validity of annotations which may be repeatable
* Till Brychcy - Contributions for
@@ -1167,6 +1168,8 @@
ReferenceBinding enclosingType = enclosingType();
if (enclosingType != null && enclosingType.isViewedAsDeprecated() && !isDeprecated())
this.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly;
+ fields();
+ methods();
//{ObjectTeams: do not cache memberTypes.length!
// During faultInTypesForFieldsAndMethods(), memberTypes may be added (role files, on demand)
@@ -1178,8 +1181,6 @@
if (!this.memberTypes[i].isBinaryBinding()) // roles could be binary contained in source
//carp}
((SourceTypeBinding) this.memberTypes[i]).faultInTypesForFieldsAndMethods();
- fields();
- methods();
}
// NOTE: the type of each field of a source type is resolved when needed
public FieldBinding[] fields() {
@@ -1204,32 +1205,28 @@
ReferenceBinding.sortFields(this.fields, 0, length);
this.tagBits |= TagBits.AreFieldsSorted;
}
-//{ObjectTeams: don't cache length, may shrink in re-entrant executions
-/*orig
- for (int i = 0, length = this.fields.length; i < length; i++) {
- */
- for (int i = 0; i < this.fields.length; i++) {
-// check discouraged field in @Instantation(ALWAYS) roles:
+ FieldBinding[] fieldsSnapshot = this.fields;
+ for (int i = 0, length = fieldsSnapshot.length; i < length; i++) {
+//{ObjectTeams: check discouraged field in @Instantation(ALWAYS) roles:
if ( this.scope != null
&& (this.tagBits & TagBits.AnnotationInstantiation) != 0
- && !this.fields[i].isStatic())
- this.scope.problemReporter().fieldInRoleWithInstantiationPolicy(this, this.fields[i]);
+ && !fieldsSnapshot[i].isStatic())
+ this.scope.problemReporter().fieldInRoleWithInstantiationPolicy(this, fieldsSnapshot[i]);
// after compilation is finished we have no scope, can't resolve any better
// resolveTypeFor would NPE!
- int length = this.fields.length;
if ( this.model!=null
&& this.model.getState() == ITranslationStates.STATE_FINAL
- && this.fields[i].type == null)
+ && fieldsSnapshot[i].type == null)
{
- this.fields[i] = null;
+ fieldsSnapshot[i] = null;
failed++;
} else
//SH}
- if (resolveTypeFor(this.fields[i]) == null) {
+ if (resolveTypeFor(fieldsSnapshot[i]) == null) {
// do not alter original field array until resolution is over, due to reentrance (143259)
- if (resolvedFields == this.fields) {
- System.arraycopy(this.fields, 0, resolvedFields = new FieldBinding[length], 0, length);
+ if (resolvedFields == fieldsSnapshot) {
+ System.arraycopy(fieldsSnapshot, 0, resolvedFields = new FieldBinding[length], 0, length);
}
resolvedFields[i] = null;
failed++;
@@ -2697,12 +2694,9 @@
if (nullTagBits != 0) {
TypeReference returnTypeRef = ((MethodDeclaration)methodDecl).returnType;
if (this.scope.environment().usesNullTypeAnnotations()) {
- if (nullTagBits != (method.returnType.tagBits & TagBits.AnnotationNullMASK)) {
- if (!this.scope.validateNullAnnotation(nullTagBits, returnTypeRef, methodDecl.annotations)) {
- method.returnType.tagBits &= ~TagBits.AnnotationNullMASK;
- }
- method.tagBits &= ~TagBits.AnnotationNullMASK;
- }
+ if (!this.scope.validateNullAnnotation(nullTagBits, returnTypeRef, methodDecl.annotations))
+ method.returnType.tagBits &= ~TagBits.AnnotationNullMASK;
+ method.tagBits &= ~TagBits.AnnotationNullMASK;
} else {
if (!this.scope.validateNullAnnotation(nullTagBits, returnTypeRef, methodDecl.annotations))
method.tagBits &= ~TagBits.AnnotationNullMASK;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredInitializer.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredInitializer.java
index 13a6dfc..d39a3be 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredInitializer.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredInitializer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2009 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -316,8 +316,19 @@
initializer.bodyEnd = initializer.bodyStart;
}
} else {
- initializer.declarationSourceEnd = braceEnd;
- initializer.bodyEnd = braceStart - 1;
+ if (braceEnd < initializer.declarationSourceStart) {
+ initializer.declarationSourceEnd = initializer.declarationSourceStart;
+ initializer.bodyEnd = initializer.declarationSourceEnd;
+ } else {
+ initializer.declarationSourceEnd = braceEnd;
+ initializer.bodyEnd = braceStart - 1;
+ }
+ if (initializer.bodyStart > initializer.declarationSourceEnd) {
+ initializer.bodyStart = initializer.declarationSourceEnd;
+ if(initializer.block != null) {
+ initializer.block.sourceStart = initializer.declarationSourceStart;
+ }
+ }
}
if(initializer.block != null) {
initializer.block.sourceEnd = initializer.declarationSourceEnd;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java
index 428b453..151d5da 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java
@@ -27,6 +27,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.StringTokenizer;
@@ -718,7 +719,7 @@
}
/**
* Returns the contents of the given zip entry as a byte array.
- * @throws IOException if a problem occured reading the zip entry.
+ * @throws IOException if a problem occurred reading the zip entry.
*/
public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip)
throws IOException {
@@ -1132,6 +1133,15 @@
}
public static void collectRunningVMBootclasspath(List bootclasspaths) {
+ for (String filePath : collectFilesNames()) {
+ FileSystem.Classpath currentClasspath = FileSystem.getClasspath(filePath, null, null);
+ if (currentClasspath != null) {
+ bootclasspaths.add(currentClasspath);
+ }
+ }
+ }
+
+ public static List<String> collectFilesNames() {
/* no bootclasspath specified
* we can try to retrieve the default librairies of the VM used to run
* the batch compiler
@@ -1154,15 +1164,11 @@
bootclasspathProperty = System.getProperty("org.apache.harmony.boot.class.path"); //$NON-NLS-1$
}
}
+ List<String> filePaths = new ArrayList<>();
if ((bootclasspathProperty != null) && (bootclasspathProperty.length() != 0)) {
StringTokenizer tokenizer = new StringTokenizer(bootclasspathProperty, File.pathSeparator);
- String token;
while (tokenizer.hasMoreTokens()) {
- token = tokenizer.nextToken();
- FileSystem.Classpath currentClasspath = FileSystem.getClasspath(token, null, null);
- if (currentClasspath != null) {
- bootclasspaths.add(currentClasspath);
- }
+ filePaths.add(tokenizer.nextToken());
}
} else {
// try to get all jars inside the lib folder of the java home
@@ -1185,18 +1191,14 @@
File[] current = systemLibrariesJars[i];
if (current != null) {
for (int j = 0, max2 = current.length; j < max2; j++) {
- FileSystem.Classpath classpath =
- FileSystem.getClasspath(current[j].getAbsolutePath(),
- null, false, null, null);
- if (classpath != null) {
- bootclasspaths.add(classpath);
- }
+ filePaths.add(current[j].getAbsolutePath());
}
}
}
}
}
}
+ return filePaths;
}
public static int getParameterCount(char[] methodSignature) {
try {
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ImportName.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ImportName.java
index 34d6bc9..6ce9e75 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ImportName.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ImportName.java
@@ -49,12 +49,12 @@
/* orig:
public static ImportName createFor(boolean isStatic, String qualifiedName) {
String containerName = Signature.getQualifier(qualifiedName);
- String simpleName = qualifiedName.substring(containerName.length() + 1);
+ String simpleName = Signature.getSimpleName(qualifiedName);
return new ImportName(isStatic, containerName, simpleName);
:giro */
public static ImportName createFor(boolean isStatic, /*OT:*/boolean isBase, String qualifiedName) {
String containerName = Signature.getQualifier(qualifiedName);
- String simpleName = qualifiedName.substring(containerName.length() + 1);
+ String simpleName = Signature.getSimpleName(qualifiedName);
return new ImportName(isStatic, /*OT:*/isBase, containerName, simpleName);
// SH}
}
@@ -76,7 +76,7 @@
this.containerName = containerName;
this.simpleName = simpleName;
- this.qualifiedName = this.containerName + "." + this.simpleName; //$NON-NLS-1$;
+ this.qualifiedName = containerName.isEmpty() ? simpleName : containerName + "." + simpleName; //$NON-NLS-1$;
}
@Override
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ImportRewriteConfiguration.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ImportRewriteConfiguration.java
index c77fee8..2055df1 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ImportRewriteConfiguration.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/ImportRewriteConfiguration.java
@@ -18,6 +18,9 @@
import java.util.Set;
import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IPackageDeclaration;
+import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaProject;
/**
@@ -159,7 +162,25 @@
implicitImportContainerNames.add("java.lang"); //$NON-NLS-1$
- String compilationUnitPackageName = compilationUnit.getParent().getElementName();
+ IJavaElement packageFragment = compilationUnit.getParent();
+ String compilationUnitPackageName = packageFragment.getElementName();
+ if (compilationUnitPackageName.isEmpty() && !packageFragment.exists() && compilationUnit.exists()) {
+ /*
+ * For a file outside of the build path, JavaCore#create(IFile) creates an
+ * ICompilationUnit with the file's parent folder as package fragment root, and a default package.
+ * That "wrong" package is problematic for the ImportRewrite, since it doesn't get filtered
+ * and eventually leads to unused import statements.
+ */
+ try {
+ IPackageDeclaration[] packageDeclarations = compilationUnit.getPackageDeclarations();
+ if (packageDeclarations.length > 0) {
+ implicitImportContainerNames.add(packageDeclarations[0].getElementName());
+ return implicitImportContainerNames;
+ }
+ } catch (JavaModelException e) {
+ // continue
+ }
+ }
implicitImportContainerNames.add(compilationUnitPackageName);
return implicitImportContainerNames;
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/OnDemandComputer.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/OnDemandComputer.java
index c180b35..069fc0f 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/OnDemandComputer.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/imports/OnDemandComputer.java
@@ -59,15 +59,18 @@
//{ObjectTeams: no on-demand base imports
if (containerOnDemand.isBase) continue;
// SH}
- Collection<ImportName> containerImports = containerAndImports.getValue();
+ // Imports from an unnamed package should not be reduced (see bug 461863).
+ boolean isUnnamedPackage = containerOnDemand.containerName.isEmpty();
- Set<String> explicitSimpleNames =
- containerOnDemand.isStatic ? staticExplicitSimpleNames : typeExplicitSimpleNames;
+ if (touchedContainers.contains(containerOnDemand) && !isUnnamedPackage) {
+ Collection<ImportName> containerImports = containerAndImports.getValue();
- int onDemandThreshold =
- containerOnDemand.isStatic ? this.staticOnDemandThreshold : this.typeOnDemandThreshold;
+ Set<String> explicitSimpleNames =
+ containerOnDemand.isStatic ? staticExplicitSimpleNames : typeExplicitSimpleNames;
- if (touchedContainers.contains(containerOnDemand)) {
+ int onDemandThreshold =
+ containerOnDemand.isStatic ? this.staticOnDemandThreshold : this.typeOnDemandThreshold;
+
OnDemandReduction candidate = maybeReduce(
containerOnDemand, containerImports, onDemandThreshold, explicitSimpleNames);
if (candidate != null) {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java
index 2e3eea4..a24a91a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java
@@ -37,6 +37,7 @@
import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider;
import org.eclipse.jdt.internal.compiler.lookup.SignatureWrapper;
import org.eclipse.jdt.internal.core.ClasspathEntry;
+import org.eclipse.jdt.internal.core.util.KeyToSignature;
/**
* Utilities for accessing and manipulating text files that externally define annotations for a given Java type.
@@ -48,16 +49,16 @@
public final class ExternalAnnotationUtil {
/** Representation of a 'nullable' annotation, independent of the concrete annotation name used in Java sources. */
- public static final char NULLABLE = '0';
+ public static final char NULLABLE = ExternalAnnotationProvider.NULLABLE;
/** Representation of a 'nonnull' annotation, independent of the concrete annotation name used in Java sources. */
- public static final char NONNULL = '1';
+ public static final char NONNULL = ExternalAnnotationProvider.NONNULL;
/**
* Represents absence of a null annotation. Useful for removing an existing null annotation.
* This character is used only internally, it is not part of the Eclipse External Annotation file format.
*/
- public static final char NO_ANNOTATION = '@';
+ public static final char NO_ANNOTATION = ExternalAnnotationProvider.NO_ANNOTATION;
/** Strategy for merging a new signature with an existing (possibly annotated) signature. */
public static enum MergeStrategy {
@@ -78,12 +79,21 @@
* @return a signature in class file format
*/
public static String extractGenericSignature(IMethodBinding methodBinding) {
- // Note that IMethodBinding.binding is not accessible, hence we need to reverse engineer from the key:
-
- // method key contains the signature between '(' and '|': "class.selector(params)return|throws"
- int open= methodBinding.getKey().indexOf('(');
- int throwStart= methodBinding.getKey().indexOf('|');
- return throwStart == -1 ? methodBinding.getKey().substring(open) : methodBinding.getKey().substring(open, throwStart);
+ // Note that IMethodBinding.binding is not accessible, hence we need to recover the signature from the key:
+ KeyToSignature parser = new KeyToSignature(methodBinding.getKey(), KeyToSignature.SIGNATURE, true);
+ parser.parse();
+ return parser.toString();
+ }
+
+ /**
+ * Answer the given types's signature in class file format.
+ * @param type binding representing a type
+ * @return a signature in class file format
+ */
+ public static String extractGenericTypeSignature(ITypeBinding type) {
+ KeyToSignature parser = new KeyToSignature(type.getKey(), KeyToSignature.SIGNATURE, true);
+ parser.parse();
+ return parser.toString();
}
/**
@@ -157,7 +167,7 @@
if (!targetType.exists())
return null;
- String binaryTypeName = targetType.getFullyQualifiedName('.').replace('.', '/');
+ String binaryTypeName = targetType.getFullyQualifiedName('$').replace('.', '/');
IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) targetType.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
IClasspathEntry entry = packageRoot.getResolvedClasspathEntry();
@@ -214,20 +224,50 @@
* @param monitor progress monitor to be passed through into file operations, or null if no reporting is desired
* @throws CoreException if access to the file fails
* @throws IOException if reading file content fails
+ * @throws IllegalArgumentException if the annotatedReturnType does not structurally match to originalSignature
*/
public static void annotateMethodReturnType(String typeName, IFile file, String selector, String originalSignature,
String annotatedReturnType, MergeStrategy mergeStrategy, IProgressMonitor monitor)
- throws CoreException, IOException
+ throws CoreException, IOException, IllegalArgumentException
{
annotateMember(typeName, file, selector, originalSignature, annotatedReturnType, POSITION_RETURN_TYPE, mergeStrategy, monitor);
}
- static void annotateMember(String typeName, IFile file, String selector, String originalSignature, String annotatedSignature,
+ /**
+ * Update the given external annotation file with details regarding annotations of a parameter type of a given method.
+ * If the specified method already has external annotations, old and new annotations will be merged,
+ * with priorities controlled by the parameter 'mergeStrategy'.
+ * <p>
+ * This method is suitable for declaration annotations and type use annotations.
+ * </p>
+ * @param typeName binary name (slash separated) of the type being annotated
+ * @param file a file assumed to be in .eea format, will be created if it doesn't exist.
+ * @param selector selector of the method
+ * @param originalSignature unannotated signature of the member, used for identification
+ * @param annotatedParameterType signature of the new parameter type whose annotations should be superimposed on the method
+ * @param paramIdx 0-based index of the parameter to which the annotation should be attached
+ * @param mergeStrategy controls how old and new signatures should be merged
+ * @param monitor progress monitor to be passed through into file operations, or null if no reporting is desired
+ * @throws CoreException if access to the file fails
+ * @throws IOException if reading file content fails
+ * @throws IllegalArgumentException if the annotatedParameterType does not structurally match to originalSignature
+ */
+ public static void annotateMethodParameterType(String typeName, IFile file, String selector, String originalSignature,
+ String annotatedParameterType, int paramIdx, MergeStrategy mergeStrategy, IProgressMonitor monitor)
+ throws CoreException, IOException, IllegalArgumentException
+ {
+ annotateMember(typeName, file, selector, originalSignature, annotatedParameterType, paramIdx, mergeStrategy, monitor);
+ }
+
+ private static void annotateMember(String typeName, IFile file, String selector, String originalSignature, String annotatedSignature,
int updatePosition, MergeStrategy mergeStrategy, IProgressMonitor monitor)
- throws CoreException, IOException
+ throws CoreException, IOException, IllegalArgumentException
{
if (!file.exists()) {
+ // assemble full annotatedSignature (don't bother merging since no previous signature exists):
+ annotatedSignature = updateSignature(originalSignature, annotatedSignature, updatePosition, MergeStrategy.REPLACE_SIGNATURE);
+
StringBuffer newContent= new StringBuffer();
// header:
newContent.append(ExternalAnnotationProvider.CLASS_PREFIX);
@@ -265,6 +305,9 @@
if (relation == 0) {
StringBuffer pending = new StringBuffer(line).append('\n');
pending.append(line = reader.readLine());
+ if (line == null) {
+ break; // found only the selector at EOF, append right here, ignoring 'pending'
+ }
// compare original signatures:
relation = line.trim().compareTo(originalSignature);
if (relation > 0) { // past the insertion point
@@ -277,28 +320,24 @@
continue;
if (relation == 0) {
// update existing entry:
- String nextLine = reader.readLine();
- if (nextLine == null)
- nextLine = line; // no annotated line yet, use unannotated line instead
- if (nextLine.startsWith(" ")) { //$NON-NLS-1$
+ String annotationLine = reader.readLine();
+ String nextLine = null;
+ if (annotationLine == null || annotationLine.isEmpty() || !annotationLine.startsWith(" ")) { //$NON-NLS-1$
+ nextLine = annotationLine; // push back, since not a signature line
+ annotationLine = line; // no annotated line yet, use unannotated line instead
+ }
+ if (annotationLine.startsWith(" ")) { //$NON-NLS-1$
switch (mergeStrategy) {
case REPLACE_SIGNATURE:
break; // unconditionally use annotatedSignature
case OVERWRITE_ANNOTATIONS:
case ADD_ANNOTATIONS:
- if (updatePosition == POSITION_FULL_SIGNATURE) {
- annotatedSignature = addAnnotationsTo(annotatedSignature, nextLine.trim(), mergeStrategy);
- } else if (updatePosition == POSITION_RETURN_TYPE) {
- annotatedSignature = updateMethodReturnType(annotatedSignature, nextLine.trim(), mergeStrategy);
- } else {
- // parameter i
- }
+ annotatedSignature = updateSignature(annotationLine.trim(), annotatedSignature, updatePosition, mergeStrategy);
break;
default:
JavaCore.getJavaCore().getLog().log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID,
"Unexpected value for enum MergeStrategy")); //$NON-NLS-1$
}
- nextLine = null; // discard old annotated signature (may have been merged above)
}
writeFile(file, newContent, annotatedSignature, nextLine, reader, monitor);
return;
@@ -308,13 +347,7 @@
// add new entry:
newContent.append(selector).append('\n');
newContent.append(' ').append(originalSignature).append('\n');
- if (updatePosition == POSITION_FULL_SIGNATURE) {
- // annotatedSignature is already complete
- } else if (updatePosition == POSITION_RETURN_TYPE) {
- annotatedSignature = updateMethodReturnType(annotatedSignature, originalSignature, mergeStrategy);
- } else {
- // parameter i
- }
+ annotatedSignature = updateSignature(originalSignature, annotatedSignature, updatePosition, mergeStrategy);
writeFile(file, newContent, annotatedSignature, line, reader, monitor);
} finally {
reader.close();
@@ -322,6 +355,38 @@
}
}
+ private static String updateSignature(String originalSignature, String annotatedSignature, int updatePosition, MergeStrategy mergeStrategy) {
+ StringBuffer buf = new StringBuffer();
+ String signatureToReplace;
+ String postfix = null;
+ switch (updatePosition) {
+ case POSITION_FULL_SIGNATURE:
+ signatureToReplace = originalSignature;
+ break;
+ case POSITION_RETURN_TYPE:
+ assert originalSignature.charAt(0) == '(' : "signature must start with '('"; //$NON-NLS-1$
+ int close = originalSignature.indexOf(')');
+ buf.append(originalSignature, 0, close+1);
+ signatureToReplace = originalSignature.substring(close+1);
+ break;
+ default: // parameter
+ SignatureWrapper wrapper = new SignatureWrapper(originalSignature.toCharArray(), true, true); // may already contain annotations
+ wrapper.start = 1;
+ for (int i = 0; i < updatePosition; i++)
+ wrapper.start = wrapper.computeEnd() + 1;
+ int start = wrapper.start;
+ int end = wrapper.computeEnd();
+ end = wrapper.skipAngleContents(end);
+ buf.append(originalSignature, 0, start);
+ signatureToReplace = originalSignature.substring(start, end+1);
+ postfix = originalSignature.substring(end+1, originalSignature.length());
+ }
+ updateType(buf, signatureToReplace.toCharArray(), annotatedSignature.toCharArray(), mergeStrategy);
+ if (postfix != null)
+ buf.append(postfix);
+ return buf.toString();
+ }
+
/**
* Insert that given annotation at the given position into the given signature.
* @param mergeStrategy if set to {@link MergeStrategy#ADD_ANNOTATIONS}, refuse to
@@ -342,96 +407,49 @@
return result.toString();
}
- private static String addAnnotationsTo(String newSignature, String oldSignature, MergeStrategy mergeStategy) {
- // TODO: consider rewrite using updateType() below
- StringBuffer buf = new StringBuffer();
- assert newSignature.charAt(0) == '(' : "signature must start with '('"; //$NON-NLS-1$
- assert oldSignature.charAt(0) == '(' : "signature must start with '('"; //$NON-NLS-1$
- buf.append('(');
- SignatureWrapper wrapperNew = new SignatureWrapper(newSignature.toCharArray(), true); // when using annotations we must be at 1.5+
- wrapperNew.start = 1;
- SignatureWrapper wrapperOld = new SignatureWrapper(oldSignature.toCharArray(), true);
- wrapperOld.start = 1;
- while (!wrapperNew.atEnd() && !wrapperOld.atEnd()) {
- int startNew = wrapperNew.start;
- int startOld = wrapperOld.start;
- if (wrapperNew.signature[startNew] == ')') {
- if (wrapperOld.signature[startOld] != ')')
- throw new IllegalArgumentException("Structural difference between signatures "+newSignature+" and "+oldSignature); //$NON-NLS-1$//$NON-NLS-2$
- startNew = ++wrapperNew.start;
- startOld = ++wrapperOld.start;
- buf.append(')');
- }
- int endNew = wrapperNew.computeEnd();
- int endOld = wrapperOld.computeEnd();
- int lenNew = endNew-startNew+1;
- int lenOld = endOld-startOld+1;
- // TODO detailed comparison / merging:
- if (lenNew == lenOld) {
- switch (mergeStategy) {
- case OVERWRITE_ANNOTATIONS:
- buf.append(wrapperNew.signature, startNew, lenNew);
- break;
- case ADD_ANNOTATIONS:
- buf.append(wrapperOld.signature, startOld, lenOld);
- break;
- //$CASES-OMITTED$ should only be called with the two strategies handled above
- default:
- JavaCore.getJavaCore().getLog().log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID,
- "Unexpected value for enum MergeStrategy")); //$NON-NLS-1$
- }
- } else if (lenNew > lenOld) {
- buf.append(wrapperNew.signature, startNew, lenNew);
- } else {
- buf.append(wrapperOld.signature, startOld, lenOld);
- }
- }
- return buf.toString();
- }
-
- private static String updateMethodReturnType(String newReturnType, String oldSignature, MergeStrategy mergeStrategy) {
- StringBuffer buf = new StringBuffer();
- assert oldSignature.charAt(0) == '(' : "signature must start with '('"; //$NON-NLS-1$
- int close = oldSignature.indexOf(')');
- buf.append(oldSignature, 0, close+1);
- updateType(buf, oldSignature.substring(close+1).toCharArray(), newReturnType.toCharArray(), mergeStrategy);
- return buf.toString();
- }
-
/**
* Update 'oldType' with annotations from 'newType' guided by 'mergeStrategy'.
* The result is written into 'buf' as we go.
*/
private static boolean updateType(StringBuffer buf, char[] oldType, char[] newType, MergeStrategy mergeStrategy) {
- SignatureWrapper oWrap = new SignatureWrapper(oldType, true);
- SignatureWrapper nWrap = new SignatureWrapper(newType, true);
- if (match(buf, oWrap, nWrap, 'L', false)
- || match(buf, oWrap, nWrap, 'T', false))
- {
- mergeAnnotation(buf, oWrap, nWrap, mergeStrategy);
- buf.append(oWrap.nextName());
- nWrap.nextName(); // skip
- if (match(buf, oWrap, nWrap, '<', false)) {
- do {
- int oStart = oWrap.start;
- int nStart = nWrap.start;
- oWrap.computeEnd();
- nWrap.computeEnd();
- if (updateType(buf, oWrap.getFrom(oStart), nWrap.getFrom(nStart), mergeStrategy))
- mergeAnnotation(buf, oWrap, nWrap, mergeStrategy);
- } while (!match(buf, oWrap, nWrap, '>', false));
+ if (mergeStrategy == MergeStrategy.REPLACE_SIGNATURE) {
+ buf.append(newType);
+ return false;
+ }
+ try {
+ SignatureWrapper oWrap = new SignatureWrapper(oldType, true, true); // may already contain annotations
+ SignatureWrapper nWrap = new SignatureWrapper(newType, true, true); // may already contain annotations
+ if (match(buf, oWrap, nWrap, 'L', false)
+ || match(buf, oWrap, nWrap, 'T', false))
+ {
+ mergeAnnotation(buf, oWrap, nWrap, mergeStrategy);
+ buf.append(oWrap.nextName());
+ nWrap.nextName(); // skip
+ if (match(buf, oWrap, nWrap, '<', false)) {
+ do {
+ int oStart = oWrap.start;
+ int nStart = nWrap.start;
+ oWrap.computeEnd();
+ nWrap.computeEnd();
+ if (updateType(buf, oWrap.getFrom(oStart), nWrap.getFrom(nStart), mergeStrategy))
+ mergeAnnotation(buf, oWrap, nWrap, mergeStrategy);
+ } while (!match(buf, oWrap, nWrap, '>', false));
+ }
+ match(buf, oWrap, nWrap, ';', true);
+ } else if (match(buf, oWrap, nWrap, '[', false)) {
+ mergeAnnotation(buf, oWrap, nWrap, mergeStrategy);
+ updateType(buf, oWrap.tail(), nWrap.tail(), mergeStrategy);
+ } else if (match(buf, oWrap, nWrap, '*', false)
+ || match(buf, oWrap, nWrap, '+', false)
+ || match(buf, oWrap, nWrap, '-', false))
+ {
+ return true; // annotation allowed after this (not included in oldType / newType)
+ } else {
+ buf.append(oldType);
}
- match(buf, oWrap, nWrap, ';', true);
- } else if (match(buf, oWrap, nWrap, '[', false)) {
- mergeAnnotation(buf, oWrap, nWrap, mergeStrategy);
- updateType(buf, oWrap.tail(), nWrap.tail(), mergeStrategy);
- } else if (match(buf, oWrap, nWrap, '*', false)
- || match(buf, oWrap, nWrap, '+', false)
- || match(buf, oWrap, nWrap, '-', false))
- {
- return true; // annotation allowed after this (not included in oldType / newType)
- } else {
- buf.append(oldType);
+ } catch (ArrayIndexOutOfBoundsException aioobe) { // from several locations inside match() or mergeAnnotation().
+ StringBuilder msg = new StringBuilder("Structural mismatch between ").append(oldType).append(" and ").append(newType); //$NON-NLS-1$ //$NON-NLS-2$
+ throw new IllegalArgumentException(msg.toString(), aioobe);
}
return false;
}
@@ -444,8 +462,9 @@
boolean match1 = sig1.signature[sig1.start] == expected;
boolean match2 = sig2.signature[sig2.start] == expected;
if (match1 != match2) {
- throw new IllegalArgumentException("Mismatching type structures" //$NON-NLS-1$
- + new String(sig1.signature)+" vs "+new String(sig2.signature)); //$NON-NLS-1$
+ StringBuilder msg = new StringBuilder("Mismatching type structures ") //$NON-NLS-1$
+ .append(sig1.signature).append(" vs ").append(sig2.signature); //$NON-NLS-1$
+ throw new IllegalArgumentException(msg.toString());
}
if (match1) {
buf.append(expected);
@@ -486,9 +505,18 @@
break;
case NO_ANNOTATION:
newS.start++; // don't insert
- switch (oldAnn) { case NULLABLE: case NONNULL: oldS.start++; } // just skip // skip
+ switch (oldAnn) { case NULLABLE: case NONNULL: oldS.start++; } // just skip
break;
+ default:
+ switch (oldAnn) {
+ case NULLABLE: case NONNULL:
+ oldS.start++;
+ buf.append(oldAnn); // keep
+ }
}
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected merge strategy"); // REPLACE_SIGNATURE does not reach this point, see initial check in updateType() //$NON-NLS-1$
}
}
@@ -532,4 +560,129 @@
}
((IFolder) container).create(false, true, monitor);
}
+
+ /**
+ * Retrieve the annotated signature of a specified member as found in the given external annotation file, if any.
+ * @param typeName fully qualified slash-separated name of the type for which the file defines external annotations
+ * @param file a file assumed to be in .eea format, must not be null, but may not exist
+ * @param selector name of the member whose annotation we are looking for
+ * @param originalSignature the unannotated signature by which the member is identified
+ * @return the annotated signature as found in the file, or null.
+ */
+ public static String getAnnotatedSignature(String typeName, IFile file, String selector, String originalSignature) {
+ if (file.exists()) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getContents()))) {
+ ExternalAnnotationProvider.assertClassHeader(reader.readLine(), typeName);
+ while (true) {
+ String line = reader.readLine();
+ // selector:
+ if (selector.equals(line)) {
+ // original signature:
+ line = reader.readLine();
+ if (originalSignature.equals(ExternalAnnotationProvider.extractSignature(line))) {
+ // annotated signature:
+ return ExternalAnnotationProvider.extractSignature(reader.readLine());
+ }
+ }
+ if (line == null)
+ break;
+ }
+ } catch (IOException | CoreException e) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Apply the specified changes on the given type.
+ * This method can be used as a dry run without modifying an annotation file.
+ *
+ * @param originalSignature the original type signature, may be annotated already
+ * @param annotatedType a type signature with additional annotations (incl. {@link #NO_ANNOTATION}).
+ * @param mergeStrategy controls how old and new signatures should be merged
+ * @return an array of length four: <ul>
+ * <li>prefix up-to the changed type</li>
+ * <li>original type</li>
+ * <li>changed type</li>
+ * <li>postfix after the changed type <em>(here: empty string)</li>
+ * </ul>
+ */
+ public static String[] annotateType(String originalSignature, String annotatedType, MergeStrategy mergeStrategy)
+ {
+ String[] result = new String[4]; // prefix, orig, replacement, postfix
+ StringBuffer buf;
+ result[0] = ""; //$NON-NLS-1$
+ buf = new StringBuffer();
+ result[1] = originalSignature;
+ updateType(buf, originalSignature.toCharArray(), annotatedType.toCharArray(), mergeStrategy);
+ result[2] = buf.toString();
+ result[3] = ""; //$NON-NLS-1$
+ return result;
+ }
+
+ /**
+ * Apply the specified changes on the return type of the given signature.
+ * This method can be used as a dry run without modifying an annotation file.
+ *
+ * @param originalSignature the original full signature, may be annotated already
+ * @param annotatedType a type signature with additional annotations (incl. {@link #NO_ANNOTATION}).
+ * @param mergeStrategy controls how old and new signatures should be merged
+ * @return an array of length four: <ul>
+ * <li>prefix up-to the changed type</li>
+ * <li>original type</li>
+ * <li>changed type</li>
+ * <li>postfix after the changed type <em>(here: empty string)</li>
+ * </ul>
+ */
+ public static String[] annotateReturnType(String originalSignature, String annotatedType, MergeStrategy mergeStrategy)
+ {
+ String[] result = new String[4]; // prefix, orig, replacement, postfix
+ StringBuffer buf;
+ assert originalSignature.charAt(0) == '(' : "signature must start with '('"; //$NON-NLS-1$
+ int close = originalSignature.indexOf(')');
+ result[0] = originalSignature.substring(0, close+1);
+ buf = new StringBuffer();
+ result[1] = originalSignature.substring(close+1);
+ updateType(buf, result[1].toCharArray(), annotatedType.toCharArray(), mergeStrategy);
+ result[2] = buf.toString();
+ result[3] = ""; //$NON-NLS-1$
+ return result;
+ }
+
+
+ /**
+ * Apply the specified changes on a parameter within the given signature.
+ * This method can be used as a dry run without modifying an annotation file.
+ *
+ * @param originalSignature the original full signature, may be annotated already
+ * @param annotatedType a type signature with additional annotations (incl. {@link #NO_ANNOTATION}).
+ * @param paramIdx the index of a parameter to annotated
+ * @param mergeStrategy controls how old and new signatures should be merged
+ * @return an array of length four: <ul>
+ * <li>prefix up-to the changed type</li>
+ * <li>original type</li>
+ * <li>changed type</li>
+ * <li>postfix after the changed type</li>
+ * </ul>
+ */
+ public static String[] annotateParameterType(String originalSignature, String annotatedType, int paramIdx, MergeStrategy mergeStrategy)
+ {
+ String[] result = new String[4]; // prefix, orig, replacement, postfix
+ StringBuffer buf;
+ SignatureWrapper wrapper = new SignatureWrapper(originalSignature.toCharArray(), true, true); // may already contain annotations
+ wrapper.start = 1;
+ for (int i = 0; i < paramIdx; i++)
+ wrapper.start = wrapper.computeEnd() + 1;
+ int start = wrapper.start;
+ int end = wrapper.computeEnd();
+ end = wrapper.skipAngleContents(end);
+ result[0] = originalSignature.substring(0, start);
+ buf = new StringBuffer();
+ result[1] = originalSignature.substring(start, end+1);
+ updateType(buf, result[1].toCharArray(), annotatedType.toCharArray(), mergeStrategy);
+ result[2] = buf.toString();
+ result[3] = originalSignature.substring(end+1, originalSignature.length());
+ return result;
+ }
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/KeyToSignature.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/KeyToSignature.java
index ebbc201..47343ab 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/KeyToSignature.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/KeyToSignature.java
@@ -9,6 +9,7 @@
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contribution for
* Bug 425183 - [1.8][inference] make CaptureBinding18 safe
+ * Bug 462025 - [null][test] create tests for manipulating external null annotations
*******************************************************************************/
package org.eclipse.jdt.internal.core.util;
@@ -32,6 +33,7 @@
public StringBuffer signature = new StringBuffer();
private int kind;
+ private boolean asBinarySignature = false; // '.' vs. '/' and '$'
private ArrayList arguments = new ArrayList();
private ArrayList typeArguments = new ArrayList();
private ArrayList typeParameters = new ArrayList();
@@ -42,7 +44,9 @@
public KeyToSignature(BindingKeyParser parser) {
super(parser);
- this.kind = ((KeyToSignature) parser).kind;
+ KeyToSignature keyToSignature = (KeyToSignature) parser;
+ this.kind = keyToSignature.kind;
+ this.asBinarySignature = keyToSignature.asBinarySignature;
}
public KeyToSignature(String key, int kind) {
@@ -50,6 +54,12 @@
this.kind = kind;
}
+ public KeyToSignature(String key, int kind, boolean asBinarySignature) {
+ super(key);
+ this.kind = kind;
+ this.asBinarySignature = asBinarySignature;
+ }
+
public void consumeArrayDimension(char[] brakets) {
this.signature.append(brakets);
}
@@ -74,14 +84,16 @@
this.signature = new StringBuffer();
// remove trailing semi-colon as it is added later in comsumeType()
uniqueKey = CharOperation.subarray(uniqueKey, 0, uniqueKey.length-1);
- CharOperation.replace(uniqueKey, '/', '.');
+ if (!this.asBinarySignature)
+ CharOperation.replace(uniqueKey, '/', '.');
this.signature.append(uniqueKey);
}
public void consumeMethod(char[] selector, char[] methodSignature) {
this.arguments = new ArrayList();
this.typeArguments = new ArrayList();
- CharOperation.replace(methodSignature, '/', '.');
+ if (!this.asBinarySignature)
+ CharOperation.replace(methodSignature, '/', '.');
switch(this.kind) {
case SIGNATURE:
this.signature = new StringBuffer();
@@ -198,7 +210,7 @@
public void consumeParameterizedType(char[] simpleTypeName, boolean isRaw) {
if (simpleTypeName != null) {
// member type
- this.signature.append('.');
+ this.signature.append(this.asBinarySignature ? '$' : '.');
this.signature.append(simpleTypeName);
}
if (!isRaw) {
@@ -237,12 +249,14 @@
public void consumeFullyQualifiedName(char[] fullyQualifiedName) {
this.typeSigStart = this.signature.length();
this.signature.append('L');
- this.signature.append(CharOperation.replaceOnCopy(fullyQualifiedName, '/', '.'));
+ if (!this.asBinarySignature)
+ fullyQualifiedName = CharOperation.replaceOnCopy(fullyQualifiedName, '/', '.');
+ this.signature.append(fullyQualifiedName);
}
public void consumeSecondaryType(char[] simpleTypeName) {
this.signature.append('~');
- this.mainTypeStart = this.signature.lastIndexOf(".") + 1; //$NON-NLS-1$
+ this.mainTypeStart = this.signature.lastIndexOf(this.asBinarySignature ? "/" : ".") + 1; //$NON-NLS-1$ //$NON-NLS-2$
if (this.mainTypeStart == 0) {
this.mainTypeStart = 1; // default package (1 for the 'L')
int i = 0;
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ConstructorLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ConstructorLocator.java
index a234f40..fb6d3fb 100644
--- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ConstructorLocator.java
+++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ConstructorLocator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 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
@@ -13,6 +13,7 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.internal.compiler.ast.*;
@@ -44,6 +45,7 @@
return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
}
public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) {
+ if (this.pattern.fineGrain != 0 && !this.pattern.findDeclarations) return IMPOSSIBLE_MATCH;
int referencesLevel = this.pattern.findReferences ? matchLevelForReferences(node) : IMPOSSIBLE_MATCH;
int declarationsLevel = this.pattern.findDeclarations ? matchLevelForDeclarations(node) : IMPOSSIBLE_MATCH;
@@ -100,6 +102,10 @@
public int match(TypeDeclaration node, MatchingNodeSet nodeSet) {
if (!this.pattern.findReferences) return IMPOSSIBLE_MATCH;
+ if (this.pattern.fineGrain != 0 &&
+ (this.pattern.fineGrain & ~IJavaSearchConstants.METHOD_REFERENCE_EXPRESSION) == 0 )
+ return IMPOSSIBLE_MATCH;
+
// need to look for a generated default constructor
return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
}