6993978: Project Coin: Compiler support of annotation to reduce varargs warnings

Reviewed-by: jjg, darcy
This commit is contained in:
Maurizio Cimadamore 2010-12-13 15:11:00 -08:00
parent 87db401cf5
commit 6ef5228632
30 changed files with 718 additions and 300 deletions

View File

@ -177,6 +177,9 @@ public enum Source {
public boolean allowStringsInSwitch() {
return compareTo(JDK1_7) >= 0;
}
public boolean allowSimplifiedVarargs() {
return compareTo(JDK1_7) >= 0;
}
public static SourceVersion toSourceVersion(Source source) {
switch(source) {
case JDK1_2:

View File

@ -154,6 +154,7 @@ public class Symtab {
public final Type proprietaryType;
public final Type systemType;
public final Type autoCloseableType;
public final Type trustMeType;
/** The symbol representing the length field of an array.
*/
@ -461,6 +462,7 @@ public class Symtab {
new MethodType(List.<Type>nil(), voidType,
List.of(exceptionType), methodClass),
autoCloseableType.tsym);
trustMeType = enterClass("java.lang.SafeVarargs");
synthesizeEmptyInterfaceIfMissing(cloneableType);
synthesizeEmptyInterfaceIfMissing(serializableType);

View File

@ -754,6 +754,10 @@ public class Type implements PrimitiveType {
return (ARRAY << 5) + elemtype.hashCode();
}
public boolean isVarargs() {
return false;
}
public List<Type> allparams() { return elemtype.allparams(); }
public boolean isErroneous() {
@ -768,6 +772,15 @@ public class Type implements PrimitiveType {
return elemtype.isRaw();
}
public ArrayType makeVarargs() {
return new ArrayType(elemtype, tsym) {
@Override
public boolean isVarargs() {
return true;
}
};
}
public Type map(Mapping f) {
Type elemtype1 = f.apply(elemtype);
if (elemtype1 == elemtype) return this;

View File

@ -33,6 +33,7 @@ import com.sun.tools.javac.util.List;
import com.sun.tools.javac.jvm.ClassReader;
import com.sun.tools.javac.code.Attribute.RetentionPolicy;
import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.comp.Check;
import static com.sun.tools.javac.code.Type.*;
@ -272,13 +273,36 @@ public class Types {
public boolean isConvertible(Type t, Type s, Warner warn) {
boolean tPrimitive = t.isPrimitive();
boolean sPrimitive = s.isPrimitive();
if (tPrimitive == sPrimitive)
if (tPrimitive == sPrimitive) {
checkUnsafeVarargsConversion(t, s, warn);
return isSubtypeUnchecked(t, s, warn);
}
if (!allowBoxing) return false;
return tPrimitive
? 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
@ -301,9 +325,18 @@ public class Types {
*/
public boolean isSubtypeUnchecked(Type t, Type s, Warner warn) {
if (t.tag == ARRAY && s.tag == ARRAY) {
return (((ArrayType)t).elemtype.tag <= lastBaseTag)
? isSameType(elemtype(t), elemtype(s))
: isSubtypeUnchecked(elemtype(t), elemtype(s), warn);
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);
}
return isSubtypeUnchecked(elemtype(t), elemtype(s), warn);
}
} else if (isSubtype(t, s)) {
return true;
}
@ -319,9 +352,9 @@ public class Types {
Type t2 = asSuper(t, s.tsym);
if (t2 != null && t2.isRaw()) {
if (isReifiable(s))
warn.silentUnchecked();
warn.silentWarn(LintCategory.UNCHECKED);
else
warn.warnUnchecked();
warn.warn(LintCategory.UNCHECKED);
return true;
}
}
@ -922,6 +955,7 @@ public class Types {
if (warn != warnStack.head) {
try {
warnStack = warnStack.prepend(warn);
checkUnsafeVarargsConversion(t, s, warn);
return isCastable.visit(t,s);
} finally {
warnStack = warnStack.tail;
@ -964,7 +998,7 @@ public class Types {
if (s.tag == TYPEVAR) {
if (isCastable(t, s.getUpperBound(), Warner.noWarnings)) {
warnStack.head.warnUnchecked();
warnStack.head.warn(LintCategory.UNCHECKED);
return true;
} else {
return false;
@ -980,8 +1014,8 @@ public class Types {
if (!visit(intf, s))
return false;
}
if (warnStack.head.unchecked == true)
oldWarner.warnUnchecked();
if (warnStack.head.hasLint(LintCategory.UNCHECKED))
oldWarner.warn(LintCategory.UNCHECKED);
return true;
}
@ -996,13 +1030,13 @@ public class Types {
|| isSubtype(erasure(s), erasure(t))) {
if (!upcast && s.tag == ARRAY) {
if (!isReifiable(s))
warnStack.head.warnUnchecked();
warnStack.head.warn(LintCategory.UNCHECKED);
return true;
} else if (s.isRaw()) {
return true;
} else if (t.isRaw()) {
if (!isUnbounded(s))
warnStack.head.warnUnchecked();
warnStack.head.warn(LintCategory.UNCHECKED);
return true;
}
// Assume |a| <: |b|
@ -1035,7 +1069,7 @@ public class Types {
&& !disjointTypes(aLow.allparams(), lowSub.allparams())) {
if (upcast ? giveWarning(a, b) :
giveWarning(b, a))
warnStack.head.warnUnchecked();
warnStack.head.warn(LintCategory.UNCHECKED);
return true;
}
}
@ -1072,7 +1106,7 @@ public class Types {
return true;
case TYPEVAR:
if (isCastable(s, t, Warner.noWarnings)) {
warnStack.head.warnUnchecked();
warnStack.head.warn(LintCategory.UNCHECKED);
return true;
} else {
return false;
@ -1101,7 +1135,7 @@ public class Types {
if (isSubtype(t, s)) {
return true;
} else if (isCastable(t.bound, s, Warner.noWarnings)) {
warnStack.head.warnUnchecked();
warnStack.head.warn(LintCategory.UNCHECKED);
return true;
} else {
return false;
@ -2906,7 +2940,7 @@ public class Types {
return true;
if (!isSubtype(r1.getReturnType(), erasure(r2res)))
return false;
warner.warnUnchecked();
warner.warn(LintCategory.UNCHECKED);
return true;
}
@ -3122,7 +3156,7 @@ public class Types {
commonSupers = commonSupers.tail;
}
if (giveWarning && !isReifiable(reverse ? from : to))
warn.warnUnchecked();
warn.warn(LintCategory.UNCHECKED);
if (!source.allowCovariantReturns())
// reject if there is a common method signature with
// incompatible return types.
@ -3156,7 +3190,7 @@ public class Types {
chk.checkCompatibleAbstracts(warn.pos(), from, to);
if (!isReifiable(target) &&
(reverse ? giveWarning(t2, t1) : giveWarning(t1, t2)))
warn.warnUnchecked();
warn.warn(LintCategory.UNCHECKED);
return true;
}

View File

@ -38,6 +38,7 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.code.Type.*;
@ -669,6 +670,7 @@ public class Attr extends JCTree.Visitor {
Lint lint = env.info.lint.augment(m.attributes_field, m.flags());
Lint prevLint = chk.setLint(lint);
MethodSymbol prevMethod = chk.setMethod(m);
try {
chk.checkDeprecatedAnnotation(tree.pos(), m);
@ -700,7 +702,7 @@ public class Attr extends JCTree.Visitor {
attribStat(l.head, localEnv);
}
chk.checkVarargMethodDecl(tree);
chk.checkVarargsMethodDecl(localEnv, tree);
// Check that type parameters are well-formed.
chk.validate(tree.typarams, localEnv);
@ -789,6 +791,7 @@ public class Attr extends JCTree.Visitor {
}
finally {
chk.setLint(prevLint);
chk.setMethod(prevMethod);
}
}
@ -2272,8 +2275,8 @@ public class Attr extends JCTree.Visitor {
((VarSymbol)sitesym).isResourceVariable() &&
sym.kind == MTH &&
sym.overrides(syms.autoCloseableClose, sitesym.type.tsym, types, true) &&
env.info.lint.isEnabled(Lint.LintCategory.TRY)) {
log.warning(Lint.LintCategory.TRY, tree, "try.explicit.close.call");
env.info.lint.isEnabled(LintCategory.TRY)) {
log.warning(LintCategory.TRY, tree, "try.explicit.close.call");
}
// Disallow selecting a type from an expression
@ -2700,7 +2703,7 @@ public class Attr extends JCTree.Visitor {
// For methods, we need to compute the instance type by
// Resolve.instantiate from the symbol's type as well as
// any type arguments and value arguments.
noteWarner.warned = false;
noteWarner.clear();
Type owntype = rs.instantiate(env,
site,
sym,
@ -2709,7 +2712,7 @@ public class Attr extends JCTree.Visitor {
true,
useVarargs,
noteWarner);
boolean warned = noteWarner.warned;
boolean warned = noteWarner.hasNonSilentLint(LintCategory.UNCHECKED);
// If this fails, something went wrong; we should not have
// found the identifier in the first place.
@ -2734,7 +2737,7 @@ public class Attr extends JCTree.Visitor {
JCTree arg = args.head;
Warner warn = chk.convertWarner(arg.pos(), arg.type, formals.head);
assertConvertible(arg, arg.type, formals.head, warn);
warned |= warn.warned;
warned |= warn.hasNonSilentLint(LintCategory.UNCHECKED);
args = args.tail;
formals = formals.tail;
}
@ -2744,7 +2747,7 @@ public class Attr extends JCTree.Visitor {
JCTree arg = args.head;
Warner warn = chk.convertWarner(arg.pos(), arg.type, varArg);
assertConvertible(arg, arg.type, varArg, warn);
warned |= warn.warned;
warned |= warn.hasNonSilentLint(LintCategory.UNCHECKED);
args = args.tail;
}
} else if ((sym.flags() & VARARGS) != 0 && allowVarargs) {
@ -2776,7 +2779,7 @@ public class Attr extends JCTree.Visitor {
JCTree tree = env.tree;
Type argtype = owntype.getParameterTypes().last();
if (owntype.getReturnType().tag != FORALL || warned) {
chk.checkVararg(env.tree.pos(), owntype.getParameterTypes(), sym, env);
chk.checkVararg(env.tree.pos(), owntype.getParameterTypes(), sym);
}
Type elemtype = types.elemtype(argtype);
switch (tree.getTag()) {
@ -3175,7 +3178,7 @@ public class Attr extends JCTree.Visitor {
chk.checkNonCyclicElements(tree);
// Check for proper use of serialVersionUID
if (env.info.lint.isEnabled(Lint.LintCategory.SERIAL) &&
if (env.info.lint.isEnabled(LintCategory.SERIAL) &&
isSerializable(c) &&
(c.flags() & Flags.ENUM) == 0 &&
(c.flags() & ABSTRACT) == 0) {
@ -3204,7 +3207,7 @@ public class Attr extends JCTree.Visitor {
Scope.Entry e = c.members().lookup(names.serialVersionUID);
while (e.scope != null && e.sym.kind != VAR) e = e.next();
if (e.scope == null) {
log.warning(Lint.LintCategory.SERIAL,
log.warning(LintCategory.SERIAL,
tree.pos(), "missing.SVUID", c);
return;
}
@ -3213,17 +3216,17 @@ public class Attr extends JCTree.Visitor {
VarSymbol svuid = (VarSymbol)e.sym;
if ((svuid.flags() & (STATIC | FINAL)) !=
(STATIC | FINAL))
log.warning(Lint.LintCategory.SERIAL,
log.warning(LintCategory.SERIAL,
TreeInfo.diagnosticPositionFor(svuid, tree), "improper.SVUID", c);
// check that it is long
else if (svuid.type.tag != TypeTags.LONG)
log.warning(Lint.LintCategory.SERIAL,
log.warning(LintCategory.SERIAL,
TreeInfo.diagnosticPositionFor(svuid, tree), "long.SVUID", c);
// check constant
else if (svuid.getConstValue() == null)
log.warning(Lint.LintCategory.SERIAL,
log.warning(LintCategory.SERIAL,
TreeInfo.diagnosticPositionFor(svuid, tree), "constant.SVUID", c);
}

View File

@ -75,6 +75,10 @@ public class Check {
// visits all the various parts of the trees during attribution.
private Lint lint;
// The method being analyzed in Attr - it is set/reset as needed by
// Attr as it visits new method declarations.
private MethodSymbol method;
public static Check instance(Context context) {
Check instance = context.get(checkKey);
if (instance == null)
@ -100,6 +104,7 @@ public class Check {
allowGenerics = source.allowGenerics();
allowAnnotations = source.allowAnnotations();
allowCovariantReturns = source.allowCovariantReturns();
allowSimplifiedVarargs = source.allowSimplifiedVarargs();
complexInference = options.isSet(COMPLEXINFERENCE);
skipAnnotations = options.isSet("skipAnnotations");
warnOnSyntheticConflicts = options.isSet("warnOnSyntheticConflicts");
@ -136,6 +141,10 @@ public class Check {
*/
boolean allowCovariantReturns;
/** Switch: simplified varargs enabled?
*/
boolean allowSimplifiedVarargs;
/** Switch: -complexinference option set?
*/
boolean complexInference;
@ -175,6 +184,12 @@ public class Check {
return prev;
}
MethodSymbol setMethod(MethodSymbol newMethod) {
MethodSymbol prev = method;
method = newMethod;
return prev;
}
/** Warn about deprecated symbol.
* @param pos Position to be used for error reporting.
* @param sym The deprecated symbol.
@ -197,9 +212,9 @@ public class Check {
* @param pos Position to be used for error reporting.
* @param sym The deprecated symbol.
*/
void warnUnsafeVararg(DiagnosticPosition pos, Type elemType) {
if (!lint.isSuppressed(LintCategory.VARARGS))
unsafeVarargsHandler.report(pos, "varargs.non.reifiable.type", elemType);
void warnUnsafeVararg(DiagnosticPosition pos, String key, Object... args) {
if (lint.isEnabled(LintCategory.VARARGS) && allowSimplifiedVarargs)
log.warning(LintCategory.VARARGS, pos, key, args);
}
/** Warn about using proprietary API.
@ -222,7 +237,6 @@ public class Check {
public void reportDeferredDiagnostics() {
deprecationHandler.reportDeferredDiagnostic();
uncheckedHandler.reportDeferredDiagnostic();
unsafeVarargsHandler.reportDeferredDiagnostic();
sunApiHandler.reportDeferredDiagnostic();
}
@ -705,29 +719,56 @@ public class Check {
}
}
void checkVarargMethodDecl(JCMethodDecl tree) {
void checkVarargsMethodDecl(Env<AttrContext> env, JCMethodDecl tree) {
MethodSymbol m = tree.sym;
//check the element type of the vararg
if (!allowSimplifiedVarargs) return;
boolean hasTrustMeAnno = m.attribute(syms.trustMeType.tsym) != null;
Type varargElemType = null;
if (m.isVarArgs()) {
Type varargElemType = types.elemtype(tree.params.last().type);
if (!types.isReifiable(varargElemType)) {
warnUnsafeVararg(tree.params.head.pos(), varargElemType);
varargElemType = types.elemtype(tree.params.last().type);
}
if (hasTrustMeAnno && !isTrustMeAllowedOnMethod(m)) {
if (varargElemType != null) {
log.error(tree,
"varargs.invalid.trustme.anno",
syms.trustMeType.tsym,
diags.fragment("varargs.trustme.on.virtual.varargs", m));
} else {
log.error(tree,
"varargs.invalid.trustme.anno",
syms.trustMeType.tsym,
diags.fragment("varargs.trustme.on.non.varargs.meth", m));
}
} else if (hasTrustMeAnno && varargElemType != null &&
types.isReifiable(varargElemType)) {
warnUnsafeVararg(tree,
"varargs.redundant.trustme.anno",
syms.trustMeType.tsym,
diags.fragment("varargs.trustme.on.reifiable.varargs", varargElemType));
}
else if (!hasTrustMeAnno && varargElemType != null &&
!types.isReifiable(varargElemType)) {
warnUnchecked(tree.params.head.pos(), "unchecked.varargs.non.reifiable.type", varargElemType);
}
}
//where
private boolean isTrustMeAllowedOnMethod(Symbol s) {
return (s.flags() & VARARGS) != 0 &&
(s.isConstructor() ||
(s.flags() & (STATIC | FINAL)) != 0);
}
/**
* Check that vararg method call is sound
* @param pos Position to be used for error reporting.
* @param argtypes Actual arguments supplied to vararg method.
*/
void checkVararg(DiagnosticPosition pos, List<Type> argtypes, Symbol msym, Env<AttrContext> env) {
Env<AttrContext> calleeLintEnv = env;
while (calleeLintEnv.info.lint == null)
calleeLintEnv = calleeLintEnv.next;
Lint calleeLint = calleeLintEnv.info.lint.augment(msym.attributes_field, msym.flags());
void checkVararg(DiagnosticPosition pos, List<Type> argtypes, Symbol msym) {
Type argtype = argtypes.last();
if (!types.isReifiable(argtype) && !calleeLint.isSuppressed(Lint.LintCategory.VARARGS)) {
if (!types.isReifiable(argtype) &&
(!allowSimplifiedVarargs ||
msym.attribute(syms.trustMeType.tsym) == null ||
!isTrustMeAllowedOnMethod(msym))) {
warnUnchecked(pos,
"unchecked.generic.array.creation",
argtype);
@ -1075,12 +1116,12 @@ public class Check {
}
void checkRaw(JCTree tree, Env<AttrContext> env) {
if (lint.isEnabled(Lint.LintCategory.RAW) &&
if (lint.isEnabled(LintCategory.RAW) &&
tree.type.tag == CLASS &&
!TreeInfo.isDiamond(tree) &&
!env.enclClass.name.isEmpty() && //anonymous or intersection
tree.type.isRaw()) {
log.warning(Lint.LintCategory.RAW,
log.warning(LintCategory.RAW,
tree.pos(), "raw.class.use", tree.type, tree.type.tsym.type);
}
}
@ -1347,7 +1388,7 @@ public class Check {
Type mtres = mt.getReturnType();
Type otres = types.subst(ot.getReturnType(), otvars, mtvars);
overrideWarner.warned = false;
overrideWarner.clear();
boolean resultTypesOK =
types.returnTypeSubstitutable(mt, ot, otres, overrideWarner);
if (!resultTypesOK) {
@ -1362,7 +1403,7 @@ public class Check {
mtres, otres);
return;
}
} else if (overrideWarner.warned) {
} else if (overrideWarner.hasNonSilentLint(LintCategory.UNCHECKED)) {
warnUnchecked(TreeInfo.diagnosticPositionFor(m, tree),
"override.unchecked.ret",
uncheckedOverrides(m, other),
@ -1391,7 +1432,7 @@ public class Check {
// Optional warning if varargs don't agree
if ((((m.flags() ^ other.flags()) & Flags.VARARGS) != 0)
&& lint.isEnabled(Lint.LintCategory.OVERRIDES)) {
&& lint.isEnabled(LintCategory.OVERRIDES)) {
log.warning(TreeInfo.diagnosticPositionFor(m, tree),
((m.flags() & Flags.VARARGS) != 0)
? "override.varargs.missing"
@ -2380,11 +2421,11 @@ public class Check {
void checkDeprecatedAnnotation(DiagnosticPosition pos, Symbol s) {
if (allowAnnotations &&
lint.isEnabled(Lint.LintCategory.DEP_ANN) &&
lint.isEnabled(LintCategory.DEP_ANN) &&
(s.flags() & DEPRECATED) != 0 &&
!syms.deprecatedType.isErroneous() &&
s.attribute(syms.deprecatedType.tsym) == null) {
log.warning(Lint.LintCategory.DEP_ANN,
log.warning(LintCategory.DEP_ANN,
pos, "missing.deprecated.annotation");
}
}
@ -2530,13 +2571,13 @@ public class Check {
*/
void checkDivZero(DiagnosticPosition pos, Symbol operator, Type operand) {
if (operand.constValue() != null
&& lint.isEnabled(Lint.LintCategory.DIVZERO)
&& lint.isEnabled(LintCategory.DIVZERO)
&& operand.tag <= LONG
&& ((Number) (operand.constValue())).longValue() == 0) {
int opc = ((OperatorSymbol)operator).opcode;
if (opc == ByteCodes.idiv || opc == ByteCodes.imod
|| opc == ByteCodes.ldiv || opc == ByteCodes.lmod) {
log.warning(Lint.LintCategory.DIVZERO, pos, "div.zero");
log.warning(LintCategory.DIVZERO, pos, "div.zero");
}
}
}
@ -2545,8 +2586,8 @@ public class Check {
* Check for empty statements after if
*/
void checkEmptyIf(JCIf tree) {
if (tree.thenpart.getTag() == JCTree.SKIP && tree.elsepart == null && lint.isEnabled(Lint.LintCategory.EMPTY))
log.warning(Lint.LintCategory.EMPTY, tree.thenpart.pos(), "empty.if");
if (tree.thenpart.getTag() == JCTree.SKIP && tree.elsepart == null && lint.isEnabled(LintCategory.EMPTY))
log.warning(LintCategory.EMPTY, tree.thenpart.pos(), "empty.if");
}
/** Check that symbol is unique in given scope.
@ -2654,23 +2695,36 @@ public class Check {
}
private class ConversionWarner extends Warner {
final String key;
final String uncheckedKey;
final Type found;
final Type expected;
public ConversionWarner(DiagnosticPosition pos, String key, Type found, Type expected) {
public ConversionWarner(DiagnosticPosition pos, String uncheckedKey, Type found, Type expected) {
super(pos);
this.key = key;
this.uncheckedKey = uncheckedKey;
this.found = found;
this.expected = expected;
}
@Override
public void warnUnchecked() {
public void warn(LintCategory lint) {
boolean warned = this.warned;
super.warnUnchecked();
super.warn(lint);
if (warned) return; // suppress redundant diagnostics
Object problem = diags.fragment(key);
Check.this.warnUnchecked(pos(), "prob.found.req", problem, found, expected);
switch (lint) {
case UNCHECKED:
Check.this.warnUnchecked(pos(), "prob.found.req", diags.fragment(uncheckedKey), found, expected);
break;
case VARARGS:
if (method != null &&
method.attribute(syms.trustMeType.tsym) != null &&
isTrustMeAllowedOnMethod(method) &&
!types.isReifiable(method.type.getParameterTypes().last())) {
Check.this.warnUnsafeVararg(pos(), "varargs.unsafe.use.varargs.param", method.params.last());
}
break;
default:
throw new AssertionError("Unexpected lint: " + lint);
}
}
}

View File

@ -481,7 +481,7 @@ public class Infer {
checkWithinBounds(all_tvars,
types.subst(inferredTypes, tvars, inferred), warn);
if (useVarargs) {
chk.checkVararg(env.tree.pos(), formals, msym, env);
chk.checkVararg(env.tree.pos(), formals, msym);
}
return super.inst(inferred, types);
}};

View File

@ -77,6 +77,7 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
private final Target target;
private final boolean skipAnnotations;
private final boolean allowSimplifiedVarargs;
public static MemberEnter instance(Context context) {
MemberEnter instance = context.get(memberEnterKey);
@ -103,6 +104,8 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
target = Target.instance(context);
Options options = Options.instance(context);
skipAnnotations = options.isSet("skipAnnotations");
Source source = Source.instance(context);
allowSimplifiedVarargs = source.allowSimplifiedVarargs();
}
/** A queue for classes whose members still need to be entered into the
@ -617,6 +620,14 @@ public class MemberEnter extends JCTree.Visitor implements Completer {
localEnv.info.staticLevel++;
}
attr.attribType(tree.vartype, localEnv);
if ((tree.mods.flags & VARARGS) != 0) {
//if we are entering a varargs parameter, we need to replace its type
//(a plain array type) with the more precise VarargsType --- we need
//to do it this way because varargs is represented in the tree as a modifier
//on the parameter declaration, and not as a distinct type of array node.
ArrayType atype = (ArrayType)tree.vartype.type;
tree.vartype.type = atype.makeVarargs();
}
Scope enclScope = enter.enterScope(env);
VarSymbol v =
new VarSymbol(0, tree.name, tree.vartype.type, enclScope.owner);

View File

@ -815,13 +815,13 @@ public class Resolve {
}
//where
private boolean signatureMoreSpecific(Env<AttrContext> env, Type site, Symbol m1, Symbol m2, boolean allowBoxing, boolean useVarargs) {
noteWarner.clear();
Type mtype1 = types.memberType(site, adjustVarargs(m1, m2, useVarargs));
noteWarner.unchecked = false;
return (instantiate(env, site, adjustVarargs(m2, m1, useVarargs), types.lowerBoundArgtypes(mtype1), null,
allowBoxing, false, noteWarner) != null ||
useVarargs && instantiate(env, site, adjustVarargs(m2, m1, useVarargs), types.lowerBoundArgtypes(mtype1), null,
allowBoxing, true, noteWarner) != null) &&
!noteWarner.unchecked;
!noteWarner.hasLint(Lint.LintCategory.UNCHECKED);
}
//where
private Symbol adjustVarargs(Symbol to, Symbol from, boolean useVarargs) {

View File

@ -105,10 +105,15 @@ public class ClassReader implements Completer {
*/
boolean allowAnnotations;
/** Lint option: warn about classfile issues
/** Switch: allow simplified varargs.
*/
boolean allowSimplifiedVarargs;
/** Lint option: warn about classfile issues
*/
boolean lintClassfile;
/** Switch: preserve parameter names from the variable table.
*/
public boolean saveParameterNames;
@ -279,6 +284,7 @@ public class ClassReader implements Completer {
allowGenerics = source.allowGenerics();
allowVarargs = source.allowVarargs();
allowAnnotations = source.allowAnnotations();
allowSimplifiedVarargs = source.allowSimplifiedVarargs();
saveParameterNames = options.isSet("save-parameter-names");
cacheCompletionFailure = options.isUnset("dev");
preferSource = "source".equals(options.get("-Xprefer"));
@ -1883,7 +1889,7 @@ public class ClassReader implements Completer {
// instance, however, there is no reliable way to tell so
// we never strip this$n
if (!currentOwner.name.isEmpty())
type = new MethodType(type.getParameterTypes().tail,
type = new MethodType(adjustMethodParams(flags, type.getParameterTypes()),
type.getReturnType(),
type.getThrownTypes(),
syms.methodClass);
@ -1903,6 +1909,21 @@ public class ClassReader implements Completer {
return m;
}
private List<Type> adjustMethodParams(long flags, List<Type> args) {
boolean isVarargs = (flags & VARARGS) != 0;
if (isVarargs) {
Type varargsElem = args.last();
ListBuffer<Type> adjustedArgs = ListBuffer.lb();
for (Type t : args) {
adjustedArgs.append(t != varargsElem ?
t :
((ArrayType)t).makeVarargs());
}
args = adjustedArgs.toList();
}
return args.tail;
}
/**
* Init the parameter names array.
* Parameter names are currently inferred from the names in the

View File

@ -516,6 +516,15 @@ compiler.err.var.might.not.have.been.initialized=\
compiler.err.var.might.be.assigned.in.loop=\
variable {0} might be assigned in loop
compiler.err.varargs.invalid.trustme.anno=\
Invalid {0} annotation. {1}
compiler.misc.varargs.trustme.on.reifiable.varargs=\
Varargs element type {0} is reifiable.
compiler.misc.varargs.trustme.on.non.varargs.meth=\
Method {0} is not a varargs method.
compiler.misc.varargs.trustme.on.virtual.varargs=\
Instance method {0} is not final.
# In the following string, {1} will always be the detail message from
# java.io.IOException.
compiler.err.class.cant.write=\
@ -600,20 +609,6 @@ compiler.note.unchecked.filename.additional=\
compiler.note.unchecked.plural.additional=\
Some input files additionally use unchecked or unsafe operations.
compiler.note.varargs.filename=\
{0} declares unsafe vararg methods.
compiler.note.varargs.plural=\
Some input files declare unsafe vararg methods.
# The following string may appear after one of the above unsafe varargs
# messages.
compiler.note.varargs.recompile=\
Recompile with -Xlint:varargs for details.
compiler.note.varargs.filename.additional=\
{0} declares additional unsafe vararg methods.
compiler.note.varargs.plural.additional=\
Some input files additionally declares unsafe vararg methods.
compiler.note.sunapi.filename=\
{0} uses internal proprietary API that may be removed in a future release.
compiler.note.sunapi.plural=\
@ -841,9 +836,12 @@ compiler.warn.unchecked.meth.invocation.applied=\
compiler.warn.unchecked.generic.array.creation=\
unchecked generic array creation for varargs parameter of type {0}
compiler.warn.varargs.non.reifiable.type=\
compiler.warn.unchecked.varargs.non.reifiable.type=\
Possible heap pollution from parameterized vararg type {0}
compiler.warn.varargs.unsafe.use.varargs.param=\
Varargs method could cause heap pollution from non-reifiable varargs parameter {0}
compiler.warn.missing.deprecated.annotation=\
deprecated item is not annotated with @Deprecated
@ -876,6 +874,9 @@ compiler.warn.diamond.redundant.args.1=\
explicit: {0}\n\
inferred: {1}
compiler.warn.varargs.redundant.trustme.anno=\
Redundant {0} annotation. {1}
#####
## The following are tokens which are non-terminals in the language. They should

View File

@ -103,7 +103,7 @@ public class List<A> extends AbstractCollection<A> implements java.util.List<A>
/** Construct a list consisting of given elements.
*/
@SuppressWarnings("varargs")
@SuppressWarnings({"varargs", "unchecked"})
public static <A> List<A> of(A x1, A x2, A x3, A... rest) {
return new List<A>(x1, new List<A>(x2, new List<A>(x3, from(rest))));
}

View File

@ -25,7 +25,9 @@
package com.sun.tools.javac.util;
import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import java.util.EnumSet;
/**
* An interface to support optional warnings, needed for support of
@ -40,25 +42,45 @@ public class Warner {
public static final Warner noWarnings = new Warner();
private DiagnosticPosition pos = null;
public boolean warned = false;
public boolean unchecked = false;
protected boolean warned = false;
private EnumSet<LintCategory> nonSilentLintSet = EnumSet.noneOf(LintCategory.class);
private EnumSet<LintCategory> silentLintSet = EnumSet.noneOf(LintCategory.class);
public DiagnosticPosition pos() {
return pos;
}
public void warnUnchecked() {
warned = true;
unchecked = true;
public void warn(LintCategory lint) {
nonSilentLintSet.add(lint);
}
public void silentUnchecked() {
unchecked = true;
public void silentWarn(LintCategory lint) {
silentLintSet.add(lint);
}
public Warner(DiagnosticPosition pos) {
this.pos = pos;
}
public boolean hasSilentLint(LintCategory lint) {
return silentLintSet.contains(lint);
}
public boolean hasNonSilentLint(LintCategory lint) {
return nonSilentLintSet.contains(lint);
}
public boolean hasLint(LintCategory lint) {
return hasSilentLint(lint) ||
hasNonSilentLint(lint);
}
public void clear() {
nonSilentLintSet.clear();
silentLintSet.clear();
this.warned = false;
}
public Warner() {
this(null);
}

View File

@ -129,12 +129,17 @@ public class CheckExamples {
File testSrc = new File(System.getProperty("test.src"));
File examples = new File(testSrc, "examples");
for (File f: examples.listFiles()) {
if (f.isDirectory() || f.isFile() && f.getName().endsWith(".java"))
if (isValidExample(f))
results.add(new Example(f));
}
return results;
}
boolean isValidExample(File f) {
return (f.isDirectory() && f.list().length > 0) ||
(f.isFile() && f.getName().endsWith(".java"));
}
/**
* Get the contents of the "not-yet" list.
*/

View File

@ -52,7 +52,7 @@ import java.util.regex.Pattern;
*/
public class RunExamples {
public static void main(String... args) throws Exception {
boolean jtreg = (System.getProperty("test.src") != null);
jtreg = (System.getProperty("test.src") != null);
File tmpDir;
if (jtreg) {
// use standard jtreg scratch directory: the current directory
@ -166,12 +166,17 @@ public class RunExamples {
Set<Example> getExamples(File examplesDir) {
Set<Example> results = new TreeSet<Example>();
for (File f: examplesDir.listFiles()) {
if (f.isDirectory() || f.isFile() && f.getName().endsWith(".java"))
if (isValidExample(f))
results.add(new Example(f));
}
return results;
}
boolean isValidExample(File f) {
return (f.isDirectory() && (!jtreg || f.list().length > 0)) ||
(f.isFile() && f.getName().endsWith(".java"));
}
/**
* Report an error.
*/
@ -180,6 +185,8 @@ public class RunExamples {
errors++;
}
static boolean jtreg;
int errors;
/**

View File

@ -21,10 +21,10 @@
* questions.
*/
// key: compiler.note.varargs.plural.additional
// key: compiler.warn.varargs.non.reifiable.type
// options: -Xlint:varargs -Xmaxwarns 1
// key: compiler.err.varargs.invalid.trustme.anno
// key: compiler.misc.varargs.trustme.on.non.varargs.meth
// options: -Xlint:varargs
class VarargsPluralAdditional<T> {
void m(T... items) { }
class TrustMeOnNonVarargsMeth {
@SafeVarargs static void m(String[] args) { }
}

View File

@ -21,9 +21,10 @@
* questions.
*/
// key: compiler.note.varargs.plural
// key: compiler.note.varargs.recompile
// key: compiler.warn.varargs.redundant.trustme.anno
// key: compiler.misc.varargs.trustme.on.reifiable.varargs
// options: -Xlint:varargs
class VarargsPlural<T> {
void m(T... items) { }
class TrustMeOnReifiableVarargsParam {
@SafeVarargs static void m(String... args) { }
}

View File

@ -21,11 +21,12 @@
* questions.
*/
// key: compiler.note.varargs.filename.additional
// key: compiler.warn.varargs.non.reifiable.type
// options: -Xlint:varargs -Xmaxwarns 1
// key: compiler.err.varargs.invalid.trustme.anno
// key: compiler.misc.varargs.trustme.on.virtual.varargs
// options: -Xlint:varargs,unchecked
class VarargsFilenameAdditional<T> {
void m1(T... items) { }
void m2(T... items) { }
import java.util.List;
class TrustMeOnVirtualMethod {
@SafeVarargs void m(List<String>... args) { }
}

View File

@ -22,10 +22,8 @@
*/
// key: compiler.warn.unchecked.generic.array.creation
// key: compiler.warn.varargs.non.reifiable.type
// options: -Xlint:unchecked,varargs
import java.util.*;
// key: compiler.warn.unchecked.varargs.non.reifiable.type
// options: -Xlint:unchecked
class UncheckedGenericArrayCreation<T> {
void m(T t1, T t2, T t3) {

View File

@ -21,9 +21,11 @@
* questions.
*/
// key: compiler.note.varargs.filename
// key: compiler.note.varargs.recompile
// key: compiler.warn.varargs.unsafe.use.varargs.param
// options: -Xlint:varargs
class VarargsFilename<T> {
void m(T... items) { }
class UnsafeUseOfVarargsParam {
@SafeVarargs static <X> void m(X... x) {
Object[] o = x;
}
}

View File

@ -21,10 +21,8 @@
* questions.
*/
// key: compiler.warn.varargs.non.reifiable.type
// options: -Xlint:varargs
import java.util.*;
// key: compiler.warn.unchecked.varargs.non.reifiable.type
// options: -Xlint:unchecked
class VarargsNonReifiableType<T> {
void m(T... items) {

View File

@ -1,26 +0,0 @@
/*
* Copyright (c) 2010, 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.
*/
class VarargsFilename<T> {
void m(T... items) { }
}

View File

@ -1,26 +0,0 @@
/*
* Copyright (c) 2010, 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.
*/
class VarargsFilename<T> {
void m(T... items) { }
}

View File

@ -1,26 +0,0 @@
/*
* Copyright (c) 2010, 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.
*/
class VarargsPlural<T> {
void m(T... items) { }
}

View File

@ -32,6 +32,7 @@
*/
class T6730476a {
@SuppressWarnings("unchecked")
<T> void f(int i, T ... x) {}
void g() {
f(1);

View File

@ -1,6 +1,5 @@
T6806876.java:11:32: compiler.warn.unchecked.generic.array.creation: java.lang.Number&java.lang.Comparable<? extends java.lang.Number&java.lang.Comparable<?>>[]
T6806876.java:14:19: compiler.warn.unchecked.varargs.non.reifiable.type: T
- compiler.err.warnings.and.werror
- compiler.note.varargs.filename: T6806876.java
- compiler.note.varargs.recompile
1 error
1 warning
2 warnings

View File

@ -0,0 +1,17 @@
/*
* @test /nodynamiccopyright/
* @bug 6993978
* @author mcimadamore
* @summary ClassCastException occurs in assignment expressions without any heap pollutions
* @compile/fail/ref=T6993978neg.out -Xlint:unchecked -Werror -XDrawDiagnostics T6993978neg.java
*/
import java.util.List;
class T6993978neg {
@SuppressWarnings({"varargs","unchecked"})
static <X> void m(X... x) { }
static void test(List<String> ls) {
m(ls); //compiler should still give unchecked here
}
}

View File

@ -0,0 +1,4 @@
T6993978neg.java:15:9: compiler.warn.unchecked.generic.array.creation: java.util.List<java.lang.String>[]
- compiler.err.warnings.and.werror
1 error
1 warning

View File

@ -23,7 +23,7 @@
/**
* @test
* @bug 6945418
* @bug 6945418 6993978
* @summary Project Coin: Simplified Varargs Method Invocation
* @author mcimadamore
* @run main Warn4
@ -48,96 +48,95 @@ public class Warn4 {
final static Warning[] both = new Warning[] { Warning.VARARGS, Warning.UNCHECKED };
enum Warning {
UNCHECKED("unchecked"),
VARARGS("varargs");
UNCHECKED("generic.array.creation"),
VARARGS("varargs.non.reifiable.type");
String category;
String key;
Warning(String category) {
this.category = category;
Warning(String key) {
this.key = key;
}
boolean isEnabled(XlintOption xlint, SuppressLevel suppressLevel) {
return Arrays.asList(xlint.enabledWarnings).contains(this);
}
boolean isSuppressed(TrustMe trustMe, SourceLevel source, SuppressLevel suppressLevelClient,
SuppressLevel suppressLevelDecl, ModifierKind modKind) {
switch(this) {
case VARARGS:
return source == SourceLevel.JDK_6 ||
suppressLevelDecl == SuppressLevel.UNCHECKED ||
trustMe == TrustMe.TRUST;
case UNCHECKED:
return suppressLevelClient == SuppressLevel.UNCHECKED ||
(trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE && source == SourceLevel.JDK_7);
}
boolean isSuppressed(SuppressLevel suppressLevel) {
return Arrays.asList(suppressLevel.suppressedWarnings).contains(VARARGS);
SuppressLevel supLev = this == VARARGS ?
suppressLevelDecl :
suppressLevelClient;
return supLev == SuppressLevel.UNCHECKED ||
(trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE);
}
}
enum XlintOption {
NONE(),
UNCHECKED(Warning.UNCHECKED),
VARARGS(Warning.VARARGS),
ALL(Warning.UNCHECKED, Warning.VARARGS);
enum SourceLevel {
JDK_6("6"),
JDK_7("7");
Warning[] enabledWarnings;
String sourceKey;
XlintOption(Warning... enabledWarnings) {
this.enabledWarnings = enabledWarnings;
SourceLevel(String sourceKey) {
this.sourceKey = sourceKey;
}
}
String getXlintOption() {
StringBuilder buf = new StringBuilder();
String sep = "";
for (Warning w : enabledWarnings) {
buf.append(sep);
buf.append(w.category);
sep=",";
}
return "-Xlint:" +
(this == NONE ? "none" : buf.toString());
enum TrustMe {
DONT_TRUST(""),
TRUST("@java.lang.SafeVarargs");
String anno;
TrustMe(String anno) {
this.anno = anno;
}
}
enum ModifierKind {
NONE(" "),
FINAL("final "),
STATIC("static ");
String mod;
ModifierKind(String mod) {
this.mod = mod;
}
}
enum SuppressLevel {
NONE(),
UNCHECKED(Warning.UNCHECKED),
VARARGS(Warning.VARARGS),
ALL(Warning.UNCHECKED, Warning.VARARGS);
NONE(""),
UNCHECKED("unchecked");
Warning[] suppressedWarnings;
String lint;
SuppressLevel(Warning... suppressedWarnings) {
this.suppressedWarnings = suppressedWarnings;
SuppressLevel(String lint) {
this.lint = lint;
}
String getSuppressAnnotation() {
StringBuilder buf = new StringBuilder();
String sep = "";
for (Warning w : suppressedWarnings) {
buf.append(sep);
buf.append("\"");
buf.append(w.category);
buf.append("\"");
sep=",";
}
return this == NONE ? "" :
"@SuppressWarnings({" + buf.toString() + "})";
String getSuppressAnno() {
return "@SuppressWarnings(\"" + lint + "\")";
}
}
enum Signature {
EXTENDS_TVAR("<Z> void #name(List<? extends Z>#arity arg) { #body }",
new Warning[][] {both, both, both, both, error, both, both, both, error}),
SUPER_TVAR("<Z> void #name(List<? super Z>#arity arg) { #body }",
new Warning[][] {error, both, error, both, error, error, both, both, error}),
UNBOUND("void #name(List<?>#arity arg) { #body }",
new Warning[][] {none, none, none, none, none, none, none, none, error}),
new Warning[][] {none, none, none, none, error}),
INVARIANT_TVAR("<Z> void #name(List<Z>#arity arg) { #body }",
new Warning[][] {both, both, both, both, error, both, both, both, error}),
new Warning[][] {both, both, error, both, error}),
TVAR("<Z> void #name(Z#arity arg) { #body }",
new Warning[][] {both, both, both, both, both, both, both, both, vararg}),
EXTENDS("void #name(List<? extends String>#arity arg) { #body }",
new Warning[][] {error, error, error, error, error, both, error, both, error}),
SUPER("void #name(List<? super String>#arity arg) { #body }",
new Warning[][] {error, error, error, error, error, error, both, both, error}),
new Warning[][] {both, both, both, both, vararg}),
INVARIANT("void #name(List<String>#arity arg) { #body }",
new Warning[][] {error, error, error, error, error, error, error, both, error}),
new Warning[][] {error, error, error, both, error}),
UNPARAMETERIZED("void #name(String#arity arg) { #body }",
new Warning[][] {error, error, error, error, error, error, error, error, none});
new Warning[][] {error, error, error, error, none});
String template;
Warning[][] warnings;
@ -163,15 +162,24 @@ public class Warn4 {
}
public static void main(String... args) throws Exception {
for (XlintOption xlint : XlintOption.values()) {
for (SuppressLevel suppressLevel : SuppressLevel.values()) {
for (Signature vararg_meth : Signature.values()) {
for (Signature client_meth : Signature.values()) {
if (vararg_meth.isApplicableTo(client_meth)) {
test(xlint,
suppressLevel,
vararg_meth,
client_meth);
for (SourceLevel sourceLevel : SourceLevel.values()) {
for (TrustMe trustMe : TrustMe.values()) {
for (SuppressLevel suppressLevelClient : SuppressLevel.values()) {
for (SuppressLevel suppressLevelDecl : SuppressLevel.values()) {
for (ModifierKind modKind : ModifierKind.values()) {
for (Signature vararg_meth : Signature.values()) {
for (Signature client_meth : Signature.values()) {
if (vararg_meth.isApplicableTo(client_meth)) {
test(sourceLevel,
trustMe,
suppressLevelClient,
suppressLevelDecl,
modKind,
vararg_meth,
client_meth);
}
}
}
}
}
}
@ -179,37 +187,37 @@ public class Warn4 {
}
}
static void test(XlintOption xlint, SuppressLevel suppressLevel,
Signature vararg_meth, Signature client_meth) throws Exception {
static void test(SourceLevel sourceLevel, TrustMe trustMe, SuppressLevel suppressLevelClient,
SuppressLevel suppressLevelDecl, ModifierKind modKind, Signature vararg_meth, Signature client_meth) throws Exception {
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
JavaSource source = new JavaSource(suppressLevel, vararg_meth, client_meth);
JavaSource source = new JavaSource(trustMe, suppressLevelClient, suppressLevelDecl, modKind, vararg_meth, client_meth);
DiagnosticChecker dc = new DiagnosticChecker();
JavacTask ct = (JavacTask)tool.getTask(null, null, dc,
Arrays.asList(xlint.getXlintOption()), null, Arrays.asList(source));
Arrays.asList("-Xlint:unchecked", "-source", sourceLevel.sourceKey),
null, Arrays.asList(source));
ct.generate(); //to get mandatory notes
check(dc.warnings,
dc.notes,
check(dc.warnings, sourceLevel,
new boolean[] {vararg_meth.giveUnchecked(client_meth),
vararg_meth.giveVarargs(client_meth)},
source, xlint, suppressLevel);
source, trustMe, suppressLevelClient, suppressLevelDecl, modKind);
}
static void check(Set<Warning> warnings, Set<Warning> notes, boolean[] warnArr, JavaSource source, XlintOption xlint, SuppressLevel suppressLevel) {
static void check(Set<Warning> warnings, SourceLevel sourceLevel, boolean[] warnArr, JavaSource source,
TrustMe trustMe, SuppressLevel suppressLevelClient, SuppressLevel suppressLevelDecl, ModifierKind modKind) {
boolean badOutput = false;
for (Warning wkind : Warning.values()) {
badOutput |= (warnArr[wkind.ordinal()] && !wkind.isSuppressed(suppressLevel)) !=
(wkind.isEnabled(xlint, suppressLevel) ?
warnings.contains(wkind) :
notes.contains(wkind));
boolean isSuppressed = wkind.isSuppressed(trustMe, sourceLevel,
suppressLevelClient, suppressLevelDecl, modKind);
System.out.println("SUPPRESSED = " + isSuppressed);
badOutput |= (warnArr[wkind.ordinal()] && !isSuppressed) != warnings.contains(wkind);
}
if (badOutput) {
throw new Error("invalid diagnostics for source:\n" +
source.getCharContent(true) +
"\nOptions: " + xlint.getXlintOption() +
"\nExpected unchecked warning: " + warnArr[0] +
"\nExpected unsafe vararg warning: " + warnArr[1] +
"\nWarnings: " + warnings +
"\nNotes: " + notes);
"\nSource level: " + sourceLevel);
}
}
@ -217,18 +225,20 @@ public class Warn4 {
String source;
public JavaSource(SuppressLevel suppressLevel, Signature vararg_meth, Signature client_meth) {
public JavaSource(TrustMe trustMe, SuppressLevel suppressLevelClient, SuppressLevel suppressLevelDecl,
ModifierKind modKind, Signature vararg_meth, Signature client_meth) {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
String meth1 = vararg_meth.template.replace("#arity", "...");
meth1 = meth1.replace("#name", "m");
meth1 = meth1.replace("#body", "");
meth1 = suppressLevel.getSuppressAnnotation() + meth1;
meth1 = trustMe.anno + "\n" + suppressLevelDecl.getSuppressAnno() + modKind.mod + meth1;
String meth2 = client_meth.template.replace("#arity", "");
meth2 = meth2.replace("#name", "test");
meth2 = meth2.replace("#body", "m(arg);");
meth2 = suppressLevelClient.getSuppressAnno() + meth2;
source = "import java.util.List;\n" +
"class Test {\n" + meth1 +
"\n" + meth2 + "\n}\n";
"class Test {\n" + meth1 +
"\n" + meth2 + "\n}\n";
}
@Override
@ -240,19 +250,15 @@ public class Warn4 {
static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
Set<Warning> warnings = new HashSet<>();
Set<Warning> notes = new HashSet<>();
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING ||
diagnostic.getKind() == Diagnostic.Kind.WARNING) {
warnings.add(diagnostic.getCode().contains("varargs") ?
Warning.VARARGS :
Warning.UNCHECKED);
}
else if (diagnostic.getKind() == Diagnostic.Kind.NOTE) {
notes.add(diagnostic.getCode().contains("varargs") ?
Warning.VARARGS :
Warning.UNCHECKED);
if (diagnostic.getCode().contains(Warning.VARARGS.key)) {
warnings.add(Warning.VARARGS);
} else if(diagnostic.getCode().contains(Warning.UNCHECKED.key)) {
warnings.add(Warning.UNCHECKED);
}
}
}
}

View File

@ -0,0 +1,293 @@
/*
* Copyright (c) 2010, 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 6993978
* @summary Project Coin: Annotation to reduce varargs warnings
* @author mcimadamore
* @run main Warn5
*/
import com.sun.source.util.JavacTask;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
public class Warn5 {
enum XlintOption {
NONE("none"),
ALL("all");
String opt;
XlintOption(String opt) {
this.opt = opt;
}
String getXlintOption() {
return "-Xlint:" + opt;
}
}
enum TrustMe {
DONT_TRUST(""),
TRUST("@java.lang.SafeVarargs");
String anno;
TrustMe(String anno) {
this.anno = anno;
}
}
enum SuppressLevel {
NONE,
VARARGS;
String getSuppressAnno() {
return this == VARARGS ?
"@SuppressWarnings(\"varargs\")" :
"";
}
}
enum ModifierKind {
NONE(""),
FINAL("final"),
STATIC("static");
String mod;
ModifierKind(String mod) {
this.mod = mod;
}
}
enum MethodKind {
METHOD("void m"),
CONSTRUCTOR("Test");
String name;
MethodKind(String name) {
this.name = name;
}
}
enum SourceLevel {
JDK_6("6"),
JDK_7("7");
String sourceKey;
SourceLevel(String sourceKey) {
this.sourceKey = sourceKey;
}
}
enum SignatureKind {
VARARGS_X("#K <X>#N(X... x)", false, true),
VARARGS_STRING("#K #N(String... x)", true, true),
ARRAY_X("#K <X>#N(X[] x)", false, false),
ARRAY_STRING("#K #N(String[] x)", true, false);
String stub;
boolean isReifiableArg;
boolean isVarargs;
SignatureKind(String stub, boolean isReifiableArg, boolean isVarargs) {
this.stub = stub;
this.isReifiableArg = isReifiableArg;
this.isVarargs = isVarargs;
}
String getSignature(ModifierKind modKind, MethodKind methKind) {
return methKind != MethodKind.CONSTRUCTOR ?
stub.replace("#K", modKind.mod).replace("#N", methKind.name) :
stub.replace("#K", "").replace("#N", methKind.name);
}
}
enum BodyKind {
ASSIGN("Object o = x;", true),
CAST("Object o = (Object)x;", true),
METH("test(x);", true),
PRINT("System.out.println(x.toString());", false),
ARRAY_ASSIGN("Object[] o = x;", true),
ARRAY_CAST("Object[] o = (Object[])x;", true),
ARRAY_METH("testArr(x);", true);
String body;
boolean hasAliasing;
BodyKind(String body, boolean hasAliasing) {
this.body = body;
this.hasAliasing = hasAliasing;
}
}
static class JavaSource extends SimpleJavaFileObject {
String template = "import com.sun.tools.javac.api.*;\n" +
"import java.util.List;\n" +
"class Test {\n" +
" static void test(Object o) {}\n" +
" static void testArr(Object[] o) {}\n" +
" #T \n #S #M { #B }\n" +
"}\n";
String source;
public JavaSource(TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind,
MethodKind methKind, SignatureKind meth, BodyKind body) {
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("#B", body.body);
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
}
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);
}
}
}
}
}
}
}
}
}
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, null, 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;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.WARNING) {
if (diagnostic.getCode().contains("unsafe.use.varargs.param")) {
hasPotentiallyUnsafeBody = true;
} else if (diagnostic.getCode().contains("redundant.trustme")) {
hasRedundantAnnoInDecl = true;
}
} else if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING &&
diagnostic.getCode().contains("varargs.non.reifiable.type")) {
hasPotentiallyPollutingDecl = true;
} else if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
diagnostic.getCode().contains("invalid.trustme")) {
hasMalformedAnnoInDecl = true;
}
}
}
}