diff options
| author | Stephan Herrmann | 2014-02-25 17:21:23 +0000 |
|---|---|---|
| committer | Stephan Herrmann | 2014-02-25 17:21:23 +0000 |
| commit | b16af955ad096598d372b7b23da79db705ddee11 (patch) | |
| tree | 2353982f07478b8ac05b2794352ae7d181093bf8 | |
| parent | 745840be480d4e43f22d0c4f40c198cdee6926a4 (diff) | |
| download | eclipse.jdt.core-b16af955ad096598d372b7b23da79db705ddee11.tar.gz eclipse.jdt.core-b16af955ad096598d372b7b23da79db705ddee11.tar.xz eclipse.jdt.core-b16af955ad096598d372b7b23da79db705ddee11.zip | |
Bug 428985 - [1.8][null] help the type inference to find a
nullness-annotated type if possible
- more tests and fix
4 files changed, 73 insertions, 10 deletions
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 8132e223dd..8e87c5ea9a 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 @@ -2560,6 +2560,65 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest { "----------\n"); } + // demonstrate that null annotations from the functional interface win, resulting in successful inference but null-safety issues + public void testNullTypeInference2e() { + runNegativeTestWithLibs( + new String[] { + "PolyNull.java", + "import org.eclipse.jdt.annotation.*;\n" + + "\n" + + "interface Func<T> {\n" + + " T a(T i);\n" + + "}\n" + + "public class PolyNull {\n" + + " String extract(Func<@Nullable String> f, @Nullable String s) { return f.a(s); }\n" + + " @NonNull String testWARN() {\n" + + " return extract(i -> null, \"hallo\"); // OK to pass null\n" + + " }\n" + + "}\n" + }, + getCompilerOptions(), + "----------\n" + + "1. WARNING in PolyNull.java (at line 9)\n" + + " return extract(i -> null, \"hallo\"); // OK to pass null\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Null type safety (type annotations): The expression of type \'String\' needs unchecked conversion to conform to \'@NonNull String\'\n" + + "----------\n"); + } + + // demonstrate that null annotations from the functional interface win, resulting in successful inference but null-safety issues + public void testNullTypeInference2f() { + runNegativeTestWithLibs( + new String[] { + "PolyNull.java", + "import org.eclipse.jdt.annotation.*;\n" + + "\n" + + "interface Func<T> {\n" + + " T a(T i);\n" + + "}\n" + + "public class PolyNull {\n" + + " <X> X extract(Func<@Nullable X> f, @Nullable X s) { return f.a(s); }\n" + + " @NonNull String testERR() {\n" + + " return extract(i -> needNN(i), \"ola\");\n" + + " }\n" + + " @NonNull String needNN(@NonNull String s) { return \"\"; }\n" + + "" + + "}\n" + }, + getCompilerOptions(), + "----------\n" + + "1. WARNING in PolyNull.java (at line 9)\n" + + " return extract(i -> needNN(i), \"ola\");\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Null type safety (type annotations): The expression of type \'String\' needs unchecked conversion to conform to \'@NonNull String\'\n" + + "----------\n" + + "2. ERROR in PolyNull.java (at line 9)\n" + + " return extract(i -> needNN(i), \"ola\");\n" + + " ^\n" + + "Null type mismatch (type annotations): required \'@NonNull String\' but this expression has type \'@Nullable String\'\n" + + "----------\n"); + } + // missing return type should not cause NPE public void testBug415850_01() { runNegativeTestWithLibs( 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 242027709a..dfa9258457 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 @@ -69,10 +69,10 @@ class BoundSet { } } // pre: this.superBounds != null - public TypeBinding[] lowerBounds(boolean onlyProper, LookupEnvironment environment) { + public TypeBinding[] lowerBounds(boolean onlyProper, InferenceVariable variable) { TypeBinding[] boundTypes = new TypeBinding[this.superBounds.size()]; Iterator<TypeBound> it = this.superBounds.iterator(); - long nullHints = 0; + long nullHints = variable.nullHints; int i = 0; while(it.hasNext()) { TypeBound current = it.next(); @@ -86,16 +86,16 @@ class BoundSet { return Binding.NO_TYPES; if (i < boundTypes.length) System.arraycopy(boundTypes, 0, boundTypes=new TypeBinding[i], 0, i); - useNullHints(nullHints, boundTypes, environment); + useNullHints(nullHints, boundTypes, variable.environment); InferenceContext18.sortTypes(boundTypes); return boundTypes; } // pre: this.subBounds != null - public TypeBinding[] upperBounds(boolean onlyProper, LookupEnvironment environment) { + public TypeBinding[] upperBounds(boolean onlyProper, InferenceVariable variable) { ReferenceBinding[] rights = new ReferenceBinding[this.subBounds.size()]; TypeBinding simpleUpper = null; Iterator<TypeBound> it = this.subBounds.iterator(); - long nullHints = 0; + long nullHints = variable.nullHints; int i = 0; while(it.hasNext()) { TypeBinding right=it.next().right; @@ -116,7 +116,7 @@ class BoundSet { return new TypeBinding[] { simpleUpper }; // no nullHints since not a reference type if (i < rights.length) System.arraycopy(rights, 0, rights=new ReferenceBinding[i], 0, i); - useNullHints(nullHints, rights, environment); + useNullHints(nullHints, rights, variable.environment); InferenceContext18.sortTypes(rights); return rights; } @@ -782,7 +782,7 @@ class BoundSet { ThreeSets three = this.boundsPerVariable.get(variable); if (three == null || three.subBounds == null) return Binding.NO_TYPES; - return three.upperBounds(onlyProper, variable.environment); + return three.upperBounds(onlyProper, variable); // TODO: if !onlyProper: should we also consider ThreeSets.inverseBounds, // or is it safe to rely on incorporation to produce the required bounds? } @@ -795,7 +795,7 @@ class BoundSet { ThreeSets three = this.boundsPerVariable.get(variable); if (three == null || three.superBounds == null) return Binding.NO_TYPES; - return three.lowerBounds(onlyProper, variable.environment); + return three.lowerBounds(onlyProper, variable); // bounds where 'variable' appears at the RHS are not relevant because // we're only interested in bounds with a proper type, but if 'variable' // appears as RHS the bound is by construction an inference variable,too. 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 0f345bdc75..babb0322d5 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 GK Software AG. + * Copyright (c) 2013, 2014 GK Software AG. * 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 @@ -34,8 +34,10 @@ public class InferenceSubstitution extends Scope.Substitutor implements Substitu public TypeBinding substitute(Substitution substitution, TypeBinding originalType) { for (int i = 0; i < this.variables.length; i++) { InferenceVariable variable = this.variables[i]; - if (TypeBinding.equalsEquals(variable.typeParameter, originalType)) + if (TypeBinding.equalsEquals(variable.typeParameter, originalType)) { + variable.nullHints |= originalType.tagBits & TagBits.AnnotationNullMASK; return variable; + } } return super.substitute(substitution, originalType); diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceVariable.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceVariable.java index 9d0ca68f7c..8c454434eb 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceVariable.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceVariable.java @@ -25,12 +25,14 @@ public class InferenceVariable extends TypeVariableBinding { InvocationSite site; TypeBinding typeParameter; + long nullHints; public InferenceVariable(TypeBinding typeParameter, int variableRank, InvocationSite site, LookupEnvironment environment, ReferenceBinding object) { super(CharOperation.concat(typeParameter.shortReadableName(), Integer.toString(variableRank).toCharArray(), '#'), null/*declaringElement*/, variableRank, environment); this.site = site; this.typeParameter = typeParameter; + this.nullHints |= typeParameter.tagBits & TagBits.AnnotationNullMASK; this.superclass = object; } |
