1 /*
2  * Copyright (c) 2011, 2015, 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 7086601
27  * @summary Error message bug: cause for method mismatch is 'null'
28  * @modules jdk.compiler
29  */
30 
31 import com.sun.source.util.JavacTask;
32 import java.net.URI;
33 import java.util.Arrays;
34 import java.util.ArrayList;
35 import javax.tools.Diagnostic;
36 import javax.tools.JavaCompiler;
37 import javax.tools.JavaFileObject;
38 import javax.tools.SimpleJavaFileObject;
39 import javax.tools.StandardJavaFileManager;
40 import javax.tools.ToolProvider;
41 
42 
43 public class T7086601b {
44 
45     static int checkCount = 0;
46 
47     enum TypeKind {
48         STRING("String", false),
49         INTEGER("Integer", false),
50         NUMBER("Number", false),
51         SERIALIZABLE("java.io.Serializable", true),
52         CLONEABLE("Cloneable", true),
53         X("X", false),
54         Y("Y", false),
55         Z("Z", false);
56 
57         String typeStr;
58         boolean isInterface;
59 
TypeKind(String typeStr, boolean isInterface)60         private TypeKind(String typeStr, boolean isInterface) {
61             this.typeStr = typeStr;
62             this.isInterface = isInterface;
63         }
64 
isSubtypeof(TypeKind other)65         boolean isSubtypeof(TypeKind other) {
66             return (this == INTEGER && other == NUMBER ||
67                     this == Z && other == Y ||
68                     this == other);
69         }
70     }
71 
72     enum MethodCallKind {
73         ARITY_ONE("m(a1);", 1),
74         ARITY_TWO("m(a1, a2);", 2),
75         ARITY_THREE("m(a1, a2, a3);", 3);
76 
77         String invokeString;
78         int arity;
79 
MethodCallKind(String invokeString, int arity)80         private MethodCallKind(String invokeString, int arity) {
81             this.invokeString = invokeString;
82             this.arity = arity;
83         }
84     }
85 
main(String... args)86     public static void main(String... args) throws Exception {
87 
88         //create default shared JavaCompiler - reused across multiple compilations
89         JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
90         try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) {
91 
92             for (TypeKind a1 : TypeKind.values()) {
93                 for (TypeKind a2 : TypeKind.values()) {
94                     for (TypeKind a3 : TypeKind.values()) {
95                         for (MethodCallKind mck : MethodCallKind.values()) {
96                             new T7086601b(a1, a2, a3, mck).run(comp, fm);
97                         }
98                     }
99                 }
100             }
101             System.out.println("Total check executed: " + checkCount);
102         }
103     }
104 
105     TypeKind a1;
106     TypeKind a2;
107     TypeKind a3;
108     MethodCallKind mck;
109     JavaSource source;
110     DiagnosticChecker diagChecker;
111 
T7086601b(TypeKind a1, TypeKind a2, TypeKind a3, MethodCallKind mck)112     T7086601b(TypeKind a1, TypeKind a2, TypeKind a3, MethodCallKind mck) {
113         this.a1 = a1;
114         this.a2 = a2;
115         this.a3 = a3;
116         this.mck = mck;
117         this.source = new JavaSource();
118         this.diagChecker = new DiagnosticChecker();
119     }
120 
121     class JavaSource extends SimpleJavaFileObject {
122 
123         final String bodyTemplate = "import java.util.List;\n"+
124                               "class Test {\n" +
125                               "   <Z> void m(List<? super Z> l1) { }\n" +
126                               "   <Z> void m(List<? super Z> l1, List<? super Z> l2) { }\n" +
127                               "   <Z> void m(List<? super Z> l1, List<? super Z> l2, List<? super Z> l3) { }\n" +
128                               "   <X,Y,Z extends Y> void test(List<#A1> a1, List<#A2> a2, List<#A3> a3) { #MC } }";
129 
130         String source;
131 
JavaSource()132         public JavaSource() {
133             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
134             source = bodyTemplate.replace("#A1", a1.typeStr)
135                              .replace("#A2", a2.typeStr).replace("#A3", a3.typeStr)
136                              .replace("#MC", mck.invokeString);
137         }
138 
139         @Override
getCharContent(boolean ignoreEncodingErrors)140         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
141             return source;
142         }
143     }
144 
run(JavaCompiler tool, StandardJavaFileManager fm)145     void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
146         JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
147                 null, null, Arrays.asList(source));
148         try {
149             ct.analyze();
150         } catch (Throwable ex) {
151             throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true));
152         }
153         check();
154     }
155 
check()156     void check() {
157         checkCount++;
158 
159         boolean errorExpected = false;
160 
161         if (mck.arity > 1) {
162             TypeKind[] argtypes = { a1, a2, a3 };
163             ArrayList<TypeKind> classes = new ArrayList<>();
164             for (int i = 0 ; i < mck.arity ; i ++ ) {
165                 if (!argtypes[i].isInterface) {
166                     classes.add(argtypes[i]);
167                 }
168             }
169             boolean glb_exists = true;
170             for (TypeKind arg_i : classes) {
171                 glb_exists = true;
172                 for (TypeKind arg_j : classes) {
173                     if (!arg_i.isSubtypeof(arg_j)) {
174                         glb_exists = false;
175                         break;
176                     }
177                 }
178                 if (glb_exists) break;
179             }
180             errorExpected = !glb_exists;
181         }
182 
183         if (errorExpected != diagChecker.errorFound) {
184             throw new Error("invalid diagnostics for source:\n" +
185                 source.getCharContent(true) +
186                 "\nFound error: " + diagChecker.errorFound +
187                 "\nExpected error: " + errorExpected);
188         }
189     }
190 
191     static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
192 
193         boolean errorFound;
194 
report(Diagnostic<? extends JavaFileObject> diagnostic)195         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
196             if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
197                 errorFound = true;
198             }
199         }
200     }
201 }
202