Warn5.java revision 891:3e30c95da3c6
1/*
2 * Copyright (c) 2010, 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
27 * @summary Project Coin: Annotation to reduce varargs warnings
28 * @author  mcimadamore
29 * @run main Warn5
30 */
31import com.sun.source.util.JavacTask;
32import com.sun.tools.javac.api.JavacTool;
33import java.net.URI;
34import java.util.ArrayList;
35import java.util.Arrays;
36import javax.tools.Diagnostic;
37import javax.tools.JavaCompiler;
38import javax.tools.JavaFileObject;
39import javax.tools.SimpleJavaFileObject;
40import javax.tools.StandardJavaFileManager;
41import javax.tools.ToolProvider;
42
43public class Warn5 {
44
45    enum XlintOption {
46        NONE("none"),
47        ALL("all");
48
49        String opt;
50
51        XlintOption(String opt) {
52            this.opt = opt;
53        }
54
55        String getXlintOption() {
56            return "-Xlint:" + opt;
57        }
58    }
59
60    enum TrustMe {
61        DONT_TRUST(""),
62        TRUST("@java.lang.SafeVarargs");
63
64        String anno;
65
66        TrustMe(String anno) {
67            this.anno = anno;
68        }
69    }
70
71    enum SuppressLevel {
72        NONE,
73        VARARGS;
74
75        String getSuppressAnno() {
76            return this == VARARGS ?
77                "@SuppressWarnings(\"varargs\")" :
78                "";
79        }
80    }
81
82    enum ModifierKind {
83        NONE(""),
84        FINAL("final"),
85        STATIC("static");
86
87        String mod;
88
89        ModifierKind(String mod) {
90            this.mod = mod;
91        }
92    }
93
94    enum MethodKind {
95        METHOD("void m"),
96        CONSTRUCTOR("Test");
97
98
99        String name;
100
101        MethodKind(String name) {
102            this.name = name;
103        }
104    }
105
106    enum SourceLevel {
107        JDK_6("6"),
108        JDK_7("7");
109
110        String sourceKey;
111
112        SourceLevel(String sourceKey) {
113            this.sourceKey = sourceKey;
114        }
115    }
116
117    enum SignatureKind {
118        VARARGS_X("#K <X>#N(X... x)", false, true),
119        VARARGS_STRING("#K #N(String... x)", true, true),
120        ARRAY_X("#K <X>#N(X[] x)", false, false),
121        ARRAY_STRING("#K #N(String[] x)", true, false);
122
123        String stub;
124        boolean isReifiableArg;
125        boolean isVarargs;
126
127        SignatureKind(String stub, boolean isReifiableArg, boolean isVarargs) {
128            this.stub = stub;
129            this.isReifiableArg = isReifiableArg;
130            this.isVarargs = isVarargs;
131        }
132
133        String getSignature(ModifierKind modKind, MethodKind methKind) {
134            return methKind != MethodKind.CONSTRUCTOR ?
135                stub.replace("#K", modKind.mod).replace("#N", methKind.name) :
136                stub.replace("#K", "").replace("#N", methKind.name);
137        }
138    }
139
140    enum BodyKind {
141        ASSIGN("Object o = x;", true),
142        CAST("Object o = (Object)x;", true),
143        METH("test(x);", true),
144        PRINT("System.out.println(x.toString());", false),
145        ARRAY_ASSIGN("Object[] o = x;", true),
146        ARRAY_CAST("Object[] o = (Object[])x;", true),
147        ARRAY_METH("testArr(x);", true);
148
149        String body;
150        boolean hasAliasing;
151
152        BodyKind(String body, boolean hasAliasing) {
153            this.body = body;
154            this.hasAliasing = hasAliasing;
155        }
156    }
157
158    static class JavaSource extends SimpleJavaFileObject {
159
160        String template = "import com.sun.tools.javac.api.*;\n" +
161                          "import java.util.List;\n" +
162                          "class Test {\n" +
163                          "   static void test(Object o) {}\n" +
164                          "   static void testArr(Object[] o) {}\n" +
165                          "   #T \n #S #M { #B }\n" +
166                          "}\n";
167
168        String source;
169
170        public JavaSource(TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind,
171                MethodKind methKind, SignatureKind meth, BodyKind body) {
172            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
173            source = template.replace("#T", trustMe.anno).
174                    replace("#S", suppressLevel.getSuppressAnno()).
175                    replace("#M", meth.getSignature(modKind, methKind)).
176                    replace("#B", body.body);
177        }
178
179        @Override
180        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
181            return source;
182        }
183    }
184
185    public static void main(String... args) throws Exception {
186        for (SourceLevel sourceLevel : SourceLevel.values()) {
187            for (XlintOption xlint : XlintOption.values()) {
188                for (TrustMe trustMe : TrustMe.values()) {
189                    for (SuppressLevel suppressLevel : SuppressLevel.values()) {
190                        for (ModifierKind modKind : ModifierKind.values()) {
191                            for (MethodKind methKind : MethodKind.values()) {
192                                for (SignatureKind sig : SignatureKind.values()) {
193                                    for (BodyKind body : BodyKind.values()) {
194                                        test(sourceLevel,
195                                                xlint,
196                                                trustMe,
197                                                suppressLevel,
198                                                modKind,
199                                                methKind,
200                                                sig,
201                                                body);
202                                    }
203                                }
204                            }
205                        }
206                    }
207                }
208            }
209        }
210    }
211
212    // Create a single file manager and reuse it for each compile to save time.
213    static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
214
215    static void test(SourceLevel sourceLevel, XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel,
216            ModifierKind modKind, MethodKind methKind, SignatureKind sig, BodyKind body) throws Exception {
217        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
218        JavaSource source = new JavaSource(trustMe, suppressLevel, modKind, methKind, sig, body);
219        DiagnosticChecker dc = new DiagnosticChecker();
220        JavacTask ct = (JavacTask)tool.getTask(null, fm, dc,
221                Arrays.asList(xlint.getXlintOption(), "-source", sourceLevel.sourceKey), null, Arrays.asList(source));
222        ct.analyze();
223        check(sourceLevel, dc, source, xlint, trustMe,
224                suppressLevel, modKind, methKind, sig, body);
225    }
226
227    static void check(SourceLevel sourceLevel, DiagnosticChecker dc, JavaSource source,
228            XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind,
229            MethodKind methKind, SignatureKind meth, BodyKind body) {
230
231        boolean hasPotentiallyUnsafeBody = sourceLevel == SourceLevel.JDK_7 &&
232                trustMe == TrustMe.TRUST &&
233                suppressLevel != SuppressLevel.VARARGS &&
234                xlint != XlintOption.NONE &&
235                meth.isVarargs && !meth.isReifiableArg && body.hasAliasing &&
236                (methKind == MethodKind.CONSTRUCTOR || (methKind == MethodKind.METHOD && modKind != ModifierKind.NONE));
237
238        boolean hasPotentiallyPollutingDecl = sourceLevel == SourceLevel.JDK_7 &&
239                trustMe == TrustMe.DONT_TRUST &&
240                meth.isVarargs &&
241                !meth.isReifiableArg &&
242                xlint == XlintOption.ALL;
243
244        boolean hasMalformedAnnoInDecl = sourceLevel == SourceLevel.JDK_7 &&
245                trustMe == TrustMe.TRUST &&
246                (!meth.isVarargs ||
247                (modKind == ModifierKind.NONE && methKind == MethodKind.METHOD));
248
249        boolean hasRedundantAnnoInDecl = sourceLevel == SourceLevel.JDK_7 &&
250                trustMe == TrustMe.TRUST &&
251                xlint != XlintOption.NONE &&
252                suppressLevel != SuppressLevel.VARARGS &&
253                (modKind != ModifierKind.NONE || methKind == MethodKind.CONSTRUCTOR) &&
254                meth.isVarargs &&
255                meth.isReifiableArg;
256
257        if (hasPotentiallyUnsafeBody != dc.hasPotentiallyUnsafeBody ||
258                hasPotentiallyPollutingDecl != dc.hasPotentiallyPollutingDecl ||
259                hasMalformedAnnoInDecl != dc.hasMalformedAnnoInDecl ||
260                hasRedundantAnnoInDecl != dc.hasRedundantAnnoInDecl) {
261            throw new Error("invalid diagnostics for source:\n" +
262                    source.getCharContent(true) +
263                    "\nOptions: " + xlint.getXlintOption() +
264                    "\nExpected potentially unsafe body warning: " + hasPotentiallyUnsafeBody +
265                    "\nExpected potentially polluting decl warning: " + hasPotentiallyPollutingDecl +
266                    "\nExpected malformed anno error: " + hasMalformedAnnoInDecl +
267                    "\nExpected redundant anno warning: " + hasRedundantAnnoInDecl +
268                    "\nFound potentially unsafe body warning: " + dc.hasPotentiallyUnsafeBody +
269                    "\nFound potentially polluting decl warning: " + dc.hasPotentiallyPollutingDecl +
270                    "\nFound malformed anno error: " + dc.hasMalformedAnnoInDecl +
271                    "\nFound redundant anno warning: " + dc.hasRedundantAnnoInDecl);
272        }
273    }
274
275    static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
276
277        boolean hasPotentiallyUnsafeBody = false;
278        boolean hasPotentiallyPollutingDecl = false;
279        boolean hasMalformedAnnoInDecl = false;
280        boolean hasRedundantAnnoInDecl = false;
281
282        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
283            if (diagnostic.getKind() == Diagnostic.Kind.WARNING) {
284                    if (diagnostic.getCode().contains("unsafe.use.varargs.param")) {
285                        hasPotentiallyUnsafeBody = true;
286                    } else if (diagnostic.getCode().contains("redundant.trustme")) {
287                        hasRedundantAnnoInDecl = true;
288                    }
289            } else if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING &&
290                    diagnostic.getCode().contains("varargs.non.reifiable.type")) {
291                hasPotentiallyPollutingDecl = true;
292            } else if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
293                    diagnostic.getCode().contains("invalid.trustme")) {
294                hasMalformedAnnoInDecl = true;
295            }
296        }
297    }
298}
299