1 /* 2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 7030150 7039931 27 * @summary Type inference for generic instance creation failed for formal type parameter 28 */ 29 30 import com.sun.source.util.JavacTask; 31 import java.net.URI; 32 import java.util.Arrays; 33 import javax.tools.Diagnostic; 34 import javax.tools.JavaCompiler; 35 import javax.tools.JavaFileObject; 36 import javax.tools.SimpleJavaFileObject; 37 import javax.tools.StandardJavaFileManager; 38 import javax.tools.ToolProvider; 39 40 public class GenericConstructorAndDiamondTest { 41 42 enum BoundKind { 43 NO_BOUND(""), 44 STRING_BOUND("extends String"), 45 INTEGER_BOUND("extends Integer"); 46 47 String boundStr; 48 BoundKind(String boundStr)49 private BoundKind(String boundStr) { 50 this.boundStr = boundStr; 51 } 52 matches(TypeArgumentKind tak)53 boolean matches(TypeArgumentKind tak) { 54 switch (tak) { 55 case NONE: return true; 56 case STRING: return this != INTEGER_BOUND; 57 case INTEGER: return this != STRING_BOUND; 58 default: return false; 59 } 60 } 61 } 62 63 enum ConstructorKind { 64 NON_GENERIC("Foo(Object o) {}"), 65 GENERIC_NO_BOUND("<T> Foo(T t) {}"), 66 GENERIC_STRING_BOUND("<T extends String> Foo(T t) {}"), 67 GENERIC_INTEGER_BOUND("<T extends Integer> Foo(T t) {}"); 68 69 String constrStr; 70 ConstructorKind(String constrStr)71 private ConstructorKind(String constrStr) { 72 this.constrStr = constrStr; 73 } 74 matches(ArgumentKind ak)75 boolean matches(ArgumentKind ak) { 76 switch (ak) { 77 case STRING: return this != GENERIC_INTEGER_BOUND; 78 case INTEGER: return this != GENERIC_STRING_BOUND; 79 default: return false; 80 } 81 } 82 } 83 84 enum TypeArgArity { 85 ONE(1), 86 TWO(2), 87 THREE(3); 88 89 int n; 90 TypeArgArity(int n)91 private TypeArgArity(int n) { 92 this.n = n; 93 } 94 } 95 96 enum TypeArgumentKind { 97 NONE(""), 98 STRING("String"), 99 INTEGER("Integer"); 100 101 String typeargStr; 102 TypeArgumentKind(String typeargStr)103 private TypeArgumentKind(String typeargStr) { 104 this.typeargStr = typeargStr; 105 } 106 getArgs(TypeArgArity arity)107 String getArgs(TypeArgArity arity) { 108 if (this == NONE) return ""; 109 else { 110 StringBuilder buf = new StringBuilder(); 111 String sep = ""; 112 for (int i = 0 ; i < arity.n ; i++) { 113 buf.append(sep); 114 buf.append(typeargStr); 115 sep = ","; 116 } 117 return "<" + buf.toString() + ">"; 118 } 119 } 120 matches(ArgumentKind ak)121 boolean matches(ArgumentKind ak) { 122 switch (ak) { 123 case STRING: return this != INTEGER; 124 case INTEGER: return this != STRING; 125 default: return false; 126 } 127 } 128 matches(TypeArgumentKind other)129 boolean matches(TypeArgumentKind other) { 130 switch (other) { 131 case STRING: return this != INTEGER; 132 case INTEGER: return this != STRING; 133 default: return true; 134 } 135 } 136 } 137 138 enum ArgumentKind { 139 STRING("\"\""), 140 INTEGER("1"); 141 142 String argStr; 143 ArgumentKind(String argStr)144 private ArgumentKind(String argStr) { 145 this.argStr = argStr; 146 } 147 } 148 main(String... args)149 public static void main(String... args) throws Exception { 150 151 //create default shared JavaCompiler - reused across multiple compilations 152 JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); 153 StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); 154 155 for (BoundKind boundKind : BoundKind.values()) { 156 for (ConstructorKind constructorKind : ConstructorKind.values()) { 157 for (TypeArgumentKind declArgKind : TypeArgumentKind.values()) { 158 for (TypeArgArity arity : TypeArgArity.values()) { 159 for (TypeArgumentKind useArgKind : TypeArgumentKind.values()) { 160 for (TypeArgumentKind diamondArgKind : TypeArgumentKind.values()) { 161 for (ArgumentKind argKind : ArgumentKind.values()) { 162 new GenericConstructorAndDiamondTest(boundKind, constructorKind, 163 declArgKind, arity, useArgKind, diamondArgKind, argKind).run(comp, fm); 164 } 165 } 166 } 167 } 168 } 169 } 170 } 171 } 172 173 BoundKind boundKind; 174 ConstructorKind constructorKind; 175 TypeArgumentKind declTypeArgumentKind; 176 TypeArgArity useTypeArgArity; 177 TypeArgumentKind useTypeArgumentKind; 178 TypeArgumentKind diamondTypeArgumentKind; 179 ArgumentKind argumentKind; 180 JavaSource source; 181 DiagnosticChecker diagChecker; 182 GenericConstructorAndDiamondTest(BoundKind boundKind, ConstructorKind constructorKind, TypeArgumentKind declTypeArgumentKind, TypeArgArity useTypeArgArity, TypeArgumentKind useTypeArgumentKind, TypeArgumentKind diamondTypeArgumentKind, ArgumentKind argumentKind)183 GenericConstructorAndDiamondTest(BoundKind boundKind, ConstructorKind constructorKind, 184 TypeArgumentKind declTypeArgumentKind, TypeArgArity useTypeArgArity, 185 TypeArgumentKind useTypeArgumentKind, TypeArgumentKind diamondTypeArgumentKind, 186 ArgumentKind argumentKind) { 187 this.boundKind = boundKind; 188 this.constructorKind = constructorKind; 189 this.declTypeArgumentKind = declTypeArgumentKind; 190 this.useTypeArgArity = useTypeArgArity; 191 this.useTypeArgumentKind = useTypeArgumentKind; 192 this.diamondTypeArgumentKind = diamondTypeArgumentKind; 193 this.argumentKind = argumentKind; 194 this.source = new JavaSource(); 195 this.diagChecker = new DiagnosticChecker(); 196 } 197 198 class JavaSource extends SimpleJavaFileObject { 199 200 String template = "class Foo<X #BK> {\n" + 201 "#CK\n" + 202 "}\n" + 203 "class Test {\n" + 204 "void test() {\n" + 205 "Foo#TA1 f = new #TA2 Foo<#TA3>(#A);\n" + 206 "}\n" + 207 "}\n"; 208 209 String source; 210 JavaSource()211 public JavaSource() { 212 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 213 source = template.replace("#BK", boundKind.boundStr). 214 replace("#CK", constructorKind.constrStr) 215 .replace("#TA1", declTypeArgumentKind.getArgs(TypeArgArity.ONE)) 216 .replace("#TA2", useTypeArgumentKind.getArgs(useTypeArgArity)) 217 .replace("#TA3", diamondTypeArgumentKind.typeargStr) 218 .replace("#A", argumentKind.argStr); 219 } 220 221 @Override getCharContent(boolean ignoreEncodingErrors)222 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 223 return source; 224 } 225 } 226 run(JavaCompiler tool, StandardJavaFileManager fm)227 void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception { 228 JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker, 229 null, null, Arrays.asList(source)); 230 ct.analyze(); 231 check(); 232 } 233 check()234 void check() { 235 boolean badActual = !constructorKind.matches(argumentKind); 236 237 boolean badArity = constructorKind != ConstructorKind.NON_GENERIC && 238 useTypeArgumentKind != TypeArgumentKind.NONE && 239 useTypeArgArity != TypeArgArity.ONE; 240 241 boolean badMethodTypeArg = constructorKind != ConstructorKind.NON_GENERIC && 242 !useTypeArgumentKind.matches(argumentKind); 243 244 boolean badExplicitParams = (useTypeArgumentKind != TypeArgumentKind.NONE && 245 diamondTypeArgumentKind == TypeArgumentKind.NONE) || 246 !boundKind.matches(diamondTypeArgumentKind); 247 248 boolean badGenericType = !boundKind.matches(declTypeArgumentKind) || 249 !diamondTypeArgumentKind.matches(declTypeArgumentKind); 250 251 boolean shouldFail = badActual || badArity || 252 badMethodTypeArg || badExplicitParams || badGenericType; 253 254 if (shouldFail != diagChecker.errorFound) { 255 throw new Error("invalid diagnostics for source:\n" + 256 source.getCharContent(true) + 257 "\nFound error: " + diagChecker.errorFound + 258 "\nExpected error: " + shouldFail); 259 } 260 } 261 262 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { 263 264 boolean errorFound; 265 report(Diagnostic<? extends JavaFileObject> diagnostic)266 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 267 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { 268 errorFound = true; 269 } 270 } 271 } 272 } 273