Tue, 29 Mar 2011 16:41:18 +0100
7027157: Project Coin: javac warnings for AutoCloseable.close throwing InterruptedException
Summary: javac should warn about use/declaration of AutoCloseable subclasses that can throw InterruptedException
Reviewed-by: jjg
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Symtab.java Tue Mar 29 16:40:51 2011 +0100 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symtab.java Tue Mar 29 16:41:18 2011 +0100 1.3 @@ -131,6 +131,7 @@ 1.4 public final Type polymorphicSignatureType; 1.5 public final Type throwableType; 1.6 public final Type errorType; 1.7 + public final Type interruptedExceptionType; 1.8 public final Type illegalArgumentExceptionType; 1.9 public final Type exceptionType; 1.10 public final Type runtimeExceptionType; 1.11 @@ -441,6 +442,7 @@ 1.12 polymorphicSignatureType = enterClass("java.lang.invoke.MethodHandle$PolymorphicSignature"); 1.13 errorType = enterClass("java.lang.Error"); 1.14 illegalArgumentExceptionType = enterClass("java.lang.IllegalArgumentException"); 1.15 + interruptedExceptionType = enterClass("java.lang.InterruptedException"); 1.16 exceptionType = enterClass("java.lang.Exception"); 1.17 runtimeExceptionType = enterClass("java.lang.RuntimeException"); 1.18 classNotFoundExceptionType = enterClass("java.lang.ClassNotFoundException"); 1.19 @@ -480,6 +482,7 @@ 1.20 autoCloseableType.tsym); 1.21 trustMeType = enterClass("java.lang.SafeVarargs"); 1.22 1.23 + synthesizeEmptyInterfaceIfMissing(autoCloseableType); 1.24 synthesizeEmptyInterfaceIfMissing(cloneableType); 1.25 synthesizeEmptyInterfaceIfMissing(serializableType); 1.26 synthesizeEmptyInterfaceIfMissing(transientPolymorphicSignatureType); // transient - 292
2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Tue Mar 29 16:40:51 2011 +0100 2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Tue Mar 29 16:41:18 2011 +0100 2.3 @@ -1089,6 +1089,10 @@ 2.4 if (resource.getTag() == JCTree.VARDEF) { 2.5 attribStat(resource, tryEnv); 2.6 chk.checkType(resource, resource.type, syms.autoCloseableType, "try.not.applicable.to.type"); 2.7 + 2.8 + //check that resource type cannot throw InterruptedException 2.9 + checkAutoCloseable(resource.pos(), localEnv, resource.type); 2.10 + 2.11 VarSymbol var = (VarSymbol)TreeInfo.symbolFor(resource); 2.12 var.setData(ElementKind.RESOURCE_VARIABLE); 2.13 } else { 2.14 @@ -1127,6 +1131,35 @@ 2.15 result = null; 2.16 } 2.17 2.18 + void checkAutoCloseable(DiagnosticPosition pos, Env<AttrContext> env, Type resource) { 2.19 + if (!resource.isErroneous() && 2.20 + types.asSuper(resource, syms.autoCloseableType.tsym) != null) { 2.21 + Symbol close = syms.noSymbol; 2.22 + boolean prevDeferDiags = log.deferDiagnostics; 2.23 + Queue<JCDiagnostic> prevDeferredDiags = log.deferredDiagnostics; 2.24 + try { 2.25 + log.deferDiagnostics = true; 2.26 + log.deferredDiagnostics = ListBuffer.lb(); 2.27 + close = rs.resolveQualifiedMethod(pos, 2.28 + env, 2.29 + resource, 2.30 + names.close, 2.31 + List.<Type>nil(), 2.32 + List.<Type>nil()); 2.33 + } 2.34 + finally { 2.35 + log.deferDiagnostics = prevDeferDiags; 2.36 + log.deferredDiagnostics = prevDeferredDiags; 2.37 + } 2.38 + if (close.kind == MTH && 2.39 + close.overrides(syms.autoCloseableClose, resource.tsym, types, true) && 2.40 + chk.isHandled(syms.interruptedExceptionType, types.memberType(resource, close).getThrownTypes()) && 2.41 + env.info.lint.isEnabled(LintCategory.TRY)) { 2.42 + log.warning(LintCategory.TRY, pos, "try.resource.throws.interrupted.exc", resource); 2.43 + } 2.44 + } 2.45 + } 2.46 + 2.47 public void visitConditional(JCConditional tree) { 2.48 attribExpr(tree.cond, env, syms.booleanType); 2.49 attribExpr(tree.truepart, env); 2.50 @@ -3169,6 +3202,9 @@ 2.51 // method conform to the method they implement. 2.52 chk.checkImplementations(tree); 2.53 2.54 + //check that a resource implementing AutoCloseable cannot throw InterruptedException 2.55 + checkAutoCloseable(tree.pos(), env, c.type); 2.56 + 2.57 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 2.58 // Attribute declaration 2.59 attribStat(l.head, env);
3.1 --- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties Tue Mar 29 16:40:51 2011 +0100 3.2 +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties Tue Mar 29 16:41:18 2011 +0100 3.3 @@ -1244,6 +1244,10 @@ 3.4 compiler.warn.try.resource.not.referenced=\ 3.5 auto-closeable resource {0} is never referenced in body of corresponding try statement 3.6 3.7 +# 0: type 3.8 +compiler.warn.try.resource.throws.interrupted.exc=\ 3.9 + auto-closeable resource {0} has a member method close() that could throw InterruptedException 3.10 + 3.11 compiler.warn.unchecked.assign=\ 3.12 unchecked assignment: {0} to {1} 3.13
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/tools/javac/TryWithResources/InterruptedExceptionTest.java Tue Mar 29 16:41:18 2011 +0100 4.3 @@ -0,0 +1,234 @@ 4.4 +/* 4.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + */ 4.26 + 4.27 +/* 4.28 + * @test 4.29 + * @bug 7027157 4.30 + * @summary Project Coin: javac warnings for AutoCloseable.close throwing InterruptedException 4.31 + */ 4.32 + 4.33 +import com.sun.source.util.JavacTask; 4.34 +import java.net.URI; 4.35 +import java.util.Arrays; 4.36 +import javax.tools.Diagnostic; 4.37 +import javax.tools.JavaCompiler; 4.38 +import javax.tools.JavaFileObject; 4.39 +import javax.tools.SimpleJavaFileObject; 4.40 +import javax.tools.StandardJavaFileManager; 4.41 +import javax.tools.ToolProvider; 4.42 + 4.43 +public class InterruptedExceptionTest { 4.44 + 4.45 + enum XlintOption { 4.46 + NONE("none"), 4.47 + TRY("try"); 4.48 + 4.49 + String opt; 4.50 + 4.51 + XlintOption(String opt) { 4.52 + this.opt = opt; 4.53 + } 4.54 + 4.55 + String getXlintOption() { 4.56 + return "-Xlint:" + opt; 4.57 + } 4.58 + } 4.59 + 4.60 + enum SuppressLevel { 4.61 + NONE, 4.62 + SUPPRESS; 4.63 + 4.64 + String getSuppressAnno() { 4.65 + return this == SUPPRESS ? 4.66 + "@SuppressWarnings(\"try\")" : 4.67 + ""; 4.68 + } 4.69 + } 4.70 + 4.71 + enum ClassKind { 4.72 + ABSTRACT_CLASS("abstract class", "implements", false), 4.73 + CLASS("class", "implements", true), 4.74 + INTERFACE("interface", "extends", false); 4.75 + 4.76 + String kindName; 4.77 + String extendsClause; 4.78 + boolean hasBody; 4.79 + 4.80 + private ClassKind(String kindName, String extendsClause, boolean hasBody) { 4.81 + this.kindName = kindName; 4.82 + this.extendsClause = extendsClause; 4.83 + this.hasBody = hasBody; 4.84 + } 4.85 + 4.86 + String getBody() { 4.87 + return hasBody ? "{}" : ";"; 4.88 + } 4.89 + } 4.90 + 4.91 + enum ExceptionKind { 4.92 + NONE("", false), 4.93 + EXCEPTION("Exception", true), 4.94 + INTERRUPTED_EXCEPTION("InterruptedException", true), 4.95 + ILLEGAL_ARGUMENT_EXCEPTION("IllegalArgumentException", false), 4.96 + X("X", false); 4.97 + 4.98 + String exName; 4.99 + boolean shouldWarn; 4.100 + 4.101 + private ExceptionKind(String exName, boolean shouldWarn) { 4.102 + this.exName = exName; 4.103 + this.shouldWarn = shouldWarn; 4.104 + } 4.105 + 4.106 + String getThrowsClause() { 4.107 + return this == NONE ? "" : "throws " + exName; 4.108 + } 4.109 + 4.110 + String getTypeArguments(ExceptionKind decl) { 4.111 + return (decl != X || this == NONE) ? "" : "<" + exName + ">"; 4.112 + } 4.113 + 4.114 + String getTypeParameter() { 4.115 + return this == X ? "<X extends Exception>" : ""; 4.116 + } 4.117 + } 4.118 + 4.119 + public static void main(String... args) throws Exception { 4.120 + 4.121 + //create default shared JavaCompiler - reused across multiple compilations 4.122 + JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); 4.123 + StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); 4.124 + 4.125 + for (XlintOption xlint : XlintOption.values()) { 4.126 + for (SuppressLevel suppress_decl : SuppressLevel.values()) { 4.127 + for (SuppressLevel suppress_use : SuppressLevel.values()) { 4.128 + for (ClassKind ck : ClassKind.values()) { 4.129 + for (ExceptionKind ek_decl : ExceptionKind.values()) { 4.130 + for (ExceptionKind ek_use : ExceptionKind.values()) { 4.131 + new InterruptedExceptionTest(xlint, suppress_decl, 4.132 + suppress_use, ck, ek_decl, ek_use).run(comp, fm); 4.133 + } 4.134 + } 4.135 + } 4.136 + } 4.137 + } 4.138 + } 4.139 + } 4.140 + 4.141 + XlintOption xlint; 4.142 + SuppressLevel suppress_decl; 4.143 + SuppressLevel suppress_use; 4.144 + ClassKind ck; 4.145 + ExceptionKind ek_decl; 4.146 + ExceptionKind ek_use; 4.147 + JavaSource source; 4.148 + DiagnosticChecker diagChecker; 4.149 + 4.150 + InterruptedExceptionTest(XlintOption xlint, SuppressLevel suppress_decl, SuppressLevel suppress_use, 4.151 + ClassKind ck, ExceptionKind ek_decl, ExceptionKind ek_use) { 4.152 + this.xlint = xlint; 4.153 + this.suppress_decl = suppress_decl; 4.154 + this.suppress_use = suppress_use; 4.155 + this.ck = ck; 4.156 + this.ek_decl = ek_decl; 4.157 + this.ek_use = ek_use; 4.158 + this.source = new JavaSource(); 4.159 + this.diagChecker = new DiagnosticChecker(); 4.160 + } 4.161 + 4.162 + class JavaSource extends SimpleJavaFileObject { 4.163 + 4.164 + String template = "#S1 #CK Resource#G #EC AutoCloseable {\n" + 4.165 + "public void close() #TK #BK\n" + 4.166 + "}\n" + 4.167 + "class Test {\n" + 4.168 + "#S2 <X> void test() {\n" + 4.169 + "try (Resource#PK r = null) { }\n" + 4.170 + "}\n" + 4.171 + "}\n"; 4.172 + 4.173 + String source; 4.174 + 4.175 + public JavaSource() { 4.176 + super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 4.177 + source = template.replace("#S1", suppress_decl.getSuppressAnno()) 4.178 + .replace("#S2", suppress_use.getSuppressAnno()) 4.179 + .replace("#CK", ck.kindName) 4.180 + .replace("#EC", ck.extendsClause) 4.181 + .replace("#G", ek_decl.getTypeParameter()) 4.182 + .replace("#TK", ek_decl.getThrowsClause()) 4.183 + .replace("#BK", ck.getBody()) 4.184 + .replace("#PK", ek_use.getTypeArguments(ek_decl)); 4.185 + } 4.186 + 4.187 + @Override 4.188 + public CharSequence getCharContent(boolean ignoreEncodingErrors) { 4.189 + return source; 4.190 + } 4.191 + } 4.192 + 4.193 + void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception { 4.194 + JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker, 4.195 + Arrays.asList(xlint.getXlintOption()), null, Arrays.asList(source)); 4.196 + ct.analyze(); 4.197 + check(); 4.198 + } 4.199 + 4.200 + void check() { 4.201 + 4.202 + boolean shouldWarnDecl = ek_decl.shouldWarn && 4.203 + xlint == XlintOption.TRY && 4.204 + suppress_decl != SuppressLevel.SUPPRESS; 4.205 + 4.206 + boolean shouldWarnUse = (ek_decl.shouldWarn || 4.207 + ((ek_use.shouldWarn || ek_use == ExceptionKind.NONE) && ek_decl == ExceptionKind.X)) && 4.208 + xlint == XlintOption.TRY && 4.209 + suppress_use != SuppressLevel.SUPPRESS; 4.210 + 4.211 + int foundWarnings = 0; 4.212 + 4.213 + if (shouldWarnDecl) foundWarnings++; 4.214 + if (shouldWarnUse) foundWarnings++; 4.215 + 4.216 + if (foundWarnings != diagChecker.tryWarnFound) { 4.217 + throw new Error("invalid diagnostics for source:\n" + 4.218 + source.getCharContent(true) + 4.219 + "\nOptions: " + xlint.getXlintOption() + 4.220 + "\nFound warnings: " + diagChecker.tryWarnFound + 4.221 + "\nExpected decl warning: " + shouldWarnDecl + 4.222 + "\nExpected use warning: " + shouldWarnUse); 4.223 + } 4.224 + } 4.225 + 4.226 + static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { 4.227 + 4.228 + int tryWarnFound; 4.229 + 4.230 + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 4.231 + if (diagnostic.getKind() == Diagnostic.Kind.WARNING && 4.232 + diagnostic.getCode().contains("try.resource.throws.interrupted.exc")) { 4.233 + tryWarnFound++; 4.234 + } 4.235 + } 4.236 + } 4.237 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/tools/javac/diags/examples/TryResourceThrowsInterruptedExc.java Tue Mar 29 16:41:18 2011 +0100 5.3 @@ -0,0 +1,29 @@ 5.4 +/* 5.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.23 + * or visit www.oracle.com if you need additional information or have any 5.24 + * questions. 5.25 + */ 5.26 + 5.27 +// key: compiler.warn.try.resource.throws.interrupted.exc 5.28 +// options: -Xlint:try 5.29 + 5.30 +class TryResourceThrowsInterruptedException implements AutoCloseable { 5.31 + public void close() throws InterruptedException {} 5.32 +}