153 this.body = body; |
152 this.body = body; |
154 this.hasAliasing = hasAliasing; |
153 this.hasAliasing = hasAliasing; |
155 } |
154 } |
156 } |
155 } |
157 |
156 |
158 static class JavaSource extends SimpleJavaFileObject { |
157 enum WarningKind { |
159 |
158 UNSAFE_BODY, |
160 String template = "import com.sun.tools.javac.api.*;\n" + |
159 UNSAFE_DECL, |
161 "import java.util.List;\n" + |
160 MALFORMED_SAFEVARARGS, |
162 "class Test {\n" + |
161 REDUNDANT_SAFEVARARGS; |
163 " static void test(Object o) {}\n" + |
162 } |
164 " static void testArr(Object[] o) {}\n" + |
163 |
165 " #T \n #S #M { #B }\n" + |
164 // Create a single file manager and reuse it for each compile to save time. |
166 "}\n"; |
165 static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null); |
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 |
166 |
185 public static void main(String... args) throws Exception { |
167 public static void main(String... args) throws Exception { |
186 for (SourceLevel sourceLevel : SourceLevel.values()) { |
168 for (SourceLevel sourceLevel : SourceLevel.values()) { |
187 for (XlintOption xlint : XlintOption.values()) { |
169 for (XlintOption xlint : XlintOption.values()) { |
188 for (TrustMe trustMe : TrustMe.values()) { |
170 for (TrustMe trustMe : TrustMe.values()) { |
189 for (SuppressLevel suppressLevel : SuppressLevel.values()) { |
171 for (SuppressLevel suppressLevel : SuppressLevel.values()) { |
190 for (ModifierKind modKind : ModifierKind.values()) { |
172 for (ModifierKind modKind : ModifierKind.values()) { |
191 for (MethodKind methKind : MethodKind.values()) { |
173 for (MethodKind methKind : MethodKind.values()) { |
192 for (SignatureKind sig : SignatureKind.values()) { |
174 for (SignatureKind sig : SignatureKind.values()) { |
193 for (BodyKind body : BodyKind.values()) { |
175 for (BodyKind body : BodyKind.values()) { |
194 test(sourceLevel, |
176 new Warn5(sourceLevel, |
195 xlint, |
177 xlint, |
196 trustMe, |
178 trustMe, |
197 suppressLevel, |
179 suppressLevel, |
198 modKind, |
180 modKind, |
199 methKind, |
181 methKind, |
200 sig, |
182 sig, |
201 body); |
183 body).test(); |
202 } |
184 } |
203 } |
185 } |
204 } |
186 } |
205 } |
187 } |
206 } |
188 } |
207 } |
189 } |
208 } |
190 } |
209 } |
191 } |
210 } |
192 } |
211 |
193 |
212 // Create a single file manager and reuse it for each compile to save time. |
194 final SourceLevel sourceLevel; |
213 static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null); |
195 final XlintOption xlint; |
214 |
196 final TrustMe trustMe; |
215 static void test(SourceLevel sourceLevel, XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel, |
197 final SuppressLevel suppressLevel; |
216 ModifierKind modKind, MethodKind methKind, SignatureKind sig, BodyKind body) throws Exception { |
198 final ModifierKind modKind; |
|
199 final MethodKind methKind; |
|
200 final SignatureKind sig; |
|
201 final BodyKind body; |
|
202 final JavaSource source; |
|
203 final DiagnosticChecker dc; |
|
204 |
|
205 public Warn5(SourceLevel sourceLevel, XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind, MethodKind methKind, SignatureKind sig, BodyKind body) { |
|
206 this.sourceLevel = sourceLevel; |
|
207 this.xlint = xlint; |
|
208 this.trustMe = trustMe; |
|
209 this.suppressLevel = suppressLevel; |
|
210 this.modKind = modKind; |
|
211 this.methKind = methKind; |
|
212 this.sig = sig; |
|
213 this.body = body; |
|
214 this.source = new JavaSource(); |
|
215 this.dc = new DiagnosticChecker(); |
|
216 } |
|
217 |
|
218 void test() throws Exception { |
217 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); |
219 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, |
220 JavacTask ct = (JavacTask)tool.getTask(null, fm, dc, |
221 Arrays.asList(xlint.getXlintOption(), "-source", sourceLevel.sourceKey), null, Arrays.asList(source)); |
221 Arrays.asList(xlint.getXlintOption(), "-source", sourceLevel.sourceKey), null, Arrays.asList(source)); |
222 ct.analyze(); |
222 ct.analyze(); |
223 check(sourceLevel, dc, source, xlint, trustMe, |
223 check(); |
224 suppressLevel, modKind, methKind, sig, body); |
224 } |
225 } |
225 |
226 |
226 void check() { |
227 static void check(SourceLevel sourceLevel, DiagnosticChecker dc, JavaSource source, |
227 |
228 XlintOption xlint, TrustMe trustMe, SuppressLevel suppressLevel, ModifierKind modKind, |
228 EnumSet<WarningKind> expectedWarnings = EnumSet.noneOf(WarningKind.class); |
229 MethodKind methKind, SignatureKind meth, BodyKind body) { |
229 |
230 |
230 if (sourceLevel == SourceLevel.JDK_7 && |
231 boolean hasPotentiallyUnsafeBody = sourceLevel == SourceLevel.JDK_7 && |
|
232 trustMe == TrustMe.TRUST && |
231 trustMe == TrustMe.TRUST && |
233 suppressLevel != SuppressLevel.VARARGS && |
232 suppressLevel != SuppressLevel.VARARGS && |
234 xlint != XlintOption.NONE && |
233 xlint != XlintOption.NONE && |
235 meth.isVarargs && !meth.isReifiableArg && body.hasAliasing && |
234 sig.isVarargs && !sig.isReifiableArg && body.hasAliasing && |
236 (methKind == MethodKind.CONSTRUCTOR || (methKind == MethodKind.METHOD && modKind != ModifierKind.NONE)); |
235 (methKind == MethodKind.CONSTRUCTOR || (methKind == MethodKind.METHOD && modKind != ModifierKind.NONE))) { |
237 |
236 expectedWarnings.add(WarningKind.UNSAFE_BODY); |
238 boolean hasPotentiallyPollutingDecl = sourceLevel == SourceLevel.JDK_7 && |
237 } |
|
238 |
|
239 if (sourceLevel == SourceLevel.JDK_7 && |
239 trustMe == TrustMe.DONT_TRUST && |
240 trustMe == TrustMe.DONT_TRUST && |
240 meth.isVarargs && |
241 sig.isVarargs && |
241 !meth.isReifiableArg && |
242 !sig.isReifiableArg && |
242 xlint == XlintOption.ALL; |
243 xlint == XlintOption.ALL) { |
243 |
244 expectedWarnings.add(WarningKind.UNSAFE_DECL); |
244 boolean hasMalformedAnnoInDecl = sourceLevel == SourceLevel.JDK_7 && |
245 } |
|
246 |
|
247 if (sourceLevel == SourceLevel.JDK_7 && |
245 trustMe == TrustMe.TRUST && |
248 trustMe == TrustMe.TRUST && |
246 (!meth.isVarargs || |
249 (!sig.isVarargs || |
247 (modKind == ModifierKind.NONE && methKind == MethodKind.METHOD)); |
250 (modKind == ModifierKind.NONE && methKind == MethodKind.METHOD))) { |
248 |
251 expectedWarnings.add(WarningKind.MALFORMED_SAFEVARARGS); |
249 boolean hasRedundantAnnoInDecl = sourceLevel == SourceLevel.JDK_7 && |
252 } |
|
253 |
|
254 if (sourceLevel == SourceLevel.JDK_7 && |
250 trustMe == TrustMe.TRUST && |
255 trustMe == TrustMe.TRUST && |
251 xlint != XlintOption.NONE && |
256 xlint != XlintOption.NONE && |
252 suppressLevel != SuppressLevel.VARARGS && |
257 suppressLevel != SuppressLevel.VARARGS && |
253 (modKind != ModifierKind.NONE || methKind == MethodKind.CONSTRUCTOR) && |
258 (modKind != ModifierKind.NONE || methKind == MethodKind.CONSTRUCTOR) && |
254 meth.isVarargs && |
259 sig.isVarargs && |
255 meth.isReifiableArg; |
260 sig.isReifiableArg) { |
256 |
261 expectedWarnings.add(WarningKind.REDUNDANT_SAFEVARARGS); |
257 if (hasPotentiallyUnsafeBody != dc.hasPotentiallyUnsafeBody || |
262 } |
258 hasPotentiallyPollutingDecl != dc.hasPotentiallyPollutingDecl || |
263 |
259 hasMalformedAnnoInDecl != dc.hasMalformedAnnoInDecl || |
264 if (!expectedWarnings.containsAll(dc.warnings) || |
260 hasRedundantAnnoInDecl != dc.hasRedundantAnnoInDecl) { |
265 !dc.warnings.containsAll(expectedWarnings)) { |
261 throw new Error("invalid diagnostics for source:\n" + |
266 throw new Error("invalid diagnostics for source:\n" + |
262 source.getCharContent(true) + |
267 source.getCharContent(true) + |
263 "\nOptions: " + xlint.getXlintOption() + |
268 "\nOptions: " + xlint.getXlintOption() + |
264 "\nExpected potentially unsafe body warning: " + hasPotentiallyUnsafeBody + |
269 "\nExpected warnings: " + expectedWarnings + |
265 "\nExpected potentially polluting decl warning: " + hasPotentiallyPollutingDecl + |
270 "\nFound warnings: " + dc.warnings); |
266 "\nExpected malformed anno error: " + hasMalformedAnnoInDecl + |
271 } |
267 "\nExpected redundant anno warning: " + hasRedundantAnnoInDecl + |
272 } |
268 "\nFound potentially unsafe body warning: " + dc.hasPotentiallyUnsafeBody + |
273 |
269 "\nFound potentially polluting decl warning: " + dc.hasPotentiallyPollutingDecl + |
274 class JavaSource extends SimpleJavaFileObject { |
270 "\nFound malformed anno error: " + dc.hasMalformedAnnoInDecl + |
275 |
271 "\nFound redundant anno warning: " + dc.hasRedundantAnnoInDecl); |
276 String template = "import com.sun.tools.javac.api.*;\n" + |
272 } |
277 "import java.util.List;\n" + |
273 } |
278 "class Test {\n" + |
274 |
279 " static void test(Object o) {}\n" + |
275 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { |
280 " static void testArr(Object[] o) {}\n" + |
276 |
281 " #T \n #S #M { #B }\n" + |
277 boolean hasPotentiallyUnsafeBody = false; |
282 "}\n"; |
278 boolean hasPotentiallyPollutingDecl = false; |
283 |
279 boolean hasMalformedAnnoInDecl = false; |
284 String source; |
280 boolean hasRedundantAnnoInDecl = false; |
285 |
|
286 public JavaSource() { |
|
287 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); |
|
288 source = template.replace("#T", trustMe.anno). |
|
289 replace("#S", suppressLevel.getSuppressAnno()). |
|
290 replace("#M", sig.getSignature(modKind, methKind)). |
|
291 replace("#B", body.body); |
|
292 } |
|
293 |
|
294 @Override |
|
295 public CharSequence getCharContent(boolean ignoreEncodingErrors) { |
|
296 return source; |
|
297 } |
|
298 } |
|
299 |
|
300 class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { |
|
301 |
|
302 EnumSet<WarningKind> warnings = EnumSet.noneOf(WarningKind.class); |
281 |
303 |
282 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { |
304 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { |
283 if (diagnostic.getKind() == Diagnostic.Kind.WARNING) { |
305 if (diagnostic.getKind() == Diagnostic.Kind.WARNING) { |
284 if (diagnostic.getCode().contains("unsafe.use.varargs.param")) { |
306 if (diagnostic.getCode().contains("unsafe.use.varargs.param")) { |
285 hasPotentiallyUnsafeBody = true; |
307 setWarning(WarningKind.UNSAFE_BODY); |
286 } else if (diagnostic.getCode().contains("redundant.trustme")) { |
308 } else if (diagnostic.getCode().contains("redundant.trustme")) { |
287 hasRedundantAnnoInDecl = true; |
309 setWarning(WarningKind.REDUNDANT_SAFEVARARGS); |
288 } |
310 } |
289 } else if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING && |
311 } else if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING && |
290 diagnostic.getCode().contains("varargs.non.reifiable.type")) { |
312 diagnostic.getCode().contains("varargs.non.reifiable.type")) { |
291 hasPotentiallyPollutingDecl = true; |
313 setWarning(WarningKind.UNSAFE_DECL); |
292 } else if (diagnostic.getKind() == Diagnostic.Kind.ERROR && |
314 } else if (diagnostic.getKind() == Diagnostic.Kind.ERROR && |
293 diagnostic.getCode().contains("invalid.trustme")) { |
315 diagnostic.getCode().contains("invalid.trustme")) { |
294 hasMalformedAnnoInDecl = true; |
316 setWarning(WarningKind.MALFORMED_SAFEVARARGS); |
295 } |
317 } |
296 } |
318 } |
|
319 |
|
320 void setWarning(WarningKind wk) { |
|
321 if (!warnings.add(wk)) { |
|
322 throw new AssertionError("Duplicate warning of kind " + wk + " in source:\n" + source); |
|
323 } |
|
324 } |
|
325 |
|
326 boolean hasWarning(WarningKind wk) { |
|
327 return warnings.contains(wk); |
|
328 } |
297 } |
329 } |
298 } |
330 } |