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