1/*
2 * Copyright (c) 2010, 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 6930507
27 * @summary Symbols for anonymous and local classes made too late for use by java tree API
28 * @modules jdk.compiler
29 */
30
31import java.io.*;
32import java.util.*;
33import javax.annotation.processing.*;
34import javax.lang.model.SourceVersion;
35import javax.lang.model.element.*;
36import javax.tools.Diagnostic;
37import static javax.lang.model.util.ElementFilter.*;
38
39import com.sun.source.tree.*;
40import com.sun.source.util.*;
41
42@SupportedOptions({"test", "last"})
43@SupportedAnnotationTypes("*")
44public class TestGetElement extends AbstractProcessor {
45    public static void main(String... args) throws Exception {
46        new TestGetElement().run();
47    }
48
49    public TestGetElement() { }
50
51    public void run() throws Exception {
52        final String testSrc = System.getProperty("test.src");
53        final String testClasses = System.getProperty("test.classes");
54        final String myClassName = getClass().getName();
55        final String mySrc = new File(testSrc, myClassName + ".java").getPath();
56
57        final int NUM_TESTS = 90; // #decls in this source file
58        for (int i = 1; i <= NUM_TESTS; i++) {
59            System.err.println("test " + i);
60            File testDir = new File("test" + i);
61            File classesDir = new File(testDir, "classes");
62            classesDir.mkdirs();
63            String[] args = {
64                "-d", classesDir.getPath(),
65                "-processorpath", testClasses,
66                "-processor", myClassName,
67                "-proc:only",
68                "-Atest=" + i,
69                "-Alast=" + (i == NUM_TESTS),
70                mySrc
71            };
72
73//            System.err.println("compile: " + Arrays.asList(args));
74
75            StringWriter sw = new StringWriter();
76            PrintWriter pw = new PrintWriter(sw);
77            int rc = com.sun.tools.javac.Main.compile(args, pw);
78            pw.close();
79            String out = sw.toString();
80            if (out != null)
81                System.err.println(out);
82            if (rc != 0) {
83                System.err.println("compilation failed: rc=" + rc);
84                errors++;
85            }
86        }
87
88        if (errors > 0)
89            throw new Exception(errors + " errors occurred");
90    }
91
92
93    int errors;
94
95    public boolean process(Set<? extends TypeElement> annotations,
96                           RoundEnvironment roundEnvironment)
97    {
98        if (roundEnvironment.processingOver())
99            return true;
100
101        Map<String,String> options = processingEnv.getOptions();
102        int test = Integer.parseInt(options.get("test"));
103        boolean _last = Boolean.parseBoolean(options.get("last"));
104
105        Trees trees = Trees.instance(processingEnv);
106        Scanner scanner = new Scanner(trees, _last);
107        int nelems = 0;
108        for (TypeElement e : typesIn(roundEnvironment.getRootElements())) {
109            nelems += scanner.scan(trees.getPath(e), test);
110        }
111
112        Messager m = processingEnv.getMessager();
113        int EXPECT = 1;
114        if (nelems != EXPECT) {
115            m.printMessage(Diagnostic.Kind.ERROR,
116                    "Unexpected number of elements found: " + nelems + " expected: " + EXPECT);
117        }
118        return true;
119    }
120
121    @Override
122    public SourceVersion getSupportedSourceVersion() {
123        return SourceVersion.latest();
124    }
125
126    class Scanner extends TreePathScanner<Integer,Integer> {
127        final Trees trees;
128        final boolean last;
129        int count;
130
131        Scanner(Trees trees, boolean last) {
132            this.trees = trees;
133            this.last = last;
134        }
135
136        @Override
137        public Integer visitClass(ClassTree tree, Integer test) {
138            return reduce(check(test), super.visitClass(tree, test));
139        }
140
141        @Override
142        public Integer visitMethod(MethodTree tree, Integer test) {
143            return reduce(check(test), super.visitMethod(tree, test));
144        }
145
146        @Override
147        public Integer visitVariable(VariableTree tree, Integer test) {
148            return reduce(check(test), super.visitVariable(tree, test));
149        }
150
151        @Override
152        public Integer reduce(Integer i1, Integer i2) {
153            if (i1 == null || i1.intValue() == 0)
154                return i2;
155            if (i2 == null || i2.intValue() == 0)
156                return i1;
157            return (i1 + i2);
158        }
159
160        int check(int test) {
161            count++;
162
163            if (count != test)
164                return 0;
165
166            TreePath p = getCurrentPath();
167            Element e = trees.getElement(p);
168
169            String text = p.getLeaf().toString().replaceAll("\\s+", " ").trim();
170            int MAXLEN = 40;
171            if (text.length() > MAXLEN)
172                text = text.substring(0, MAXLEN - 3) + "...";
173
174            System.err.println(String.format("%3d: %-" + MAXLEN + "s -- %s",
175                    count, text,
176                    (e == null ? "null" : e.getKind() + " " + e)));
177
178            Messager m = processingEnv.getMessager();
179            if (e == null) {
180                m.printMessage(Diagnostic.Kind.ERROR, "Null element found for " + text);
181                return 0;
182            }
183
184            if (last && !e.getSimpleName().contentEquals("last")) {
185                m.printMessage(Diagnostic.Kind.ERROR, "Unexpected name in last test: "
186                        + e.getSimpleName() + ", expected: last");
187            }
188
189            return 1;
190        }
191    }
192
193    // following are all fodder for the test
194
195    class MemberClass {
196        class NestedMemberClass { }
197    }
198
199    {
200        class InnerClassInInit { }
201        Object o = new Object() { };
202    }
203
204    TestGetElement(TestGetElement unused) {
205        class InnerClassInConstr { }
206        Object o = new Object() { };
207    }
208
209    void m() {
210        class InnerClassInMethod { }
211        Object o = new Object() { };
212
213        class C {
214            class MemberClass {
215                class NestedMemberClass { }
216            }
217
218            {
219                class InnerClassInInit { }
220                Object o = new Object() { };
221            }
222
223            C(Object unused) {
224                class InnerClassInConstr { }
225                Object o = new Object() { };
226            }
227
228            void m() {
229                class InnerClassInMethod { }
230                Object o = new Object() { };
231            }
232        }
233    }
234
235    int last; // this name is verified by the test to make sure that all decls are checked
236}
237