/* * Copyright (c) 2013, 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 7118412 * @summary Shadowing of type-variables vs. member types * @modules jdk.compiler */ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; public class ShadowingTest { // We generate a method "test" that tries to call T.<something, // depending on the value of MethodCall>. This controls whether // "test" is static or not. private enum MethodContext { STATIC("static "), INSTANCE(""); public final String methodcontext; MethodContext(final String methodcontext) { this.methodcontext = methodcontext; } } // These control whether or not a type parameter, method type // parameter, or inner class get declared (and in the case of // inner classes, whether it's static or not. private enum MethodTypeParameterDecl { NO(""), YES("<T extends Number> "); public final String tyvar; MethodTypeParameterDecl(final String tyvar) { this.tyvar = tyvar; } } private enum InsideDef { NONE(""), STATIC("static class T { public void inner() {} }\n"), INSTANCE("class T { public void inner() {} }\n"); public final String instancedef; InsideDef(final String instancedef) { this.instancedef = instancedef; } } private enum TypeParameterDecl { NO(""), YES("<T extends Collection>"); public final String tyvar; TypeParameterDecl(final String tyvar) { this.tyvar = tyvar; } } // Represents what method we try to call. This is a way of // checking which T we're seeing. private enum MethodCall { // Method type variables extend Number, so we have intValue METHOD_TYPEVAR("intValue"), // The inner class declaration has a method called "inner" INNER_CLASS("inner"), // The class type variables extend Collection, so we call iterator TYPEVAR("iterator"), // The outer class declaration has a method called "outer" OUTER_CLASS("outer"); public final String methodcall; MethodCall(final String methodcall) { this.methodcall = methodcall; } } public boolean succeeds(final MethodCall call, final MethodTypeParameterDecl mtyvar, final MethodContext ctx, final InsideDef inside, final TypeParameterDecl tyvar) { switch(call) { // We want to resolve to the method type variable case METHOD_TYPEVAR: switch(mtyvar) { // If the method type variable exists, then T will // resolve to it, and we'll have intValue. case YES: return true; // Otherwise, this cannot succeed. default: return false; } // We want to resolve to the inner class case INNER_CLASS: switch(mtyvar) { // The method type parameter will shadow the inner // class, so there can't be one. case NO: switch(ctx) { // If we're not static, then either one should succeed. case INSTANCE: switch(inside) { case INSTANCE: case STATIC: return true; default: return false; } case STATIC: switch(inside) { // If we are static, and the inner class is // static, then we also succeed, because we // can't see the type variable. case STATIC: return true; case INSTANCE: switch(tyvar) { // If we're calling from a non-static // context, there can't be a class type // variable, because that will shadow the // static inner class definition. case NO: return true; default: return false; } // If the inner class isn't declared, we can't // see it. default: return false; } // Can't get here. default: return false; } default: return false; } // We want to resolve to the class type parameter case TYPEVAR: switch(mtyvar) { // We can't have a method type parameter, as that would // shadow the class type parameter case NO: switch(ctx) { case INSTANCE: switch(inside) { // We have to be in an instance context. If // we're static, we can't see the type // variable. case NONE: switch(tyvar) { // Obviously, the type parameter has to be declared. case YES: return true; default: return false; } default: return false; } default: return false; } default: return false; } // We want to resolve to the outer class case OUTER_CLASS: switch(mtyvar) { case NO: switch(inside) { case NONE: switch(tyvar) { // Basically, nothing else can be declared, or // else we can't see it. Even if our context // is static, the compiler will complain if // non-static T's exist, because they will // shadow the outer class. case NO: return true; default: return false; } default: return false; } default: return false; } } return false; } private static final File classesdir = new File("7118412"); private int errors = 0; private int dirnum = 0; private void doTest(final MethodTypeParameterDecl mtyvar, final TypeParameterDecl tyvar, final InsideDef insidedef, final MethodContext ctx, final MethodCall call) throws IOException { final String content = "import java.util.Collection;\n" + "class Test" + tyvar.tyvar + " {\n" + " " + insidedef.instancedef + " " + ctx.methodcontext + mtyvar.tyvar + "void test(T t) { t." + call.methodcall + "(); }\n" + "}\n" + "class T { void outer() {} }\n"; final File dir = new File(classesdir, "" + dirnum); final File Test_java = writeFile(dir, "Test.java", content); dirnum++; if(succeeds(call, mtyvar, ctx, insidedef, tyvar)) { if(!assert_compile_succeed(Test_java)) System.err.println("Failed file:\n" + content); } else { if(!assert_compile_fail(Test_java)) System.err.println("Failed file:\n" + content); } } private void run() throws Exception { classesdir.mkdir(); for(MethodTypeParameterDecl mtyvar : MethodTypeParameterDecl.values()) for(TypeParameterDecl tyvar : TypeParameterDecl.values()) for(InsideDef insidedef : InsideDef.values()) for(MethodContext ctx : MethodContext.values()) for(MethodCall methodcall : MethodCall.values()) doTest(mtyvar, tyvar, insidedef, ctx, methodcall); if (errors != 0) throw new Exception("ShadowingTest test failed with " + errors + " errors."); } private boolean assert_compile_fail(final File file) { final String filename = file.getPath(); final String[] args = { filename }; final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw); final int rc = com.sun.tools.javac.Main.compile(args, pw); pw.close(); if (rc == 0) { System.err.println("Compilation of " + file.getName() + " didn't fail as expected."); errors++; return false; } else return true; } private boolean assert_compile_succeed(final File file) { final String filename = file.getPath(); final String[] args = { filename }; final StringWriter sw = new StringWriter(); final PrintWriter pw = new PrintWriter(sw); final int rc = com.sun.tools.javac.Main.compile(args, pw); pw.close(); if (rc != 0) { System.err.println("Compilation of " + file.getName() + " didn't succeed as expected. Output:"); System.err.println(sw.toString()); errors++; return false; } else return true; } private File writeFile(final File dir, final String path, final String body) throws IOException { final File f = new File(dir, path); f.getParentFile().mkdirs(); final FileWriter out = new FileWriter(f); out.write(body); out.close(); return f; } public static void main(String... args) throws Exception { new ShadowingTest().run(); } }