7097436: Project Coin: duplicate varargs warnings on method annotated with @SafeVarargs
Duplicate aliasing check during subtyping leads to spurious varargs diagnostic Reviewed-by: jjg
This commit is contained in:
parent
b6f671b41e
commit
1a335eb047
@ -278,7 +278,6 @@ public class Types {
|
||||
boolean tPrimitive = t.isPrimitive();
|
||||
boolean sPrimitive = s.isPrimitive();
|
||||
if (tPrimitive == sPrimitive) {
|
||||
checkUnsafeVarargsConversion(t, s, warn);
|
||||
return isSubtypeUnchecked(t, s, warn);
|
||||
}
|
||||
if (!allowBoxing) return false;
|
||||
@ -286,27 +285,6 @@ public class Types {
|
||||
? isSubtype(boxedClass(t).type, s)
|
||||
: isSubtype(unboxedType(t), s);
|
||||
}
|
||||
//where
|
||||
private void checkUnsafeVarargsConversion(Type t, Type s, Warner warn) {
|
||||
if (t.tag != ARRAY || isReifiable(t)) return;
|
||||
ArrayType from = (ArrayType)t;
|
||||
boolean shouldWarn = false;
|
||||
switch (s.tag) {
|
||||
case ARRAY:
|
||||
ArrayType to = (ArrayType)s;
|
||||
shouldWarn = from.isVarargs() &&
|
||||
!to.isVarargs() &&
|
||||
!isReifiable(from);
|
||||
break;
|
||||
case CLASS:
|
||||
shouldWarn = from.isVarargs() &&
|
||||
isSubtype(from, s);
|
||||
break;
|
||||
}
|
||||
if (shouldWarn) {
|
||||
warn.warn(LintCategory.VARARGS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is t a subtype of or convertiable via boxing/unboxing
|
||||
@ -328,42 +306,63 @@ public class Types {
|
||||
* Is t an unchecked subtype of s?
|
||||
*/
|
||||
public boolean isSubtypeUnchecked(Type t, Type s, Warner warn) {
|
||||
if (t.tag == ARRAY && s.tag == ARRAY) {
|
||||
if (((ArrayType)t).elemtype.tag <= lastBaseTag) {
|
||||
return isSameType(elemtype(t), elemtype(s));
|
||||
} else {
|
||||
ArrayType from = (ArrayType)t;
|
||||
ArrayType to = (ArrayType)s;
|
||||
if (from.isVarargs() &&
|
||||
!to.isVarargs() &&
|
||||
!isReifiable(from)) {
|
||||
warn.warn(LintCategory.VARARGS);
|
||||
boolean result = isSubtypeUncheckedInternal(t, s, warn);
|
||||
if (result) {
|
||||
checkUnsafeVarargsConversion(t, s, warn);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
//where
|
||||
private boolean isSubtypeUncheckedInternal(Type t, Type s, Warner warn) {
|
||||
if (t.tag == ARRAY && s.tag == ARRAY) {
|
||||
if (((ArrayType)t).elemtype.tag <= lastBaseTag) {
|
||||
return isSameType(elemtype(t), elemtype(s));
|
||||
} else {
|
||||
return isSubtypeUnchecked(elemtype(t), elemtype(s), warn);
|
||||
}
|
||||
return isSubtypeUnchecked(elemtype(t), elemtype(s), warn);
|
||||
}
|
||||
} else if (isSubtype(t, s)) {
|
||||
return true;
|
||||
}
|
||||
else if (t.tag == TYPEVAR) {
|
||||
return isSubtypeUnchecked(t.getUpperBound(), s, warn);
|
||||
}
|
||||
else if (s.tag == UNDETVAR) {
|
||||
UndetVar uv = (UndetVar)s;
|
||||
if (uv.inst != null)
|
||||
return isSubtypeUnchecked(t, uv.inst, warn);
|
||||
}
|
||||
else if (!s.isRaw()) {
|
||||
Type t2 = asSuper(t, s.tsym);
|
||||
if (t2 != null && t2.isRaw()) {
|
||||
if (isReifiable(s))
|
||||
warn.silentWarn(LintCategory.UNCHECKED);
|
||||
else
|
||||
warn.warn(LintCategory.UNCHECKED);
|
||||
} else if (isSubtype(t, s)) {
|
||||
return true;
|
||||
}
|
||||
else if (t.tag == TYPEVAR) {
|
||||
return isSubtypeUnchecked(t.getUpperBound(), s, warn);
|
||||
}
|
||||
else if (s.tag == UNDETVAR) {
|
||||
UndetVar uv = (UndetVar)s;
|
||||
if (uv.inst != null)
|
||||
return isSubtypeUnchecked(t, uv.inst, warn);
|
||||
}
|
||||
else if (!s.isRaw()) {
|
||||
Type t2 = asSuper(t, s.tsym);
|
||||
if (t2 != null && t2.isRaw()) {
|
||||
if (isReifiable(s))
|
||||
warn.silentWarn(LintCategory.UNCHECKED);
|
||||
else
|
||||
warn.warn(LintCategory.UNCHECKED);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkUnsafeVarargsConversion(Type t, Type s, Warner warn) {
|
||||
if (t.tag != ARRAY || isReifiable(t)) return;
|
||||
ArrayType from = (ArrayType)t;
|
||||
boolean shouldWarn = false;
|
||||
switch (s.tag) {
|
||||
case ARRAY:
|
||||
ArrayType to = (ArrayType)s;
|
||||
shouldWarn = from.isVarargs() &&
|
||||
!to.isVarargs() &&
|
||||
!isReifiable(from);
|
||||
break;
|
||||
case CLASS:
|
||||
shouldWarn = from.isVarargs();
|
||||
break;
|
||||
}
|
||||
if (shouldWarn) {
|
||||
warn.warn(LintCategory.VARARGS);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is t a subtype of s?<br>
|
||||
|
18
langtools/test/tools/javac/varargs/7097436/T7097436.java
Normal file
18
langtools/test/tools/javac/varargs/7097436/T7097436.java
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* @test /nodynamiccopyright/
|
||||
* @bug 7097436
|
||||
* @summary ClassCastException occurs in assignment expressions without any heap pollutions
|
||||
* @compile/fail/ref=T7097436.out -Xlint:varargs -Werror -XDrawDiagnostics T7097436.java
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class T7097436 {
|
||||
@SafeVarargs
|
||||
static void m(List<String>... ls) {
|
||||
Object o = ls; //warning
|
||||
Object[] oArr = ls; //warning
|
||||
String s = ls; // no warning
|
||||
Integer[] iArr = ls; // no warning
|
||||
}
|
||||
}
|
6
langtools/test/tools/javac/varargs/7097436/T7097436.out
Normal file
6
langtools/test/tools/javac/varargs/7097436/T7097436.out
Normal file
@ -0,0 +1,6 @@
|
||||
T7097436.java:13:20: compiler.warn.varargs.unsafe.use.varargs.param: ls
|
||||
T7097436.java:14:25: compiler.warn.varargs.unsafe.use.varargs.param: ls
|
||||
T7097436.java:15:20: compiler.err.prob.found.req: (compiler.misc.incompatible.types), java.util.List<java.lang.String>[], java.lang.String
|
||||
T7097436.java:16:26: compiler.err.prob.found.req: (compiler.misc.incompatible.types), java.util.List<java.lang.String>[], java.lang.Integer[]
|
||||
2 errors
|
||||
2 warnings
|
@ -23,7 +23,7 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 6993978
|
||||
* @bug 6993978 7097436
|
||||
* @summary Project Coin: Annotation to reduce varargs warnings
|
||||
* @author mcimadamore
|
||||
* @run main Warn5
|
||||
@ -31,8 +31,8 @@
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.tools.javac.api.JavacTool;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
@ -95,7 +95,6 @@ public class Warn5 {
|
||||
METHOD("void m"),
|
||||
CONSTRUCTOR("Test");
|
||||
|
||||
|
||||
String name;
|
||||
|
||||
MethodKind(String name) {
|
||||
@ -155,7 +154,124 @@ public class Warn5 {
|
||||
}
|
||||
}
|
||||
|
||||
static class JavaSource extends SimpleJavaFileObject {
|
||||
enum WarningKind {
|
||||
UNSAFE_BODY,
|
||||
UNSAFE_DECL,
|
||||
MALFORMED_SAFEVARARGS,
|
||||
REDUNDANT_SAFEVARARGS;
|
||||
}
|
||||
|
||||
// Create a single file manager and reuse it for each compile to save time.
|
||||
static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
for (SourceLevel sourceLevel : SourceLevel.values()) {
|
||||
for (XlintOption xlint : XlintOption.values()) {
|
||||
for (TrustMe trustMe : TrustMe.values()) {
|
||||
for (SuppressLevel suppressLevel : SuppressLevel.values()) {
|
||||
for (ModifierKind modKind : ModifierKind.values()) {
|
||||
for (MethodKind methKind : MethodKind.values()) {
|
||||
for (SignatureKind sig : SignatureKind.values()) {
|
||||
for (BodyKind body : BodyKind.values()) {
|
||||
new Warn5(sourceLevel,
|
||||
xlint,
|
||||
trustMe,
|
||||
suppressLevel,
|
||||
modKind,
|
||||
methKind,
|
||||
sig,
|
||||
body).test();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final SourceLevel sourceLevel;
|
||||
final XlintOption xlint;
|
||||
final TrustMe trustMe;
|
||||
final SuppressLevel suppressLevel;
|
||||
final ModifierKind modKind;
|
||||
final MethodKind methKind;
|
||||
final SignatureKind sig;
|
||||
final BodyKind body;
|
||||
final JavaSource source;
|
||||
final DiagnosticChecker dc;
|
||||
|
||||
public Warn5(SourceLevel sourceLevel, XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind, MethodKind methKind, SignatureKind sig, BodyKind body) {
|
||||
this.sourceLevel = sourceLevel;
|
||||
this.xlint = xlint;
|
||||
this.trustMe = trustMe;
|
||||
this.suppressLevel = suppressLevel;
|
||||
this.modKind = modKind;
|
||||
this.methKind = methKind;
|
||||
this.sig = sig;
|
||||
this.body = body;
|
||||
this.source = new JavaSource();
|
||||
this.dc = new DiagnosticChecker();
|
||||
}
|
||||
|
||||
void test() throws Exception {
|
||||
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
|
||||
JavacTask ct = (JavacTask)tool.getTask(null, fm, dc,
|
||||
Arrays.asList(xlint.getXlintOption(), "-source", sourceLevel.sourceKey), null, Arrays.asList(source));
|
||||
ct.analyze();
|
||||
check();
|
||||
}
|
||||
|
||||
void check() {
|
||||
|
||||
EnumSet<WarningKind> expectedWarnings = EnumSet.noneOf(WarningKind.class);
|
||||
|
||||
if (sourceLevel == SourceLevel.JDK_7 &&
|
||||
trustMe == TrustMe.TRUST &&
|
||||
suppressLevel != SuppressLevel.VARARGS &&
|
||||
xlint != XlintOption.NONE &&
|
||||
sig.isVarargs && !sig.isReifiableArg && body.hasAliasing &&
|
||||
(methKind == MethodKind.CONSTRUCTOR || (methKind == MethodKind.METHOD && modKind != ModifierKind.NONE))) {
|
||||
expectedWarnings.add(WarningKind.UNSAFE_BODY);
|
||||
}
|
||||
|
||||
if (sourceLevel == SourceLevel.JDK_7 &&
|
||||
trustMe == TrustMe.DONT_TRUST &&
|
||||
sig.isVarargs &&
|
||||
!sig.isReifiableArg &&
|
||||
xlint == XlintOption.ALL) {
|
||||
expectedWarnings.add(WarningKind.UNSAFE_DECL);
|
||||
}
|
||||
|
||||
if (sourceLevel == SourceLevel.JDK_7 &&
|
||||
trustMe == TrustMe.TRUST &&
|
||||
(!sig.isVarargs ||
|
||||
(modKind == ModifierKind.NONE && methKind == MethodKind.METHOD))) {
|
||||
expectedWarnings.add(WarningKind.MALFORMED_SAFEVARARGS);
|
||||
}
|
||||
|
||||
if (sourceLevel == SourceLevel.JDK_7 &&
|
||||
trustMe == TrustMe.TRUST &&
|
||||
xlint != XlintOption.NONE &&
|
||||
suppressLevel != SuppressLevel.VARARGS &&
|
||||
(modKind != ModifierKind.NONE || methKind == MethodKind.CONSTRUCTOR) &&
|
||||
sig.isVarargs &&
|
||||
sig.isReifiableArg) {
|
||||
expectedWarnings.add(WarningKind.REDUNDANT_SAFEVARARGS);
|
||||
}
|
||||
|
||||
if (!expectedWarnings.containsAll(dc.warnings) ||
|
||||
!dc.warnings.containsAll(expectedWarnings)) {
|
||||
throw new Error("invalid diagnostics for source:\n" +
|
||||
source.getCharContent(true) +
|
||||
"\nOptions: " + xlint.getXlintOption() +
|
||||
"\nExpected warnings: " + expectedWarnings +
|
||||
"\nFound warnings: " + dc.warnings);
|
||||
}
|
||||
}
|
||||
|
||||
class JavaSource extends SimpleJavaFileObject {
|
||||
|
||||
String template = "import com.sun.tools.javac.api.*;\n" +
|
||||
"import java.util.List;\n" +
|
||||
@ -167,12 +283,11 @@ public class Warn5 {
|
||||
|
||||
String source;
|
||||
|
||||
public JavaSource(TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind,
|
||||
MethodKind methKind, SignatureKind meth, BodyKind body) {
|
||||
public JavaSource() {
|
||||
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
|
||||
source = template.replace("#T", trustMe.anno).
|
||||
replace("#S", suppressLevel.getSuppressAnno()).
|
||||
replace("#M", meth.getSignature(modKind, methKind)).
|
||||
replace("#M", sig.getSignature(modKind, methKind)).
|
||||
replace("#B", body.body);
|
||||
}
|
||||
|
||||
@ -182,117 +297,34 @@ public class Warn5 {
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
for (SourceLevel sourceLevel : SourceLevel.values()) {
|
||||
for (XlintOption xlint : XlintOption.values()) {
|
||||
for (TrustMe trustMe : TrustMe.values()) {
|
||||
for (SuppressLevel suppressLevel : SuppressLevel.values()) {
|
||||
for (ModifierKind modKind : ModifierKind.values()) {
|
||||
for (MethodKind methKind : MethodKind.values()) {
|
||||
for (SignatureKind sig : SignatureKind.values()) {
|
||||
for (BodyKind body : BodyKind.values()) {
|
||||
test(sourceLevel,
|
||||
xlint,
|
||||
trustMe,
|
||||
suppressLevel,
|
||||
modKind,
|
||||
methKind,
|
||||
sig,
|
||||
body);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
|
||||
|
||||
// Create a single file manager and reuse it for each compile to save time.
|
||||
static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
|
||||
|
||||
static void test(SourceLevel sourceLevel, XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel,
|
||||
ModifierKind modKind, MethodKind methKind, SignatureKind sig, BodyKind body) throws Exception {
|
||||
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
|
||||
JavaSource source = new JavaSource(trustMe, suppressLevel, modKind, methKind, sig, body);
|
||||
DiagnosticChecker dc = new DiagnosticChecker();
|
||||
JavacTask ct = (JavacTask)tool.getTask(null, fm, dc,
|
||||
Arrays.asList(xlint.getXlintOption(), "-source", sourceLevel.sourceKey), null, Arrays.asList(source));
|
||||
ct.analyze();
|
||||
check(sourceLevel, dc, source, xlint, trustMe,
|
||||
suppressLevel, modKind, methKind, sig, body);
|
||||
}
|
||||
|
||||
static void check(SourceLevel sourceLevel, DiagnosticChecker dc, JavaSource source,
|
||||
XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind,
|
||||
MethodKind methKind, SignatureKind meth, BodyKind body) {
|
||||
|
||||
boolean hasPotentiallyUnsafeBody = sourceLevel == SourceLevel.JDK_7 &&
|
||||
trustMe == TrustMe.TRUST &&
|
||||
suppressLevel != SuppressLevel.VARARGS &&
|
||||
xlint != XlintOption.NONE &&
|
||||
meth.isVarargs && !meth.isReifiableArg && body.hasAliasing &&
|
||||
(methKind == MethodKind.CONSTRUCTOR || (methKind == MethodKind.METHOD && modKind != ModifierKind.NONE));
|
||||
|
||||
boolean hasPotentiallyPollutingDecl = sourceLevel == SourceLevel.JDK_7 &&
|
||||
trustMe == TrustMe.DONT_TRUST &&
|
||||
meth.isVarargs &&
|
||||
!meth.isReifiableArg &&
|
||||
xlint == XlintOption.ALL;
|
||||
|
||||
boolean hasMalformedAnnoInDecl = sourceLevel == SourceLevel.JDK_7 &&
|
||||
trustMe == TrustMe.TRUST &&
|
||||
(!meth.isVarargs ||
|
||||
(modKind == ModifierKind.NONE && methKind == MethodKind.METHOD));
|
||||
|
||||
boolean hasRedundantAnnoInDecl = sourceLevel == SourceLevel.JDK_7 &&
|
||||
trustMe == TrustMe.TRUST &&
|
||||
xlint != XlintOption.NONE &&
|
||||
suppressLevel != SuppressLevel.VARARGS &&
|
||||
(modKind != ModifierKind.NONE || methKind == MethodKind.CONSTRUCTOR) &&
|
||||
meth.isVarargs &&
|
||||
meth.isReifiableArg;
|
||||
|
||||
if (hasPotentiallyUnsafeBody != dc.hasPotentiallyUnsafeBody ||
|
||||
hasPotentiallyPollutingDecl != dc.hasPotentiallyPollutingDecl ||
|
||||
hasMalformedAnnoInDecl != dc.hasMalformedAnnoInDecl ||
|
||||
hasRedundantAnnoInDecl != dc.hasRedundantAnnoInDecl) {
|
||||
throw new Error("invalid diagnostics for source:\n" +
|
||||
source.getCharContent(true) +
|
||||
"\nOptions: " + xlint.getXlintOption() +
|
||||
"\nExpected potentially unsafe body warning: " + hasPotentiallyUnsafeBody +
|
||||
"\nExpected potentially polluting decl warning: " + hasPotentiallyPollutingDecl +
|
||||
"\nExpected malformed anno error: " + hasMalformedAnnoInDecl +
|
||||
"\nExpected redundant anno warning: " + hasRedundantAnnoInDecl +
|
||||
"\nFound potentially unsafe body warning: " + dc.hasPotentiallyUnsafeBody +
|
||||
"\nFound potentially polluting decl warning: " + dc.hasPotentiallyPollutingDecl +
|
||||
"\nFound malformed anno error: " + dc.hasMalformedAnnoInDecl +
|
||||
"\nFound redundant anno warning: " + dc.hasRedundantAnnoInDecl);
|
||||
}
|
||||
}
|
||||
|
||||
static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
|
||||
|
||||
boolean hasPotentiallyUnsafeBody = false;
|
||||
boolean hasPotentiallyPollutingDecl = false;
|
||||
boolean hasMalformedAnnoInDecl = false;
|
||||
boolean hasRedundantAnnoInDecl = false;
|
||||
EnumSet<WarningKind> warnings = EnumSet.noneOf(WarningKind.class);
|
||||
|
||||
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
|
||||
if (diagnostic.getKind() == Diagnostic.Kind.WARNING) {
|
||||
if (diagnostic.getCode().contains("unsafe.use.varargs.param")) {
|
||||
hasPotentiallyUnsafeBody = true;
|
||||
setWarning(WarningKind.UNSAFE_BODY);
|
||||
} else if (diagnostic.getCode().contains("redundant.trustme")) {
|
||||
hasRedundantAnnoInDecl = true;
|
||||
setWarning(WarningKind.REDUNDANT_SAFEVARARGS);
|
||||
}
|
||||
} else if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING &&
|
||||
diagnostic.getCode().contains("varargs.non.reifiable.type")) {
|
||||
hasPotentiallyPollutingDecl = true;
|
||||
setWarning(WarningKind.UNSAFE_DECL);
|
||||
} else if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
|
||||
diagnostic.getCode().contains("invalid.trustme")) {
|
||||
hasMalformedAnnoInDecl = true;
|
||||
setWarning(WarningKind.MALFORMED_SAFEVARARGS);
|
||||
}
|
||||
}
|
||||
|
||||
void setWarning(WarningKind wk) {
|
||||
if (!warnings.add(wk)) {
|
||||
throw new AssertionError("Duplicate warning of kind " + wk + " in source:\n" + source);
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasWarning(WarningKind wk) {
|
||||
return warnings.contains(wk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user