Mon, 13 Dec 2010 15:11:00 -0800
6993978: Project Coin: Compiler support of annotation to reduce varargs warnings
Reviewed-by: jjg, darcy
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 */
24 /**
25 * @test
26 * @bug 6945418 6993978
27 * @summary Project Coin: Simplified Varargs Method Invocation
28 * @author mcimadamore
29 * @run main Warn4
30 */
31 import com.sun.source.util.JavacTask;
32 import java.net.URI;
33 import java.util.Arrays;
34 import java.util.Set;
35 import java.util.HashSet;
36 import javax.tools.Diagnostic;
37 import javax.tools.JavaCompiler;
38 import javax.tools.JavaFileObject;
39 import javax.tools.SimpleJavaFileObject;
40 import javax.tools.ToolProvider;
42 public class Warn4 {
44 final static Warning[] error = null;
45 final static Warning[] none = new Warning[] {};
46 final static Warning[] vararg = new Warning[] { Warning.VARARGS };
47 final static Warning[] unchecked = new Warning[] { Warning.UNCHECKED };
48 final static Warning[] both = new Warning[] { Warning.VARARGS, Warning.UNCHECKED };
50 enum Warning {
51 UNCHECKED("generic.array.creation"),
52 VARARGS("varargs.non.reifiable.type");
54 String key;
56 Warning(String key) {
57 this.key = key;
58 }
60 boolean isSuppressed(TrustMe trustMe, SourceLevel source, SuppressLevel suppressLevelClient,
61 SuppressLevel suppressLevelDecl, ModifierKind modKind) {
62 switch(this) {
63 case VARARGS:
64 return source == SourceLevel.JDK_6 ||
65 suppressLevelDecl == SuppressLevel.UNCHECKED ||
66 trustMe == TrustMe.TRUST;
67 case UNCHECKED:
68 return suppressLevelClient == SuppressLevel.UNCHECKED ||
69 (trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE && source == SourceLevel.JDK_7);
70 }
72 SuppressLevel supLev = this == VARARGS ?
73 suppressLevelDecl :
74 suppressLevelClient;
75 return supLev == SuppressLevel.UNCHECKED ||
76 (trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE);
77 }
78 }
80 enum SourceLevel {
81 JDK_6("6"),
82 JDK_7("7");
84 String sourceKey;
86 SourceLevel(String sourceKey) {
87 this.sourceKey = sourceKey;
88 }
89 }
91 enum TrustMe {
92 DONT_TRUST(""),
93 TRUST("@java.lang.SafeVarargs");
95 String anno;
97 TrustMe(String anno) {
98 this.anno = anno;
99 }
100 }
102 enum ModifierKind {
103 NONE(" "),
104 FINAL("final "),
105 STATIC("static ");
107 String mod;
109 ModifierKind(String mod) {
110 this.mod = mod;
111 }
112 }
114 enum SuppressLevel {
115 NONE(""),
116 UNCHECKED("unchecked");
118 String lint;
120 SuppressLevel(String lint) {
121 this.lint = lint;
122 }
124 String getSuppressAnno() {
125 return "@SuppressWarnings(\"" + lint + "\")";
126 }
127 }
129 enum Signature {
130 UNBOUND("void #name(List<?>#arity arg) { #body }",
131 new Warning[][] {none, none, none, none, error}),
132 INVARIANT_TVAR("<Z> void #name(List<Z>#arity arg) { #body }",
133 new Warning[][] {both, both, error, both, error}),
134 TVAR("<Z> void #name(Z#arity arg) { #body }",
135 new Warning[][] {both, both, both, both, vararg}),
136 INVARIANT("void #name(List<String>#arity arg) { #body }",
137 new Warning[][] {error, error, error, both, error}),
138 UNPARAMETERIZED("void #name(String#arity arg) { #body }",
139 new Warning[][] {error, error, error, error, none});
141 String template;
142 Warning[][] warnings;
144 Signature(String template, Warning[][] warnings) {
145 this.template = template;
146 this.warnings = warnings;
147 }
149 boolean isApplicableTo(Signature other) {
150 return warnings[other.ordinal()] != null;
151 }
153 boolean giveUnchecked(Signature other) {
154 return warnings[other.ordinal()] == unchecked ||
155 warnings[other.ordinal()] == both;
156 }
158 boolean giveVarargs(Signature other) {
159 return warnings[other.ordinal()] == vararg ||
160 warnings[other.ordinal()] == both;
161 }
162 }
164 public static void main(String... args) throws Exception {
165 for (SourceLevel sourceLevel : SourceLevel.values()) {
166 for (TrustMe trustMe : TrustMe.values()) {
167 for (SuppressLevel suppressLevelClient : SuppressLevel.values()) {
168 for (SuppressLevel suppressLevelDecl : SuppressLevel.values()) {
169 for (ModifierKind modKind : ModifierKind.values()) {
170 for (Signature vararg_meth : Signature.values()) {
171 for (Signature client_meth : Signature.values()) {
172 if (vararg_meth.isApplicableTo(client_meth)) {
173 test(sourceLevel,
174 trustMe,
175 suppressLevelClient,
176 suppressLevelDecl,
177 modKind,
178 vararg_meth,
179 client_meth);
180 }
181 }
182 }
183 }
184 }
185 }
186 }
187 }
188 }
190 static void test(SourceLevel sourceLevel, TrustMe trustMe, SuppressLevel suppressLevelClient,
191 SuppressLevel suppressLevelDecl, ModifierKind modKind, Signature vararg_meth, Signature client_meth) throws Exception {
192 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
193 JavaSource source = new JavaSource(trustMe, suppressLevelClient, suppressLevelDecl, modKind, vararg_meth, client_meth);
194 DiagnosticChecker dc = new DiagnosticChecker();
195 JavacTask ct = (JavacTask)tool.getTask(null, null, dc,
196 Arrays.asList("-Xlint:unchecked", "-source", sourceLevel.sourceKey),
197 null, Arrays.asList(source));
198 ct.generate(); //to get mandatory notes
199 check(dc.warnings, sourceLevel,
200 new boolean[] {vararg_meth.giveUnchecked(client_meth),
201 vararg_meth.giveVarargs(client_meth)},
202 source, trustMe, suppressLevelClient, suppressLevelDecl, modKind);
203 }
205 static void check(Set<Warning> warnings, SourceLevel sourceLevel, boolean[] warnArr, JavaSource source,
206 TrustMe trustMe, SuppressLevel suppressLevelClient, SuppressLevel suppressLevelDecl, ModifierKind modKind) {
207 boolean badOutput = false;
208 for (Warning wkind : Warning.values()) {
209 boolean isSuppressed = wkind.isSuppressed(trustMe, sourceLevel,
210 suppressLevelClient, suppressLevelDecl, modKind);
211 System.out.println("SUPPRESSED = " + isSuppressed);
212 badOutput |= (warnArr[wkind.ordinal()] && !isSuppressed) != warnings.contains(wkind);
213 }
214 if (badOutput) {
215 throw new Error("invalid diagnostics for source:\n" +
216 source.getCharContent(true) +
217 "\nExpected unchecked warning: " + warnArr[0] +
218 "\nExpected unsafe vararg warning: " + warnArr[1] +
219 "\nWarnings: " + warnings +
220 "\nSource level: " + sourceLevel);
221 }
222 }
224 static class JavaSource extends SimpleJavaFileObject {
226 String source;
228 public JavaSource(TrustMe trustMe, SuppressLevel suppressLevelClient, SuppressLevel suppressLevelDecl,
229 ModifierKind modKind, Signature vararg_meth, Signature client_meth) {
230 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
231 String meth1 = vararg_meth.template.replace("#arity", "...");
232 meth1 = meth1.replace("#name", "m");
233 meth1 = meth1.replace("#body", "");
234 meth1 = trustMe.anno + "\n" + suppressLevelDecl.getSuppressAnno() + modKind.mod + meth1;
235 String meth2 = client_meth.template.replace("#arity", "");
236 meth2 = meth2.replace("#name", "test");
237 meth2 = meth2.replace("#body", "m(arg);");
238 meth2 = suppressLevelClient.getSuppressAnno() + meth2;
239 source = "import java.util.List;\n" +
240 "class Test {\n" + meth1 +
241 "\n" + meth2 + "\n}\n";
242 }
244 @Override
245 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
246 return source;
247 }
248 }
250 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
252 Set<Warning> warnings = new HashSet<>();
254 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
255 if (diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING ||
256 diagnostic.getKind() == Diagnostic.Kind.WARNING) {
257 if (diagnostic.getCode().contains(Warning.VARARGS.key)) {
258 warnings.add(Warning.VARARGS);
259 } else if(diagnostic.getCode().contains(Warning.UNCHECKED.key)) {
260 warnings.add(Warning.UNCHECKED);
261 }
262 }
263 }
264 }
265 }