794N/A/*
961N/A * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
794N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
794N/A *
794N/A * This code is free software; you can redistribute it and/or modify it
794N/A * under the terms of the GNU General Public License version 2 only, as
794N/A * published by the Free Software Foundation.
794N/A *
794N/A * This code is distributed in the hope that it will be useful, but WITHOUT
794N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
794N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
794N/A * version 2 for more details (a copy is included in the LICENSE file that
794N/A * accompanied this code).
794N/A *
794N/A * You should have received a copy of the GNU General Public License version
794N/A * 2 along with this work; if not, write to the Free Software Foundation,
794N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
794N/A *
794N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
794N/A * or visit www.oracle.com if you need additional information or have any
794N/A * questions.
794N/A */
794N/A
794N/A/**
794N/A * @test
1107N/A * @bug 6993978 7097436
794N/A * @summary Project Coin: Annotation to reduce varargs warnings
794N/A * @author mcimadamore
794N/A * @run main Warn5
794N/A */
794N/Aimport com.sun.source.util.JavacTask;
891N/Aimport com.sun.tools.javac.api.JavacTool;
794N/Aimport java.net.URI;
794N/Aimport java.util.Arrays;
1107N/Aimport java.util.EnumSet;
794N/Aimport javax.tools.Diagnostic;
794N/Aimport javax.tools.JavaCompiler;
794N/Aimport javax.tools.JavaFileObject;
794N/Aimport javax.tools.SimpleJavaFileObject;
891N/Aimport javax.tools.StandardJavaFileManager;
794N/Aimport javax.tools.ToolProvider;
794N/A
794N/Apublic class Warn5 {
794N/A
794N/A enum XlintOption {
794N/A NONE("none"),
794N/A ALL("all");
794N/A
794N/A String opt;
794N/A
794N/A XlintOption(String opt) {
794N/A this.opt = opt;
794N/A }
794N/A
794N/A String getXlintOption() {
794N/A return "-Xlint:" + opt;
794N/A }
794N/A }
794N/A
794N/A enum TrustMe {
794N/A DONT_TRUST(""),
794N/A TRUST("@java.lang.SafeVarargs");
794N/A
794N/A String anno;
794N/A
794N/A TrustMe(String anno) {
794N/A this.anno = anno;
794N/A }
794N/A }
794N/A
794N/A enum SuppressLevel {
794N/A NONE,
794N/A VARARGS;
794N/A
794N/A String getSuppressAnno() {
794N/A return this == VARARGS ?
794N/A "@SuppressWarnings(\"varargs\")" :
794N/A "";
794N/A }
794N/A }
794N/A
794N/A enum ModifierKind {
794N/A NONE(""),
794N/A FINAL("final"),
794N/A STATIC("static");
794N/A
794N/A String mod;
794N/A
794N/A ModifierKind(String mod) {
794N/A this.mod = mod;
794N/A }
794N/A }
794N/A
794N/A enum MethodKind {
794N/A METHOD("void m"),
794N/A CONSTRUCTOR("Test");
794N/A
794N/A String name;
794N/A
794N/A MethodKind(String name) {
794N/A this.name = name;
794N/A }
794N/A }
794N/A
794N/A enum SourceLevel {
794N/A JDK_6("6"),
794N/A JDK_7("7");
794N/A
794N/A String sourceKey;
794N/A
794N/A SourceLevel(String sourceKey) {
794N/A this.sourceKey = sourceKey;
794N/A }
794N/A }
794N/A
794N/A enum SignatureKind {
794N/A VARARGS_X("#K <X>#N(X... x)", false, true),
794N/A VARARGS_STRING("#K #N(String... x)", true, true),
794N/A ARRAY_X("#K <X>#N(X[] x)", false, false),
794N/A ARRAY_STRING("#K #N(String[] x)", true, false);
794N/A
794N/A String stub;
794N/A boolean isReifiableArg;
794N/A boolean isVarargs;
794N/A
794N/A SignatureKind(String stub, boolean isReifiableArg, boolean isVarargs) {
794N/A this.stub = stub;
794N/A this.isReifiableArg = isReifiableArg;
794N/A this.isVarargs = isVarargs;
794N/A }
794N/A
794N/A String getSignature(ModifierKind modKind, MethodKind methKind) {
794N/A return methKind != MethodKind.CONSTRUCTOR ?
794N/A stub.replace("#K", modKind.mod).replace("#N", methKind.name) :
794N/A stub.replace("#K", "").replace("#N", methKind.name);
794N/A }
794N/A }
794N/A
794N/A enum BodyKind {
794N/A ASSIGN("Object o = x;", true),
794N/A CAST("Object o = (Object)x;", true),
794N/A METH("test(x);", true),
794N/A PRINT("System.out.println(x.toString());", false),
794N/A ARRAY_ASSIGN("Object[] o = x;", true),
794N/A ARRAY_CAST("Object[] o = (Object[])x;", true),
794N/A ARRAY_METH("testArr(x);", true);
794N/A
794N/A String body;
794N/A boolean hasAliasing;
794N/A
794N/A BodyKind(String body, boolean hasAliasing) {
794N/A this.body = body;
794N/A this.hasAliasing = hasAliasing;
794N/A }
794N/A }
794N/A
1107N/A enum WarningKind {
1107N/A UNSAFE_BODY,
1107N/A UNSAFE_DECL,
1107N/A MALFORMED_SAFEVARARGS,
1107N/A REDUNDANT_SAFEVARARGS;
1107N/A }
1107N/A
1107N/A // Create a single file manager and reuse it for each compile to save time.
1107N/A static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
1107N/A
1107N/A public static void main(String... args) throws Exception {
1107N/A for (SourceLevel sourceLevel : SourceLevel.values()) {
1107N/A for (XlintOption xlint : XlintOption.values()) {
1107N/A for (TrustMe trustMe : TrustMe.values()) {
1107N/A for (SuppressLevel suppressLevel : SuppressLevel.values()) {
1107N/A for (ModifierKind modKind : ModifierKind.values()) {
1107N/A for (MethodKind methKind : MethodKind.values()) {
1107N/A for (SignatureKind sig : SignatureKind.values()) {
1107N/A for (BodyKind body : BodyKind.values()) {
1107N/A new Warn5(sourceLevel,
1107N/A xlint,
1107N/A trustMe,
1107N/A suppressLevel,
1107N/A modKind,
1107N/A methKind,
1107N/A sig,
1107N/A body).test();
1107N/A }
1107N/A }
1107N/A }
1107N/A }
1107N/A }
1107N/A }
1107N/A }
1107N/A }
1107N/A }
1107N/A
1107N/A final SourceLevel sourceLevel;
1107N/A final XlintOption xlint;
1107N/A final TrustMe trustMe;
1107N/A final SuppressLevel suppressLevel;
1107N/A final ModifierKind modKind;
1107N/A final MethodKind methKind;
1107N/A final SignatureKind sig;
1107N/A final BodyKind body;
1107N/A final JavaSource source;
1107N/A final DiagnosticChecker dc;
1107N/A
1107N/A public Warn5(SourceLevel sourceLevel, XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind, MethodKind methKind, SignatureKind sig, BodyKind body) {
1107N/A this.sourceLevel = sourceLevel;
1107N/A this.xlint = xlint;
1107N/A this.trustMe = trustMe;
1107N/A this.suppressLevel = suppressLevel;
1107N/A this.modKind = modKind;
1107N/A this.methKind = methKind;
1107N/A this.sig = sig;
1107N/A this.body = body;
1107N/A this.source = new JavaSource();
1107N/A this.dc = new DiagnosticChecker();
1107N/A }
1107N/A
1107N/A void test() throws Exception {
1107N/A final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
1107N/A JavacTask ct = (JavacTask)tool.getTask(null, fm, dc,
1107N/A Arrays.asList(xlint.getXlintOption(), "-source", sourceLevel.sourceKey), null, Arrays.asList(source));
1107N/A ct.analyze();
1107N/A check();
1107N/A }
1107N/A
1107N/A void check() {
1107N/A
1107N/A EnumSet<WarningKind> expectedWarnings = EnumSet.noneOf(WarningKind.class);
1107N/A
1107N/A if (sourceLevel == SourceLevel.JDK_7 &&
1107N/A trustMe == TrustMe.TRUST &&
1107N/A suppressLevel != SuppressLevel.VARARGS &&
1107N/A xlint != XlintOption.NONE &&
1107N/A sig.isVarargs && !sig.isReifiableArg && body.hasAliasing &&
1107N/A (methKind == MethodKind.CONSTRUCTOR || (methKind == MethodKind.METHOD && modKind != ModifierKind.NONE))) {
1107N/A expectedWarnings.add(WarningKind.UNSAFE_BODY);
1107N/A }
1107N/A
1107N/A if (sourceLevel == SourceLevel.JDK_7 &&
1107N/A trustMe == TrustMe.DONT_TRUST &&
1107N/A sig.isVarargs &&
1107N/A !sig.isReifiableArg &&
1107N/A xlint == XlintOption.ALL) {
1107N/A expectedWarnings.add(WarningKind.UNSAFE_DECL);
1107N/A }
1107N/A
1107N/A if (sourceLevel == SourceLevel.JDK_7 &&
1107N/A trustMe == TrustMe.TRUST &&
1107N/A (!sig.isVarargs ||
1107N/A (modKind == ModifierKind.NONE && methKind == MethodKind.METHOD))) {
1107N/A expectedWarnings.add(WarningKind.MALFORMED_SAFEVARARGS);
1107N/A }
1107N/A
1107N/A if (sourceLevel == SourceLevel.JDK_7 &&
1107N/A trustMe == TrustMe.TRUST &&
1107N/A xlint != XlintOption.NONE &&
1107N/A suppressLevel != SuppressLevel.VARARGS &&
1107N/A (modKind != ModifierKind.NONE || methKind == MethodKind.CONSTRUCTOR) &&
1107N/A sig.isVarargs &&
1107N/A sig.isReifiableArg) {
1107N/A expectedWarnings.add(WarningKind.REDUNDANT_SAFEVARARGS);
1107N/A }
1107N/A
1107N/A if (!expectedWarnings.containsAll(dc.warnings) ||
1107N/A !dc.warnings.containsAll(expectedWarnings)) {
1107N/A throw new Error("invalid diagnostics for source:\n" +
1107N/A source.getCharContent(true) +
1107N/A "\nOptions: " + xlint.getXlintOption() +
1107N/A "\nExpected warnings: " + expectedWarnings +
1107N/A "\nFound warnings: " + dc.warnings);
1107N/A }
1107N/A }
1107N/A
1107N/A class JavaSource extends SimpleJavaFileObject {
794N/A
794N/A String template = "import com.sun.tools.javac.api.*;\n" +
794N/A "import java.util.List;\n" +
794N/A "class Test {\n" +
794N/A " static void test(Object o) {}\n" +
794N/A " static void testArr(Object[] o) {}\n" +
794N/A " #T \n #S #M { #B }\n" +
794N/A "}\n";
794N/A
794N/A String source;
794N/A
1107N/A public JavaSource() {
794N/A super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
794N/A source = template.replace("#T", trustMe.anno).
794N/A replace("#S", suppressLevel.getSuppressAnno()).
1107N/A replace("#M", sig.getSignature(modKind, methKind)).
794N/A replace("#B", body.body);
794N/A }
794N/A
794N/A @Override
794N/A public CharSequence getCharContent(boolean ignoreEncodingErrors) {
794N/A return source;
794N/A }
794N/A }
794N/A
1107N/A class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
794N/A
1107N/A EnumSet<WarningKind> warnings = EnumSet.noneOf(WarningKind.class);
794N/A
794N/A public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
794N/A if (diagnostic.getKind() == Diagnostic.Kind.WARNING) {
794N/A if (diagnostic.getCode().contains("unsafe.use.varargs.param")) {
1107N/A setWarning(WarningKind.UNSAFE_BODY);
794N/A } else if (diagnostic.getCode().contains("redundant.trustme")) {
1107N/A setWarning(WarningKind.REDUNDANT_SAFEVARARGS);
794N/A }
794N/A } else if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING &&
794N/A diagnostic.getCode().contains("varargs.non.reifiable.type")) {
1107N/A setWarning(WarningKind.UNSAFE_DECL);
794N/A } else if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
794N/A diagnostic.getCode().contains("invalid.trustme")) {
1107N/A setWarning(WarningKind.MALFORMED_SAFEVARARGS);
794N/A }
794N/A }
1107N/A
1107N/A void setWarning(WarningKind wk) {
1107N/A if (!warnings.add(wk)) {
1107N/A throw new AssertionError("Duplicate warning of kind " + wk + " in source:\n" + source);
1107N/A }
1107N/A }
1107N/A
1107N/A boolean hasWarning(WarningKind wk) {
1107N/A return warnings.contains(wk);
1107N/A }
794N/A }
794N/A}