1 /* 2 * Copyright (c) 2011, 2015, 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 7023233 27 * @summary False positive for -Xlint:try with nested try with resources blocks 28 * @modules jdk.compiler/com.sun.tools.javac.api 29 * jdk.compiler/com.sun.tools.javac.file 30 * jdk.compiler/com.sun.tools.javac.util 31 */ 32 33 import com.sun.source.util.JavacTask; 34 import com.sun.tools.javac.api.ClientCodeWrapper; 35 import com.sun.tools.javac.api.JavacTool; 36 import com.sun.tools.javac.util.JCDiagnostic; 37 import java.net.URI; 38 import java.util.Arrays; 39 import javax.tools.Diagnostic; 40 import javax.tools.JavaCompiler; 41 import javax.tools.JavaFileObject; 42 import javax.tools.SimpleJavaFileObject; 43 import javax.tools.StandardJavaFileManager; 44 import javax.tools.ToolProvider; 45 46 public class UnusedResourcesTest { 47 48 enum XlintOption { 49 NONE("none"), 50 TRY("try"); 51 52 String opt; 53 XlintOption(String opt)54 XlintOption(String opt) { 55 this.opt = opt; 56 } 57 getXlintOption()58 String getXlintOption() { 59 return "-Xlint:" + opt; 60 } 61 } 62 63 enum TwrStmt { 64 TWR1("res1"), 65 TWR2("res2"), 66 TWR3("res3"); 67 68 final String resourceName; 69 TwrStmt(String resourceName)70 private TwrStmt(String resourceName) { 71 this.resourceName = resourceName; 72 } 73 } 74 75 enum SuppressLevel { 76 NONE, 77 SUPPRESS; 78 getSuppressAnno()79 String getSuppressAnno() { 80 return this == SUPPRESS ? 81 "@SuppressWarnings(\"try\")" : 82 ""; 83 } 84 } 85 86 enum ResourceUsage { 87 NONE(null), 88 USE_R1(TwrStmt.TWR1), 89 USE_R2(TwrStmt.TWR2), 90 USE_R3(TwrStmt.TWR3); 91 92 TwrStmt stmt; 93 ResourceUsage(TwrStmt stmt)94 private ResourceUsage(TwrStmt stmt) { 95 this.stmt = stmt; 96 } 97 usedResourceName()98 String usedResourceName() { 99 return stmt != null ? stmt.resourceName : null; 100 } 101 isUsedIn(TwrStmt res, TwrStmt stmt)102 boolean isUsedIn(TwrStmt res, TwrStmt stmt) { 103 return this.stmt == res && 104 stmt.ordinal() >= this.stmt.ordinal(); 105 } 106 getUsage(TwrStmt stmt)107 String getUsage(TwrStmt stmt) { 108 return this != NONE && stmt.ordinal() >= this.stmt.ordinal() ? 109 "use(" + usedResourceName() + ");" : 110 ""; 111 } 112 } 113 114 static class JavaSource extends SimpleJavaFileObject { 115 116 String template = "class Resource implements AutoCloseable {\n" + 117 "public void close() {}\n" + 118 "}\n" + 119 "class Test {\n" + 120 "void use(Resource r) {}\n" + 121 "#S void test() {\n" + 122 "try (Resource #R1 = new Resource()) {\n" + 123 "#U1_R1\n" + 124 "#U1_R2\n" + 125 "#U1_R3\n" + 126 "try (Resource #R2 = new Resource()) {\n" + 127 "#U2_R1\n" + 128 "#U2_R2\n" + 129 "#U2_R3\n" + 130 "try (Resource #R3 = new Resource()) {\n" + 131 "#U3_R1\n" + 132 "#U3_R2\n" + 133 "#U3_R3\n" + 134 "}\n" + 135 "}\n" + 136 "}\n" + 137 "}\n" + 138 "}\n"; 139 140 String source; 141 JavaSource(SuppressLevel suppressLevel, ResourceUsage usage1, ResourceUsage usage2, ResourceUsage usage3)142 public JavaSource(SuppressLevel suppressLevel, ResourceUsage usage1, 143 ResourceUsage usage2, ResourceUsage usage3) { 144 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 145 source = template.replace("#S", suppressLevel.getSuppressAnno()). 146 replace("#R1", TwrStmt.TWR1.resourceName). 147 replace("#R2", TwrStmt.TWR2.resourceName). 148 replace("#R3", TwrStmt.TWR3.resourceName). 149 replace("#U1_R1", usage1.getUsage(TwrStmt.TWR1)). 150 replace("#U1_R2", usage2.getUsage(TwrStmt.TWR1)). 151 replace("#U1_R3", usage3.getUsage(TwrStmt.TWR1)). 152 replace("#U2_R1", usage1.getUsage(TwrStmt.TWR2)). 153 replace("#U2_R2", usage2.getUsage(TwrStmt.TWR2)). 154 replace("#U2_R3", usage3.getUsage(TwrStmt.TWR2)). 155 replace("#U3_R1", usage1.getUsage(TwrStmt.TWR3)). 156 replace("#U3_R2", usage2.getUsage(TwrStmt.TWR3)). 157 replace("#U3_R3", usage3.getUsage(TwrStmt.TWR3)); 158 } 159 160 @Override getCharContent(boolean ignoreEncodingErrors)161 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 162 return source; 163 } 164 } 165 main(String... args)166 public static void main(String... args) throws Exception { 167 try { 168 for (XlintOption xlint : XlintOption.values()) { 169 for (SuppressLevel suppressLevel : SuppressLevel.values()) { 170 for (ResourceUsage usage1 : ResourceUsage.values()) { 171 for (ResourceUsage usage2 : ResourceUsage.values()) { 172 for (ResourceUsage usage3 : ResourceUsage.values()) { 173 test(xlint, 174 suppressLevel, 175 usage1, 176 usage2, 177 usage3); 178 } 179 } 180 } 181 } 182 } 183 } finally { 184 fm.close(); 185 } 186 } 187 188 // Create a single file manager and reuse it for each compile to save time. 189 static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null); 190 test(XlintOption xlint, SuppressLevel suppressLevel, ResourceUsage usage1, ResourceUsage usage2, ResourceUsage usage3)191 static void test(XlintOption xlint, SuppressLevel suppressLevel, ResourceUsage usage1, 192 ResourceUsage usage2, ResourceUsage usage3) throws Exception { 193 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 194 JavaSource source = new JavaSource(suppressLevel, usage1, usage2, usage3); 195 DiagnosticChecker dc = new DiagnosticChecker(); 196 JavacTask ct = (JavacTask)tool.getTask(null, fm, dc, 197 Arrays.asList(xlint.getXlintOption()), null, Arrays.asList(source)); 198 ct.analyze(); 199 check(source, xlint, suppressLevel, usage1, usage2, usage3, dc); 200 } 201 check(JavaSource source, XlintOption xlint, SuppressLevel suppressLevel, ResourceUsage usage1, ResourceUsage usage2, ResourceUsage usage3, DiagnosticChecker dc)202 static void check(JavaSource source, XlintOption xlint, SuppressLevel suppressLevel, 203 ResourceUsage usage1, ResourceUsage usage2, ResourceUsage usage3, DiagnosticChecker dc) { 204 205 ResourceUsage[] usages = { usage1, usage2, usage3 }; 206 boolean[] unusedFound = { dc.unused_r1, dc.unused_r2, dc.unused_r3 }; 207 boolean[] usedResources = { false, false, false }; 208 209 for (TwrStmt res : TwrStmt.values()) { 210 outer: for (TwrStmt stmt : TwrStmt.values()) { 211 for (ResourceUsage usage : usages) { 212 if (usage.isUsedIn(res, stmt)) { 213 usedResources[res.ordinal()] = true; 214 break outer; 215 } 216 } 217 } 218 } 219 220 for (TwrStmt stmt : TwrStmt.values()) { 221 boolean unused = !usedResources[stmt.ordinal()] && 222 xlint == XlintOption.TRY && 223 suppressLevel != SuppressLevel.SUPPRESS; 224 if (unused != unusedFound[stmt.ordinal()]) { 225 throw new Error("invalid diagnostics for source:\n" + 226 source.getCharContent(true) + 227 "\nOptions: " + xlint.getXlintOption() + 228 "\nFound unused res1: " + unusedFound[0] + 229 "\nFound unused res2: " + unusedFound[1] + 230 "\nFound unused res3: " + unusedFound[2] + 231 "\nExpected unused res1: " + !usedResources[0] + 232 "\nExpected unused res2: " + !usedResources[1] + 233 "\nExpected unused res3: " + !usedResources[2]); 234 } 235 } 236 } 237 238 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { 239 240 boolean unused_r1 = false; 241 boolean unused_r2 = false; 242 boolean unused_r3 = false; 243 report(Diagnostic<? extends JavaFileObject> diagnostic)244 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 245 if (diagnostic.getKind() == Diagnostic.Kind.WARNING && 246 diagnostic.getCode().contains("try.resource.not.referenced")) { 247 String varName = unwrap(diagnostic).getArgs()[0].toString(); 248 if (varName.equals(TwrStmt.TWR1.resourceName)) { 249 unused_r1 = true; 250 } else if (varName.equals(TwrStmt.TWR2.resourceName)) { 251 unused_r2 = true; 252 } else if (varName.equals(TwrStmt.TWR3.resourceName)) { 253 unused_r3 = true; 254 } 255 } 256 } 257 unwrap(Diagnostic<? extends JavaFileObject> diagnostic)258 private JCDiagnostic unwrap(Diagnostic<? extends JavaFileObject> diagnostic) { 259 if (diagnostic instanceof JCDiagnostic) 260 return (JCDiagnostic) diagnostic; 261 if (diagnostic instanceof ClientCodeWrapper.DiagnosticSourceUnwrapper) 262 return ((ClientCodeWrapper.DiagnosticSourceUnwrapper)diagnostic).d; 263 throw new IllegalArgumentException(); 264 } 265 } 266 } 267