/* * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 7030150 7039931 * @summary Type inference for generic instance creation failed for formal type parameter * @modules jdk.compiler */ import com.sun.source.util.JavacTask; import java.net.URI; import java.util.Arrays; import javax.tools.Diagnostic; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class GenericConstructorAndDiamondTest { enum BoundKind { NO_BOUND(""), STRING_BOUND("extends String"), INTEGER_BOUND("extends Integer"); String boundStr; private BoundKind(String boundStr) { this.boundStr = boundStr; } boolean matches(TypeArgumentKind tak) { switch (tak) { case NONE: return true; case STRING: return this != INTEGER_BOUND; case INTEGER: return this != STRING_BOUND; default: return false; } } } enum ConstructorKind { NON_GENERIC("Foo(Object o) {}"), GENERIC_NO_BOUND("<T> Foo(T t) {}"), GENERIC_STRING_BOUND("<T extends String> Foo(T t) {}"), GENERIC_INTEGER_BOUND("<T extends Integer> Foo(T t) {}"); String constrStr; private ConstructorKind(String constrStr) { this.constrStr = constrStr; } boolean matches(ArgumentKind ak) { switch (ak) { case STRING: return this != GENERIC_INTEGER_BOUND; case INTEGER: return this != GENERIC_STRING_BOUND; default: return false; } } } enum TypeArgArity { ONE(1), TWO(2), THREE(3); int n; private TypeArgArity(int n) { this.n = n; } } enum TypeArgumentKind { NONE(""), STRING("String"), INTEGER("Integer"); String typeargStr; private TypeArgumentKind(String typeargStr) { this.typeargStr = typeargStr; } String getArgs(TypeArgArity arity) { if (this == NONE) return ""; else { StringBuilder buf = new StringBuilder(); String sep = ""; for (int i = 0 ; i < arity.n ; i++) { buf.append(sep); buf.append(typeargStr); sep = ","; } return "<" + buf.toString() + ">"; } } boolean matches(ArgumentKind ak) { switch (ak) { case STRING: return this != INTEGER; case INTEGER: return this != STRING; default: return false; } } boolean matches(TypeArgumentKind other) { switch (other) { case STRING: return this != INTEGER; case INTEGER: return this != STRING; default: return true; } } } enum ArgumentKind { STRING("\"\""), INTEGER("1"); String argStr; private ArgumentKind(String argStr) { this.argStr = argStr; } } public static void main(String... args) throws Exception { //create default shared JavaCompiler - reused across multiple compilations JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) { for (BoundKind boundKind : BoundKind.values()) { for (ConstructorKind constructorKind : ConstructorKind.values()) { for (TypeArgumentKind declArgKind : TypeArgumentKind.values()) { for (TypeArgArity arity : TypeArgArity.values()) { for (TypeArgumentKind useArgKind : TypeArgumentKind.values()) { for (TypeArgumentKind diamondArgKind : TypeArgumentKind.values()) { for (ArgumentKind argKind : ArgumentKind.values()) { new GenericConstructorAndDiamondTest(boundKind, constructorKind, declArgKind, arity, useArgKind, diamondArgKind, argKind).run(comp, fm); } } } } } } } } } BoundKind boundKind; ConstructorKind constructorKind; TypeArgumentKind declTypeArgumentKind; TypeArgArity useTypeArgArity; TypeArgumentKind useTypeArgumentKind; TypeArgumentKind diamondTypeArgumentKind; ArgumentKind argumentKind; JavaSource source; DiagnosticChecker diagChecker; GenericConstructorAndDiamondTest(BoundKind boundKind, ConstructorKind constructorKind, TypeArgumentKind declTypeArgumentKind, TypeArgArity useTypeArgArity, TypeArgumentKind useTypeArgumentKind, TypeArgumentKind diamondTypeArgumentKind, ArgumentKind argumentKind) { this.boundKind = boundKind; this.constructorKind = constructorKind; this.declTypeArgumentKind = declTypeArgumentKind; this.useTypeArgArity = useTypeArgArity; this.useTypeArgumentKind = useTypeArgumentKind; this.diamondTypeArgumentKind = diamondTypeArgumentKind; this.argumentKind = argumentKind; this.source = new JavaSource(); this.diagChecker = new DiagnosticChecker(); } class JavaSource extends SimpleJavaFileObject { String template = "class Foo<X #BK> {\n" + "#CK\n" + "}\n" + "class Test {\n" + "void test() {\n" + "Foo#TA1 f = new #TA2 Foo<#TA3>(#A);\n" + "}\n" + "}\n"; String source; public JavaSource() { super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); source = template.replace("#BK", boundKind.boundStr). replace("#CK", constructorKind.constrStr) .replace("#TA1", declTypeArgumentKind.getArgs(TypeArgArity.ONE)) .replace("#TA2", useTypeArgumentKind.getArgs(useTypeArgArity)) .replace("#TA3", diamondTypeArgumentKind.typeargStr) .replace("#A", argumentKind.argStr); } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return source; } } void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception { JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker, null, null, Arrays.asList(source)); ct.analyze(); check(); } void check() { boolean badActual = !constructorKind.matches(argumentKind); boolean badArity = constructorKind != ConstructorKind.NON_GENERIC && useTypeArgumentKind != TypeArgumentKind.NONE && useTypeArgArity != TypeArgArity.ONE; boolean badMethodTypeArg = constructorKind != ConstructorKind.NON_GENERIC && !useTypeArgumentKind.matches(argumentKind); boolean badExplicitParams = (useTypeArgumentKind != TypeArgumentKind.NONE && diamondTypeArgumentKind == TypeArgumentKind.NONE) || !boundKind.matches(diamondTypeArgumentKind); boolean badGenericType = !boundKind.matches(declTypeArgumentKind) || !diamondTypeArgumentKind.matches(declTypeArgumentKind); boolean shouldFail = badActual || badArity || badMethodTypeArg || badExplicitParams || badGenericType; if (shouldFail != diagChecker.errorFound) { throw new Error("invalid diagnostics for source:\n" + source.getCharContent(true) + "\nFound error: " + diagChecker.errorFound + "\nExpected error: " + shouldFail); } } static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { boolean errorFound; public void report(Diagnostic<? extends JavaFileObject> diagnostic) { if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { errorFound = true; } } } }