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