|
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 */ |
|
31 import com.sun.source.util.JavacTask; |
|
32 import java.net.URI; |
|
33 import java.util.ArrayList; |
|
34 import java.util.Arrays; |
|
35 import javax.tools.Diagnostic; |
|
36 import javax.tools.JavaCompiler; |
|
37 import javax.tools.JavaFileObject; |
|
38 import javax.tools.SimpleJavaFileObject; |
|
39 import javax.tools.ToolProvider; |
|
40 |
|
41 public 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 } |