1 /* 2 * Copyright (c) 2013, 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 7118412 27 * @summary Shadowing of type-variables vs. member types 28 */ 29 import java.io.File; 30 import java.io.FileWriter; 31 import java.io.IOException; 32 import java.io.PrintWriter; 33 import java.io.StringWriter; 34 35 public class ShadowingTest { 36 37 // We generate a method "test" that tries to call T.<something, 38 // depending on the value of MethodCall>. This controls whether 39 // "test" is static or not. 40 private enum MethodContext { 41 STATIC("static "), 42 INSTANCE(""); 43 44 public final String methodcontext; 45 46 MethodContext(final String methodcontext) { 47 this.methodcontext = methodcontext; 48 } 49 } 50 51 // These control whether or not a type parameter, method type 52 // parameter, or inner class get declared (and in the case of 53 // inner classes, whether it's static or not. 54 55 private enum MethodTypeParameterDecl { 56 NO(""), 57 YES("<T extends Number> "); 58 59 public final String tyvar; 60 61 MethodTypeParameterDecl(final String tyvar) { 62 this.tyvar = tyvar; 63 } 64 } 65 66 private enum InsideDef { 67 NONE(""), 68 STATIC("static class T { public void inner() {} }\n"), 69 INSTANCE("class T { public void inner() {} }\n"); 70 71 public final String instancedef; 72 73 InsideDef(final String instancedef) { 74 this.instancedef = instancedef; 75 } 76 } 77 78 private enum TypeParameterDecl { 79 NO(""), 80 YES("<T extends Collection>"); 81 82 public final String tyvar; 83 84 TypeParameterDecl(final String tyvar) { 85 this.tyvar = tyvar; 86 } 87 } 88 89 // Represents what method we try to call. This is a way of 90 // checking which T we're seeing. 91 private enum MethodCall { 92 // Method type variables extend Number, so we have intValue 93 METHOD_TYPEVAR("intValue"), 94 // The inner class declaration has a method called "inner" 95 INNER_CLASS("inner"), 96 // The class type variables extend Collection, so we call iterator 97 TYPEVAR("iterator"), 98 // The outer class declaration has a method called "outer" 99 OUTER_CLASS("outer"); 100 101 public final String methodcall; 102 103 MethodCall(final String methodcall) { 104 this.methodcall = methodcall; 105 } 106 107 } 108 109 public boolean succeeds(final MethodCall call, 110 final MethodTypeParameterDecl mtyvar, 111 final MethodContext ctx, 112 final InsideDef inside, 113 final TypeParameterDecl tyvar) { 114 switch(call) { 115 // We want to resolve to the method type variable 116 case METHOD_TYPEVAR: switch(mtyvar) { 117 // If the method type variable exists, then T will 118 // resolve to it, and we'll have intValue. 119 case YES: return true; 120 // Otherwise, this cannot succeed. 121 default: return false; 122 } 123 // We want to resolve to the inner class 124 case INNER_CLASS: switch(mtyvar) { 125 // The method type parameter will shadow the inner 126 // class, so there can't be one. 127 case NO: switch(ctx) { 128 // If we're not static, then either one should succeed. 129 case INSTANCE: switch(inside) { 130 case INSTANCE: 131 case STATIC: 132 return true; 133 default: return false; 134 } 135 case STATIC: switch(inside) { 136 // If we are static, and the inner class is 137 // static, then we also succeed, because we 138 // can't see the type variable. 139 case STATIC: return true; 140 case INSTANCE: switch(tyvar) { 141 // If we're calling from a non-static 142 // context, there can't be a class type 143 // variable, because that will shadow the 144 // static inner class definition. 145 case NO: return true; 146 default: return false; 147 } 148 // If the inner class isn't declared, we can't 149 // see it. 150 default: return false; 151 } 152 // Can't get here. 153 default: return false; 154 } 155 default: return false; 156 } 157 // We want to resolve to the class type parameter 158 case TYPEVAR: switch(mtyvar) { 159 // We can't have a method type parameter, as that would 160 // shadow the class type parameter 161 case NO: switch(ctx) { 162 case INSTANCE: switch(inside) { 163 // We have to be in an instance context. If 164 // we're static, we can't see the type 165 // variable. 166 case NONE: switch(tyvar) { 167 // Obviously, the type parameter has to be declared. 168 case YES: return true; 169 default: return false; 170 } 171 default: return false; 172 } 173 default: return false; 174 } 175 default: return false; 176 } 177 // We want to resolve to the outer class 178 case OUTER_CLASS: switch(mtyvar) { 179 case NO: switch(inside) { 180 case NONE: switch(tyvar) { 181 // Basically, nothing else can be declared, or 182 // else we can't see it. Even if our context 183 // is static, the compiler will complain if 184 // non-static T's exist, because they will 185 // shadow the outer class. 186 case NO: return true; 187 default: return false; 188 } 189 default: return false; 190 } 191 default: return false; 192 } 193 } 194 return false; 195 } 196 197 private static final File classesdir = new File("7118412"); 198 199 private int errors = 0; 200 201 private int dirnum = 0; 202 203 private void doTest(final MethodTypeParameterDecl mtyvar, 204 final TypeParameterDecl tyvar, 205 final InsideDef insidedef, final MethodContext ctx, 206 final MethodCall call) 207 throws IOException { 208 final String content = "import java.util.Collection;\n" + 209 "class Test" + tyvar.tyvar + " {\n" + 210 " " + insidedef.instancedef + 211 " " + ctx.methodcontext + mtyvar.tyvar + "void test(T t) { t." + 212 call.methodcall + "(); }\n" + 213 "}\n" + 214 "class T { void outer() {} }\n"; 215 final File dir = new File(classesdir, "" + dirnum); 216 final File Test_java = writeFile(dir, "Test.java", content); 217 dirnum++; 218 if(succeeds(call, mtyvar, ctx, insidedef, tyvar)) { 219 if(!assert_compile_succeed(Test_java)) 220 System.err.println("Failed file:\n" + content); 221 } 222 else { 223 if(!assert_compile_fail(Test_java)) 224 System.err.println("Failed file:\n" + content); 225 } 226 } 227 228 private void run() throws Exception { 229 classesdir.mkdir(); 230 for(MethodTypeParameterDecl mtyvar : MethodTypeParameterDecl.values()) 231 for(TypeParameterDecl tyvar : TypeParameterDecl.values()) 232 for(InsideDef insidedef : InsideDef.values()) 233 for(MethodContext ctx : MethodContext.values()) 234 for(MethodCall methodcall : MethodCall.values()) 235 doTest(mtyvar, tyvar, insidedef, ctx, methodcall); 236 if (errors != 0) 237 throw new Exception("ShadowingTest test failed with " + 238 errors + " errors."); 239 } 240 241 private boolean assert_compile_fail(final File file) { 242 final String filename = file.getPath(); 243 final String[] args = { filename }; 244 final StringWriter sw = new StringWriter(); 245 final PrintWriter pw = new PrintWriter(sw); 246 final int rc = com.sun.tools.javac.Main.compile(args, pw); 247 pw.close(); 248 if (rc == 0) { 249 System.err.println("Compilation of " + file.getName() + 250 " didn't fail as expected."); 251 errors++; 252 return false; 253 } else return true; 254 } 255 256 private boolean assert_compile_succeed(final File file) { 257 final String filename = file.getPath(); 258 final String[] args = { filename }; 259 final StringWriter sw = new StringWriter(); 260 final PrintWriter pw = new PrintWriter(sw); 261 final int rc = com.sun.tools.javac.Main.compile(args, pw); 262 pw.close(); 263 if (rc != 0) { 264 System.err.println("Compilation of " + file.getName() + 265 " didn't succeed as expected. Output:"); 266 System.err.println(sw.toString()); 267 errors++; 268 return false; 269 } else return true; 270 } 271 272 private File writeFile(final File dir, 273 final String path, 274 final String body) throws IOException { 275 final File f = new File(dir, path); 276 f.getParentFile().mkdirs(); 277 final FileWriter out = new FileWriter(f); 278 out.write(body); 279 out.close(); 280 return f; 281 } 282 283 public static void main(String... args) throws Exception { 284 new ShadowingTest().run(); 285 } 286 287 }