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 7101822
27  * @summary static import fails to resolve interfaces on nested enums via import statements
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 javax.tools.Diagnostic;
35 import javax.tools.JavaCompiler;
36 import javax.tools.JavaFileObject;
37 import javax.tools.SimpleJavaFileObject;
38 import javax.tools.StandardJavaFileManager;
39 import javax.tools.ToolProvider;
40 
41 
42 public class TestDuplicateImport {
43 
44     static int checkCount = 0;
45 
46     enum ImportKind {
47         NORMAL("import a.#Q.#N;"),
48         STATIC("import static a.#Q.#N;");
49 
50         String importStr;
51 
ImportKind(String importStr)52         ImportKind(String importStr) {
53             this.importStr = importStr;
54         }
55 
getImportStatement(QualifierKind qk, NameKind nk)56         String getImportStatement(QualifierKind qk, NameKind nk) {
57             return importStr.replaceAll("#Q", qk.qualifierStr)
58                     .replaceAll("#N", nk.nameStr);
59         }
60 
isStatic()61         boolean isStatic() {
62             return this == STATIC;
63         }
64     }
65 
66     enum QualifierKind {
67         A("A"),
68         B("B"),
69         C("C");
70 
71         String qualifierStr;
72 
QualifierKind(String qualifierStr)73         QualifierKind(String qualifierStr) {
74             this.qualifierStr = qualifierStr;
75         }
76 
compatible(QualifierKind ik)77         public boolean compatible(QualifierKind ik) {
78             return this == ik || (this != A && ik != A);
79         }
80     }
81 
82     enum NameKind {
83         D("D"),
84         E("E"),
85         M("m"),
86         F("f"),
87         STAR("*"),
88         NON_EXISTENT("NonExistent");
89 
90         String nameStr;
91 
NameKind(String nameStr)92         NameKind(String nameStr) {
93             this.nameStr = nameStr;
94         }
95 
exists()96         boolean exists() {
97             return this != NON_EXISTENT;
98         }
99 
isMember()100         boolean isMember() {
101             return this == M || this == F;
102         }
103 
isType()104         boolean isType() {
105             return this == D || this == E;
106         }
107     }
108 
main(String... args)109     public static void main(String... args) throws Exception {
110 
111         //create default shared JavaCompiler - reused across multiple compilations
112         JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
113         StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
114 
115         for (ImportKind ik1 : ImportKind.values()) {
116             for (ImportKind ik2 : ImportKind.values()) {
117                 for (QualifierKind qk1 : QualifierKind.values()) {
118                     for (QualifierKind qk2 : QualifierKind.values()) {
119                         for (NameKind nk1 : NameKind.values()) {
120                             for (NameKind nk2 : NameKind.values()) {
121                                 new TestDuplicateImport(ik1, ik2, qk1, qk2, nk1, nk2).run(comp, fm);
122                             }
123                         }
124                     }
125                 }
126             }
127         }
128         System.out.println("Total check executed: " + checkCount);
129     }
130 
131     ImportKind ik1;
132     ImportKind ik2;
133     QualifierKind qk1;
134     QualifierKind qk2;
135     NameKind nk1;
136     NameKind nk2;
137     JavaSource source;
138     DiagnosticChecker diagChecker;
139 
TestDuplicateImport(ImportKind ik1, ImportKind ik2, QualifierKind qk1, QualifierKind qk2, NameKind nk1, NameKind nk2)140     TestDuplicateImport(ImportKind ik1, ImportKind ik2, QualifierKind qk1, QualifierKind qk2, NameKind nk1, NameKind nk2) {
141         this.ik1 = ik1;
142         this.ik2 = ik2;
143         this.qk1 = qk1;
144         this.qk2 = qk2;
145         this.nk1 = nk1;
146         this.nk2 = nk2;
147         this.source = new JavaSource();
148         this.diagChecker = new DiagnosticChecker();
149     }
150     class JavaSource extends SimpleJavaFileObject {
151 
152         String bodyTemplate = "package a;\n" +
153                               "#I1\n" +
154                               "#I2\n" +
155                               "class A {\n" +
156                               "   static class D { }\n" +
157                               "   static class E { }\n" +
158                               "   static Object f;\n" +
159                               "   static void m() { }\n" +
160                               "}\n" +
161                               "class B {\n" +
162                               "   static class D { }\n" +
163                               "   static class E { }\n" +
164                               "   static Object f;\n" +
165                               "   static void m() { }\n" +
166                               "}\n" +
167                               "class C extends B {\n" +
168                               "}\n";
169 
170         String source;
171 
JavaSource()172         public JavaSource() {
173             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
174             source = bodyTemplate.replaceAll("#I1", ik1.getImportStatement(qk1, nk1))
175                     .replaceAll("#I2", ik2.getImportStatement(qk2, nk2));
176         }
177 
178         @Override
getCharContent(boolean ignoreEncodingErrors)179         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
180             return source;
181         }
182     }
183 
run(JavaCompiler tool, StandardJavaFileManager fm)184     void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
185         JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
186                 null, null, Arrays.asList(source));
187         try {
188             ct.analyze();
189         } catch (Throwable ex) {
190             throw new AssertionError("Error thrown when compiling the following code:\n" + source.getCharContent(true));
191         }
192         check();
193     }
194 
check()195     void check() {
196         checkCount++;
197 
198         boolean errorExpected = false;
199 
200         //error if the import refers to a non-existent symbol
201         if (!nk1.exists() || !nk2.exists()) {
202             errorExpected = true;
203         }
204 
205         //error if a non-static import refers to a non-type symbol
206         if ((nk1.isMember() && !ik1.isStatic()) ||
207                 (nk2.isMember() && !ik2.isStatic())) {
208             errorExpected = true;
209         }
210 
211         //error if two single non-static (or one static and one non-static)
212         //imports import same names from different places
213         if (nk1 == nk2 && nk1 != NameKind.STAR && !qk1.compatible(qk2) &&
214                 (!ik1.isStatic() || !ik2.isStatic())) {
215             errorExpected = true;
216         }
217 
218         if ((qk1 == QualifierKind.C && !ik1.isStatic() && nk1 != NameKind.STAR) ||
219             (qk2 == QualifierKind.C && !ik2.isStatic() && nk2 != NameKind.STAR)) {
220             errorExpected = true;
221         }
222 
223         if (errorExpected != diagChecker.errorFound) {
224             throw new Error("invalid diagnostics for source:\n" +
225                 source.getCharContent(true) +
226                 "\nFound error: " + diagChecker.errorFound +
227                 "\nExpected error: " + errorExpected);
228         }
229     }
230 
231     static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
232 
233         boolean errorFound;
234 
report(Diagnostic<? extends JavaFileObject> diagnostic)235         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
236             if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
237                 errorFound = true;
238             }
239         }
240     }
241 }
242