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.compareTo(SourceLevel.JDK_7) < 0 ||
  77                             suppressLevelDecl == SuppressLevel.UNCHECKED ||
  78                             trustMe == TrustMe.TRUST;
  79                 case UNCHECKED:
  80                     return suppressLevelClient == SuppressLevel.UNCHECKED ||
  81                         (trustMe == TrustMe.TRUST &&
  82                          (((modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC) &&
  83                            source.compareTo( SourceLevel.JDK_7) >= 0 ) ||
  84                           (modKind == ModifierKind.PRIVATE && source.compareTo( SourceLevel.JDK_9) >= 0 )));
  85             }
  86 
  87             SuppressLevel supLev = this == VARARGS ?
  88                 suppressLevelDecl :
  89                 suppressLevelClient;
  90             return supLev == SuppressLevel.UNCHECKED ||
  91                     (trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE);
  92         }
  93     }
  94 
  95     enum SourceLevel {
  96         JDK_6("6"),
  97         JDK_7("7"),
  98         JDK_9("9");
  99 
 100         String sourceKey;
 101 
 102         SourceLevel(String sourceKey) {
 103             this.sourceKey = sourceKey;
 104         }
 105     }
 106 
 107     enum TrustMe {
 108         DONT_TRUST(""),
 109         TRUST("@java.lang.SafeVarargs");
 110 
 111         String anno;
 112 
 113         TrustMe(String anno) {
 114             this.anno = anno;
 115         }
 116     }
 117 
 118     enum ModifierKind {
 119         NONE(" "),
 120         FINAL("final "),
 121         STATIC("static "),
 122         PRIVATE("private ");
 123 
 124         String mod;
 125 
 126         ModifierKind(String mod) {
 127             this.mod = mod;
 128         }
 129     }
 130 
 131     enum SuppressLevel {
 132         NONE(""),
 133         UNCHECKED("unchecked");
 134 
 135         String lint;
 136 
 137         SuppressLevel(String lint) {
 138             this.lint = lint;
 139         }
 140 
 141         String getSuppressAnno() {
 142             return "@SuppressWarnings(\"" + lint + "\")";
 143         }
 144     }
 145 
 146     enum Signature {
 147         UNBOUND("void #name(List<?>#arity arg) { #body }",
 148             new Warning[][] {none, none, none, none, error}),
 149         INVARIANT_TVAR("<Z> void #name(List<Z>#arity arg) { #body }",
 150             new Warning[][] {both, both, error, both, error}),
 151         TVAR("<Z> void #name(Z#arity arg) { #body }",
 152             new Warning[][] {both, both, both, both, vararg}),
 153         INVARIANT("void #name(List<String>#arity arg) { #body }",
 154             new Warning[][] {error, error, error, both, error}),
 155         UNPARAMETERIZED("void #name(String#arity arg) { #body }",
 156             new Warning[][] {error, error, error, error, none});
 157 
 158         String template;
 159         Warning[][] warnings;
 160 
 161         Signature(String template, Warning[][] warnings) {
 162             this.template = template;
 163             this.warnings = warnings;
 164         }
 165 
 166         boolean isApplicableTo(Signature other) {
 167             return warnings[other.ordinal()] != null;
 168         }
 169 
 170         boolean giveUnchecked(Signature other) {
 171             return warnings[other.ordinal()] == unchecked ||
 172                     warnings[other.ordinal()] == both;
 173         }
 174 
 175         boolean giveVarargs(Signature other) {
 176             return warnings[other.ordinal()] == vararg ||
 177                     warnings[other.ordinal()] == both;
 178         }
 179     }
 180 
 181     public static void main(String... args) throws Exception {
 182         for (SourceLevel sourceLevel : SourceLevel.values()) {
 183             for (TrustMe trustMe : TrustMe.values()) {
 184                 for (SuppressLevel suppressLevelClient : SuppressLevel.values()) {
 185                     for (SuppressLevel suppressLevelDecl : SuppressLevel.values()) {
 186                         for (ModifierKind modKind : ModifierKind.values()) {
 187                             for (Signature vararg_meth : Signature.values()) {
 188                                 for (Signature client_meth : Signature.values()) {
 189                                     if (vararg_meth.isApplicableTo(client_meth)) {
 190                                         pool.execute(new Warn4(sourceLevel,
 191                                                 trustMe,
 192                                                 suppressLevelClient,
 193                                                 suppressLevelDecl,
 194                                                 modKind,
 195                                                 vararg_meth,
 196                                                 client_meth));
 197                                     }
 198                                 }
 199                             }
 200                         }
 201                     }
 202                 }
 203             }
 204         }
 205 
 206         checkAfterExec();
 207     }
 208 
 209     SourceLevel sourceLevel;
 210     TrustMe trustMe;
 211     SuppressLevel suppressLevelClient;
 212     SuppressLevel suppressLevelDecl;
 213     ModifierKind modKind;
 214     Signature vararg_meth;
 215     Signature client_meth;
 216     DiagnosticChecker diagChecker;
 217 
 218     public Warn4(SourceLevel sourceLevel, TrustMe trustMe,
 219             SuppressLevel suppressLevelClient, SuppressLevel suppressLevelDecl,
 220             ModifierKind modKind, Signature vararg_meth, Signature client_meth) {
 221         this.sourceLevel = sourceLevel;
 222         this.trustMe = trustMe;
 223         this.suppressLevelClient = suppressLevelClient;
 224         this.suppressLevelDecl = suppressLevelDecl;
 225         this.modKind = modKind;
 226         this.vararg_meth = vararg_meth;
 227         this.client_meth = client_meth;
 228         this.diagChecker = new DiagnosticChecker();
 229     }
 230 
 231     @Override
 232     public void run() {
 233         int id = checkCount.incrementAndGet();
 234         final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
 235         JavaSource source = new JavaSource(id);
 236         JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), diagChecker,
 237                 Arrays.asList("-Xlint:unchecked", "-source", sourceLevel.sourceKey),
 238                 null, Arrays.asList(source));
 239         ct.call(); //to get mandatory notes
 240         check(source, new boolean[] {vararg_meth.giveUnchecked(client_meth),
 241                                vararg_meth.giveVarargs(client_meth)});
 242     }
 243 
 244     void check(JavaSource source, boolean[] warnArr) {
 245         boolean badOutput = false;
 246         for (Warning wkind : Warning.values()) {
 247             boolean isSuppressed = wkind.isSuppressed(trustMe, sourceLevel,
 248                     suppressLevelClient, suppressLevelDecl, modKind);
 249             badOutput |= (warnArr[wkind.ordinal()] && !isSuppressed) !=
 250                     diagChecker.warnings.contains(wkind);
 251         }
 252         if (badOutput) {
 253             throw new Error("invalid diagnostics for source:\n" +
 254                     source.getCharContent(true) +
 255                     "\nExpected unchecked warning: " + warnArr[0] +
 256                     "\nExpected unsafe vararg warning: " + warnArr[1] +
 257                     "\nWarnings: " + diagChecker.warnings +
 258                     "\nSource level: " + sourceLevel);
 259         }
 260     }
 261 
 262     class JavaSource extends SimpleJavaFileObject {
 263 
 264         String source;
 265 
 266         public JavaSource(int id) {
 267             super(URI.create(String.format("myfo:/Test%d.java", id)),
 268                     JavaFileObject.Kind.SOURCE);
 269             String meth1 = vararg_meth.template.replace("#arity", "...");
 270             meth1 = meth1.replace("#name", "m");
 271             meth1 = meth1.replace("#body", "");
 272             meth1 = trustMe.anno + "\n" + suppressLevelDecl.getSuppressAnno() +
 273                     modKind.mod + meth1;
 274             String meth2 = client_meth.template.replace("#arity", "");
 275             meth2 = meth2.replace("#name", "test");
 276             meth2 = meth2.replace("#body", "m(arg);");
 277             meth2 = suppressLevelClient.getSuppressAnno() + meth2;
 278             source = String.format("import java.util.List;\n" +
 279                      "class Test%s {\n %s \n %s \n } \n", id, meth1, meth2);
 280         }
 281 
 282         @Override
 283         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
 284             return source;
 285         }
 286     }
 287 
 288     static class DiagnosticChecker
 289         implements javax.tools.DiagnosticListener<JavaFileObject> {
 290 
 291         Set<Warning> warnings = new HashSet<>();
 292 
 293         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
 294             if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING ||
 295                     diagnostic.getKind() == Diagnostic.Kind.WARNING) {
 296                 if (diagnostic.getCode().contains(Warning.VARARGS.key)) {
 297                     warnings.add(Warning.VARARGS);
 298                 } else if(diagnostic.getCode().contains(Warning.UNCHECKED.key)) {
 299                     warnings.add(Warning.UNCHECKED);
 300                 }
 301             }
 302         }
 303     }
 304 
 305 }