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 8007344
27  * @summary javac may not make tree end positions and/or doc comments
28  *          available to processors and listeners
29  * @library /tools/javac/lib
30  * @modules jdk.compiler/com.sun.tools.javac.api
31  *          jdk.compiler/com.sun.tools.javac.file
32  *          jdk.compiler/com.sun.tools.javac.tree
33  *          jdk.compiler/com.sun.tools.javac.util
34  * @build JavacTestingAbstractProcessor
35  * @run main Test
36  */
37 
38 import java.io.File;
39 import java.io.PrintWriter;
40 import java.util.Arrays;
41 import java.util.Set;
42 
43 import javax.annotation.processing.RoundEnvironment;
44 import javax.lang.model.element.Element;
45 import javax.lang.model.element.TypeElement;
46 import javax.tools.JavaFileObject;
47 import javax.tools.StandardJavaFileManager;
48 import javax.tools.StandardLocation;
49 
50 import com.sun.source.doctree.DocCommentTree;
51 import com.sun.source.tree.*;
52 import com.sun.source.util.DocTrees;
53 import com.sun.source.util.JavacTask;
54 import com.sun.source.util.SourcePositions;
55 import com.sun.source.util.TaskEvent;
56 import com.sun.source.util.TaskListener;
57 import com.sun.source.util.TreePath;
58 import com.sun.source.util.TreePathScanner;
59 import com.sun.tools.javac.api.JavacTool;
60 import com.sun.tools.javac.tree.JCTree;
61 import com.sun.tools.javac.tree.Pretty;
62 import com.sun.tools.javac.util.Position;
63 
64 /** Doc comment: Test */
65 public class Test {
66     public static final int EXPECT_DOC_COMMENTS = 3;
67 
68     /** Doc comment: main */
main(String... args)69     public static void main(String... args) throws Exception {
70         PrintWriter out = new PrintWriter(System.err);
71         try {
72             new Test(out).run();
73         } finally {
74             out.flush();
75         }
76     }
77 
78     PrintWriter out;
79     int errors;
80 
Test(PrintWriter out)81     Test(PrintWriter out) {
82         this.out = out;
83     }
84 
85     /** Doc comment: run */
run()86     void run() throws Exception {
87         File testSrc = new File(System.getProperty("test.src"));
88         File thisFile = new File(testSrc, getClass().getName() + ".java");
89         JavacTool javac = JavacTool.create();
90         try (StandardJavaFileManager fm = javac.getStandardFileManager(null, null, null)) {
91             fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File(".")));
92             Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjects(thisFile);
93             testAnnoProcessor(javac, fm, fos, out, EXPECT_DOC_COMMENTS);
94             testTaskListener(javac, fm, fos, out, EXPECT_DOC_COMMENTS);
95 
96             if (errors > 0)
97                 throw new Exception(errors + " errors occurred");
98         }
99     }
100 
testAnnoProcessor(JavacTool javac, StandardJavaFileManager fm, Iterable<? extends JavaFileObject> files, PrintWriter out, int expectedDocComments)101     void testAnnoProcessor(JavacTool javac, StandardJavaFileManager fm,
102             Iterable<? extends JavaFileObject> files, PrintWriter out,
103             int expectedDocComments) {
104         out.println("Test annotation processor");
105         JavacTask task = javac.getTask(out, fm, null, null, null, files);
106         AnnoProc ap = new AnnoProc(DocTrees.instance(task));
107         task.setProcessors(Arrays.asList(ap));
108         task.call();
109         ap.checker.checkDocComments(expectedDocComments);
110     }
111 
testTaskListener(JavacTool javac, StandardJavaFileManager fm, Iterable<? extends JavaFileObject> files, PrintWriter out, int expectedDocComments)112     void testTaskListener(JavacTool javac, StandardJavaFileManager fm,
113             Iterable<? extends JavaFileObject> files, PrintWriter out,
114             int expectedDocComments) {
115         out.println("Test task listener");
116         JavacTask task = javac.getTask(out, fm, null, null, null, files);
117         TaskListnr tl = new TaskListnr(DocTrees.instance(task));
118         task.addTaskListener(tl);
119         task.call();
120         tl.checker.checkDocComments(expectedDocComments);
121     }
122 
error(String msg)123     void error(String msg) {
124         out.println("Error: " + msg);
125         errors++;
126     }
127 
128     class AnnoProc extends JavacTestingAbstractProcessor {
129         Checker checker;
130 
AnnoProc(DocTrees trees)131         AnnoProc(DocTrees trees) {
132             checker = new Checker(trees);
133         }
134 
135         @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)136         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
137             for (Element e : roundEnv.getRootElements()) {
138                 checker.scan(checker.trees.getPath(e), null);
139             }
140             return true;
141         }
142     }
143 
144     class TaskListnr implements TaskListener {
145         Checker checker;
146 
TaskListnr(DocTrees trees)147         TaskListnr(DocTrees trees) {
148             checker = new Checker(trees);
149         }
150 
started(TaskEvent e)151         public void started(TaskEvent e) {
152             if (e.getKind() == TaskEvent.Kind.ANALYZE)
153                 checker.scan(new TreePath(e.getCompilationUnit()), null);
154         }
155 
finished(TaskEvent e)156         public void finished(TaskEvent e) {
157         }
158     }
159 
160     class Checker extends TreePathScanner<Void,Void> {
161         DocTrees trees;
162         SourcePositions srcPosns;
163 
164         int docComments = 0;
165 
Checker(DocTrees trees)166         Checker(DocTrees trees) {
167             this.trees = trees;
168             srcPosns = trees.getSourcePositions();
169         }
170 
171         @Override
scan(Tree tree, Void ignore)172         public Void scan(Tree tree, Void ignore) {
173             if (tree != null) {
174                 switch (tree.getKind()) {
175                     // HACK: Workaround 8007350
176                     // Some tree nodes do not have endpos set
177                     case ASSIGNMENT:
178                     case BLOCK:
179                     case IDENTIFIER:
180                     case METHOD_INVOCATION:
181                         break;
182 
183                     default:
184                         checkEndPos(getCurrentPath().getCompilationUnit(), tree);
185                 }
186             }
187             return super.scan(tree, ignore);
188         }
189 
190         @Override
visitClass(ClassTree tree, Void ignore)191         public Void visitClass(ClassTree tree, Void ignore) {
192             checkComment();
193             return super.visitClass(tree, ignore);
194         }
195 
196         @Override
visitMethod(MethodTree tree, Void ignore)197         public Void visitMethod(MethodTree tree, Void ignore) {
198             checkComment();
199             return super.visitMethod(tree, ignore);
200         }
201 
202         @Override
visitVariable(VariableTree tree, Void ignore)203         public Void visitVariable(VariableTree tree, Void ignore) {
204             checkComment();
205             return super.visitVariable(tree, ignore);
206         }
207 
checkComment()208         void checkComment() {
209             DocCommentTree dc = trees.getDocCommentTree(getCurrentPath());
210             if (dc != null) {
211                 out.println("comment: " + dc.toString().replaceAll("\\s+", " "));
212                 docComments++;
213             }
214         }
215 
checkEndPos(CompilationUnitTree unit, Tree tree)216         void checkEndPos(CompilationUnitTree unit, Tree tree) {
217             long sp = srcPosns.getStartPosition(unit, tree);
218             long ep = srcPosns.getEndPosition(unit, tree);
219             if (sp >= 0 && ep == Position.NOPOS) {
220                 error("endpos not set for " + tree.getKind()
221                         + " " + Pretty.toSimpleString(((JCTree) tree))
222                         +", start:" + sp);
223             }
224         }
225 
checkDocComments(int expected)226         void checkDocComments(int expected) {
227             if (docComments != expected) {
228                 error("Unexpected number of doc comments received: "
229                         + docComments + ", expected: " + expected);
230             }
231         }
232 
233     }
234 }
235