diff options
| author | Sasikanth Bharadwaj | 2016-10-13 12:35:35 +0000 |
|---|---|---|
| committer | Sasikanth Bharadwaj | 2016-10-15 07:37:52 +0000 |
| commit | 8f0eb26398d5c8dde9ae3b5affe939426eea53ca (patch) | |
| tree | 366df3a5275c178e873f98a39e9347fab9755cc2 | |
| parent | 626cae73ca62549fe78e25b477953af148a31148 (diff) | |
| download | eclipse.jdt.core-8f0eb26398d5c8dde9ae3b5affe939426eea53ca.tar.gz eclipse.jdt.core-8f0eb26398d5c8dde9ae3b5affe939426eea53ca.tar.xz eclipse.jdt.core-8f0eb26398d5c8dde9ae3b5affe939426eea53ca.zip | |
Bug 488663: [1.9] Allow diamond with anonymous classes
Change-Id: I9308db75bac88ea34a6c1e3ffbfc3a1c18038e92
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=488663
9 files changed, 610 insertions, 86 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java index 1b05ea7483..a40a484a0e 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java @@ -849,6 +849,7 @@ public void test011_problem_categories() { expectedProblemAttributes.put("NoSuperInInterfaceContext", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX)); expectedProblemAttributes.put("NonBlankFinalLocalAssignment", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); expectedProblemAttributes.put("NonConstantExpression", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); + expectedProblemAttributes.put("NonDenotableTypeArgumentForAnonymousDiamond", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("NonExternalizedStringLiteral", new ProblemAttributes(CategorizedProblem.CAT_NLS)); expectedProblemAttributes.put("NonGenericConstructor", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("NonGenericMethod", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); @@ -1702,6 +1703,7 @@ public void test012_compiler_problems_tuning() { expectedProblemAttributes.put("NoSuperInInterfaceContext", SKIP); expectedProblemAttributes.put("NonBlankFinalLocalAssignment", SKIP); expectedProblemAttributes.put("NonConstantExpression", SKIP); + expectedProblemAttributes.put("NonDenotableTypeArgumentForAnonymousDiamond", SKIP); expectedProblemAttributes.put("NonExternalizedStringLiteral", new ProblemAttributes(JavaCore.COMPILER_PB_NON_NLS_STRING_LITERAL)); expectedProblemAttributes.put("NonGenericConstructor", SKIP); expectedProblemAttributes.put("NonGenericMethod", SKIP); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_9.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_9.java new file mode 100644 index 0000000000..d3cdb89c5c --- /dev/null +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_9.java @@ -0,0 +1,461 @@ +/******************************************************************************* + * Copyright (c) 2016 IBM Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This is an implementation of an early-draft specification developed under the Java + * Community Process (JCP) and is made available for testing and evaluation purposes + * only. The code is not compatible with any specification of the JCP. + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.compiler.regression; + +import java.util.Map; + +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; + +import junit.framework.Test; + +@SuppressWarnings({ "rawtypes" }) +public class GenericsRegressionTest_9 extends AbstractRegressionTest { + +static { +// TESTS_NAMES = new String[] { "testBug488663_006" }; +// TESTS_NUMBERS = new int[] { 40, 41, 43, 45, 63, 64 }; +// TESTS_RANGE = new int[] { 11, -1 }; +} +public GenericsRegressionTest_9(String name) { + super(name); +} +public static Test suite() { + return buildMinimalComplianceTestSuite(testClass(), F_9); +} + +// vanilla test case +public void testBug488663_001() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " public Y<String> bar() {\n" + + " Y<String> y = new Y<>() {\n" + + " @Override\n" + + " public void foo(String s) {\n" + + " this.s = s;\n" + + " }\n" + + " };\n" + + " return y;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " Y<String> y = new X().bar();\n" + + " y.foo(\"Done\");\n" + + " y.print();\n" + + " }\n" + + "}\n" + + "abstract class Y<T> {\n" + + " String s;\n" + + " public abstract void foo(String s);\n" + + " public void print() {\n" + + " System.out.println(this.s);\n" + + " }\n" + + "}\n", + }, + "Done"); +} + +// negative test case for diamond operator instantiation of denotable anonymous type but with parameterized method +public void testBug488663_002() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " public Y<String> bar() {\n" + + " Y<String> y = new Y<>() {\n" + + " @Override\n" + + " public void foo(T t) {\n" + + " this.s = t;\n" + + " }\n" + + " };\n" + + " return y;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " Y<String> y = new X().bar();\n" + + " y.foo(\"Done\");\n" + + " y.print();\n" + + " }\n" + + "}\n" + + "abstract class Y<T> {\n" + + " T s;\n" + + " public abstract void foo(T t);\n" + + " public void print() {\n" + + " System.out.println(this.s);\n" + + " }\n" + + "}", + }, + "----------\n" + + "1. ERROR in X.java (at line 3)\n" + + " Y<String> y = new Y<>() {\n" + + " ^^^^^\n" + + "The type new Y<String>(){} must implement the inherited abstract method Y<String>.foo(String)\n" + + "----------\n" + + "2. ERROR in X.java (at line 5)\n" + + " public void foo(T t) {\n" + + " ^\n" + + "T cannot be resolved to a type\n" + + "----------\n"); +} + +// diamond operator instantiation of denotable anonymous types with different type params +public void testBug488663_003() { + this.runConformTest( + new String[] { + "X.java", + "public class X { \n" + + "@SuppressWarnings(\"unused\") \n" + + " public static void main(String[] args) {\n" + + " Y<?> y1 = new Y<>(){};\n" + + " Y<String> y2 = new Y<>(){};\n" + + " Y<? extends String> y3 = new Y<>() {};\n" + + " Y<? super String> y4 = new Y<>() {};\n" + + " }\n" + + "}\n" + + "class Y<T> {}\n", + }, + ""); +} + +// inner classes with diamond operator and anonymous classes +public void testBug488663_004() { + this.runConformTest( + new String[] { + "X.java", + "public class X { \n" + + "@SuppressWarnings(\"unused\") \n" + + " public static void main(String[] args) {\n" + + " Y<?> y1 = new X().new Y<>(){};\n" + + " Y<String> y2 = new X().new Y<>(){};\n" + + " Y<? extends String> y3 = new X().new Y<>() {};\n" + + " Y<? super String> y4 = new X().new Y<>() {};\n" + + " }\n" + + "\n" + + " class Y<T> {}\n" + + "}\n", + }, + ""); +} + +// compiler error for non-denotable anonymous type with diamond operator - negative test +public void testBug488663_005() { + this.runNegativeTest( + new String[] { + "X.java", + "interface I {}\n" + + "interface J{}\n" + + "class Y<T extends I & J> {}\n" + + "\n" + + "public class X {\n" + + " public static void main(String[] args) {\n" + + " Y<?> y = new Y<>() {};\n" + + " }\n" + + "}\n", + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " Y<?> y = new Y<>() {};\n" + + " ^\n" + + "Type Y<I & J> inferred for Y<>, is not valid for an anonymous class with '<>'\n" + + "----------\n"); + +} + +//compiler error for non-denotable anonymous type with diamond operator - negative test +public void testBug488663_006() { + this.runNegativeTest( + new String[] { + "X.java", + "class Y<T> {\n" + + " Y(T x) {}\n" + + "}\n" + + "\n" + + "class X {\n" + + " public static void main(String[] args) {\n" + + " Y<? extends Integer> fi = null;\n" + + " Y<?> f = new Y<>(fi){};\n" + + " }\n" + + "}\n", + }, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " Y<?> f = new Y<>(fi){};\n" + + " ^\n" + + "Type Y<Y<capture#1-of ? extends Integer>> inferred for Y<>, is not valid for an anonymous class with '<>'\n" + + "----------\n"); + +} +// instantiate an interface using the anonymous diamond +public void testBug488663_007() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String name;\n" + + " public X(String name) {\n" + + " this.name = name;\n" + + " }\n" + + " String name() {\n" + + " return this.name;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " X x = new X(\"Success\");\n" + + " I<X> i = new I<>() {\n" + + " public String toString(X x1) {\n" + + " return x1.name();\n" + + " }\n" + + " };\n" + + " System.out.println(i.toString(x));\n" + + " }\n" + + "}\n" + + "interface I<T> {\n" + + " String toString(T t);\n" + + "}" + }, + "Success"); +} +// anonymous diamond instantiating interface as argument to an invocation +public void testBug488663_008() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String name;\n" + + " public X(String name) {\n" + + " this.name = name;\n" + + " }\n" + + " <T> void print(T o, I<T> converter) {\n" + + " System.out.println(converter.toString(o));\n" + + " }\n" + + " String name() {\n" + + " return this.name;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " X x = new X(\"Success\");\n" + + " x.print(x, new I<>() {\n" + + " public String toString(X x1) {\n" + + " return x1.name();\n" + + " }\n" + + " });\n" + + " }\n" + + "}\n" + + "interface I<T> {\n" + + " String toString(T t);\n" + + "}" + }, + "Success"); +} +// anonymous diamond instantiating an abstract class as argument to an invocation +public void testBug488663_009() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String name;\n" + + " public X(String name) {\n" + + " this.name = name;\n" + + " }\n" + + " <T> void print(T o, I<T> converter) {\n" + + " System.out.println(converter.toString(o));\n" + + " }\n" + + " String name() {\n" + + " return this.name;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " X x = new X(\"Success\");\n" + + " x.print(x, new Z<>() {\n" + + " public String toString(X x1) {\n" + + " return x1.name();\n" + + " }\n" + + " });\n" + + " }\n" + + "}\n" + + "interface I<T> {\n" + + " String toString(T t);\n" + + "}\n" + + "abstract class Z<T> implements I<T> {}\n" + }, + "Success"); +} +// anonymous diamond with polytype argument +public void testBug488663_010() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String name;\n" + + " public X(String name) {\n" + + " this.name = name;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " Y<String> y = new Y<>(() -> System.out.println(\"Done\")) {\n" + + " };\n" + + " }\n" + + "}\n" + + "interface J {\n" + + " void doSomething();\n" + + "}\n" + + "class Y<T> {\n" + + " public Y(J j) {\n" + + " j.doSomething();\n" + + " }\n" + + "}", + }, + "Done"); +} +// anonymous diamond with polytype argument +public void testBug488663_011() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String name;\n" + + " public X(String name) {\n" + + " this.name = name;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " Y<String> y = new Y<>(Y::foo) {\n" + + " };\n" + + " }\n" + + "}\n" + + "interface J {\n" + + " void doSomething();\n" + + "}\n" + + "class Y<T> {\n" + + " public Y(J j) {\n" + + " j.doSomething();\n" + + " }\n" + + " static void foo() {\n" + + " System.out.println(\"Done\");\n" + + " }\n" + + "}", + }, + "Done"); +} +// Nested anonymous diamonds - TODO - confirm that this is indeed correct as per spec +public void testBug488663_012() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " String name;\n" + + " public X(String name) {\n" + + " this.name = name;\n" + + " }\n" + + " String name() {\n" + + " return this.name;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " Y<String> y = new Y<>(\"Done\", new I<>() {\n" + + " public void doSomething(String s) {\n" + + " System.out.println(s);\n" + + " }\n" + + " }){\n" + + " };\n" + + " }\n" + + "}\n" + + "interface I<T> {\n" + + " void doSomething(T t);\n" + + "}\n" + + "class Y<T> {\n" + + " public Y(T t, I<T> i) {\n" + + " i.doSomething(t);\n" + + " }\n" + + "}", + }, + "Done"); +} +// Redundant type argument specification - TODO - confirm that this is correct +public void testBug488663_013() { + Map<String, String> options = getCompilerOptions(); + options.put(CompilerOptions.OPTION_ReportRedundantSpecificationOfTypeArguments, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " String name;\n" + + " public X(String name) {\n" + + " this.name = name;\n" + + " }\n" + + " String name() {\n" + + " return this.name;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " X x = new X(\"Success\");\n" + + " I<X> i = new I<X>() {\n" + + " public String toString(X x1) {\n" + + " return x1.name();\n" + + " }\n" + + " };\n" + + " System.out.println(i.toString(x));\n" + + " }\n" + + "}\n" + + "interface I<T> {\n" + + " String toString(T t);\n" + + "}" + }, + "----------\n" + + "1. ERROR in X.java (at line 11)\n" + + " I<X> i = new I<X>() {\n" + + " ^\n" + + "Redundant specification of type arguments <X>\n" + + "----------\n", + null, true, options); +} +// All non-private methods of an anonymous class instantiated with '<>' must be treated as being annotated with @override +public void testBug488663_014() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " String name;\n" + + " public X(String name) {\n" + + " this.name = name;\n" + + " }\n" + + " <T> void print(T o, I<T> converter) {\n" + + " System.out.println(converter.toString(o));\n" + + " }\n" + + " String name() {\n" + + " return this.name;\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " X x = new X(\"asdasfd\");\n" + + " x.print(x, new Z<>() {\n" + + " public String toString(String s) {\n" + + " return s;\n" + + " }\n" + + " });\n" + + " }\n" + + "}\n" + + "interface I<T> {\n" + + " String toString(T t);\n" + + "}\n" + + "class Z<T> implements I<T> {\n" + + " public String toString(T t) {\n" + + " return \"\";\n" + + " }\n" + + "}", + }, + "----------\n" + + "1. ERROR in X.java (at line 15)\n" + + " public String toString(String s) {\n" + + " ^^^^^^^^^^^^^^^^^^\n" + + "The method toString(String) of type new Z<X>(){} must override or implement a supertype method\n" + + "----------\n"); +} +public static Class testClass() { + return GenericsRegressionTest_9.class; +} + +} diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java index 54a34af1e2..86f65b61eb 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java @@ -1904,6 +1904,8 @@ void setSourceStart(int sourceStart); int ServiceImplNotDefinedByModule = TypeRelated + 1210; /** @since 3.13 BETA_JAVA9 */ int ExportedPackageDoesNotExistOrIsEmpty = TypeRelated + 1211; + /** @since 3.13 BETA_JAVA9 */ + int NonDenotableTypeArgumentForAnonymousDiamond = TypeRelated + 1212; /** @since 3.13 BETA_JAVA9 */ int DuplicateResource = Internal + 1251; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java index ca43ccf73e..8915ff88d0 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java @@ -594,15 +594,21 @@ public static MethodBinding inferDiamondConstructor(Scope scope, InvocationSite if (constructorTypeArguments.length > 0) System.arraycopy(((ParameterizedGenericMethodBinding)factory).typeArguments, sfmb.typeVariables().length - constructorTypeArguments.length , constructorTypeArguments, 0, constructorTypeArguments.length); + if (allocationType.isInterface()) { + ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding) factory.returnType; + return new ParameterizedMethodBinding(parameterizedType, sfmb.getConstructor()); + } return sfmb.applyTypeArgumentsOnConstructor(((ParameterizedTypeBinding)factory.returnType).arguments, constructorTypeArguments, genericFactory.inferredWithUncheckedConversion); } return null; } - public TypeBinding[] inferElidedTypes(final Scope scope) { + return inferElidedTypes((ParameterizedTypeBinding) this.resolvedType, scope); +} +public TypeBinding[] inferElidedTypes(ParameterizedTypeBinding parameterizedType, final Scope scope) { - ReferenceBinding genericType = ((ParameterizedTypeBinding) this.resolvedType).genericType(); - ReferenceBinding enclosingType = this.resolvedType.enclosingType(); + ReferenceBinding genericType = parameterizedType.genericType(); + ReferenceBinding enclosingType = parameterizedType.enclosingType(); ParameterizedTypeBinding allocationType = scope.environment().createParameterizedType(genericType, genericType.typeVariables(), enclosingType); /* Given the allocation type and the arguments to the constructor, see if we can synthesize a generic static factory @@ -646,7 +652,7 @@ public void checkTypeArgumentRedundancy(ParameterizedTypeBinding allocationType, // checking for redundant type parameters must fake a diamond, // so we infer the same results as we would get with a diamond in source code: this.type.bits |= IsDiamond; - inferredTypes = inferElidedTypes(scope); + inferredTypes = inferElidedTypes(allocationType, scope); } finally { // reset effects of inference this.type.bits = previousBits; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java index 9afc96ed3c..e2cdfd35dc 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java @@ -36,6 +36,8 @@ package org.eclipse.jdt.internal.compiler.ast; import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.INVOCATION_CONTEXT; +import java.util.Arrays; + import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; @@ -48,6 +50,7 @@ import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.ImplicitNullAnnotationVerifier; +import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18; import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding; @@ -58,8 +61,10 @@ import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBindingVisitor; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; @@ -482,8 +487,10 @@ public class QualifiedAllocationExpression extends AllocationExpression { } } else { if (isDiamond) { - scope.problemReporter().diamondNotWithAnoymousClasses(this.type); - return null; + if (sourceLevel < ClassFileConstants.JDK9) { + scope.problemReporter().diamondNotWithAnoymousClasses(this.type); + return null; + } } ReferenceBinding superType = (ReferenceBinding) receiverType; if (superType.isTypeVariable()) { @@ -494,56 +501,7 @@ public class QualifiedAllocationExpression extends AllocationExpression { scope.problemReporter().cannotInstantiate(this.type, superType); return this.resolvedType = superType; } - // anonymous type scenario - // an anonymous class inherits from java.lang.Object when declared "after" an interface - ReferenceBinding anonymousSuperclass = superType.isInterface() ? scope.getJavaLangObject() : superType; - // insert anonymous type in scope - scope.addAnonymousType(this.anonymousType, superType); - this.anonymousType.resolve(scope); - - // find anonymous super constructor - this.resolvedType = this.anonymousType.binding; // 1.2 change - if ((this.resolvedType.tagBits & TagBits.HierarchyHasProblems) != 0) { - return null; // stop secondary errors - } - MethodBinding inheritedBinding = findConstructorBinding(scope, this, anonymousSuperclass, this.argumentTypes); - - if (!inheritedBinding.isValidBinding()) { - if (inheritedBinding.declaringClass == null) { - inheritedBinding.declaringClass = anonymousSuperclass; - } - if (this.type != null && !this.type.resolvedType.isValidBinding()) { - // problem already got signaled on type reference, do not report secondary problem - return null; - } - scope.problemReporter().invalidConstructor(this, inheritedBinding); - return this.resolvedType; - } - if ((inheritedBinding.tagBits & TagBits.HasMissingType) != 0) { - scope.problemReporter().missingTypeInConstructor(this, inheritedBinding); - } - if (this.enclosingInstance != null) { - ReferenceBinding targetEnclosing = inheritedBinding.declaringClass.enclosingType(); - if (targetEnclosing == null) { - scope.problemReporter().unnecessaryEnclosingInstanceSpecification(this.enclosingInstance, superType); - return this.resolvedType; - } else if (!enclosingInstanceType.isCompatibleWith(targetEnclosing) && !scope.isBoxingCompatibleWith(enclosingInstanceType, targetEnclosing)) { - scope.problemReporter().typeMismatchError(enclosingInstanceType, targetEnclosing, this.enclosingInstance, null); - return this.resolvedType; - } - this.enclosingInstance.computeConversion(scope, targetEnclosing, enclosingInstanceType); - } - if (this.arguments != null) { - if (checkInvocationArguments(scope, null, anonymousSuperclass, inheritedBinding, this.arguments, this.argumentTypes, this.argsContainCast, this)) { - this.bits |= ASTNode.Unchecked; - } - } - if (this.typeArguments != null && inheritedBinding.original().typeVariables == Binding.NO_TYPE_VARIABLES) { - scope.problemReporter().unnecessaryTypeArgumentsForMethodInvocation(inheritedBinding, this.genericTypeArguments, this.typeArguments); - } - // Update the anonymous inner class : superclass, interface - this.binding = this.anonymousType.createDefaultConstructorWithBinding(inheritedBinding, (this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null); - return this.resolvedType; + this.resolvedType = receiverType; } } else { if (this.enclosingInstance != null) { @@ -551,8 +509,9 @@ public class QualifiedAllocationExpression extends AllocationExpression { this.resolvedType = receiverType = this.type.resolvedType; } } + MethodBinding constructorBinding = null; if (isDiamond) { - this.binding = inferConstructorOfElidedParameterizedType(scope); + this.binding = constructorBinding = inferConstructorOfElidedParameterizedType(scope); if (this.binding == null || !this.binding.isValidBinding()) { scope.problemReporter().cannotInferElidedTypes(this); return this.resolvedType = null; @@ -561,50 +520,122 @@ public class QualifiedAllocationExpression extends AllocationExpression { return new PolyTypeBinding(this); } this.resolvedType = this.type.resolvedType = receiverType = this.binding.declaringClass; + if (this.anonymousType != null) { + constructorBinding = getAnonymousConstructorBinding((ReferenceBinding) receiverType, scope); + if (constructorBinding == null) + return null; + this.resolvedType = this.anonymousType.binding; + // Check that inferred type is denotable + if (!checkTypeArgumentValidity((ParameterizedTypeBinding) receiverType, scope)) { + scope.problemReporter().anonymousDiamondWithNonDenotableTypeArguments(this.type, receiverType); + return this.resolvedType; + } + } resolvePolyExpressionArguments(this, this.binding, this.argumentTypes, scope); } else { - this.binding = findConstructorBinding(scope, this, (ReferenceBinding) receiverType, this.argumentTypes); + if (this.anonymousType != null) { + constructorBinding = getAnonymousConstructorBinding((ReferenceBinding) receiverType, scope); + if (constructorBinding == null) + return null; + this.resolvedType = this.anonymousType.binding; + } else { + this.binding = constructorBinding = findConstructorBinding(scope, this, (ReferenceBinding) receiverType, this.argumentTypes); + } } - - if (this.binding.isValidBinding()) { - if (isMethodUseDeprecated(this.binding, scope, true)) { - scope.problemReporter().deprecatedMethod(this.binding, this); + ReferenceBinding receiver = (ReferenceBinding) receiverType; + ReferenceBinding superType = receiver.isInterface() ? scope.getJavaLangObject() : receiver; + if (constructorBinding.isValidBinding()) { + if (isMethodUseDeprecated(constructorBinding, scope, true)) { + scope.problemReporter().deprecatedMethod(constructorBinding, this); } - if (checkInvocationArguments(scope, null, receiverType, this.binding, this.arguments, this.argumentTypes, this.argsContainCast, this)) { + if (checkInvocationArguments(scope, null, superType, constructorBinding, this.arguments, + this.argumentTypes, this.argsContainCast, this)) { this.bits |= ASTNode.Unchecked; } - if (this.typeArguments != null && this.binding.original().typeVariables == Binding.NO_TYPE_VARIABLES) { - scope.problemReporter().unnecessaryTypeArgumentsForMethodInvocation(this.binding, this.genericTypeArguments, this.typeArguments); + if (this.typeArguments != null && constructorBinding.original().typeVariables == Binding.NO_TYPE_VARIABLES) { + scope.problemReporter().unnecessaryTypeArgumentsForMethodInvocation(constructorBinding, + this.genericTypeArguments, this.typeArguments); } } else { - if (this.binding.declaringClass == null) { - this.binding.declaringClass = (ReferenceBinding) receiverType; + if (constructorBinding.declaringClass == null) { + constructorBinding.declaringClass = superType; } if (this.type != null && !this.type.resolvedType.isValidBinding()) { // problem already got signaled on type reference, do not report secondary problem return null; } - scope.problemReporter().invalidConstructor(this, this.binding); - return this.resolvedType = receiverType; + scope.problemReporter().invalidConstructor(this, constructorBinding); + return this.resolvedType; + } + if ((constructorBinding.tagBits & TagBits.HasMissingType) != 0) { + scope.problemReporter().missingTypeInConstructor(this, constructorBinding); } - if ((this.binding.tagBits & TagBits.HasMissingType) != 0) { - scope.problemReporter().missingTypeInConstructor(this, this.binding); + if (this.enclosingInstance != null) { + ReferenceBinding targetEnclosing = constructorBinding.declaringClass.enclosingType(); + if (targetEnclosing == null) { + scope.problemReporter().unnecessaryEnclosingInstanceSpecification(this.enclosingInstance, receiver); + return this.resolvedType; + } else if (!enclosingInstanceType.isCompatibleWith(targetEnclosing) && !scope.isBoxingCompatibleWith(enclosingInstanceType, targetEnclosing)) { + scope.problemReporter().typeMismatchError(enclosingInstanceType, targetEnclosing, this.enclosingInstance, null); + return this.resolvedType; + } + this.enclosingInstance.computeConversion(scope, targetEnclosing, enclosingInstanceType); } if (!isDiamond && receiverType.isParameterizedTypeWithActualArguments()) { - checkTypeArgumentRedundancy((ParameterizedTypeBinding)receiverType, scope); + checkTypeArgumentRedundancy((ParameterizedTypeBinding) receiverType, scope); } - // The enclosing instance must be compatible with the innermost enclosing type - ReferenceBinding expectedType = this.binding.declaringClass.enclosingType(); - if (TypeBinding.notEquals(expectedType, enclosingInstanceType)) // must call before computeConversion() and typeMismatchError() - scope.compilationUnitScope().recordTypeConversion(expectedType, enclosingInstanceType); - if (enclosingInstanceType.isCompatibleWith(expectedType) || scope.isBoxingCompatibleWith(enclosingInstanceType, expectedType)) { - this.enclosingInstance.computeConversion(scope, expectedType, enclosingInstanceType); + if (this.anonymousType != null) { + // anonymous type scenario + // Update the anonymous inner class : superclass, interface + + this.binding = this.anonymousType.createDefaultConstructorWithBinding(constructorBinding, (this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null); + return this.resolvedType; + } else { return this.resolvedType = receiverType; } - scope.problemReporter().typeMismatchError(enclosingInstanceType, expectedType, this.enclosingInstance, null); - return this.resolvedType = receiverType; } + private boolean checkTypeArgumentValidity(final ParameterizedTypeBinding allocationType, final Scope scope) { + class ValidityInspector extends TypeBindingVisitor { + private boolean noErrors; + + public ValidityInspector() { + this.noErrors = true; + } + + public boolean visit(IntersectionTypeBinding18 intersectionTypeBinding18) { + Arrays.sort(intersectionTypeBinding18.intersectingTypes, (t1, t2) -> t1.id - t2.id); + return this.noErrors = false; // stop traversal + } + public boolean visit(TypeVariableBinding typeVariable) { + if (typeVariable.isCapture()) { + return this.noErrors = false; // stop traversal + } + return true; // continue traversal + } + public boolean isValid() { + TypeBindingVisitor.visit(this, allocationType); + return this.noErrors; + } + } + + return new ValidityInspector().isValid(); + } + private MethodBinding getAnonymousConstructorBinding(ReferenceBinding receiverType, BlockScope scope) { + ReferenceBinding superType = receiverType; + // an anonymous class inherits from java.lang.Object when declared "after" an interface + ReferenceBinding anonymousSuperclass = superType.isInterface() ? scope.getJavaLangObject() : superType; + // insert anonymous type in scope + scope.addAnonymousType(this.anonymousType, superType); + this.anonymousType.resolve(scope); + + // find anonymous super constructor + this.resolvedType = this.anonymousType.binding; // 1.2 change + if ((this.resolvedType.tagBits & TagBits.HierarchyHasProblems) != 0) { + return null; // stop secondary errors + } + return findConstructorBinding(scope, this, anonymousSuperclass, this.argumentTypes); + } public void traverse(ASTVisitor visitor, BlockScope scope) { if (visitor.visit(this, scope)) { if (this.enclosingInstance != null) diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java index 8ea7a19e58..6d5e816a54 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java @@ -179,12 +179,11 @@ private void checkAndSetModifiersForMethod(MethodBinding methodBinding) { // after this point, tests on the 16 bits reserved. int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; - + long sourceLevel = compilerOptions().sourceLevel; // set the requested modifiers for a method in an interface/annotation if (declaringClass.isInterface()) { int expectedModifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract; boolean isDefaultMethod = (modifiers & ExtraCompilerModifiers.AccDefaultMethod) != 0; // no need to check validity, is done by the parser - long sourceLevel = compilerOptions().sourceLevel; if (sourceLevel >= ClassFileConstants.JDK1_8 && !declaringClass.isAnnotationType()) { expectedModifiers |= ClassFileConstants.AccStrictfp | ExtraCompilerModifiers.AccDefaultMethod | ClassFileConstants.AccStatic; @@ -219,6 +218,18 @@ private void checkAndSetModifiersForMethod(MethodBinding methodBinding) { methodBinding.modifiers &= (expectedModifiers | ~ExtraCompilerModifiers.AccJustFlag); } return; + } else if (declaringClass.isAnonymousType() && sourceLevel >= ClassFileConstants.JDK9) { + // If the class instance creation expression elides the supertype's type arguments using '<>', + // then for all non-private methods declared in the class body, it is as if the method declaration + // is annotated with @Override - https://bugs.openjdk.java.net/browse/JDK-8073593 + LocalTypeBinding local = (LocalTypeBinding) declaringClass; + TypeReference ref = local.scope.referenceContext.allocation.type; + if (ref != null && (ref.bits & ASTNode.IsDiamond) != 0) { + // + if ((realModifiers & (ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic )) == 0) { + methodBinding.tagBits |= TagBits.AnnotationOverride; + } + } } // check for abnormal modifiers 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 49afea1ae1..c006650371 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 @@ -4910,8 +4910,10 @@ public abstract class Scope { break; currentType = currentType.enclosingType(); } + boolean isInterface = allocationType.isInterface(); + ReferenceBinding typeToSearch = isInterface ? getJavaLangObject() : allocationType; - MethodBinding[] methods = allocationType.getMethods(TypeConstants.INIT, argumentTypes.length); + MethodBinding[] methods = typeToSearch.getMethods(TypeConstants.INIT, argumentTypes.length); MethodBinding [] staticFactories = new MethodBinding[methods.length]; int sfi = 0; for (int i = 0, length = methods.length; i < length; i++) { @@ -4929,8 +4931,8 @@ public abstract class Scope { int methodTypeVariablesArity = methodTypeVariables.length; final int factoryArity = classTypeVariablesArity + methodTypeVariablesArity; final LookupEnvironment environment = environment(); - - MethodBinding staticFactory = new SyntheticFactoryMethodBinding(method.original(), environment, originalEnclosingType); + MethodBinding targetMethod = isInterface ? new MethodBinding(method.original(), genericType) : method.original(); + MethodBinding staticFactory = new SyntheticFactoryMethodBinding(targetMethod, environment, originalEnclosingType); staticFactory.typeVariables = new TypeVariableBinding[factoryArity]; final SimpleLookupTable map = new SimpleLookupTable(factoryArity); @@ -5007,7 +5009,7 @@ public abstract class Scope { if (staticFactory.thrownExceptions == null) { staticFactory.thrownExceptions = Binding.NO_EXCEPTIONS; } - staticFactories[sfi++] = new ParameterizedMethodBinding((ParameterizedTypeBinding) environment.convertToParameterizedType(staticFactory.declaringClass), + staticFactories[sfi++] = new ParameterizedMethodBinding((ParameterizedTypeBinding) environment.convertToParameterizedType(isInterface ? allocationType : staticFactory.declaringClass), staticFactory); } if (sfi == 0) diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index e0c72e4ed2..3502321aa2 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -9283,6 +9283,14 @@ public void diamondNotWithAnoymousClasses(TypeReference type) { type.sourceStart, type.sourceEnd); } +public void anonymousDiamondWithNonDenotableTypeArguments(TypeReference type, TypeBinding tb) { + this.handle( + IProblem.NonDenotableTypeArgumentForAnonymousDiamond, + new String[]{new String(tb.leafComponentType().shortReadableName()), type.toString()}, + new String[]{new String(tb.leafComponentType().shortReadableName()), type.toString()}, + type.sourceStart, + type.sourceEnd); +} public void redundantSpecificationOfTypeArguments(ASTNode location, TypeBinding[] argumentTypes) { int severity = computeSeverity(IProblem.RedundantSpecificationOfTypeArguments); if (severity != ProblemSeverities.Ignore) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties index 5dcd376fa6..da9d141861 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties @@ -884,6 +884,7 @@ 1209 = Invalid service implementation, the type {0} is an inner class 1210 = Service implementation {0} is not defined in the module with the provides directive 1211 = The exported package {0} does not exist or is empty +1212 = Type {0} inferred for {1}, is not valid for an anonymous class with '<>' ### Autoclosable try 1251 = Duplicate resource reference {0} |
