mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-17 17:44:40 +02:00
7027157: Project Coin: javac warnings for AutoCloseable.close throwing InterruptedException
Javac should warn about use/declaration of AutoCloseable subclasses that can throw InterruptedException Reviewed-by: jjg
This commit is contained in:
parent
bd9526ecbe
commit
acf788aa1f
5 changed files with 306 additions and 0 deletions
|
@ -131,6 +131,7 @@ public class Symtab {
|
||||||
public final Type polymorphicSignatureType;
|
public final Type polymorphicSignatureType;
|
||||||
public final Type throwableType;
|
public final Type throwableType;
|
||||||
public final Type errorType;
|
public final Type errorType;
|
||||||
|
public final Type interruptedExceptionType;
|
||||||
public final Type illegalArgumentExceptionType;
|
public final Type illegalArgumentExceptionType;
|
||||||
public final Type exceptionType;
|
public final Type exceptionType;
|
||||||
public final Type runtimeExceptionType;
|
public final Type runtimeExceptionType;
|
||||||
|
@ -441,6 +442,7 @@ public class Symtab {
|
||||||
polymorphicSignatureType = enterClass("java.lang.invoke.MethodHandle$PolymorphicSignature");
|
polymorphicSignatureType = enterClass("java.lang.invoke.MethodHandle$PolymorphicSignature");
|
||||||
errorType = enterClass("java.lang.Error");
|
errorType = enterClass("java.lang.Error");
|
||||||
illegalArgumentExceptionType = enterClass("java.lang.IllegalArgumentException");
|
illegalArgumentExceptionType = enterClass("java.lang.IllegalArgumentException");
|
||||||
|
interruptedExceptionType = enterClass("java.lang.InterruptedException");
|
||||||
exceptionType = enterClass("java.lang.Exception");
|
exceptionType = enterClass("java.lang.Exception");
|
||||||
runtimeExceptionType = enterClass("java.lang.RuntimeException");
|
runtimeExceptionType = enterClass("java.lang.RuntimeException");
|
||||||
classNotFoundExceptionType = enterClass("java.lang.ClassNotFoundException");
|
classNotFoundExceptionType = enterClass("java.lang.ClassNotFoundException");
|
||||||
|
@ -480,6 +482,7 @@ public class Symtab {
|
||||||
autoCloseableType.tsym);
|
autoCloseableType.tsym);
|
||||||
trustMeType = enterClass("java.lang.SafeVarargs");
|
trustMeType = enterClass("java.lang.SafeVarargs");
|
||||||
|
|
||||||
|
synthesizeEmptyInterfaceIfMissing(autoCloseableType);
|
||||||
synthesizeEmptyInterfaceIfMissing(cloneableType);
|
synthesizeEmptyInterfaceIfMissing(cloneableType);
|
||||||
synthesizeEmptyInterfaceIfMissing(serializableType);
|
synthesizeEmptyInterfaceIfMissing(serializableType);
|
||||||
synthesizeEmptyInterfaceIfMissing(transientPolymorphicSignatureType); // transient - 292
|
synthesizeEmptyInterfaceIfMissing(transientPolymorphicSignatureType); // transient - 292
|
||||||
|
|
|
@ -1089,6 +1089,10 @@ public class Attr extends JCTree.Visitor {
|
||||||
if (resource.getTag() == JCTree.VARDEF) {
|
if (resource.getTag() == JCTree.VARDEF) {
|
||||||
attribStat(resource, tryEnv);
|
attribStat(resource, tryEnv);
|
||||||
chk.checkType(resource, resource.type, syms.autoCloseableType, "try.not.applicable.to.type");
|
chk.checkType(resource, resource.type, syms.autoCloseableType, "try.not.applicable.to.type");
|
||||||
|
|
||||||
|
//check that resource type cannot throw InterruptedException
|
||||||
|
checkAutoCloseable(resource.pos(), localEnv, resource.type);
|
||||||
|
|
||||||
VarSymbol var = (VarSymbol)TreeInfo.symbolFor(resource);
|
VarSymbol var = (VarSymbol)TreeInfo.symbolFor(resource);
|
||||||
var.setData(ElementKind.RESOURCE_VARIABLE);
|
var.setData(ElementKind.RESOURCE_VARIABLE);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1127,6 +1131,35 @@ public class Attr extends JCTree.Visitor {
|
||||||
result = null;
|
result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkAutoCloseable(DiagnosticPosition pos, Env<AttrContext> env, Type resource) {
|
||||||
|
if (!resource.isErroneous() &&
|
||||||
|
types.asSuper(resource, syms.autoCloseableType.tsym) != null) {
|
||||||
|
Symbol close = syms.noSymbol;
|
||||||
|
boolean prevDeferDiags = log.deferDiagnostics;
|
||||||
|
Queue<JCDiagnostic> prevDeferredDiags = log.deferredDiagnostics;
|
||||||
|
try {
|
||||||
|
log.deferDiagnostics = true;
|
||||||
|
log.deferredDiagnostics = ListBuffer.lb();
|
||||||
|
close = rs.resolveQualifiedMethod(pos,
|
||||||
|
env,
|
||||||
|
resource,
|
||||||
|
names.close,
|
||||||
|
List.<Type>nil(),
|
||||||
|
List.<Type>nil());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
log.deferDiagnostics = prevDeferDiags;
|
||||||
|
log.deferredDiagnostics = prevDeferredDiags;
|
||||||
|
}
|
||||||
|
if (close.kind == MTH &&
|
||||||
|
close.overrides(syms.autoCloseableClose, resource.tsym, types, true) &&
|
||||||
|
chk.isHandled(syms.interruptedExceptionType, types.memberType(resource, close).getThrownTypes()) &&
|
||||||
|
env.info.lint.isEnabled(LintCategory.TRY)) {
|
||||||
|
log.warning(LintCategory.TRY, pos, "try.resource.throws.interrupted.exc", resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void visitConditional(JCConditional tree) {
|
public void visitConditional(JCConditional tree) {
|
||||||
attribExpr(tree.cond, env, syms.booleanType);
|
attribExpr(tree.cond, env, syms.booleanType);
|
||||||
attribExpr(tree.truepart, env);
|
attribExpr(tree.truepart, env);
|
||||||
|
@ -3169,6 +3202,9 @@ public class Attr extends JCTree.Visitor {
|
||||||
// method conform to the method they implement.
|
// method conform to the method they implement.
|
||||||
chk.checkImplementations(tree);
|
chk.checkImplementations(tree);
|
||||||
|
|
||||||
|
//check that a resource implementing AutoCloseable cannot throw InterruptedException
|
||||||
|
checkAutoCloseable(tree.pos(), env, c.type);
|
||||||
|
|
||||||
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
|
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
|
||||||
// Attribute declaration
|
// Attribute declaration
|
||||||
attribStat(l.head, env);
|
attribStat(l.head, env);
|
||||||
|
|
|
@ -1244,6 +1244,10 @@ compiler.warn.try.explicit.close.call=\
|
||||||
compiler.warn.try.resource.not.referenced=\
|
compiler.warn.try.resource.not.referenced=\
|
||||||
auto-closeable resource {0} is never referenced in body of corresponding try statement
|
auto-closeable resource {0} is never referenced in body of corresponding try statement
|
||||||
|
|
||||||
|
# 0: type
|
||||||
|
compiler.warn.try.resource.throws.interrupted.exc=\
|
||||||
|
auto-closeable resource {0} has a member method close() that could throw InterruptedException
|
||||||
|
|
||||||
compiler.warn.unchecked.assign=\
|
compiler.warn.unchecked.assign=\
|
||||||
unchecked assignment: {0} to {1}
|
unchecked assignment: {0} to {1}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, 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 7027157
|
||||||
|
* @summary Project Coin: javac warnings for AutoCloseable.close throwing InterruptedException
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 InterruptedExceptionTest {
|
||||||
|
|
||||||
|
enum XlintOption {
|
||||||
|
NONE("none"),
|
||||||
|
TRY("try");
|
||||||
|
|
||||||
|
String opt;
|
||||||
|
|
||||||
|
XlintOption(String opt) {
|
||||||
|
this.opt = opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getXlintOption() {
|
||||||
|
return "-Xlint:" + opt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SuppressLevel {
|
||||||
|
NONE,
|
||||||
|
SUPPRESS;
|
||||||
|
|
||||||
|
String getSuppressAnno() {
|
||||||
|
return this == SUPPRESS ?
|
||||||
|
"@SuppressWarnings(\"try\")" :
|
||||||
|
"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ClassKind {
|
||||||
|
ABSTRACT_CLASS("abstract class", "implements", false),
|
||||||
|
CLASS("class", "implements", true),
|
||||||
|
INTERFACE("interface", "extends", false);
|
||||||
|
|
||||||
|
String kindName;
|
||||||
|
String extendsClause;
|
||||||
|
boolean hasBody;
|
||||||
|
|
||||||
|
private ClassKind(String kindName, String extendsClause, boolean hasBody) {
|
||||||
|
this.kindName = kindName;
|
||||||
|
this.extendsClause = extendsClause;
|
||||||
|
this.hasBody = hasBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getBody() {
|
||||||
|
return hasBody ? "{}" : ";";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ExceptionKind {
|
||||||
|
NONE("", false),
|
||||||
|
EXCEPTION("Exception", true),
|
||||||
|
INTERRUPTED_EXCEPTION("InterruptedException", true),
|
||||||
|
ILLEGAL_ARGUMENT_EXCEPTION("IllegalArgumentException", false),
|
||||||
|
X("X", false);
|
||||||
|
|
||||||
|
String exName;
|
||||||
|
boolean shouldWarn;
|
||||||
|
|
||||||
|
private ExceptionKind(String exName, boolean shouldWarn) {
|
||||||
|
this.exName = exName;
|
||||||
|
this.shouldWarn = shouldWarn;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getThrowsClause() {
|
||||||
|
return this == NONE ? "" : "throws " + exName;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getTypeArguments(ExceptionKind decl) {
|
||||||
|
return (decl != X || this == NONE) ? "" : "<" + exName + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
String getTypeParameter() {
|
||||||
|
return this == X ? "<X extends Exception>" : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String... args) throws Exception {
|
||||||
|
|
||||||
|
//create default shared JavaCompiler - reused across multiple compilations
|
||||||
|
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
|
||||||
|
StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
|
||||||
|
|
||||||
|
for (XlintOption xlint : XlintOption.values()) {
|
||||||
|
for (SuppressLevel suppress_decl : SuppressLevel.values()) {
|
||||||
|
for (SuppressLevel suppress_use : SuppressLevel.values()) {
|
||||||
|
for (ClassKind ck : ClassKind.values()) {
|
||||||
|
for (ExceptionKind ek_decl : ExceptionKind.values()) {
|
||||||
|
for (ExceptionKind ek_use : ExceptionKind.values()) {
|
||||||
|
new InterruptedExceptionTest(xlint, suppress_decl,
|
||||||
|
suppress_use, ck, ek_decl, ek_use).run(comp, fm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XlintOption xlint;
|
||||||
|
SuppressLevel suppress_decl;
|
||||||
|
SuppressLevel suppress_use;
|
||||||
|
ClassKind ck;
|
||||||
|
ExceptionKind ek_decl;
|
||||||
|
ExceptionKind ek_use;
|
||||||
|
JavaSource source;
|
||||||
|
DiagnosticChecker diagChecker;
|
||||||
|
|
||||||
|
InterruptedExceptionTest(XlintOption xlint, SuppressLevel suppress_decl, SuppressLevel suppress_use,
|
||||||
|
ClassKind ck, ExceptionKind ek_decl, ExceptionKind ek_use) {
|
||||||
|
this.xlint = xlint;
|
||||||
|
this.suppress_decl = suppress_decl;
|
||||||
|
this.suppress_use = suppress_use;
|
||||||
|
this.ck = ck;
|
||||||
|
this.ek_decl = ek_decl;
|
||||||
|
this.ek_use = ek_use;
|
||||||
|
this.source = new JavaSource();
|
||||||
|
this.diagChecker = new DiagnosticChecker();
|
||||||
|
}
|
||||||
|
|
||||||
|
class JavaSource extends SimpleJavaFileObject {
|
||||||
|
|
||||||
|
String template = "#S1 #CK Resource#G #EC AutoCloseable {\n" +
|
||||||
|
"public void close() #TK #BK\n" +
|
||||||
|
"}\n" +
|
||||||
|
"class Test {\n" +
|
||||||
|
"#S2 <X> void test() {\n" +
|
||||||
|
"try (Resource#PK r = null) { }\n" +
|
||||||
|
"}\n" +
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
String source;
|
||||||
|
|
||||||
|
public JavaSource() {
|
||||||
|
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
|
||||||
|
source = template.replace("#S1", suppress_decl.getSuppressAnno())
|
||||||
|
.replace("#S2", suppress_use.getSuppressAnno())
|
||||||
|
.replace("#CK", ck.kindName)
|
||||||
|
.replace("#EC", ck.extendsClause)
|
||||||
|
.replace("#G", ek_decl.getTypeParameter())
|
||||||
|
.replace("#TK", ek_decl.getThrowsClause())
|
||||||
|
.replace("#BK", ck.getBody())
|
||||||
|
.replace("#PK", ek_use.getTypeArguments(ek_decl));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
|
||||||
|
JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
|
||||||
|
Arrays.asList(xlint.getXlintOption()), null, Arrays.asList(source));
|
||||||
|
ct.analyze();
|
||||||
|
check();
|
||||||
|
}
|
||||||
|
|
||||||
|
void check() {
|
||||||
|
|
||||||
|
boolean shouldWarnDecl = ek_decl.shouldWarn &&
|
||||||
|
xlint == XlintOption.TRY &&
|
||||||
|
suppress_decl != SuppressLevel.SUPPRESS;
|
||||||
|
|
||||||
|
boolean shouldWarnUse = (ek_decl.shouldWarn ||
|
||||||
|
((ek_use.shouldWarn || ek_use == ExceptionKind.NONE) && ek_decl == ExceptionKind.X)) &&
|
||||||
|
xlint == XlintOption.TRY &&
|
||||||
|
suppress_use != SuppressLevel.SUPPRESS;
|
||||||
|
|
||||||
|
int foundWarnings = 0;
|
||||||
|
|
||||||
|
if (shouldWarnDecl) foundWarnings++;
|
||||||
|
if (shouldWarnUse) foundWarnings++;
|
||||||
|
|
||||||
|
if (foundWarnings != diagChecker.tryWarnFound) {
|
||||||
|
throw new Error("invalid diagnostics for source:\n" +
|
||||||
|
source.getCharContent(true) +
|
||||||
|
"\nOptions: " + xlint.getXlintOption() +
|
||||||
|
"\nFound warnings: " + diagChecker.tryWarnFound +
|
||||||
|
"\nExpected decl warning: " + shouldWarnDecl +
|
||||||
|
"\nExpected use warning: " + shouldWarnUse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
|
||||||
|
|
||||||
|
int tryWarnFound;
|
||||||
|
|
||||||
|
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
|
||||||
|
if (diagnostic.getKind() == Diagnostic.Kind.WARNING &&
|
||||||
|
diagnostic.getCode().contains("try.resource.throws.interrupted.exc")) {
|
||||||
|
tryWarnFound++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// key: compiler.warn.try.resource.throws.interrupted.exc
|
||||||
|
// options: -Xlint:try
|
||||||
|
|
||||||
|
class TryResourceThrowsInterruptedException implements AutoCloseable {
|
||||||
|
public void close() throws InterruptedException {}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue