8268420: new Reporter method to report a diagnostic within a DocTree node
Reviewed-by: prappo
This commit is contained in:
parent
5a74291013
commit
3588634d54
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,6 @@ package com.sun.source.util;
|
||||
import java.io.IOException;
|
||||
import java.text.BreakIterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
@ -195,7 +194,7 @@ public abstract class DocTrees extends Trees {
|
||||
|
||||
/**
|
||||
* Prints a message of the specified kind at the location of the
|
||||
* tree within the provided compilation unit
|
||||
* tree within the provided compilation unit.
|
||||
*
|
||||
* @param kind the kind of message
|
||||
* @param msg the message, or an empty string if none
|
||||
|
@ -75,6 +75,7 @@ import com.sun.tools.javac.code.Flags;
|
||||
import com.sun.tools.javac.code.Scope.NamedImportScope;
|
||||
import com.sun.tools.javac.code.Scope.StarImportScope;
|
||||
import com.sun.tools.javac.code.Scope.WriteableScope;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
|
||||
@ -85,10 +86,8 @@ import com.sun.tools.javac.code.Symtab;
|
||||
import com.sun.tools.javac.code.Type;
|
||||
import com.sun.tools.javac.code.Type.ArrayType;
|
||||
import com.sun.tools.javac.code.Type.ClassType;
|
||||
import com.sun.tools.javac.code.Type.ErrorType;
|
||||
import com.sun.tools.javac.code.Type.UnionClassType;
|
||||
import com.sun.tools.javac.code.Types;
|
||||
import com.sun.tools.javac.code.Types.TypeRelation;
|
||||
import com.sun.tools.javac.comp.Attr;
|
||||
import com.sun.tools.javac.comp.AttrContext;
|
||||
import com.sun.tools.javac.comp.Check;
|
||||
@ -97,7 +96,6 @@ import com.sun.tools.javac.comp.Env;
|
||||
import com.sun.tools.javac.comp.MemberEnter;
|
||||
import com.sun.tools.javac.comp.Modules;
|
||||
import com.sun.tools.javac.comp.Resolve;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import com.sun.tools.javac.file.BaseFileManager;
|
||||
import com.sun.tools.javac.model.JavacElements;
|
||||
import com.sun.tools.javac.parser.DocCommentParser;
|
||||
@ -151,7 +149,6 @@ import com.sun.tools.javac.util.Pair;
|
||||
import com.sun.tools.javac.util.Position;
|
||||
|
||||
import static com.sun.tools.javac.code.Kinds.Kind.*;
|
||||
import static com.sun.tools.javac.code.TypeTag.*;
|
||||
|
||||
/**
|
||||
* Provides an implementation of Trees.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -332,7 +332,9 @@ public class JavadocTokenizer extends JavaTokenizer {
|
||||
private int[] map;
|
||||
|
||||
/**
|
||||
* Logical size of map (number of valid entries.)
|
||||
* Logical size of map.
|
||||
* This is the number of occupied positions in {@code map},
|
||||
* and equals {@code NOFFSETS} multiplied by the number of entries.
|
||||
*/
|
||||
private int size;
|
||||
|
||||
@ -349,25 +351,25 @@ public class JavadocTokenizer extends JavaTokenizer {
|
||||
* if there is a change in relative offset.
|
||||
*
|
||||
* @param sbOffset comment offset member of pair.
|
||||
* @param posOffet input offset member of pair.
|
||||
* @param posOffset input offset member of pair.
|
||||
*
|
||||
* @return true if it is worthwhile adding the entry pair.
|
||||
*/
|
||||
boolean shouldAdd(int sbOffset, int posOffet) {
|
||||
return sbOffset - lastSBOffset() != posOffet - lastPosOffset();
|
||||
boolean shouldAdd(int sbOffset, int posOffset) {
|
||||
return sbOffset - lastSBOffset() != posOffset - lastPosOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entry pair if worthwhile.
|
||||
*
|
||||
* @param sbOffset comment offset member of pair.
|
||||
* @param posOffet input offset member of pair.
|
||||
* @param posOffset input offset member of pair.
|
||||
*/
|
||||
void add(int sbOffset, int posOffet) {
|
||||
if (size == 0 || shouldAdd(sbOffset, posOffet)) {
|
||||
void add(int sbOffset, int posOffset) {
|
||||
if (size == 0 || shouldAdd(sbOffset, posOffset)) {
|
||||
ensure(NOFFSETS);
|
||||
map[size + SB_OFFSET] = sbOffset;
|
||||
map[size + POS_OFFSET] = posOffet;
|
||||
map[size + POS_OFFSET] = posOffset;
|
||||
size += NOFFSETS;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -63,10 +63,26 @@ public abstract class DCTree implements DocTree {
|
||||
*/
|
||||
public int pos;
|
||||
|
||||
/**
|
||||
* {@return the source position for this tree node}
|
||||
*
|
||||
* @param dc the enclosing doc comment
|
||||
*/
|
||||
public long getSourcePosition(DCDocComment dc) {
|
||||
return dc.comment.getSourcePos(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the source position for position relative to this tree node}
|
||||
* This is primarily useful for nodes that wrap a single string child.
|
||||
*
|
||||
* @param dc the enclosing doc comment
|
||||
* @param offset the offset
|
||||
*/
|
||||
public long getSourcePosition(DCDocComment dc, int offset) {
|
||||
return dc.comment.getSourcePos(pos + offset);
|
||||
}
|
||||
|
||||
public JCDiagnostic.DiagnosticPosition pos(DCDocComment dc) {
|
||||
return new SimpleDiagnosticPosition(dc.comment.getSourcePos(pos));
|
||||
}
|
||||
|
@ -31,6 +31,10 @@ import javax.lang.model.element.Element;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.FileObject;
|
||||
|
||||
import com.sun.source.doctree.CommentTree;
|
||||
import com.sun.source.doctree.DocTypeTree;
|
||||
import com.sun.source.doctree.ReferenceTree;
|
||||
import com.sun.source.doctree.TextTree;
|
||||
import com.sun.source.util.DocTreePath;
|
||||
|
||||
/**
|
||||
@ -80,6 +84,36 @@ public interface Reporter {
|
||||
*/
|
||||
void print(Diagnostic.Kind kind, DocTreePath path, String message);
|
||||
|
||||
/**
|
||||
* Prints a diagnostic message related to a position within a range of characters in a tree node.
|
||||
*
|
||||
* Only kinds of {@code DocTree} that wrap a simple string value are supported as leaf nodes
|
||||
* of the given path. This currently includes
|
||||
* {@link CommentTree}, {@link DocTypeTree}, {@link ReferenceTree}, and {@link TextTree}.
|
||||
*
|
||||
* The positions are all 0-based character offsets from the beginning of string.
|
||||
* The positions should satisfy the relation {@code start <= pos <= end}.
|
||||
*
|
||||
* @implSpec
|
||||
* This implementation ignores the {@code (start, pos, end)} values and simply calls
|
||||
* {@link #print(Diagnostic.Kind, DocTreePath, String) print(kind, path, message)}.
|
||||
*
|
||||
* @param kind the kind of diagnostic
|
||||
* @param path the path for the tree node
|
||||
* @param start the beginning of the enclosing range
|
||||
* @param pos the position
|
||||
* @param end the end of the enclosing range
|
||||
* @param message the message to be printed
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code start}, {@code pos} and {@code end} do
|
||||
* not form a valid range.
|
||||
*
|
||||
* @since 18
|
||||
*/
|
||||
default void print(Diagnostic.Kind kind, DocTreePath path, int start, int pos, int end, String message) {
|
||||
print(kind, path, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a diagnostic message related to an element.
|
||||
*
|
||||
|
@ -95,6 +95,20 @@ public class Messages {
|
||||
report(ERROR, path, resources.getText(key, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an error message to the doclet's reporter.
|
||||
*
|
||||
* @param path a path identifying the position to be included with the message
|
||||
* @param start the start of a range of characters to be associated with the message
|
||||
* @param pos the position to be associated with the message
|
||||
* @param end the end of a range of characters to be associated with the message
|
||||
* @param key the name of a resource containing the message to be printed
|
||||
* @param args optional arguments to be replaced in the message
|
||||
*/
|
||||
public void error(DocTreePath path, int start, int pos, int end, String key, Object... args) {
|
||||
report(ERROR, path, start, pos, end, resources.getText(key, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an error message to the doclet's reporter.
|
||||
*
|
||||
@ -134,6 +148,20 @@ public class Messages {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a warning message to the doclet's reporter.
|
||||
*
|
||||
* @param path a path identifying the position to be included with the message
|
||||
* @param start the start of a range of characters to be associated with the message
|
||||
* @param pos the position to be associated with the message
|
||||
* @param end the end of a range of characters to be associated with the message
|
||||
* @param key the name of a resource containing the message to be printed
|
||||
* @param args optional arguments to be replaced in the message
|
||||
*/
|
||||
public void warning(DocTreePath path, int start, int pos, int end, String key, Object... args) {
|
||||
report(WARNING, path, start, pos, end, resources.getText(key, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a warning message to the doclet's reporter.
|
||||
*
|
||||
@ -194,4 +222,8 @@ public class Messages {
|
||||
private void report(Diagnostic.Kind k, FileObject fo, int start, int pos, int end, String msg) {
|
||||
reporter.print(k, fo, start, pos, end, msg);
|
||||
}
|
||||
|
||||
private void report(Diagnostic.Kind k, DocTreePath path, int start, int pos, int end, String msg) {
|
||||
reporter.print(k, path, start, pos, end, msg);
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,12 @@ import javax.tools.FileObject;
|
||||
import javax.tools.ForwardingFileObject;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
import com.sun.source.doctree.CommentTree;
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.DocTypeTree;
|
||||
import com.sun.source.doctree.ReferenceTree;
|
||||
import com.sun.source.doctree.TextTree;
|
||||
import com.sun.tools.javac.tree.DCTree;
|
||||
import jdk.javadoc.doclet.Reporter;
|
||||
|
||||
import com.sun.tools.javac.tree.EndPosTable;
|
||||
@ -247,6 +253,48 @@ public class JavadocLog extends Log implements Reporter {
|
||||
report(dt, flags, ds, dp, message);
|
||||
}
|
||||
|
||||
@Override // Reporter
|
||||
public void print(Diagnostic.Kind kind, DocTreePath path, int start, int pos, int end, String message) {
|
||||
if (!(start <= pos && pos <= end)) {
|
||||
throw new IllegalArgumentException("start:" + start + ",pos:" + pos + ",end:" + end);
|
||||
}
|
||||
|
||||
DocTree t = path.getLeaf();
|
||||
String s = switch (t.getKind()) {
|
||||
case COMMENT -> ((CommentTree) t).getBody();
|
||||
case DOC_TYPE -> ((DocTypeTree) t).getText();
|
||||
case REFERENCE -> ((ReferenceTree) t).getSignature();
|
||||
case TEXT -> ((TextTree) t).getBody();
|
||||
default -> throw new IllegalArgumentException(t.getKind().toString());
|
||||
};
|
||||
|
||||
if (start < 0 || end > s.length()) {
|
||||
throw new StringIndexOutOfBoundsException("start:" + start + ",pos:" + pos + ",end:" + end
|
||||
+ "; string length " + s.length());
|
||||
}
|
||||
|
||||
DiagnosticType dt = getDiagnosticType(kind);
|
||||
Set<DiagnosticFlag> flags = getDiagnosticFlags(kind);
|
||||
DiagnosticSource ds = getDiagnosticSource(path);
|
||||
|
||||
DCTree.DCDocComment docComment = (DCTree.DCDocComment) path.getDocComment();
|
||||
DCTree tree = (DCTree) path.getLeaf();
|
||||
// note: it is important to evaluate the offsets in the context of the position
|
||||
// within the comment text, and not in the context of the overall source text
|
||||
int sStart = (int) tree.getSourcePosition(docComment, start);
|
||||
int sPos = (int) tree.getSourcePosition(docComment, pos);
|
||||
int sEnd = (int) tree.getSourcePosition(docComment, end);
|
||||
DiagnosticPosition dp = createDiagnosticPosition(null, sStart, sPos, sEnd);
|
||||
|
||||
report(dt, flags, ds, dp, message);
|
||||
}
|
||||
|
||||
private int getSourcePos(DocTreePath path, int offset) {
|
||||
DCTree.DCDocComment docComment = (DCTree.DCDocComment) path.getDocComment();
|
||||
DCTree tree = (DCTree) path.getLeaf();
|
||||
return (int) tree.getSourcePosition(docComment, offset);
|
||||
}
|
||||
|
||||
@Override // Reporter
|
||||
public void print(Kind kind, Element element, String message) {
|
||||
DiagnosticType dt = getDiagnosticType(kind);
|
||||
|
169
test/langtools/jdk/javadoc/doclet/testDocTreeDiags/MyTaglet.java
Normal file
169
test/langtools/jdk/javadoc/doclet/testDocTreeDiags/MyTaglet.java
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.FileObject;
|
||||
|
||||
import com.sun.source.doctree.CommentTree;
|
||||
import com.sun.source.doctree.DocCommentTree;
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import com.sun.source.doctree.DocTypeTree;
|
||||
import com.sun.source.doctree.ReferenceTree;
|
||||
import com.sun.source.doctree.TextTree;
|
||||
import com.sun.source.util.DocTreePath;
|
||||
import com.sun.source.util.DocTreePathScanner;
|
||||
import com.sun.source.util.DocTrees;
|
||||
import com.sun.source.util.TreePath;
|
||||
import jdk.javadoc.doclet.Doclet;
|
||||
import jdk.javadoc.doclet.DocletEnvironment;
|
||||
import jdk.javadoc.doclet.Reporter;
|
||||
import jdk.javadoc.doclet.StandardDoclet;
|
||||
import jdk.javadoc.doclet.Taglet;
|
||||
|
||||
/**
|
||||
* A taglet to be called in the context of the standard doclet.
|
||||
* When invoked, it scans the entire enclosing doc comment, and
|
||||
* reports diagnostics at all instances of selected node kinds,
|
||||
* so that a test can verify the contents of the diagnostics.
|
||||
*/
|
||||
public class MyTaglet implements Taglet {
|
||||
private DocletEnvironment docEnv;
|
||||
private Reporter reporter;
|
||||
|
||||
@Override
|
||||
public void init(DocletEnvironment docEnv, Doclet doclet) {
|
||||
this.docEnv = docEnv;
|
||||
reporter = ((StandardDoclet) doclet).getReporter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Location> getAllowedLocations() {
|
||||
return EnumSet.allOf(Location.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInlineTag() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "scanMe";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(List<? extends DocTree> tags, Element element) {
|
||||
DocTrees trees = docEnv.getDocTrees();
|
||||
Elements elements = docEnv.getElementUtils();
|
||||
DocTreePath dtp;
|
||||
// Use reflection to access the file object underlying a doc-files/*.html file
|
||||
// in order to access a DocCommentTree for the file.
|
||||
// Note that using reflective access inside javadoc requires the -XDallowInternalAccess option.
|
||||
// Note also that technically the doc comment tree that is found may be a different instance
|
||||
// to the current instance, but since we only want to scan it and report diagnostics,
|
||||
// that should not matter.
|
||||
if (element.getClass().getSimpleName().equals("DocFileElement")) {
|
||||
try {
|
||||
Method getFileObjectMethod = element.getClass().getMethod("getFileObject");
|
||||
FileObject fo = (FileObject) getFileObjectMethod.invoke(element);
|
||||
DocCommentTree dct = trees.getDocCommentTree(fo);
|
||||
dtp = trees.getDocTreePath(fo, elements.getPackageElement("p"));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
return "MyTaglet[" + e + "]";
|
||||
}
|
||||
} else {
|
||||
DocCommentTree dct = trees.getDocCommentTree(element);
|
||||
TreePath tp = trees.getPath(element);
|
||||
dtp = new DocTreePath(tp, dct);
|
||||
}
|
||||
|
||||
scan(dtp);
|
||||
|
||||
return "MyTaglet[" + element + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a DocCommentTree, generating diagnostics for selected nodes.
|
||||
* Information about the expected position is encoded within the
|
||||
* text of the diagnostic, surrounded by {@code >>> <<<}.
|
||||
*
|
||||
* @param dtp the path to scan
|
||||
*/
|
||||
void scan(DocTreePath dtp) {
|
||||
DocTreePathScanner<Void, Void> s = new DocTreePathScanner<Void, Void>() {
|
||||
@Override
|
||||
public Void visitDocComment(DocCommentTree t, Void p) {
|
||||
// By default, DocTreeScanner does not include the preamble and postamble
|
||||
scan(t.getPreamble(), p);
|
||||
super.visitDocComment(t, p);
|
||||
scan(t.getPostamble(), p);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitComment(CommentTree t, Void p) {
|
||||
report(t, t.getBody());
|
||||
return super.visitComment(t, p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitDocType(DocTypeTree t, Void p) {
|
||||
report(t, t.getText());
|
||||
return super.visitDocType(t, p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitReference(ReferenceTree t, Void p) {
|
||||
report(t, t.getSignature());
|
||||
return super.visitReference(t, p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitText(TextTree t, Void p) {
|
||||
report(t, t.getBody());
|
||||
return super.visitText(t, p);
|
||||
}
|
||||
|
||||
void report(DocTree t, String s) {
|
||||
int pad = 3;
|
||||
assert (s.length() > 2 * pad + 3) : ">>>" + s + "<<<";
|
||||
int mid = s.length() / 2;
|
||||
String detail = s.substring(mid - pad, mid) + "[" + s.charAt(mid) + "]" + s.substring(mid + 1, mid + pad + 1);
|
||||
// The diagnostic is reported at a position in a range of characters
|
||||
// in the middle of the string; the characters are encoded within the
|
||||
// message of the diagnostic, with {@code [ ]} surrounding the character
|
||||
// that should be indicated by the caret.
|
||||
reporter.print(Diagnostic.Kind.WARNING, getCurrentPath(),
|
||||
mid - pad, mid, mid + pad + 1,
|
||||
"length: " + s.length() + " mid: " + mid + " >>>" + detail + "<<<");
|
||||
}
|
||||
};
|
||||
s.scan(dtp, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8268420
|
||||
* @summary new Reporter method to report a diagnostic within a DocTree node
|
||||
* @library /tools/lib ../../lib
|
||||
* @modules jdk.javadoc/jdk.javadoc.internal.tool
|
||||
* @build javadoc.tester.* MyTaglet
|
||||
* @run main TestDocTreeDiags
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.DiagnosticListener;
|
||||
import javax.tools.DocumentationTool;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import javadoc.tester.JavadocTester;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
/**
|
||||
* Tests the ability to write diagnostics related to a (start,pos,end) range in those
|
||||
* DocTrees that wrap a String value.
|
||||
*
|
||||
* Ideally, this would be tested by using a custom doclet which scans all the doc comments,
|
||||
* generating diagnostics for eligible nodes. However, one of the cases that is tested is
|
||||
* a DocTypeTree, which only occurs in the context of an HTML file in a doc-files subdirectory,
|
||||
* which is very specific to the Standard Doclet. Therefore, we use the Standard Doclet
|
||||
* in conjunction with a non-standard use of a custom taglet, which is used to access and
|
||||
* scan the doc comments that enclose the tags that trigger the taglet.
|
||||
*/
|
||||
public class TestDocTreeDiags extends JavadocTester {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
TestDocTreeDiags tester = new TestDocTreeDiags();
|
||||
tester.runTests(m -> new Object[] { Path.of(m.getName())} );
|
||||
}
|
||||
|
||||
ToolBox tb = new ToolBox();
|
||||
Path src;
|
||||
DocumentationTool tool;
|
||||
|
||||
boolean showOutput = false; // set true for to set output written by javadoc
|
||||
|
||||
TestDocTreeDiags() throws IOException {
|
||||
src = Path.of("src");
|
||||
// Note: the following comments are somewhat stylized, and need to follow some
|
||||
// simple rules to avoid exceptions and false positives.
|
||||
// 1. Each fragment must be at least 7 (and preferably 9) characters long,
|
||||
// in order to contain the range that will be generated in the diagnostic.
|
||||
// 2. There must be no non-trivial duplication in the fragments, particularly
|
||||
// in the area where the range of characters will be generated for the
|
||||
// diagnostic. This is because we use String.indexOf to determine the
|
||||
// expected values of the range.
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
package p;
|
||||
/**
|
||||
* First sentence. " Second sentence.
|
||||
* {@link java.lang.String first phrase; " second phrase }
|
||||
* And now ... <!-- this is a comment --> and so it was.
|
||||
* @scanMe
|
||||
*/
|
||||
public class C {
|
||||
/**
|
||||
* Sentence for method m(). More details for the method.
|
||||
* Embedded {@link java.lang.Object} link.
|
||||
* And another <!-- unusual comment --> strange comment.
|
||||
* @scanMe
|
||||
*/
|
||||
public void m() { }
|
||||
}
|
||||
""");
|
||||
tb.writeFile(src.resolve("p").resolve("doc-files").resolve("extra.html"),
|
||||
"""
|
||||
<!doctype doctype-description>
|
||||
<html>
|
||||
<head><title>Document Title</title></head>
|
||||
<body>
|
||||
Extra content. More content.
|
||||
@scanMe
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
);
|
||||
|
||||
tool = ToolProvider.getSystemDocumentationTool();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the diagnostics generated to the output stream when there is no
|
||||
* diagnostic listener in use.
|
||||
*
|
||||
* By default, in this context, the start and end of the range of characters are not
|
||||
* presented. The caret should point at the preferred position for the diagnostic.
|
||||
*/
|
||||
@Test
|
||||
public void testStdout(Path base) throws Exception {
|
||||
StringWriter outWriter = new StringWriter();
|
||||
javadoc(outWriter, null, base.resolve("api"));
|
||||
|
||||
// analyze and verify the generated diagnostics
|
||||
List<String> lines = outWriter.toString().lines().toList();
|
||||
Iterator<String> iter = lines.iterator();
|
||||
while (iter.hasNext()) {
|
||||
String l = iter.next();
|
||||
if (l.startsWith("src")) {
|
||||
checkDiag(null, l, iter.next(), iter.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the diagnostics received by a DiagnosticListener.
|
||||
*
|
||||
* In this context, various detailed coordinate information is available.
|
||||
*/
|
||||
@Test
|
||||
public void testDiagListener(Path base) throws Exception {
|
||||
StringWriter outWriter = new StringWriter();
|
||||
DiagnosticListener dl = diagnostic -> {
|
||||
if (diagnostic.getPosition() != -1) {
|
||||
List<String> lines = List.of(diagnostic.toString().split("\\R"));
|
||||
assert lines.size() == 3;
|
||||
String msgLine = lines.get(0);
|
||||
String srcLine = lines.get(1);
|
||||
String caretLine = lines.get(2);
|
||||
checkDiag(diagnostic, msgLine, srcLine, caretLine);
|
||||
}
|
||||
};
|
||||
javadoc(outWriter, dl, base.resolve("api"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs javadoc on package {@code p} in the {@code src} directory,
|
||||
* using the specified writer and optional diagnostic listener.
|
||||
*
|
||||
* @param writer the writer
|
||||
* @param dl the diagnostic listener, or {@code null}
|
||||
* @param outDir the output directory
|
||||
*
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
void javadoc(StringWriter writer, DiagnosticListener dl, Path outDir) throws IOException {
|
||||
Files.createDirectories(outDir);
|
||||
try (StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null)) {
|
||||
fm.setLocationFromPaths(StandardLocation.SOURCE_PATH, List.of(src));
|
||||
fm.setLocationFromPaths(DocumentationTool.Location.DOCUMENTATION_OUTPUT, List.of(outDir));
|
||||
fm.setLocationFromPaths(DocumentationTool.Location.TAGLET_PATH, List.of(Path.of(System.getProperty("test.classes"))));
|
||||
Iterable<? extends JavaFileObject> files = Collections.emptyList();
|
||||
Iterable<String> options = List.of("-taglet", MyTaglet.class.getName(), "-XDaccessInternalAPI", "p");
|
||||
DocumentationTool.DocumentationTask t = tool.getTask(writer, fm, dl, null, options, files);
|
||||
|
||||
checking("exit");
|
||||
boolean ok = t.call();
|
||||
|
||||
if (showOutput) {
|
||||
out.println("OUT: >>>" + writer.toString().replace("\n", NL) + "<<<");
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
passed("javadoc exited OK, as expected");
|
||||
} else {
|
||||
failed("javadoc failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the diagnostic output against information encoded in the diagnostics.
|
||||
*
|
||||
* The message in the message line contains a string that indicates where the
|
||||
* caret should be pointing in the source line.
|
||||
*
|
||||
* @param diag the diagnostic, or null
|
||||
* @param msgLine file:line: message >>>detail<<<
|
||||
* @param srcLine the source line
|
||||
* @param caretLine the line with the caret
|
||||
*/
|
||||
void checkDiag(Diagnostic diag, String msgLine, String srcLine, String caretLine) {
|
||||
if (diag != null) {
|
||||
out.printf("DIAG: %d:%d:%d %d:%d vvv%n%s%n^^^%n",
|
||||
diag.getStartPosition(), diag.getPosition(), diag.getEndPosition(),
|
||||
diag.getLineNumber(), diag.getColumnNumber(),
|
||||
diag.toString().replace("\\R", NL) );
|
||||
}
|
||||
out.println(msgLine);
|
||||
out.println(srcLine);
|
||||
out.println(caretLine);
|
||||
|
||||
String srcFileLine = msgLine.substring(0, msgLine.indexOf(": "));
|
||||
int caretIndex = caretLine.indexOf('^');
|
||||
Pattern p = Pattern.compile(">>>([^<]*)<<<");
|
||||
Matcher m = p.matcher(msgLine);
|
||||
if (!m.find()) {
|
||||
throw new IllegalArgumentException("detail pattern not found: " + msgLine);
|
||||
}
|
||||
String rawDetail = m.group(1);
|
||||
String detail = rawDetail.replaceAll("[\\[\\]]", "");
|
||||
|
||||
if (diag != null) {
|
||||
checking("coords-column: " + srcFileLine);
|
||||
int col = (int) diag.getColumnNumber();
|
||||
// line and column are 1-based, so col should be 1 more than caretIndex
|
||||
if (col - 1 == caretIndex) {
|
||||
passed("col: " + col + " caret: " + caretIndex);
|
||||
} else {
|
||||
failed("col: " + col + " caret: " + caretIndex);
|
||||
}
|
||||
|
||||
checking("coords-start-end: " + srcFileLine);
|
||||
String fileStr = readFile(".", msgLine.substring(0, msgLine.indexOf(":")));
|
||||
int start = (int) diag.getStartPosition();
|
||||
int end = (int) diag.getEndPosition();
|
||||
String fileRange = fileStr.substring(start, end);
|
||||
if (fileRange.equals(detail)) {
|
||||
passed("file: >>>" + fileRange + "<<< message: >>>" + detail + "<<<");
|
||||
} else {
|
||||
failed("file: >>>" + fileRange + "<<< message: >>>" + detail + "<<<");
|
||||
}
|
||||
}
|
||||
|
||||
checking("message-caret: " + srcFileLine);
|
||||
int srcIndex = srcLine.indexOf(detail);
|
||||
int pad = (detail.length() - 1) / 2;
|
||||
int srcIndexPad = srcIndex + pad;
|
||||
if (srcIndexPad == caretIndex) {
|
||||
passed("src: " + srcIndexPad + " caret: " + caretIndex);
|
||||
} else {
|
||||
failed("src: " + srcIndexPad + " caret: " + caretIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user