Thu, 25 Aug 2011 17:18:25 -0700
Added tag jdk8-b02 for changeset b3c059de2a61
1 /*
2 * Copyright (c) 2010, 2011, 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 */
24 /**
25 * @test
26 * @bug 6993978
27 * @summary Project Coin: Annotation to reduce varargs warnings
28 * @author mcimadamore
29 * @run main Warn5
30 */
31 import com.sun.source.util.JavacTask;
32 import com.sun.tools.javac.api.JavacTool;
33 import java.net.URI;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import javax.tools.Diagnostic;
37 import javax.tools.JavaCompiler;
38 import javax.tools.JavaFileObject;
39 import javax.tools.SimpleJavaFileObject;
40 import javax.tools.StandardJavaFileManager;
41 import javax.tools.ToolProvider;
43 public class Warn5 {
45 enum XlintOption {
46 NONE("none"),
47 ALL("all");
49 String opt;
51 XlintOption(String opt) {
52 this.opt = opt;
53 }
55 String getXlintOption() {
56 return "-Xlint:" + opt;
57 }
58 }
60 enum TrustMe {
61 DONT_TRUST(""),
62 TRUST("@java.lang.SafeVarargs");
64 String anno;
66 TrustMe(String anno) {
67 this.anno = anno;
68 }
69 }
71 enum SuppressLevel {
72 NONE,
73 VARARGS;
75 String getSuppressAnno() {
76 return this == VARARGS ?
77 "@SuppressWarnings(\"varargs\")" :
78 "";
79 }
80 }
82 enum ModifierKind {
83 NONE(""),
84 FINAL("final"),
85 STATIC("static");
87 String mod;
89 ModifierKind(String mod) {
90 this.mod = mod;
91 }
92 }
94 enum MethodKind {
95 METHOD("void m"),
96 CONSTRUCTOR("Test");
99 String name;
101 MethodKind(String name) {
102 this.name = name;
103 }
104 }
106 enum SourceLevel {
107 JDK_6("6"),
108 JDK_7("7");
110 String sourceKey;
112 SourceLevel(String sourceKey) {
113 this.sourceKey = sourceKey;
114 }
115 }
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);
123 String stub;
124 boolean isReifiableArg;
125 boolean isVarargs;
127 SignatureKind(String stub, boolean isReifiableArg, boolean isVarargs) {
128 this.stub = stub;
129 this.isReifiableArg = isReifiableArg;
130 this.isVarargs = isVarargs;
131 }
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 }
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);
149 String body;
150 boolean hasAliasing;
152 BodyKind(String body, boolean hasAliasing) {
153 this.body = body;
154 this.hasAliasing = hasAliasing;
155 }
156 }
158 static class JavaSource extends SimpleJavaFileObject {
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";
168 String source;
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 }
179 @Override
180 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
181 return source;
182 }
183 }
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 }
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);
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 }
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) {
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));
238 boolean hasPotentiallyPollutingDecl = sourceLevel == SourceLevel.JDK_7 &&
239 trustMe == TrustMe.DONT_TRUST &&
240 meth.isVarargs &&
241 !meth.isReifiableArg &&
242 xlint == XlintOption.ALL;
244 boolean hasMalformedAnnoInDecl = sourceLevel == SourceLevel.JDK_7 &&
245 trustMe == TrustMe.TRUST &&
246 (!meth.isVarargs ||
247 (modKind == ModifierKind.NONE && methKind == MethodKind.METHOD));
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;
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 }
275 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
277 boolean hasPotentiallyUnsafeBody = false;
278 boolean hasPotentiallyPollutingDecl = false;
279 boolean hasMalformedAnnoInDecl = false;
280 boolean hasRedundantAnnoInDecl = false;
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 }