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