1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug     6945418 6993978 8006694
  27  * @summary Project Coin: Simplified Varargs Method Invocation
  28  *  temporarily workaround combo tests are causing time out in several platforms
  29  * @author  mcimadamore
  30  * @library ../../lib
  31  * @build JavacTestingAbstractThreadedTest
  32  * @run main/othervm Warn4
  33  */
  34 
  35 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
  36 // see JDK-8006746
  37 
  38 import java.net.URI;
  39 import java.util.Arrays;
  40 import java.util.Set;
  41 import java.util.HashSet;
  42 import javax.tools.Diagnostic;
  43 import javax.tools.JavaCompiler;
  44 import javax.tools.JavaFileObject;
  45 import javax.tools.SimpleJavaFileObject;
  46 import javax.tools.ToolProvider;
  47 import com.sun.source.util.JavacTask;
  48 
  49 public class Warn4
  50     extends JavacTestingAbstractThreadedTest
  51     implements Runnable {
  52 
  53     final static Warning[] error = null;
  54     final static Warning[] none = new Warning[] {};
  55     final static Warning[] vararg = new Warning[] { Warning.VARARGS };
  56     final static Warning[] unchecked = new Warning[] { Warning.UNCHECKED };
  57     final static Warning[] both =
  58             new Warning[] { Warning.VARARGS, Warning.UNCHECKED };
  59 
  60     enum Warning {
  61         UNCHECKED("generic.array.creation"),
  62         VARARGS("varargs.non.reifiable.type");
  63 
  64         String key;
  65 
  66         Warning(String key) {
  67             this.key = key;
  68         }
  69 
  70         boolean isSuppressed(TrustMe trustMe, SourceLevel source,
  71                 SuppressLevel suppressLevelClient,
  72                 SuppressLevel suppressLevelDecl,
  73                 ModifierKind modKind) {
  74             switch(this) {
  75                 case VARARGS:
  76                     return source == SourceLevel.JDK_6 ||
  77                             suppressLevelDecl == SuppressLevel.UNCHECKED ||
  78                             trustMe == TrustMe.TRUST;
  79                 case UNCHECKED:
  80                     return suppressLevelClient == SuppressLevel.UNCHECKED ||
  81                         (trustMe == TrustMe.TRUST && modKind !=
  82                             ModifierKind.NONE && source == SourceLevel.JDK_7);
  83             }
  84 
  85             SuppressLevel supLev = this == VARARGS ?
  86                 suppressLevelDecl :
  87                 suppressLevelClient;
  88             return supLev == SuppressLevel.UNCHECKED ||
  89                     (trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE);
  90         }
  91     }
  92 
  93     enum SourceLevel {
  94         JDK_6("6"),
  95         JDK_7("7");
  96 
  97         String sourceKey;
  98 
  99         SourceLevel(String sourceKey) {
 100             this.sourceKey = sourceKey;
 101         }
 102     }
 103 
 104     enum TrustMe {
 105         DONT_TRUST(""),
 106         TRUST("@java.lang.SafeVarargs");
 107 
 108         String anno;
 109 
 110         TrustMe(String anno) {
 111             this.anno = anno;
 112         }
 113     }
 114 
 115     enum ModifierKind {
 116         NONE(" "),
 117         FINAL("final "),
 118         STATIC("static ");
 119 
 120         String mod;
 121 
 122         ModifierKind(String mod) {
 123             this.mod = mod;
 124         }
 125     }
 126 
 127     enum SuppressLevel {
 128         NONE(""),
 129         UNCHECKED("unchecked");
 130 
 131         String lint;
 132 
 133         SuppressLevel(String lint) {
 134             this.lint = lint;
 135         }
 136 
 137         String getSuppressAnno() {
 138             return "@SuppressWarnings(\"" + lint + "\")";
 139         }
 140     }
 141 
 142     enum Signature {
 143         UNBOUND("void #name(List<?>#arity arg) { #body }",
 144             new Warning[][] {none, none, none, none, error}),
 145         INVARIANT_TVAR("<Z> void #name(List<Z>#arity arg) { #body }",
 146             new Warning[][] {both, both, error, both, error}),
 147         TVAR("<Z> void #name(Z#arity arg) { #body }",
 148             new Warning[][] {both, both, both, both, vararg}),
 149         INVARIANT("void #name(List<String>#arity arg) { #body }",
 150             new Warning[][] {error, error, error, both, error}),
 151         UNPARAMETERIZED("void #name(String#arity arg) { #body }",
 152             new Warning[][] {error, error, error, error, none});
 153 
 154         String template;
 155         Warning[][] warnings;
 156 
 157         Signature(String template, Warning[][] warnings) {
 158             this.template = template;
 159             this.warnings = warnings;
 160         }
 161 
 162         boolean isApplicableTo(Signature other) {
 163             return warnings[other.ordinal()] != null;
 164         }
 165 
 166         boolean giveUnchecked(Signature other) {
 167             return warnings[other.ordinal()] == unchecked ||
 168                     warnings[other.ordinal()] == both;
 169         }
 170 
 171         boolean giveVarargs(Signature other) {
 172             return warnings[other.ordinal()] == vararg ||
 173                     warnings[other.ordinal()] == both;
 174         }
 175     }
 176 
 177     public static void main(String... args) throws Exception {
 178         for (SourceLevel sourceLevel : SourceLevel.values()) {
 179             for (TrustMe trustMe : TrustMe.values()) {
 180                 for (SuppressLevel suppressLevelClient : SuppressLevel.values()) {
 181                     for (SuppressLevel suppressLevelDecl : SuppressLevel.values()) {
 182                         for (ModifierKind modKind : ModifierKind.values()) {
 183                             for (Signature vararg_meth : Signature.values()) {
 184                                 for (Signature client_meth : Signature.values()) {
 185                                     if (vararg_meth.isApplicableTo(client_meth)) {
 186                                         pool.execute(new Warn4(sourceLevel,
 187                                                 trustMe,
 188                                                 suppressLevelClient,
 189                                                 suppressLevelDecl,
 190                                                 modKind,
 191                                                 vararg_meth,
 192                                                 client_meth));
 193                                     }
 194                                 }
 195                             }
 196                         }
 197                     }
 198                 }
 199             }
 200         }
 201 
 202         checkAfterExec();
 203     }
 204 
 205     SourceLevel sourceLevel;
 206     TrustMe trustMe;
 207     SuppressLevel suppressLevelClient;
 208     SuppressLevel suppressLevelDecl;
 209     ModifierKind modKind;
 210     Signature vararg_meth;
 211     Signature client_meth;
 212     DiagnosticChecker diagChecker;
 213 
 214     public Warn4(SourceLevel sourceLevel, TrustMe trustMe,
 215             SuppressLevel suppressLevelClient, SuppressLevel suppressLevelDecl,
 216             ModifierKind modKind, Signature vararg_meth, Signature client_meth) {
 217         this.sourceLevel = sourceLevel;
 218         this.trustMe = trustMe;
 219         this.suppressLevelClient = suppressLevelClient;
 220         this.suppressLevelDecl = suppressLevelDecl;
 221         this.modKind = modKind;
 222         this.vararg_meth = vararg_meth;
 223         this.client_meth = client_meth;
 224         this.diagChecker = new DiagnosticChecker();
 225     }
 226 
 227     @Override
 228     public void run() {
 229         int id = checkCount.incrementAndGet();
 230         final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
 231         JavaSource source = new JavaSource(id);
 232         JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), diagChecker,
 233                 Arrays.asList("-Xlint:unchecked", "-source", sourceLevel.sourceKey),
 234                 null, Arrays.asList(source));
 235         ct.call(); //to get mandatory notes
 236         check(source, new boolean[] {vararg_meth.giveUnchecked(client_meth),
 237                                vararg_meth.giveVarargs(client_meth)});
 238     }
 239 
 240     void check(JavaSource source, boolean[] warnArr) {
 241         boolean badOutput = false;
 242         for (Warning wkind : Warning.values()) {
 243             boolean isSuppressed = wkind.isSuppressed(trustMe, sourceLevel,
 244                     suppressLevelClient, suppressLevelDecl, modKind);
 245             badOutput |= (warnArr[wkind.ordinal()] && !isSuppressed) !=
 246                     diagChecker.warnings.contains(wkind);
 247         }
 248         if (badOutput) {
 249             throw new Error("invalid diagnostics for source:\n" +
 250                     source.getCharContent(true) +
 251                     "\nExpected unchecked warning: " + warnArr[0] +
 252                     "\nExpected unsafe vararg warning: " + warnArr[1] +
 253                     "\nWarnings: " + diagChecker.warnings +
 254                     "\nSource level: " + sourceLevel);
 255         }
 256     }
 257 
 258     class JavaSource extends SimpleJavaFileObject {
 259 
 260         String source;
 261 
 262         public JavaSource(int id) {
 263             super(URI.create(String.format("myfo:/Test%d.java", id)),
 264                     JavaFileObject.Kind.SOURCE);
 265             String meth1 = vararg_meth.template.replace("#arity", "...");
 266             meth1 = meth1.replace("#name", "m");
 267             meth1 = meth1.replace("#body", "");
 268             meth1 = trustMe.anno + "\n" + suppressLevelDecl.getSuppressAnno() +
 269                     modKind.mod + meth1;
 270             String meth2 = client_meth.template.replace("#arity", "");
 271             meth2 = meth2.replace("#name", "test");
 272             meth2 = meth2.replace("#body", "m(arg);");
 273             meth2 = suppressLevelClient.getSuppressAnno() + meth2;
 274             source = String.format("import java.util.List;\n" +
 275                      "class Test%s {\n %s \n %s \n } \n", id, meth1, meth2);
 276         }
 277 
 278         @Override
 279         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
 280             return source;
 281         }
 282     }
 283 
 284     static class DiagnosticChecker
 285         implements javax.tools.DiagnosticListener<JavaFileObject> {
 286 
 287         Set<Warning> warnings = new HashSet<>();
 288 
 289         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
 290             if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING ||
 291                     diagnostic.getKind() == Diagnostic.Kind.WARNING) {
 292                 if (diagnostic.getCode().contains(Warning.VARARGS.key)) {
 293                     warnings.add(Warning.VARARGS);
 294                 } else if(diagnostic.getCode().contains(Warning.UNCHECKED.key)) {
 295                     warnings.add(Warning.UNCHECKED);
 296                 }
 297             }
 298         }
 299     }
 300 
 301 }