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