1 /*
  2  * Copyright (c) 2010, 2022, 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 7196160 8129962
 27  * @summary Project Coin: Simplified Varargs Method Invocation
 28  *  temporarily workaround combo tests are causing time out in several platforms
 29  * @enablePreview
 30  * @library /tools/javac/lib
 31  * @modules jdk.compiler/com.sun.tools.javac.api
 32  *          jdk.compiler/com.sun.tools.javac.file
 33  *          jdk.compiler/com.sun.tools.javac.util
 34  * @build combo.ComboTestHelper
 35  * @run main Warn4
 36  */
 37 
 38 import java.io.IOException;
 39 import java.util.Set;
 40 import java.util.HashSet;
 41 import javax.tools.Diagnostic;
 42 import javax.tools.Diagnostic.Kind;
 43 import javax.tools.JavaFileObject;
 44 
 45 import combo.ComboInstance;
 46 import combo.ComboParameter;
 47 import combo.ComboTask.Result;
 48 import combo.ComboTestHelper;
 49 
 50 public class Warn4 extends ComboInstance<Warn4> {
 51 
 52     final static Warning[] error = null;
 53     final static Warning[] none = new Warning[] {};
 54     final static Warning[] vararg = new Warning[] { Warning.VARARGS };
 55     final static Warning[] unchecked = new Warning[] { Warning.UNCHECKED };
 56     final static Warning[] both = new Warning[] { Warning.VARARGS, Warning.UNCHECKED };
 57 
 58     enum Warning {
 59         UNCHECKED("generic.array.creation"),
 60         VARARGS("varargs.non.reifiable.type");
 61 
 62         String key;
 63 
 64         Warning(String key) {
 65             this.key = key;
 66         }
 67 
 68         boolean isSuppressed(TrustMe trustMe, SourceLevel source,
 69                 SuppressLevel suppressLevelClient,
 70                 SuppressLevel suppressLevelDecl,
 71                 ModifierKind modKind) {
 72             switch(this) {
 73                 case VARARGS:
 74                     return  suppressLevelDecl == SuppressLevel.UNCHECKED ||
 75                         trustMe == TrustMe.TRUST;
 76                 case UNCHECKED:
 77                     return suppressLevelClient == SuppressLevel.UNCHECKED ||
 78                         (trustMe == TrustMe.TRUST &&
 79                          (((modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC) ) ||
 80                           (modKind == ModifierKind.PRIVATE && source.compareTo( SourceLevel.JDK_9) >= 0 )));
 81             }
 82 
 83             SuppressLevel supLev = this == VARARGS ?
 84                 suppressLevelDecl :
 85                 suppressLevelClient;
 86             return supLev == SuppressLevel.UNCHECKED ||
 87                     (trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE);
 88         }
 89     }
 90 
 91     enum SourceLevel {
 92         JDK_9("9"),
 93         LATEST(Integer.toString(javax.lang.model.SourceVersion.latest().runtimeVersion().feature()));
 94 
 95         String sourceKey;
 96 
 97         SourceLevel(String sourceKey) {
 98             this.sourceKey = sourceKey;
 99         }
100     }
101 
102     enum TrustMe implements ComboParameter {
103         DONT_TRUST(""),
104         TRUST("@java.lang.SafeVarargs");
105 
106         String anno;
107 
108         TrustMe(String anno) {
109             this.anno = anno;
110         }
111 
112         @Override
113         public String expand(String optParameter) {
114             return anno;
115         }
116     }
117 
118     enum ModifierKind implements ComboParameter {
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         @Override
131         public String expand(String optParameter) {
132             return mod;
133         }
134     }
135 
136     enum SuppressLevel implements ComboParameter {
137         NONE(""),
138         UNCHECKED("unchecked");
139 
140         String lint;
141 
142         SuppressLevel(String lint) {
143             this.lint = lint;
144         }
145 
146         @Override
147         public String expand(String optParameter) {
148             return "@SuppressWarnings(\"" + lint + "\")";
149         }
150     }
151 
152     enum Signature implements ComboParameter {
153         UNBOUND("void #NAME(List<?>#ARITY arg) { #BODY }",
154             new Warning[][] {none, none, none, none, error}),
155         INVARIANT_TVAR("<Z> void #NAME(List<Z>#ARITY arg) { #BODY }",
156             new Warning[][] {both, both, error, both, error}),
157         TVAR("<Z> void #NAME(Z#ARITY arg) { #BODY }",
158             new Warning[][] {both, both, both, both, vararg}),
159         INVARIANT("void #NAME(List<String>#ARITY arg) { #BODY }",
160             new Warning[][] {error, error, error, both, error}),
161         UNPARAMETERIZED("void #NAME(String#ARITY arg) { #BODY }",
162             new Warning[][] {error, error, error, error, none});
163 
164         String template;
165         Warning[][] warnings;
166 
167         Signature(String template, Warning[][] warnings) {
168             this.template = template;
169             this.warnings = warnings;
170         }
171 
172         boolean isApplicableTo(Signature other) {
173             return warnings[other.ordinal()] != null;
174         }
175 
176         boolean giveUnchecked(Signature other) {
177             return warnings[other.ordinal()] == unchecked ||
178                     warnings[other.ordinal()] == both;
179         }
180 
181         boolean giveVarargs(Signature other) {
182             return warnings[other.ordinal()] == vararg ||
183                     warnings[other.ordinal()] == both;
184         }
185 
186         @Override
187         public String expand(String optParameter) {
188             if (optParameter.equals("CLIENT")) {
189                 return template.replaceAll("#ARITY", "")
190                         .replaceAll("#NAME", "test")
191                         .replaceAll("#BODY", "m(arg)");
192             } else {
193                 return template.replaceAll("#ARITY", "...")
194                         .replaceAll("#NAME", "m")
195                         .replaceAll("#BODY", "");
196             }
197         }
198     }
199 
200     public static void main(String... args) {
201         new ComboTestHelper<Warn4>()
202                 .withFilter(Warn4::badTestFilter)
203                 .withDimension("SOURCE", (x, level) -> x.sourceLevel = level, SourceLevel.values())
204                 .withDimension("TRUSTME", (x, trustme) -> x.trustMe = trustme, TrustMe.values())
205                 .withArrayDimension("SUPPRESS", (x, suppress, idx) -> x.suppress[idx] = suppress, 2, SuppressLevel.values())
206                 .withDimension("MOD", (x, mod) -> x.modKind = mod, ModifierKind.values())
207                 .withArrayDimension("MTH", (x, sig, idx) -> x.sigs[idx] = sig, 2, Signature.values())
208                 .run(Warn4::new);
209     }
210 
211     SourceLevel sourceLevel;
212     TrustMe trustMe;
213     SuppressLevel[] suppress = new SuppressLevel[2];
214     ModifierKind modKind;
215     Signature[] sigs = new Signature[2];
216 
217     boolean badTestFilter() {
218         return sigs[0].isApplicableTo(sigs[1]);
219     }
220 
221     final String template = """
222         import java.util.List;
223         class Test {
224            #{TRUSTME} #{SUPPRESS[0]} #{MOD} #{MTH[0].VARARG}
225            #{SUPPRESS[1]} #{MTH[1].CLIENT}
226         }
227         """;
228 
229     @Override
230     public void doWork() throws IOException {
231         newCompilationTask()
232                 .withOption("-Xlint:unchecked")
233                 .withOption("--release")
234                 .withOption(sourceLevel.sourceKey)
235                 .withSourceFromTemplate(template)
236                 .analyze(this::check);
237     }
238 
239     void check(Result<?> res) {
240         boolean[] warnArr = new boolean[] {sigs[0].giveUnchecked(sigs[1]),
241                                sigs[0].giveVarargs(sigs[1])};
242 
243         Set<Warning> warnings = new HashSet<>();
244         for (Diagnostic<? extends JavaFileObject> d : res.diagnosticsForKind(Kind.MANDATORY_WARNING)) {
245             if (d.getCode().contains(Warning.VARARGS.key)) {
246                     warnings.add(Warning.VARARGS);
247             } else if(d.getCode().contains(Warning.UNCHECKED.key)) {
248                 warnings.add(Warning.UNCHECKED);
249             }
250         }
251 
252         boolean badOutput = false;
253         for (Warning wkind : Warning.values()) {
254             boolean isSuppressed = wkind.isSuppressed(trustMe, sourceLevel,
255                     suppress[1], suppress[0], modKind);
256             badOutput |= (warnArr[wkind.ordinal()] && !isSuppressed) !=
257                     warnings.contains(wkind);
258         }
259         if (badOutput) {
260             fail("invalid diagnostics for source:\n" +
261                     res.compilationInfo() +
262                     "\nExpected unchecked warning: " + warnArr[0] +
263                     "\nExpected unsafe vararg warning: " + warnArr[1] +
264                     "\nWarnings: " + warnings +
265                     "\nSource level: " + sourceLevel);
266         }
267     }
268 }