1 /*
   2  * Copyright (c) 2017, 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 import combo.ComboInstance;
  25 import combo.ComboParameter;
  26 import combo.ComboTask.Result;
  27 import combo.ComboTestHelper;
  28 
  29 import javax.lang.model.element.Element;
  30 import java.util.stream.Stream;
  31 
  32 /*
  33  * @test
  34  * @bug 8176534
  35  * @summary Missing check against target-type during applicability inference
  36  * @library /tools/javac/lib
  37  * @modules jdk.compiler/com.sun.tools.javac.api
  38  *          jdk.compiler/com.sun.tools.javac.code
  39  *          jdk.compiler/com.sun.tools.javac.comp
  40  *          jdk.compiler/com.sun.tools.javac.main
  41  *          jdk.compiler/com.sun.tools.javac.tree
  42  *          jdk.compiler/com.sun.tools.javac.util
  43  * @build combo.ComboTestHelper
  44  *
  45  * @run main TestUncheckedCalls
  46  */
  47 public class TestUncheckedCalls extends ComboInstance<TestUncheckedCalls> {
  48     enum InputExpressionKind implements ComboParameter {
  49         A("(A)null"),
  50         A_STRING("(A<String>)null"),
  51         B("(B)null"),
  52         B_STRING("(B<String>)null");
  53 
  54         String inputExpr;
  55 
  56         InputExpressionKind(String inputExpr) {
  57             this.inputExpr = inputExpr;
  58         }
  59 
  60 
  61         @Override
  62         public String expand(String optParameter) {
  63             return inputExpr;
  64         }
  65     }
  66 
  67     enum TypeKind implements ComboParameter {
  68         Z("Z"),
  69         C_T("#C<T>"),
  70         C_STRING("#C<String>"),
  71         C("#C");
  72 
  73         String typeTemplate;
  74 
  75         TypeKind(String typeTemplate) {
  76             this.typeTemplate = typeTemplate;
  77         }
  78 
  79         boolean hasTypeVars() {
  80             return this == Z || this == C_T;
  81         }
  82 
  83         @Override
  84         public String expand(String className) {
  85             return typeTemplate.replaceAll("#C", className);
  86         }
  87     }
  88 
  89     enum TypeVarsKind implements ComboParameter {
  90         NONE("", "Object"),
  91         Z_T("<Z extends #C<T>, T>", "Z");
  92 
  93         String typeVarsTemplate;
  94         String paramString;
  95 
  96         TypeVarsKind(String typeVarsTemplate, String paramString) {
  97             this.typeVarsTemplate = typeVarsTemplate;
  98             this.paramString = paramString;
  99         }
 100 
 101 
 102         @Override
 103         public String expand(String className) {
 104             if (className.equals("Z")) {
 105                 return paramString;
 106             } else {
 107                 return typeVarsTemplate.replaceAll("#C", className);
 108             }
 109         }
 110     }
 111 
 112     enum CallKind implements ComboParameter {
 113         M("M(#{IN}, #{IN})"),
 114         M_G("M(G(#{IN}, #{IN}), #{IN})"),
 115         M_G_G("M(G(#{IN}, #{IN}), G(#{IN}, #{IN}))");
 116 
 117         String callExpr;
 118 
 119         CallKind(String callExpr) {
 120             this.callExpr = callExpr;
 121         }
 122 
 123 
 124         @Override
 125         public String expand(String optParameter) {
 126             return callExpr;
 127         }
 128     }
 129 
 130     enum DeclKind implements ComboParameter {
 131         NONE(""),
 132         ONE("#{TVARS[#M_IDX].I1} #{RET[#M_IDX].A} #M(#{ARG[#M_IDX].A} x1, #{TVARS[#M_IDX].Z} x2) { return null; }"),
 133         TWO("#{TVARS[#M_IDX].I1} #{RET[#M_IDX].A} #M(#{ARG[#M_IDX].A} x1, #{TVARS[#M_IDX].Z} x2) { return null; }\n" +
 134         "    #{TVARS[#M_IDX].I2} #{RET[#M_IDX].B} #M(#{ARG[#M_IDX].B} x1, #{TVARS[#M_IDX].Z} x2) { return null; }");
 135 
 136         String declTemplate;
 137 
 138         DeclKind(String declTemplate) {
 139             this.declTemplate = declTemplate;
 140         }
 141 
 142         @Override
 143         public String expand(String methName) {
 144             return declTemplate.replaceAll("#M_IDX", methName.equals("M") ? "0" : "1")
 145                     .replaceAll("#M", methName);
 146 
 147         }
 148     }
 149 
 150     static final String sourceTemplate =
 151             "class Test {\n" +
 152             "   interface I1<X> { }\n" +
 153             "   interface I2<X> { }\n" +
 154             "   static class A<X> implements I1<X> { }\n" +
 155             "   static class B<X> implements I2<X> { }\n" +
 156             "   #{DECL[0].M}\n" +
 157             "   #{DECL[1].G}\n" +
 158             "   void test() {\n" +
 159             "       #{CALL};\n" +
 160             "   }\n" +
 161             "}\n";
 162 
 163     public static void main(String... args) throws Exception {
 164         new ComboTestHelper<TestUncheckedCalls>()
 165                 .withFilter(TestUncheckedCalls::arityFilter)
 166                 .withFilter(TestUncheckedCalls::declFilter)
 167                 .withFilter(TestUncheckedCalls::tvarFilter)
 168                 .withFilter(TestUncheckedCalls::inputExprFilter)
 169                 .withDimension("IN", (x, expr) -> x.inputExpressionKind = expr, InputExpressionKind.values())
 170                 .withDimension("CALL", (x, expr) -> x.callKind = expr, CallKind.values())
 171                 .withArrayDimension("DECL", (x, decl, idx) -> x.decls[idx] = x.new Decl(decl, idx), 2, DeclKind.values())
 172                 .withArrayDimension("TVARS", (x, tvars, idx) -> x.typeVarsKinds[idx] = tvars, 2, TypeVarsKind.values())
 173                 .withArrayDimension("RET", (x, ret, idx) -> x.returnKinds[idx] = ret, 2, TypeKind.values())
 174                 .withArrayDimension("ARG", (x, arg, idx) -> x.argumentKinds[idx] = arg, 2, TypeKind.values())
 175                 .run(TestUncheckedCalls::new);
 176     }
 177 
 178     class Decl {
 179         private DeclKind declKind;
 180         private int index;
 181 
 182         Decl(DeclKind declKind, int index) {
 183             this.declKind = declKind;
 184             this.index = index;
 185         }
 186 
 187         boolean hasKind(DeclKind declKind) {
 188             return this.declKind == declKind;
 189         }
 190 
 191         boolean isGeneric() {
 192             return typeVarsKind() == TypeVarsKind.Z_T;
 193         }
 194 
 195         TypeKind returnKind() {
 196             return returnKinds[index];
 197         }
 198 
 199         TypeKind argumentsKind() {
 200             return argumentKinds[index];
 201         }
 202 
 203         TypeVarsKind typeVarsKind() {
 204             return typeVarsKinds[index];
 205         }
 206     }
 207 
 208     CallKind callKind;
 209     InputExpressionKind inputExpressionKind;
 210     TypeKind[] returnKinds = new TypeKind[2];
 211     TypeKind[] argumentKinds = new TypeKind[2];
 212     TypeVarsKind[] typeVarsKinds = new TypeVarsKind[2];
 213     Decl[] decls = new Decl[2];
 214 
 215     boolean arityFilter() {
 216         return (callKind == CallKind.M || !decls[1].hasKind(DeclKind.NONE)) &&
 217                 !decls[0].hasKind(DeclKind.NONE);
 218     }
 219 
 220     boolean declFilter() {
 221         return Stream.of(decls)
 222                 .filter(d -> d.hasKind(DeclKind.NONE))
 223                 .flatMap(d -> Stream.of(d.returnKind(), d.argumentsKind(), d.typeVarsKind()))
 224                 .noneMatch((Enum<? extends Enum<?>> tk) -> tk.ordinal() != 0);
 225     }
 226 
 227     boolean tvarFilter() {
 228         return Stream.of(decls)
 229                 .filter(d -> !d.hasKind(DeclKind.NONE))
 230                 .filter(d -> !d.isGeneric())
 231                 .flatMap(d -> Stream.of(d.returnKind(), d.argumentsKind()))
 232                 .noneMatch(TypeKind::hasTypeVars);
 233     }
 234 
 235     boolean inputExprFilter() {
 236         return (inputExpressionKind != InputExpressionKind.B && inputExpressionKind != InputExpressionKind.B_STRING) ||
 237                 Stream.of(decls).allMatch(d -> d.declKind == DeclKind.TWO);
 238     }
 239 
 240     @Override
 241     public void doWork() throws Throwable {
 242         newCompilationTask()
 243                 .withSourceFromTemplate(sourceTemplate)
 244                 .analyze(this::check);
 245     }
 246 
 247     void check(Result<Iterable<? extends Element>> result) {
 248         if (result.hasErrors()) {
 249             fail("compiler error:\n" +
 250                     result.compilationInfo());
 251         }
 252     }
 253 }