TestDocComments.java revision 2747:84a76798cff3
1/*
2 * Copyright (c) 2010, 2014, 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 6877202 6986246
27 * @summary Elements.getDocComment() is not getting JavaDocComments
28 * @library /tools/javac/lib
29 * @build JavacTestingAbstractProcessor TestDocComments
30 * @run main TestDocComments
31 */
32
33import com.sun.source.tree.*;
34import com.sun.source.util.*;
35import java.io.*;
36import java.util.*;
37import javax.annotation.processing.*;
38import javax.lang.model.*;
39import javax.lang.model.element.*;
40import javax.lang.model.util.*;
41import javax.tools.*;
42
43/*
44 * For a mixture of pre-existing and generated source files, ensure that we can
45 * get the doc comments.
46 * The test uses both a standard ElementScanner to find all the elements being
47 * processed, and a TreeScanner to find all the local and anonymous inner classes
48 * as well.
49 * And, because the relevant code paths in the compiler are different for
50 * command line and JSR 199 invocation, the test covers both ways of invoking the
51 * compiler.
52 */
53
54@SupportedOptions("scan")
55public class TestDocComments extends JavacTestingAbstractProcessor {
56    enum CompileKind { API, CMD };
57    enum ScanKind { TREE, ELEMENT };
58
59    // ----- Main test driver: invoke compiler for the various test cases ------
60
61    public static void main(String... args) throws Exception {
62        for (CompileKind ck: CompileKind.values()) {
63            for (ScanKind sk: ScanKind.values()) {
64                try {
65                    test(ck, sk);
66                } catch (IOException e) {
67                    error(e.toString());
68                }
69            }
70        }
71
72        if (errors > 0)
73            throw new Exception(errors + " errors occurred");
74    }
75
76    static void test(CompileKind ck, ScanKind sk) throws IOException {
77        String testClasses = System.getProperty("test.class.path");
78        String testSrc = System.getProperty("test.src");
79        File testDir = new File("test." + ck + "." + sk);
80        testDir.mkdirs();
81        String[] opts = {
82            "-d", testDir.getPath(),
83            "-implicit:none",
84            "-processor", TestDocComments.class.getName(),
85            "-processorpath", testClasses,
86            //"-XprintRounds",
87            "-Ascan=" + sk
88        };
89        File[] files = {
90            new File(testSrc, "a/First.java")
91        };
92
93        if (ck == CompileKind.API)
94            test_javac_api(opts, files);
95        else
96            test_javac_cmd(opts, files);
97    }
98
99    static void test_javac_api(String[] opts, File[] files) throws IOException {
100        System.err.println("test javac api: " + Arrays.asList(opts) + " " + Arrays.asList(files));
101        DiagnosticListener<JavaFileObject> dl = new DiagnosticListener<JavaFileObject>() {
102            public void report(Diagnostic diagnostic) {
103                error(diagnostic.toString());
104            }
105        };
106        JavaCompiler c = ToolProvider.getSystemJavaCompiler();
107        try (StandardJavaFileManager fm = c.getStandardFileManager(null, null, null)) {
108            Iterable<? extends JavaFileObject> units = fm.getJavaFileObjects(files);
109            JavacTask t = (JavacTask) c.getTask(null, fm, dl, Arrays.asList(opts), null, units);
110            t.parse();
111            t.analyze();
112        }
113    }
114
115    static void test_javac_cmd(String[] opts, File[] files) {
116        System.err.println("test javac cmd: " + Arrays.asList(opts) + " " + Arrays.asList(files));
117        StringWriter sw = new StringWriter();
118        PrintWriter pw = new PrintWriter(sw);
119        List<String> args = new ArrayList<String>(Arrays.asList(opts));
120        for (File f: files)
121            args.add(f.getPath());
122        int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw);
123        pw.close();
124        String out = sw.toString();
125        if (out.length() > 0)
126            System.err.println(out);
127        if (rc > 0)
128            error("Compilation failed: rc=" + rc);
129    }
130
131    static void error(String msg) {
132        System.err.println(msg);
133        errors++;
134        //throw new Error(msg);
135    }
136
137    static int errors;
138
139    // ----- Annotation processor: scan for elements and check doc comments ----
140
141    Map<String,String> options;
142    Trees trees;
143    ScanKind skind;
144
145    int round = 0;
146
147    @Override
148    public void init(ProcessingEnvironment pEnv) {
149        super.init(pEnv);
150        options = pEnv.getOptions();
151        trees = Trees.instance(processingEnv);
152        skind = ScanKind.valueOf(options.get("scan"));
153    }
154
155    @Override
156    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
157        round++;
158
159        // Scan elements using an appropriate scanner, and for each element found,
160        // call check(Element e) to verify the doc comment on that element
161        for (Element e: roundEnv.getRootElements()) {
162            System.err.println("scan " + skind + " " + e.getKind() + " " + e.getSimpleName());
163            if (skind == ScanKind.TREE) {
164                new TestTreeScanner().scan(trees.getPath(e), trees);
165            }  else
166                new TestElementScanner().scan(e);
167        }
168
169        // For a few rounds, generate new source files, so that we can check whether
170        // doc comments are correctly handled in subsequent processing rounds
171        final int MAX_ROUNDS = 3;
172        if (round <= MAX_ROUNDS) {
173            String pkg = "p";
174            String currClass = "Gen" + round;
175            String curr = pkg + "." + currClass;
176            String next = (round < MAX_ROUNDS) ? (pkg + ".Gen" + (round + 1)) : "z.Last";
177            StringBuilder text = new StringBuilder();
178            text.append("package ").append(pkg).append(";\n");
179            text.append("/** CLASS ").append(currClass).append(" */\n");
180            text.append("public class ").append(currClass).append(" {\n");
181            text.append("    /** CONSTRUCTOR <init> **/\n");
182            text.append("    ").append(currClass).append("() { }\n");
183            text.append("    /** FIELD x */\n");
184            text.append("    ").append(next).append(" x;\n");
185            text.append("    /** METHOD m */\n");
186            text.append("    void m() { }\n");
187            text.append("}\n");
188
189            try {
190                JavaFileObject fo = filer.createSourceFile(curr);
191                Writer out = fo.openWriter();
192                try {
193                    out.write(text.toString());
194                } finally {
195                    out.close();
196                }
197            } catch (IOException e) {
198                throw new Error(e);
199            }
200        }
201
202        return true;
203    }
204
205    /*
206     * Check that the doc comment on an element is as expected.
207     * This method is invoked for each element found by the scanners run by process.
208     */
209    void check(Element e) {
210        System.err.println("Checking " + e);
211
212        String dc = elements.getDocComment(e);
213        System.err.println("   found " + dc);
214
215        String expect = (e.getKind() + " " + e.getSimpleName()); // default
216
217        Name name = e.getSimpleName();
218        Element encl = e.getEnclosingElement();
219        Name enclName = encl.getSimpleName();
220        ElementKind enclKind = encl.getKind();
221        switch (e.getKind()) {
222            case PARAMETER:
223            case LOCAL_VARIABLE:
224                // doc comments not retained for these elements
225                expect = null;
226                break;
227
228            case CONSTRUCTOR:
229                if (enclName.length() == 0 || enclKind == ElementKind.ENUM) {
230                    // Enum constructor is synthetic
231                    expect = null;
232                }
233                break;
234
235            case METHOD:
236                if (enclKind == ElementKind.ENUM
237                        && (name.contentEquals("values") || name.contentEquals("valueOf"))) {
238                    // synthetic enum methods
239                    expect = null;
240                }
241                break;
242
243            case CLASS:
244                if (e.getSimpleName().length() == 0) {
245                    // anon inner class
246                    expect = null;
247                }
248                break;
249        }
250
251        System.err.println("  expect " + expect);
252
253        if (dc == null ? expect == null : dc.trim().equals(expect))
254            return;
255
256        if (dc == null)
257            messager.printMessage(Diagnostic.Kind.ERROR, "doc comment is null", e);
258        else {
259            messager.printMessage(Diagnostic.Kind.ERROR,
260                    "unexpected comment: \"" + dc + "\", expected \"" + expect + "\"", e);
261        }
262    }
263
264    // ----- Scanners to find elements -----------------------------------------
265
266    class TestElementScanner extends ElementScanner<Void, Void> {
267        @Override
268        public Void visitExecutable(ExecutableElement e, Void p) {
269            check(e);
270            return super.visitExecutable(e, p);
271        }
272        @Override
273        public Void visitType(TypeElement e, Void p) {
274            check(e);
275            return super.visitType(e, p);
276        }
277        @Override
278        public Void visitVariable(VariableElement e, Void p) {
279            check(e);
280            return super.visitVariable(e, p);
281        }
282    }
283
284    class TestTreeScanner extends TreePathScanner<Void,Trees> {
285        @Override
286        public Void visitClass(ClassTree tree, Trees trees) {
287            check(trees.getElement(getCurrentPath()));
288            return super.visitClass(tree, trees);
289        }
290        @Override
291        public Void visitMethod(MethodTree tree, Trees trees) {
292            check(trees.getElement(getCurrentPath()));
293            return super.visitMethod(tree, trees);
294        }
295        @Override
296        public Void visitVariable(VariableTree tree, Trees trees) {
297            check(trees.getElement(getCurrentPath()));
298            return super.visitVariable(tree, trees);
299        }
300    }
301}
302