diff options
4 files changed, 221 insertions, 35 deletions
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 3641963001..f3e1a1f498 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 @@ -25,6 +25,7 @@ * Bug 431408 - Java 8 (1.8) generics bug * Bug 432603 - [compile][1.7] ecj reports an Error while javac doesn't * Bug 399527 - Type inference problem + * Bug 434570 - Generic type mismatch for parametrized class annotation attribute with inner class *******************************************************************************/ package org.eclipse.jdt.core.tests.compiler.regression; @@ -4952,5 +4953,132 @@ public void testBug399527_comment1() { sourceString }); } +public void testBug434570() { + runConformTest( + new String[] { + "example/Example.java", + "package example;\n" + + "\n" + + "import example.Example.Config;\n" + + "import example.Example.CustomInitializer;\n" + + "\n" + + "@Config(initializers = CustomInitializer.class)\n" + + "public class Example {\n" + + "\n" + + " static interface Context {\n" + + " }\n" + + "\n" + + " static interface ConfigurableContext extends Context {\n" + + " }\n" + + "\n" + + " static abstract class AbstractContext implements ConfigurableContext {\n" + + " }\n" + + "\n" + + " static class GenericContext extends AbstractContext {\n" + + " }\n" + + "\n" + + " static interface Initializer<C extends ConfigurableContext> {\n" + + " }\n" + + "\n" + + " static @interface Config {\n" + + " Class<? extends Initializer<? extends ConfigurableContext>>[] initializers() default {};\n" + + " }\n" + + "\n" + + " static class CustomInitializer implements Initializer<GenericContext> {\n" + + " }\n" + + "\n" + + " @Config(initializers = CustomInitializer.class)\n" + + " static class CompilationSuccess {\n" + + " }\n" + + "\n" + + "}\n" + }); +} +public void testBug434630() { + runConformTest( + new String[] { + "Foo.java", + "interface Provider<T> {}\n" + + "@interface ProvidedBy {\n" + + " Class<? extends Provider<?>> value();" + + "}\n" + + "\n" + + "@ProvidedBy(Foo.SomeProvider.class)\n" + + "public interface Foo {\n" + + " \n" + + " public static class SomeProvider implements Provider<Foo> {\n" + + "\n" + + " public Foo get() {\n" + + " return null;\n" + + " }\n" + + " \n" + + " }\n" + + "}\n" + }); +} +public void _testBug434570_comment3() { + runConformTest( + new String[] { + "TestWontCompile.java", + "import org.bug.AnnotationWithClassParameter;\n" + + "import org.bug.CustomHandler;\n" + + "import org.bug.Handler;\n" + + "\n" + + "\n" + + "@AnnotationWithClassParameter(CustomHandler.class)\n" + + "public class TestWontCompile extends ATest<Object> {\n" + + " \n" + + " public static void main(String[] args) {\n" + + " Class<? extends Handler<?>> h = CustomHandler.class;\n" + + " }\n" + + "\n" + + "}\n", + "ATest.java", + "public abstract class ATest<T> {\n" + + "\n" + + "}\n", + "org/bug/Item.java", + "package org.bug;\n" + + "\n" + + "public interface Item {\n" + + "\n" + + "}\n", + "org/bug/CustomItem.java", + "package org.bug;\n" + + "\n" + + "public class CustomItem implements Item {\n" + + "\n" + + "}\n", + "org/bug/Handler.java", + "package org.bug;\n" + + "\n" + + "public abstract class Handler<T extends Item> {\n" + + "\n" + + "}\n", + "org/bug/CustomHandler.java", + "package org.bug;\n" + + "\n" + + "public class CustomHandler extends Handler<CustomItem> {\n" + + "\n" + + "}\n", + "org/bug/AnnotationWithClassParameter.java", + "package org.bug;\n" + + "\n" + + "import java.lang.annotation.Documented;\n" + + "import java.lang.annotation.ElementType;\n" + + "import java.lang.annotation.Retention;\n" + + "import java.lang.annotation.RetentionPolicy;\n" + + "import java.lang.annotation.Target;\n" + + "\n" + + "@Target(ElementType.TYPE)\n" + + "@Retention(RetentionPolicy.RUNTIME)\n" + + "@Documented\n" + + "public @interface AnnotationWithClassParameter {\n" + + " \n" + + " Class<? extends Handler<?>> value();\n" + + "\n" + + "}\n" + }); +} } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java index 103812bf42..81ca5c886b 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2013 IBM Corporation and others. + * Copyright (c) 2000, 2014 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 @@ -10,6 +10,7 @@ * Stephan Herrmann - Contributions for * bug 186342 - [compiler][null] Using annotations for null checking * bug 365519 - editorial cleanup after bug 186342 and bug 365387 + * Bug 434570 - Generic type mismatch for parametrized class annotation attribute with inner class *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -58,7 +59,7 @@ public class MemberValuePair extends ASTNode { return output; } - public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { + public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requiredType) { if (this.value == null) { this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding); @@ -76,7 +77,7 @@ public class MemberValuePair extends ASTNode { } this.value.setExpectedType(requiredType); // needed in case of generic method invocation - looks suspect, generic method invocation here ??? - TypeBinding valueType; + final TypeBinding valueType; if (this.value instanceof ArrayInitializer) { ArrayInitializer initializer = (ArrayInitializer) this.value; valueType = initializer.resolveTypeExpecting(scope, this.binding.returnType); @@ -101,25 +102,38 @@ public class MemberValuePair extends ASTNode { if (valueType == null) return; - TypeBinding leafType = requiredType.leafComponentType(); - if (!(this.value.isConstantValueOfTypeAssignableToType(valueType, requiredType) - || valueType.isCompatibleWith(requiredType))) { - - if (!(requiredType.isArrayType() - && requiredType.dimensions() == 1 - && (this.value.isConstantValueOfTypeAssignableToType(valueType, leafType) - || valueType.isCompatibleWith(leafType)))) { - - if (leafType.isAnnotationType() && !valueType.isAnnotationType()) { - scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType); + final TypeBinding leafType = requiredType.leafComponentType(); + // the next check may need deferring: + final boolean[] shouldExit = new boolean[1]; + Runnable check = new Runnable() { + @Override + public void run() { + if (!(MemberValuePair.this.value.isConstantValueOfTypeAssignableToType(valueType, requiredType) + || valueType.isCompatibleWith(requiredType))) { + if (!(requiredType.isArrayType() + && requiredType.dimensions() == 1 + && (MemberValuePair.this.value.isConstantValueOfTypeAssignableToType(valueType, leafType) + || valueType.isCompatibleWith(leafType)))) { + + if (leafType.isAnnotationType() && !valueType.isAnnotationType()) { + scope.problemReporter().annotationValueMustBeAnnotation(MemberValuePair.this.binding.declaringClass, + MemberValuePair.this.name, MemberValuePair.this.value, leafType); + } else { + scope.problemReporter().typeMismatchError(valueType, requiredType, MemberValuePair.this.value, null); + } + shouldExit[0] = true; // TODO may allow to proceed to find more errors at once + } } else { - scope.problemReporter().typeMismatchError(valueType, requiredType, this.value, null); + scope.compilationUnitScope().recordTypeConversion(requiredType.leafComponentType(), valueType.leafComponentType()); + MemberValuePair.this.value.computeConversion(scope, requiredType, valueType); } - return; // may allow to proceed to find more errors at once } - } else { - scope.compilationUnitScope().recordTypeConversion(requiredType.leafComponentType(), valueType.leafComponentType()); - this.value.computeConversion(scope, requiredType, valueType); + }; + // ... now or later? + if (!scope.deferCheck(check)) { + check.run(); + if (shouldExit[0]) + return; } // annotation methods can only return base types, String, Class, enum type, annotation types and arrays of these diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java index 7e4176aed4..5d45063a4d 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2013 IBM Corporation and others. + * Copyright (c) 2000, 2014 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 @@ -18,11 +18,13 @@ * Bug 416176 - [1.8][compiler][null] null type annotations cause grief on type variables * Bug 427199 - [1.8][resource] avoid resource leak warnings on Streams that have no resource * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault + * Bug 434570 - Generic type mismatch for parametrized class annotation attribute with inner class * Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for * Bug 415821 - [1.8][compiler] CLASS_EXTENDS target type annotation missing for anonymous classes *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -47,7 +49,8 @@ public class ClassScope extends Scope { public TypeDeclaration referenceContext; public TypeReference superTypeReference; - java.util.ArrayList deferredBoundChecks; + java.util.ArrayList<Object> deferredBoundChecks; // contains TypeReference or Runnable. TODO consider making this a List<Runnable> + boolean connectingHierarchy; public ClassScope(Scope parent, TypeDeclaration context) { super(Scope.CLASS_SCOPE, parent); @@ -881,8 +884,11 @@ public class ClassScope extends Scope { // Perform deferred bound checks for parameterized type references (only done after hierarchy is connected) public void checkParameterizedTypeBounds() { - for (int i = 0, l = this.deferredBoundChecks == null ? 0 : this.deferredBoundChecks.size(); i < l; i++) - ((TypeReference) this.deferredBoundChecks.get(i)).checkBounds(this); + for (int i = 0, l = this.deferredBoundChecks == null ? 0 : this.deferredBoundChecks.size(); i < l; i++) { + Object toCheck = this.deferredBoundChecks.get(i); + if (toCheck instanceof TypeReference) + ((TypeReference) toCheck).checkBounds(this); + } this.deferredBoundChecks = null; ReferenceBinding[] memberTypes = this.referenceContext.binding.memberTypes; @@ -1082,19 +1088,25 @@ public class ClassScope extends Scope { void connectTypeHierarchy() { SourceTypeBinding sourceType = this.referenceContext.binding; - if ((sourceType.tagBits & TagBits.BeginHierarchyCheck) == 0) { - sourceType.tagBits |= TagBits.BeginHierarchyCheck; - environment().typesBeingConnected.add(sourceType); - boolean noProblems = connectSuperclass(); - noProblems &= connectSuperInterfaces(); - environment().typesBeingConnected.remove(sourceType); - sourceType.tagBits |= TagBits.EndHierarchyCheck; - noProblems &= connectTypeVariables(this.referenceContext.typeParameters, false); - sourceType.tagBits |= TagBits.TypeVariablesAreConnected; - if (noProblems && sourceType.isHierarchyInconsistent()) - problemReporter().hierarchyHasProblems(sourceType); + try { + if ((sourceType.tagBits & TagBits.BeginHierarchyCheck) == 0) { + this.connectingHierarchy = true; + sourceType.tagBits |= TagBits.BeginHierarchyCheck; + environment().typesBeingConnected.add(sourceType); + boolean noProblems = connectSuperclass(); + noProblems &= connectSuperInterfaces(); + environment().typesBeingConnected.remove(sourceType); + sourceType.tagBits |= TagBits.EndHierarchyCheck; + noProblems &= connectTypeVariables(this.referenceContext.typeParameters, false); + sourceType.tagBits |= TagBits.TypeVariablesAreConnected; + if (noProblems && sourceType.isHierarchyInconsistent()) + problemReporter().hierarchyHasProblems(sourceType); + } + connectMemberTypes(); + } finally { + this.connectingHierarchy = false; + deferredMemberValueCheck(); } - connectMemberTypes(); LookupEnvironment env = environment(); try { env.missingClassFileLocation = this.referenceContext; @@ -1107,6 +1119,31 @@ public class ClassScope extends Scope { } } + @Override + public boolean deferCheck(Runnable check) { + if (this.connectingHierarchy) { + if (this.deferredBoundChecks == null) + this.deferredBoundChecks = new ArrayList<Object>(); + this.deferredBoundChecks.add(check); + return true; + } else { + return super.deferCheck(check); + } + } + + private void deferredMemberValueCheck() { + if (this.deferredBoundChecks != null) { + Iterator iterator = this.deferredBoundChecks.iterator(); + while (iterator.hasNext()) { + Object check = iterator.next(); + if (check instanceof Runnable) { + ((Runnable)check).run(); + iterator.remove(); + } + } + } + } + private void connectTypeHierarchyWithoutMembers() { // must ensure the imports are resolved if (this.parent instanceof CompilationUnitScope) { 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 1e519e37d1..01b9022145 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 @@ -42,6 +42,7 @@ * Bug 428811 - [1.8][compiler] Type witness unnecessarily required * Bug 429424 - [1.8][inference] Problem inferring type of method's parameter * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault + * Bug 434570 - Generic type mismatch for parametrized class annotation attribute with inner class * 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 @@ -4889,6 +4890,12 @@ public abstract class Scope { return null; } + public boolean deferCheck(Runnable check) { + if (this.parent != null) + return this.parent.deferCheck(check); // only ClassScope potentially records this + return false; + } + public void deferBoundCheck(TypeReference typeRef) { if (this.kind == CLASS_SCOPE) { ClassScope classScope = (ClassScope) this; |