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