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     6993978 7097436 8006694 7196160
 27  * @summary Project Coin: Annotation to reduce varargs warnings
 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/othervm Warn5
 36  */
 37 
 38 import java.io.IOException;
 39 import java.util.EnumSet;
 40 import javax.tools.Diagnostic;
 41 import javax.tools.Diagnostic.Kind;
 42 import javax.tools.JavaFileObject;
 43 
 44 import combo.ComboInstance;
 45 import combo.ComboParameter;
 46 import combo.ComboTask.Result;
 47 import combo.ComboTestHelper;
 48 
 49 
 50 public class Warn5 extends ComboInstance<Warn5> {
 51 
 52     enum XlintOption {
 53         NONE("none"),
 54         ALL("all");
 55 
 56         String opt;
 57 
 58         XlintOption(String opt) {
 59             this.opt = opt;
 60         }
 61 
 62         String getXlintOption() {
 63             return "-Xlint:" + opt;
 64         }
 65     }
 66 
 67     enum TrustMe implements ComboParameter {
 68         DONT_TRUST(""),
 69         TRUST("@java.lang.SafeVarargs");
 70 
 71         String anno;
 72 
 73         TrustMe(String anno) {
 74             this.anno = anno;
 75         }
 76 
 77         @Override
 78         public String expand(String optParameter) {
 79             return anno;
 80         }
 81     }
 82 
 83     enum SuppressLevel implements ComboParameter {
 84         NONE,
 85         VARARGS;
 86 
 87         @Override
 88         public String expand(String optParameter) {
 89             return this == VARARGS ?
 90                 "@SuppressWarnings(\"varargs\")" :
 91                 "";
 92         }
 93     }
 94 
 95     enum ModifierKind implements ComboParameter {
 96         NONE(""),
 97         FINAL("final"),
 98         STATIC("static"),
 99         PRIVATE("private");
100 
101         String mod;
102 
103         ModifierKind(String mod) {
104             this.mod = mod;
105         }
106 
107         @Override
108         public String expand(String optParameter) {
109             return mod;
110         }
111     }
112 
113     enum MethodKind implements ComboParameter {
114         METHOD("void m"),
115         CONSTRUCTOR("Test");
116 
117         String name;
118 
119         MethodKind(String name) {
120             this.name = name;
121         }
122 
123         @Override
124         public String expand(String optParameter) {
125             return name;
126         }
127     }
128 
129     // Handling of varargs warnings changed in JDK 9 compared to JDK 8
130     // and then remained consistent; test 8 and then current release.
131     enum SourceLevel {
132         JDK_8("8"),
133         LATEST(Integer.toString(javax.lang.model.SourceVersion.latest().runtimeVersion().feature()));
134 
135         String sourceKey;
136 
137         SourceLevel(String sourceKey) {
138             this.sourceKey = sourceKey;
139         }
140     }
141 
142     enum SignatureKind implements ComboParameter {
143         VARARGS_X("<X>#{NAME}(X... x)", false, true),
144         VARARGS_STRING("#{NAME}(String... x)", true, true),
145         ARRAY_X("<X>#{NAME}(X[] x)", false, false),
146         ARRAY_STRING("#{NAME}(String[] x)", true, false);
147 
148         String stub;
149         boolean isReifiableArg;
150         boolean isVarargs;
151 
152         SignatureKind(String stub, boolean isReifiableArg, boolean isVarargs) {
153             this.stub = stub;
154             this.isReifiableArg = isReifiableArg;
155             this.isVarargs = isVarargs;
156         }
157 
158         @Override
159         public String expand(String optParameter) {
160             return stub;
161         }
162     }
163 
164     // javac does not currently perform analysis of the method body
165     // with respect to the validity of the @SafeVargs annotation. If
166     // that changes, the body tests should be expanded.
167     enum BodyKind implements ComboParameter {
168         // ASSIGN("Object o = x;", true),
169         // CAST("Object o = (Object)x;", true),
170         METH("test(x);", true),
171         PRINT("System.out.println(x.toString());", false),
172         // ARRAY_ASSIGN("Object[] o = x;", true),
173         ARRAY_CAST("Object[] o = (Object[])x;", true),
174         ARRAY_METH("testArr(x);", true);
175 
176         String body;
177         boolean hasAliasing;
178 
179         BodyKind(String body, boolean hasAliasing) {
180             this.body = body;
181             this.hasAliasing = hasAliasing;
182         }
183 
184         @Override
185         public String expand(String optParameter) {
186             return body;
187         }
188     }
189 
190     enum WarningKind {
191         UNSAFE_BODY("compiler.warn.varargs.unsafe.use.varargs.param"),
192         UNSAFE_DECL("compiler.warn.unchecked.varargs.non.reifiable.type"),
193         MALFORMED_SAFEVARARGS("compiler.err.varargs.invalid.trustme.anno"),
194         REDUNDANT_SAFEVARARGS("compiler.warn.varargs.redundant.trustme.anno");
195 
196         String code;
197 
198         WarningKind(String code) {
199             this.code = code;
200         }
201     }
202 
203     public static void main(String[] args) {
204         new ComboTestHelper<Warn5>()
205                 .withFilter(Warn5::badTestFilter)
206                 .withDimension("SOURCE", (x, level) -> x.sourceLevel = level, SourceLevel.values())
207                 .withDimension("LINT", (x, lint) -> x.xlint = lint, XlintOption.values())
208                 .withDimension("TRUSTME", (x, trustme) -> x.trustMe = trustme, TrustMe.values())
209                 .withDimension("SUPPRESS", (x, suppress) -> x.suppressLevel = suppress, SuppressLevel.values())
210                 .withDimension("MOD", (x, mod) -> x.modKind = mod, ModifierKind.values())
211                 .withDimension("NAME", (x, name) -> x.methKind = name, MethodKind.values())
212                 .withDimension("SIG", (x, sig) -> x.sig = sig, SignatureKind.values())
213                 .withDimension("BODY", (x, body) -> x.body = body, BodyKind.values())
214                 .run(Warn5::new);
215     }
216 
217     SourceLevel sourceLevel;
218     XlintOption xlint;
219     TrustMe trustMe;
220     SuppressLevel suppressLevel;
221     ModifierKind modKind;
222     MethodKind methKind;
223     SignatureKind sig;
224     BodyKind body;
225 
226     boolean badTestFilter() {
227         return (methKind != MethodKind.CONSTRUCTOR || modKind == ModifierKind.NONE);
228     }
229 
230     String template = """
231         import com.sun.tols.javac.api.*;
232         import java.util.List;
233         class Test {
234            static void test(Object o) {}
235            static void testArr(Object[] o) {}
236            #{TRUSTME} #{SUPPRESS} #{MOD} #{SIG} { #{BODY} }
237         }
238         """;
239 
240     @Override
241     public void doWork() throws IOException {
242         newCompilationTask()
243                 .withOption(xlint.getXlintOption())
244                 .withOption("--release")
245                 .withOption(sourceLevel.sourceKey)
246                 .withSourceFromTemplate(template)
247                 .analyze(this::check);
248     }
249 
250     void check(Result<?> res) {
251 
252         EnumSet<WarningKind> foundWarnings = EnumSet.noneOf(WarningKind.class);
253         for (Diagnostic.Kind kind : new Kind[] { Kind.ERROR, Kind.MANDATORY_WARNING, Kind.WARNING}) {
254             for (Diagnostic<? extends JavaFileObject> diag : res.diagnosticsForKind(kind)) {
255                 for (WarningKind wk : WarningKind.values()) {
256                     if (wk.code.equals(diag.getCode())) {
257                         foundWarnings.add(wk);
258                     }
259                 }
260             }
261         }
262 
263         EnumSet<WarningKind> expectedWarnings =
264                 EnumSet.noneOf(WarningKind.class);
265 
266         if (trustMe == TrustMe.TRUST &&
267             suppressLevel != SuppressLevel.VARARGS &&
268             xlint != XlintOption.NONE &&
269             sig.isVarargs &&
270             !sig.isReifiableArg &&
271             body.hasAliasing &&
272             (methKind == MethodKind.CONSTRUCTOR ||
273              (methKind == MethodKind.METHOD &&
274               modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC ||
275               (modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_8) > 0)))) {
276             expectedWarnings.add(WarningKind.UNSAFE_BODY);
277         }
278 
279         if (trustMe == TrustMe.DONT_TRUST &&
280             sig.isVarargs &&
281             !sig.isReifiableArg &&
282             xlint == XlintOption.ALL) {
283             expectedWarnings.add(WarningKind.UNSAFE_DECL);
284         }
285 
286         if (trustMe == TrustMe.TRUST &&
287             (!sig.isVarargs ||
288              ((modKind == ModifierKind.NONE ||
289                modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_8) <= 0 ) &&
290               methKind == MethodKind.METHOD))) {
291             expectedWarnings.add(WarningKind.MALFORMED_SAFEVARARGS);
292         }
293 
294         if (trustMe == TrustMe.TRUST &&
295             xlint != XlintOption.NONE &&
296             suppressLevel != SuppressLevel.VARARGS &&
297             (modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC ||
298              (modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_8) > 0) ||
299              methKind == MethodKind.CONSTRUCTOR) &&
300             sig.isVarargs &&
301             sig.isReifiableArg) {
302             expectedWarnings.add(WarningKind.REDUNDANT_SAFEVARARGS);
303         }
304 
305         if (!expectedWarnings.containsAll(foundWarnings) ||
306                 !foundWarnings.containsAll(expectedWarnings)) {
307             fail("invalid diagnostics for source:\n" +
308                     res.compilationInfo() +
309                     "\nOptions: " + xlint.getXlintOption() +
310                     "\nSource Level: " + sourceLevel +
311                     "\nExpected warnings: " + expectedWarnings +
312                     "\nFound warnings: " + foundWarnings);
313         }
314     }
315 }