--- old/src/share/classes/com/sun/tools/javac/code/Source.java 2014-06-16 22:43:27.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/code/Source.java 2014-06-16 22:43:27.000000000 -0700 @@ -237,6 +237,9 @@ public boolean allowFunctionalInterfaceMostSpecific() { return compareTo(JDK1_8) >= 0; } + public boolean allowPrivateSafeVarargs() { + return compareTo(JDK1_9) >= 0; + } public static SourceVersion toSourceVersion(Source source) { switch(source) { case JDK1_2: --- old/src/share/classes/com/sun/tools/javac/comp/Check.java 2014-06-16 22:43:27.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/Check.java 2014-06-16 22:43:27.000000000 -0700 @@ -127,6 +127,7 @@ allowSimplifiedVarargs = source.allowSimplifiedVarargs(); allowDefaultMethods = source.allowDefaultMethods(); allowStrictMethodClashCheck = source.allowStrictMethodClashCheck(); + allowPrivateSafeVarargs = source.allowPrivateSafeVarargs(); complexInference = options.isSet("complexinference"); warnOnSyntheticConflicts = options.isSet("warnOnSyntheticConflicts"); suppressAbortOnBadClassFile = options.isSet("suppressAbortOnBadClassFile"); @@ -181,6 +182,10 @@ */ boolean allowStrictMethodClashCheck; + /** Switch: can the @SafeVarargs annotation be applied to private methods? + */ + boolean allowPrivateSafeVarargs; + /** Switch: -complexinference option set? */ boolean complexInference; @@ -816,8 +821,10 @@ if (varargElemType != null) { log.error(tree, "varargs.invalid.trustme.anno", - syms.trustMeType.tsym, - diags.fragment("varargs.trustme.on.virtual.varargs", m)); + syms.trustMeType.tsym, + allowPrivateSafeVarargs ? + diags.fragment("varargs.trustme.on.virtual.varargs", m) : + diags.fragment("varargs.trustme.on.virtual.varargs.final.only", m)); } else { log.error(tree, "varargs.invalid.trustme.anno", @@ -840,7 +847,8 @@ private boolean isTrustMeAllowedOnMethod(Symbol s) { return (s.flags() & VARARGS) != 0 && (s.isConstructor() || - (s.flags() & (STATIC | FINAL)) != 0); + (s.flags() & (STATIC | FINAL | + (allowPrivateSafeVarargs ? PRIVATE : 0) )) != 0); } Type checkMethod(final Type mtype, --- old/src/share/classes/com/sun/tools/javac/resources/compiler.properties 2014-06-16 22:43:27.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/resources/compiler.properties 2014-06-16 22:43:27.000000000 -0700 @@ -1101,6 +1101,10 @@ # 0: symbol compiler.misc.varargs.trustme.on.virtual.varargs=\ + Instance method {0} is neither final nor private. + +# 0: symbol +compiler.misc.varargs.trustme.on.virtual.varargs.final.only=\ Instance method {0} is not final. # 0: type, 1: symbol kind, 2: symbol --- old/test/tools/javac/varargs/warning/Warn4.java 2014-06-16 22:43:28.000000000 -0700 +++ new/test/tools/javac/varargs/warning/Warn4.java 2014-06-16 22:43:28.000000000 -0700 @@ -73,13 +73,15 @@ ModifierKind modKind) { switch(this) { case VARARGS: - return source == SourceLevel.JDK_6 || + return source.compareTo(SourceLevel.JDK_7) < 0 || suppressLevelDecl == SuppressLevel.UNCHECKED || trustMe == TrustMe.TRUST; case UNCHECKED: return suppressLevelClient == SuppressLevel.UNCHECKED || - (trustMe == TrustMe.TRUST && modKind != - ModifierKind.NONE && source == SourceLevel.JDK_7); + (trustMe == TrustMe.TRUST && + (((modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC) && + source.compareTo( SourceLevel.JDK_7) >= 0 ) || + (modKind == ModifierKind.PRIVATE && source.compareTo( SourceLevel.JDK_9) >= 0 ))); } SuppressLevel supLev = this == VARARGS ? @@ -92,7 +94,8 @@ enum SourceLevel { JDK_6("6"), - JDK_7("7"); + JDK_7("7"), + JDK_9("9"); String sourceKey; @@ -115,7 +118,8 @@ enum ModifierKind { NONE(" "), FINAL("final "), - STATIC("static "); + STATIC("static "), + PRIVATE("private "); String mod; --- old/test/tools/javac/varargs/warning/Warn5.java 2014-06-16 22:43:28.000000000 -0700 +++ new/test/tools/javac/varargs/warning/Warn5.java 2014-06-16 22:43:28.000000000 -0700 @@ -89,7 +89,8 @@ enum ModifierKind { NONE(""), FINAL("final"), - STATIC("static"); + STATIC("static"), + PRIVATE("private"); String mod; @@ -111,7 +112,8 @@ enum SourceLevel { JDK_6("6"), - JDK_7("7"); + JDK_7("7"), + JDK_9("9"); String sourceKey; @@ -238,7 +240,7 @@ EnumSet expectedWarnings = EnumSet.noneOf(WarningKind.class); - if (sourceLevel == SourceLevel.JDK_7 && + if (sourceLevel.compareTo(SourceLevel.JDK_7) >= 0 && trustMe == TrustMe.TRUST && suppressLevel != SuppressLevel.VARARGS && xlint != XlintOption.NONE && @@ -247,11 +249,12 @@ body.hasAliasing && (methKind == MethodKind.CONSTRUCTOR || (methKind == MethodKind.METHOD && - modKind != ModifierKind.NONE))) { + modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC || + (modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_9) >= 0)))) { expectedWarnings.add(WarningKind.UNSAFE_BODY); } - if (sourceLevel == SourceLevel.JDK_7 && + if (sourceLevel.compareTo(SourceLevel.JDK_7) >= 0 && trustMe == TrustMe.DONT_TRUST && sig.isVarargs && !sig.isReifiableArg && @@ -259,20 +262,22 @@ expectedWarnings.add(WarningKind.UNSAFE_DECL); } - if (sourceLevel == SourceLevel.JDK_7 && + if (sourceLevel.compareTo(SourceLevel.JDK_7) >= 0 && trustMe == TrustMe.TRUST && (!sig.isVarargs || - (modKind == ModifierKind.NONE && - methKind == MethodKind.METHOD))) { + ((modKind == ModifierKind.NONE || + modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_9) < 0 ) && + methKind == MethodKind.METHOD))) { expectedWarnings.add(WarningKind.MALFORMED_SAFEVARARGS); } - if (sourceLevel == SourceLevel.JDK_7 && + if (sourceLevel.compareTo(SourceLevel.JDK_7) >= 0 && trustMe == TrustMe.TRUST && xlint != XlintOption.NONE && suppressLevel != SuppressLevel.VARARGS && - (modKind != ModifierKind.NONE || - methKind == MethodKind.CONSTRUCTOR) && + (modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC || + (modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_9) >= 0) || + methKind == MethodKind.CONSTRUCTOR) && sig.isVarargs && sig.isReifiableArg) { expectedWarnings.add(WarningKind.REDUNDANT_SAFEVARARGS); @@ -283,6 +288,7 @@ throw new Error("invalid diagnostics for source:\n" + source.getCharContent(true) + "\nOptions: " + xlint.getXlintOption() + + "\nSource Level: " + sourceLevel + "\nExpected warnings: " + expectedWarnings + "\nFound warnings: " + dc.warnings); } --- /dev/null 2014-06-15 18:24:13.122065320 -0700 +++ new/test/tools/javac/diags/examples/VarargsFinalOnly.java 2014-06-16 22:43:28.000000000 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010, 2014, 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.err.varargs.invalid.trustme.anno +// key: compiler.misc.varargs.trustme.on.virtual.varargs.final.only +// options: -source 1.8 -Xlint:varargs,-options,-unchecked + +import java.util.List; + +class VarargsFinalOnly { + @SafeVarargs void m(List... args) { } +}