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