Warn5.java revision 2518:854b6d0d408c
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     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 * @author  mcimadamore
30 * @library ../../lib
31 * @build JavacTestingAbstractThreadedTest
32 * @run main/othervm Warn5
33 */
34
35// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
36// see JDK-8006746
37
38import java.net.URI;
39import java.util.Arrays;
40import java.util.EnumSet;
41import javax.tools.Diagnostic;
42import javax.tools.JavaCompiler;
43import javax.tools.JavaFileObject;
44import javax.tools.SimpleJavaFileObject;
45import javax.tools.ToolProvider;
46import com.sun.source.util.JavacTask;
47
48public class Warn5
49    extends JavacTestingAbstractThreadedTest
50    implements Runnable {
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 {
68        DONT_TRUST(""),
69        TRUST("@java.lang.SafeVarargs");
70
71        String anno;
72
73        TrustMe(String anno) {
74            this.anno = anno;
75        }
76    }
77
78    enum SuppressLevel {
79        NONE,
80        VARARGS;
81
82        String getSuppressAnno() {
83            return this == VARARGS ?
84                "@SuppressWarnings(\"varargs\")" :
85                "";
86        }
87    }
88
89    enum ModifierKind {
90        NONE(""),
91        FINAL("final"),
92        STATIC("static"),
93        PRIVATE("private");
94
95        String mod;
96
97        ModifierKind(String mod) {
98            this.mod = mod;
99        }
100    }
101
102    enum MethodKind {
103        METHOD("void m"),
104        CONSTRUCTOR("Test");
105
106        String name;
107
108        MethodKind(String name) {
109            this.name = name;
110        }
111    }
112
113    enum SourceLevel {
114        JDK_6("6"),
115        JDK_7("7"),
116        JDK_9("9");
117
118        String sourceKey;
119
120        SourceLevel(String sourceKey) {
121            this.sourceKey = sourceKey;
122        }
123    }
124
125    enum SignatureKind {
126        VARARGS_X("#K <X>#N(X... x)", false, true),
127        VARARGS_STRING("#K #N(String... x)", true, true),
128        ARRAY_X("#K <X>#N(X[] x)", false, false),
129        ARRAY_STRING("#K #N(String[] x)", true, false);
130
131        String stub;
132        boolean isReifiableArg;
133        boolean isVarargs;
134
135        SignatureKind(String stub, boolean isReifiableArg, boolean isVarargs) {
136            this.stub = stub;
137            this.isReifiableArg = isReifiableArg;
138            this.isVarargs = isVarargs;
139        }
140
141        String getSignature(ModifierKind modKind, MethodKind methKind) {
142            return methKind != MethodKind.CONSTRUCTOR ?
143                stub.replace("#K", modKind.mod).replace("#N", methKind.name) :
144                stub.replace("#K", "").replace("#N", methKind.name);
145        }
146    }
147
148    enum BodyKind {
149        ASSIGN("Object o = x;", true),
150        CAST("Object o = (Object)x;", true),
151        METH("test(x);", true),
152        PRINT("System.out.println(x.toString());", false),
153        ARRAY_ASSIGN("Object[] o = x;", true),
154        ARRAY_CAST("Object[] o = (Object[])x;", true),
155        ARRAY_METH("testArr(x);", true);
156
157        String body;
158        boolean hasAliasing;
159
160        BodyKind(String body, boolean hasAliasing) {
161            this.body = body;
162            this.hasAliasing = hasAliasing;
163        }
164    }
165
166    enum WarningKind {
167        UNSAFE_BODY,
168        UNSAFE_DECL,
169        MALFORMED_SAFEVARARGS,
170        REDUNDANT_SAFEVARARGS;
171    }
172
173    public static void main(String... args) throws Exception {
174        for (SourceLevel sourceLevel : SourceLevel.values()) {
175            for (XlintOption xlint : XlintOption.values()) {
176                for (TrustMe trustMe : TrustMe.values()) {
177                    for (SuppressLevel suppressLevel : SuppressLevel.values()) {
178                        for (ModifierKind modKind : ModifierKind.values()) {
179                            for (MethodKind methKind : MethodKind.values()) {
180                                for (SignatureKind sig : SignatureKind.values()) {
181                                    for (BodyKind body : BodyKind.values()) {
182                                        pool.execute(new Warn5(sourceLevel,
183                                                xlint, trustMe, suppressLevel,
184                                                modKind, methKind, sig, body));
185                                    }
186                                }
187                            }
188                        }
189                    }
190                }
191            }
192        }
193
194        checkAfterExec(false);
195    }
196
197    final SourceLevel sourceLevel;
198    final XlintOption xlint;
199    final TrustMe trustMe;
200    final SuppressLevel suppressLevel;
201    final ModifierKind modKind;
202    final MethodKind methKind;
203    final SignatureKind sig;
204    final BodyKind body;
205    final JavaSource source;
206    final DiagnosticChecker dc;
207
208    public Warn5(SourceLevel sourceLevel, XlintOption xlint, TrustMe trustMe,
209            SuppressLevel suppressLevel, ModifierKind modKind,
210            MethodKind methKind, SignatureKind sig, BodyKind body) {
211        this.sourceLevel = sourceLevel;
212        this.xlint = xlint;
213        this.trustMe = trustMe;
214        this.suppressLevel = suppressLevel;
215        this.modKind = modKind;
216        this.methKind = methKind;
217        this.sig = sig;
218        this.body = body;
219        this.source = new JavaSource();
220        this.dc = new DiagnosticChecker();
221    }
222
223    @Override
224    public void run() {
225        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
226        JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), dc,
227                Arrays.asList(xlint.getXlintOption(),
228                    "-source", sourceLevel.sourceKey),
229                null, Arrays.asList(source));
230        try {
231            ct.analyze();
232        } catch (Throwable t) {
233            processException(t);
234        }
235        check();
236    }
237
238    void check() {
239
240        EnumSet<WarningKind> expectedWarnings =
241                EnumSet.noneOf(WarningKind.class);
242
243        if (sourceLevel.compareTo(SourceLevel.JDK_7) >= 0 &&
244                trustMe == TrustMe.TRUST &&
245                suppressLevel != SuppressLevel.VARARGS &&
246                xlint != XlintOption.NONE &&
247                sig.isVarargs &&
248                !sig.isReifiableArg &&
249                body.hasAliasing &&
250                (methKind == MethodKind.CONSTRUCTOR ||
251                (methKind == MethodKind.METHOD &&
252                 modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC ||
253                 (modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_9) >= 0)))) {
254            expectedWarnings.add(WarningKind.UNSAFE_BODY);
255        }
256
257        if (sourceLevel.compareTo(SourceLevel.JDK_7) >= 0 &&
258                trustMe == TrustMe.DONT_TRUST &&
259                sig.isVarargs &&
260                !sig.isReifiableArg &&
261                xlint == XlintOption.ALL) {
262            expectedWarnings.add(WarningKind.UNSAFE_DECL);
263        }
264
265        if (sourceLevel.compareTo(SourceLevel.JDK_7) >= 0 &&
266                trustMe == TrustMe.TRUST &&
267                (!sig.isVarargs ||
268                 ((modKind == ModifierKind.NONE ||
269                 modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_9) < 0 ) &&
270                 methKind == MethodKind.METHOD))) {
271            expectedWarnings.add(WarningKind.MALFORMED_SAFEVARARGS);
272        }
273
274        if (sourceLevel.compareTo(SourceLevel.JDK_7) >= 0 &&
275                trustMe == TrustMe.TRUST &&
276                xlint != XlintOption.NONE &&
277                suppressLevel != SuppressLevel.VARARGS &&
278                (modKind == ModifierKind.FINAL || modKind == ModifierKind.STATIC ||
279                 (modKind == ModifierKind.PRIVATE && sourceLevel.compareTo(SourceLevel.JDK_9) >= 0) ||
280                 methKind == MethodKind.CONSTRUCTOR) &&
281                sig.isVarargs &&
282                sig.isReifiableArg) {
283            expectedWarnings.add(WarningKind.REDUNDANT_SAFEVARARGS);
284        }
285
286        if (!expectedWarnings.containsAll(dc.warnings) ||
287                !dc.warnings.containsAll(expectedWarnings)) {
288            throw new Error("invalid diagnostics for source:\n" +
289                    source.getCharContent(true) +
290                    "\nOptions: " + xlint.getXlintOption() +
291                    "\nSource Level: " + sourceLevel +
292                    "\nExpected warnings: " + expectedWarnings +
293                    "\nFound warnings: " + dc.warnings);
294        }
295    }
296
297    class JavaSource extends SimpleJavaFileObject {
298
299        String template = "import com.sun.tools.javac.api.*;\n" +
300                          "import java.util.List;\n" +
301                          "class Test {\n" +
302                          "   static void test(Object o) {}\n" +
303                          "   static void testArr(Object[] o) {}\n" +
304                          "   #T \n #S #M { #B }\n" +
305                          "}\n";
306
307        String source;
308
309        public JavaSource() {
310            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
311            source = template.replace("#T", trustMe.anno).
312                    replace("#S", suppressLevel.getSuppressAnno()).
313                    replace("#M", sig.getSignature(modKind, methKind)).
314                    replace("#B", body.body);
315        }
316
317        @Override
318        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
319            return source;
320        }
321    }
322
323    class DiagnosticChecker
324        implements javax.tools.DiagnosticListener<JavaFileObject> {
325
326        EnumSet<WarningKind> warnings = EnumSet.noneOf(WarningKind.class);
327
328        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
329            if (diagnostic.getKind() == Diagnostic.Kind.WARNING) {
330                    if (diagnostic.getCode().
331                            contains("unsafe.use.varargs.param")) {
332                        setWarning(WarningKind.UNSAFE_BODY);
333                    } else if (diagnostic.getCode().
334                            contains("redundant.trustme")) {
335                        setWarning(WarningKind.REDUNDANT_SAFEVARARGS);
336                    }
337            } else if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING &&
338                    diagnostic.getCode().
339                        contains("varargs.non.reifiable.type")) {
340                setWarning(WarningKind.UNSAFE_DECL);
341            } else if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
342                    diagnostic.getCode().contains("invalid.trustme")) {
343                setWarning(WarningKind.MALFORMED_SAFEVARARGS);
344            }
345        }
346
347        void setWarning(WarningKind wk) {
348            if (!warnings.add(wk)) {
349                throw new AssertionError("Duplicate warning of kind " +
350                        wk + " in source:\n" + source);
351            }
352        }
353
354        boolean hasWarning(WarningKind wk) {
355            return warnings.contains(wk);
356        }
357    }
358
359}
360