Warn4.java revision 794:7b99f98b3035
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 6945418 6993978 27 * @summary Project Coin: Simplified Varargs Method Invocation 28 * @author mcimadamore 29 * @run main Warn4 30 */ 31import com.sun.source.util.JavacTask; 32import java.net.URI; 33import java.util.Arrays; 34import java.util.Set; 35import java.util.HashSet; 36import javax.tools.Diagnostic; 37import javax.tools.JavaCompiler; 38import javax.tools.JavaFileObject; 39import javax.tools.SimpleJavaFileObject; 40import javax.tools.ToolProvider; 41 42public class Warn4 { 43 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 }; 49 50 enum Warning { 51 UNCHECKED("generic.array.creation"), 52 VARARGS("varargs.non.reifiable.type"); 53 54 String key; 55 56 Warning(String key) { 57 this.key = key; 58 } 59 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 } 71 72 SuppressLevel supLev = this == VARARGS ? 73 suppressLevelDecl : 74 suppressLevelClient; 75 return supLev == SuppressLevel.UNCHECKED || 76 (trustMe == TrustMe.TRUST && modKind != ModifierKind.NONE); 77 } 78 } 79 80 enum SourceLevel { 81 JDK_6("6"), 82 JDK_7("7"); 83 84 String sourceKey; 85 86 SourceLevel(String sourceKey) { 87 this.sourceKey = sourceKey; 88 } 89 } 90 91 enum TrustMe { 92 DONT_TRUST(""), 93 TRUST("@java.lang.SafeVarargs"); 94 95 String anno; 96 97 TrustMe(String anno) { 98 this.anno = anno; 99 } 100 } 101 102 enum ModifierKind { 103 NONE(" "), 104 FINAL("final "), 105 STATIC("static "); 106 107 String mod; 108 109 ModifierKind(String mod) { 110 this.mod = mod; 111 } 112 } 113 114 enum SuppressLevel { 115 NONE(""), 116 UNCHECKED("unchecked"); 117 118 String lint; 119 120 SuppressLevel(String lint) { 121 this.lint = lint; 122 } 123 124 String getSuppressAnno() { 125 return "@SuppressWarnings(\"" + lint + "\")"; 126 } 127 } 128 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}); 140 141 String template; 142 Warning[][] warnings; 143 144 Signature(String template, Warning[][] warnings) { 145 this.template = template; 146 this.warnings = warnings; 147 } 148 149 boolean isApplicableTo(Signature other) { 150 return warnings[other.ordinal()] != null; 151 } 152 153 boolean giveUnchecked(Signature other) { 154 return warnings[other.ordinal()] == unchecked || 155 warnings[other.ordinal()] == both; 156 } 157 158 boolean giveVarargs(Signature other) { 159 return warnings[other.ordinal()] == vararg || 160 warnings[other.ordinal()] == both; 161 } 162 } 163 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 } 189 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 } 204 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 } 223 224 static class JavaSource extends SimpleJavaFileObject { 225 226 String source; 227 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 } 243 244 @Override 245 public CharSequence getCharContent(boolean ignoreEncodingErrors) { 246 return source; 247 } 248 } 249 250 static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> { 251 252 Set<Warning> warnings = new HashSet<>(); 253 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} 266