You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2022/07/19 22:30:21 UTC

[groovy] 02/02: GROOVY-10689: Groovydoc for Groovy 3+ documents classes at too early a phase of compilation

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 4e81b60eaff3600e7d4be845de0673796eb6d98c
Author: Paul King <pa...@asert.com.au>
AuthorDate: Tue Jul 19 13:51:10 2022 +1000

    GROOVY-10689: Groovydoc for Groovy 3+ documents classes at too early a phase of compilation
---
 .../org/codehaus/groovy/ast/AnnotationNode.java    |  14 +-
 .../transform/PackageScopeASTTransformation.java   |   1 +
 .../java/org/codehaus/groovy/ant/Groovydoc.java    |   3 +
 .../codehaus/groovy/tools/groovydoc/Main.groovy    |   4 +
 .../org/apache/groovy/antlr/GroovydocVisitor.java  |  23 ++-
 .../org/codehaus/groovy/groovydoc/GroovyDoc.java   |   2 +
 .../tools/groovydoc/ArrayClassDocWrapper.java      |   5 +
 .../tools/groovydoc/ExternalGroovyClassDoc.java    |   5 +
 .../tools/groovydoc/GroovyRootDocBuilder.java      |   2 +-
 .../tools/groovydoc/SimpleGroovyAnnotationRef.java |  10 +-
 .../groovy/tools/groovydoc/SimpleGroovyDoc.java    |   9 ++
 .../tools/groovydoc/antlr4/GroovyDocParser.java    |  23 ++-
 .../gstringTemplates/classLevel/classDocName.html  |  10 +-
 .../groovy/tools/groovydoc/GroovyDocToolTest.java  | 174 +++++++++++----------
 .../groovydoc/testfiles/generics/Groovy.groovy     |  17 +-
 .../tools/groovydoc/testfiles/generics/Java.java   |  18 ++-
 16 files changed, 212 insertions(+), 108 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java b/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java
index 5a32327eef..f91724e94c 100644
--- a/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java
@@ -19,11 +19,15 @@
 package org.codehaus.groovy.ast;
 
 import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
 import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ListExpression;
 
 import java.util.Collections;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import static java.util.Objects.requireNonNull;
 
@@ -213,7 +217,15 @@ public class AnnotationNode extends ASTNode {
                 } else {
                     memberText.append(", ");
                 }
-                memberText.append(next.getKey()).append(": ").append(next.getValue().getText());
+                Expression value = next.getValue();
+                String text;
+                if (value instanceof ListExpression) {
+                    List result = ((ListExpression) value).getExpressions().stream().map(exp -> { return exp instanceof AnnotationConstantExpression ? ((ASTNode)((AnnotationConstantExpression)exp).getValue()).getText() : exp.getText(); }).collect(Collectors.toList());
+                    text = result.toString();
+                } else {
+                    text = value.getText();
+                }
+                memberText.append(next.getKey()).append(": ").append(text);
             }
         }
         return "@" + classNode.getText() + "(" + memberText + ")";
diff --git a/src/main/java/org/codehaus/groovy/transform/PackageScopeASTTransformation.java b/src/main/java/org/codehaus/groovy/transform/PackageScopeASTTransformation.java
index 22ac1a3360..1ee3487ac9 100644
--- a/src/main/java/org/codehaus/groovy/transform/PackageScopeASTTransformation.java
+++ b/src/main/java/org/codehaus/groovy/transform/PackageScopeASTTransformation.java
@@ -175,6 +175,7 @@ public class PackageScopeASTTransformation extends AbstractASTTransformation {
 
     private static void revertVisibility(FieldNode fNode) {
         fNode.setModifiers(fNode.getModifiers() & ~ACC_PRIVATE);
+        fNode.setSynthetic(false);
     }
 
     private static void revertVisibility(MethodNode mNode) {
diff --git a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java
index 98d2fd6046..d9813922cd 100644
--- a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java
+++ b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java
@@ -18,6 +18,7 @@
  */
 package org.codehaus.groovy.ant;
 
+import org.apache.groovy.util.SystemUtil;
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.DirectoryScanner;
 import org.apache.tools.ant.Task;
@@ -450,6 +451,8 @@ public class Groovydoc extends Task {
         properties.setProperty("fileEncoding", fileEncoding != null ? fileEncoding : "");
         properties.setProperty("timestamp", Boolean.valueOf(!noTimestamp).toString());
         properties.setProperty("versionStamp", Boolean.valueOf(!noVersionStamp).toString());
+        String phaseOverride = SystemUtil.getSystemPropertySafe("groovydoc.phase.override");
+        if (phaseOverride != null) properties.put("phaseOverride", phaseOverride);
 
         if (sourcePath != null) {
             sourceDirs.addExisting(sourcePath);
diff --git a/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy b/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy
index 5a5bcb88bc..d0079b1a99 100644
--- a/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy
+++ b/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy
@@ -20,6 +20,7 @@ package org.codehaus.groovy.tools.groovydoc
 
 import groovy.cli.internal.CliBuilderInternal
 import groovy.io.FileType
+import org.apache.groovy.util.SystemUtil
 import org.codehaus.groovy.tools.groovydoc.gstringTemplates.GroovyDocTemplateInfo
 import org.codehaus.groovy.tools.shell.IO
 import org.codehaus.groovy.tools.shell.util.Logger
@@ -200,6 +201,9 @@ class Main {
         properties.put("timestamp", (!noTimestamp).toString())
         properties.put("versionStamp", (!noVersionStamp).toString())
         properties.put("overviewFile", overviewFile?.absolutePath ?: "")
+        String phaseOverride = SystemUtil.getSystemPropertySafe("groovydoc.phase.override")
+        if (phaseOverride) properties.put("phaseOverride", phaseOverride)
+
 
         def links = new ArrayList<LinkArgument>();
         collectSourceFileNames(remainingArgs, sourcepath, exclusions)
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/apache/groovy/antlr/GroovydocVisitor.java b/subprojects/groovy-groovydoc/src/main/java/org/apache/groovy/antlr/GroovydocVisitor.java
index 02adcdb3b5..f5f0882064 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/apache/groovy/antlr/GroovydocVisitor.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/apache/groovy/antlr/GroovydocVisitor.java
@@ -36,6 +36,7 @@ import org.codehaus.groovy.ast.expr.VariableExpression;
 import org.codehaus.groovy.control.ResolveVisitor;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.groovydoc.GroovyClassDoc;
+import org.codehaus.groovy.groovydoc.GroovyFieldDoc;
 import org.codehaus.groovy.groovydoc.GroovyMethodDoc;
 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 import org.codehaus.groovy.tools.groovydoc.LinkArgument;
@@ -116,6 +117,8 @@ public class GroovydocVisitor extends ClassCodeVisitorSupport {
         currentClassDoc = new SimpleGroovyClassDoc(withDefaultImports(imports), aliases, name, links);
         if (node.isEnum()) {
             currentClassDoc.setTokenType(SimpleGroovyDoc.ENUM_DEF);
+        } else if (node.isRecord()) {
+            currentClassDoc.setTokenType(SimpleGroovyDoc.RECORD_DEF);
         } else if (node.isAnnotationDefinition()) {
             currentClassDoc.setTokenType(SimpleGroovyDoc.ANNOTATION_DEF);
         } else if (isTrait(node)) {
@@ -203,6 +206,7 @@ public class GroovydocVisitor extends ClassCodeVisitorSupport {
 
     @Override
     public void visitConstructor(ConstructorNode node) {
+        if (node.isSynthetic()) return;
         SimpleGroovyConstructorDoc cons = new SimpleGroovyConstructorDoc(currentClassDoc.simpleTypeName(), currentClassDoc);
         setConstructorOrMethodCommon(node, cons);
         currentClassDoc.add(cons);
@@ -213,6 +217,7 @@ public class GroovydocVisitor extends ClassCodeVisitorSupport {
     public void visitMethod(MethodNode node) {
         if (currentClassDoc.isEnum() && "$INIT".equals(node.getName()))
             return;
+        if (node.isSynthetic()) return;
         if ("false".equals(properties.getProperty("includeMainForScripts", "true"))
                 && currentClassDoc.isScript() && "main".equals(node.getName()) && node.isStatic() && node.getParameters().length == 1)
             return;
@@ -251,6 +256,9 @@ public class GroovydocVisitor extends ClassCodeVisitorSupport {
             return;
         }
 
+        for (GroovyFieldDoc field : currentClassDoc.properties()) {
+            if (propName.equals(field.name())) return;
+        }
         SimpleGroovyClassDoc classDoc = currentClassDoc;
         // TODO: not sure why but groovy.ui.view.BasicContentPane#buildOutputArea classDoc is null
         if (classDoc == null) {
@@ -347,21 +355,22 @@ public class GroovydocVisitor extends ClassCodeVisitorSupport {
         } else {
             if (Modifier.isPublic(mods)) {
                 element.setPublic(true);
-            }
-            if (Modifier.isProtected(mods)) {
+            } else if (Modifier.isProtected(mods)) {
                 element.setProtected(true);
-            }
-            if (Modifier.isPrivate(mods)) {
+            } else if (Modifier.isPrivate(mods)) {
                 element.setPrivate(true);
+            } else {
+                element.setPackagePrivate(true);
             }
-            if (Modifier.isFinal(mods)) {
-                element.setFinal(true);
-            }
+        }
+        if (Modifier.isFinal(mods)) {
+            element.setFinal(true);
         }
     }
 
     @Override
     public void visitField(FieldNode node) {
+        if (node.isSynthetic()) return;
         String name = node.getName();
         SimpleGroovyFieldDoc fieldDoc = new SimpleGroovyFieldDoc(name, currentClassDoc);
         fieldDoc.setType(new SimpleGroovyType(makeType(node.getType())));
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/groovydoc/GroovyDoc.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/groovydoc/GroovyDoc.java
index fedfc83d6e..508599897f 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/groovydoc/GroovyDoc.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/groovydoc/GroovyDoc.java
@@ -36,6 +36,8 @@ public interface GroovyDoc extends Comparable<GroovyDoc> {
 
     boolean isEnum();
 
+    boolean isRecord();
+
     boolean isEnumConstant();
 
     boolean isError();
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/ArrayClassDocWrapper.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/ArrayClassDocWrapper.java
index c443c630fb..5b357ea77b 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/ArrayClassDocWrapper.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/ArrayClassDocWrapper.java
@@ -304,6 +304,11 @@ public class ArrayClassDocWrapper implements GroovyClassDoc {
         return delegate.isError();
     }
 
+    @Override
+    public boolean isRecord() {
+        return delegate.isRecord();
+    }
+
     @Override
     public boolean isException() {
         return delegate.isException();
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/ExternalGroovyClassDoc.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/ExternalGroovyClassDoc.java
index f5c6ac8ad6..d212865730 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/ExternalGroovyClassDoc.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/ExternalGroovyClassDoc.java
@@ -324,6 +324,11 @@ public class ExternalGroovyClassDoc implements GroovyClassDoc {
         return false;
     }
 
+    @Override
+    public boolean isRecord() {
+        return false;
+    }
+
     @Override
     public boolean isEnumConstant() {
         return false;
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java
index 52ce41504c..9fb9023772 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java
@@ -66,7 +66,7 @@ public class GroovyRootDocBuilder {
     public void buildTree(List<String> filenames) throws IOException {
         setOverview();
 
-        List<File> sourcepathFiles = new ArrayList<File>();
+        List<File> sourcepathFiles = new ArrayList<>();
         if (sourcepaths != null) {
             for (String sourcepath : sourcepaths) {
                 sourcepathFiles.add(new File(sourcepath).getAbsoluteFile());
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyAnnotationRef.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyAnnotationRef.java
index 885498132e..3b5e120207 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyAnnotationRef.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyAnnotationRef.java
@@ -21,40 +21,32 @@ package org.codehaus.groovy.tools.groovydoc;
 import org.codehaus.groovy.groovydoc.GroovyAnnotationRef;
 import org.codehaus.groovy.groovydoc.GroovyClassDoc;
 import org.codehaus.groovy.runtime.StringGroovyMethods;
-
 public class SimpleGroovyAnnotationRef implements GroovyAnnotationRef {
     private GroovyClassDoc type;
     private final String desc;
     private String name;
-
     public SimpleGroovyAnnotationRef(String name, String desc) {
         this.name = name;
         final String params = StringGroovyMethods.minus(desc, "@" + name);
         this.desc = "()".equals(params) ? "" : params;
     }
-
     public void setType(GroovyClassDoc type) {
         this.type = type;
     }
-
     @Override
     public GroovyClassDoc type() {
         return type;
     }
-
     public boolean isTypeAvailable() {
-        return !(type == null);
+        return type != null;
     }
-
     @Override
     public String name() {
         return name;
     }
-
     public void setName(String name) {
         this.name = name;
     }
-
     @Override
     public String description() {
         return desc;
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyDoc.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyDoc.java
index 7031daba8f..58b79be0a8 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyDoc.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/SimpleGroovyDoc.java
@@ -33,6 +33,8 @@ public class SimpleGroovyDoc implements GroovyDoc/*, GroovyTokenTypes*/ {
     public static final int CLASS_DEF = 13;
     public static final int TRAIT_DEF = 15;
     public static final int INTERFACE_DEF = 14;
+
+    public static final int RECORD_DEF = 16;
     public static final int ANNOTATION_DEF = 64;
     public static final int ENUM_DEF = 61;
     private static final Pattern TAG2_PATTERN = Pattern.compile("(?s)([a-z]+)\\s+(.*)");
@@ -174,8 +176,14 @@ public class SimpleGroovyDoc implements GroovyDoc/*, GroovyTokenTypes*/ {
         return definitionType == ENUM_DEF;
     }
 
+    @Override
+    public boolean isRecord() {
+        return definitionType == RECORD_DEF;
+    }
+
     public String getTypeDescription() {
         if (isInterface()) return "Interface";
+        if (isRecord()) return "Record";
         if (isTrait()) return "Trait";
         if (isAnnotationType()) return "Annotation Type";
         if (isEnum()) return "Enum";
@@ -185,6 +193,7 @@ public class SimpleGroovyDoc implements GroovyDoc/*, GroovyTokenTypes*/ {
 
     public String getTypeSourceDescription() {
         if (isInterface()) return "interface";
+        if (isRecord()) return "record";
         if (isTrait()) return "trait";
         if (isAnnotationType()) return "@interface";
         if (isEnum()) return "enum";
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovyDocParser.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovyDocParser.java
index c2ed129406..423e76191e 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovyDocParser.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/antlr4/GroovyDocParser.java
@@ -83,10 +83,29 @@ public class GroovyDocParser implements GroovyDocParserI {
         CompilationUnit compUnit = new CompilationUnit(config);
         SourceUnit unit = new SourceUnit(file, src, config, null, new ErrorCollector(config));
         compUnit.addSource(unit);
-        compUnit.compile(Phases.CONVERSION);
+        int phase = Phases.CLASS_GENERATION;
+        if (properties.containsKey("phaseOverride")) {
+            String raw = properties.getProperty("phaseOverride");
+            try {
+                phase = Integer.parseInt(raw);
+            } catch(NumberFormatException ignore) {
+                raw = raw.toUpperCase();
+                switch(raw) {
+                    // some dup here but kept simple since we may swap Phases to an enum
+                    case "CONVERSION": phase = 3; break;
+                    case "SEMANTIC_ANALYSIS": phase = 4; break;
+                    case "CANONICALIZATION": phase = 5; break;
+                    case "INSTRUCTION_SELECTION": phase = 6; break;
+                    case "CLASS_GENERATION": phase = 7; break;
+                    default:
+                        System.err.println("Ignoring unrecognised or unsuitable phase and keeping default");
+                }
+            }
+        }
+        compUnit.compile(phase);
         ModuleNode root = unit.getAST();
         GroovydocVisitor visitor = new GroovydocVisitor(unit, packagePath, links, properties);
-        visitor.visitClass(root.getClasses().get(0));
+        root.getClasses().stream().forEach(clazz -> visitor.visitClass(clazz));
         return visitor.getGroovyClassDocs();
     }
 
diff --git a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html
index 9dd33bc3e2..8328fb4292 100644
--- a/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html
+++ b/subprojects/groovy-groovydoc/src/main/resources/org/codehaus/groovy/tools/groovydoc/gstringTemplates/classLevel/classDocName.html
@@ -79,7 +79,7 @@
     ]
     def isRequired = { f, v -> def req = f.constantValueExpression() == null; req.toString() == v }
     def upcase = { n -> n[0].toUpperCase() + n[1..-1] }
-    def paramsOf = { n, boolean brief -> n.parameters().collect{ param -> (brief?'':annotations(param, ' ')) + linkable(param.isTypeAvailable()?param.type():param.typeName()) + (param.vararg()?'... ':' ') + param.name() + (param.defaultValue() ? " = " + param.defaultValue():"") }.join(", ") }
+    def paramsOf = { n, boolean brief -> n.parameters().collect{ param -> (brief?'':annotations(param, '<br>')) + linkable(param.isTypeAvailable()?param.type():param.typeName()) + (param.vararg()?'... ':' ') + param.name() + (param.defaultValue() ? " = " + param.defaultValue():"") }.join(", ") }
     def rawType = { n -> def g = n.indexOf("<"); g >= 0 ? n.substring(0, g) : n }
     def nameFromParams = { n -> n.name() + '(' + n.parameters().collect{ param -> rawType(param.isTypeAvailable()?param.type().qualifiedTypeName():param.typeName()) }.join(', ') + ')' }
     def nameFromJavaParams = { n -> n.getName() + '(' + n.parameterTypes.collect{ param -> param.getName() }.join(', ') + ')' }
@@ -585,7 +585,7 @@ if (classDoc.isInterface() && classDoc.interfaces()) {
                         <a name="${field.name()}"><!-- --></a>
                         <ul class="blockListLast">
                             <li class="blockList">
-                                <h4>${annotations(field, '\n') + modifiersWithOptions(field, false, false) + linkable(field.type())} <strong>${field.name()}</strong></h4>
+                                <h4>${annotations(field, '<br>') + modifiersWithOptions(field, false, false) + linkable(field.type())} <strong>${field.name()}</strong></h4>
                                 <p>${field.commentText()}</p>
                             </li>
                         </ul>
@@ -605,7 +605,7 @@ if (classDoc.isInterface() && classDoc.interfaces()) {
                         <a name="${prop.name()}"><!-- --></a>
                         <ul class="blockListLast">
                             <li class="blockList">
-                                <h4>${annotations(prop, '\n') + modifiers(prop) + linkable(prop.type())} <strong>${prop.name()}</strong></h4>
+                                <h4>${annotations(prop, '<br>') + modifiers(prop) + linkable(prop.type())} <strong>${prop.name()}</strong></h4>
                                 <p>${prop.commentText()}</p>
                             </li>
                         </ul>
@@ -645,7 +645,7 @@ if (classDoc.isInterface() && classDoc.interfaces()) {
                         <a name="${nameFromParams(constructor)}"><!-- --></a>
                         <ul class="blockListLast">
                             <li class="blockList">
-                                <h4>${annotations(constructor, '\n') + modifiers(constructor)}<strong>${constructor.name()}</strong>(${paramsOf(constructor, false)})</h4>
+                                <h4>${annotations(constructor, '<br>') + modifiers(constructor)}<strong>${constructor.name()}</strong>(${paramsOf(constructor, false)})</h4>
                                 <p>${constructor.commentText()}</p>
                             </li>
                         </ul>
@@ -667,7 +667,7 @@ if (classDoc.isInterface() && classDoc.interfaces()) {
                         <ul class="blockListLast">
                             <li class="blockList">
                                 <h4>${method.typeParameters() ?
-                                    org.codehaus.groovy.tools.groovydoc.SimpleGroovyClassDoc.encodeAngleBrackets(method.typeParameters()) + ' ' : ''}${annotations(method, '\n') + modifiers(method)}${linkable(method.returnType())} <strong>${method.name()}</strong>(${paramsOf(method, false)})</h4>
+                                    org.codehaus.groovy.tools.groovydoc.SimpleGroovyClassDoc.encodeAngleBrackets(method.typeParameters()) + ' ' : ''}${annotations(method, '<br>') + modifiers(method)}${linkable(method.returnType())} <strong>${method.name()}</strong>(${paramsOf(method, false)})</h4>
                                 <p>${method.commentText()}</p>
                             </li>
                         </ul>
diff --git a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
index 5586f21bb7..b563df8553 100644
--- a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
+++ b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
@@ -32,6 +32,7 @@ import java.util.List;
 import java.util.Properties;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 public class GroovyDocToolTest extends GroovyTestCase {
     private static final String MOCK_DIR = "mock/doc";
@@ -45,25 +46,8 @@ public class GroovyDocToolTest extends GroovyTestCase {
     public void setUp() {
         plainTool = new GroovyDocTool(new String[]{"src/test/groovy"});
 
-        xmlTool = new GroovyDocTool(
-                new FileSystemResourceManager("src"), // template storage
-                new String[] {"src/main/java", "../../src/main/java", "src/test/groovy"}, // source file dirs
-                new String[]{TEMPLATES_DIR + "/topLevel/rootDocStructuredData.xml"},
-                new String[]{TEMPLATES_DIR + "/packageLevel/packageDocStructuredData.xml"},
-                new String[]{TEMPLATES_DIR + "/classLevel/classDocStructuredData.xml"},
-                new ArrayList<LinkArgument>(),
-                new Properties()
-        );
-
-        xmlToolForTests = new GroovyDocTool(
-                new FileSystemResourceManager("src"), // template storage
-                new String[] {"src/test/groovy", "src/test/resources", "../../src/test"}, // source file dirs
-                new String[]{TEMPLATES_DIR + "/topLevel/rootDocStructuredData.xml"},
-                new String[]{TEMPLATES_DIR + "/packageLevel/packageDocStructuredData.xml"},
-                new String[]{TEMPLATES_DIR + "/classLevel/classDocStructuredData.xml"},
-                new ArrayList<LinkArgument>(),
-                new Properties()
-        );
+        xmlTool = makeXmlTool(new ArrayList<LinkArgument>(), new Properties());
+        xmlToolForTests = makeXmlTool(new ArrayList<LinkArgument>(), new Properties(), new String[] {"src/test/groovy", "src/test/resources", "../../src/test"});
 
         ArrayList<LinkArgument> links = new ArrayList<LinkArgument>();
         LinkArgument link = new LinkArgument();
@@ -74,6 +58,22 @@ public class GroovyDocToolTest extends GroovyTestCase {
         htmlTool = makeHtmltool(links, new Properties());
     }
 
+    private GroovyDocTool makeXmlTool(ArrayList<LinkArgument> links, Properties props) {
+        return makeXmlTool(links, props, new String[] {"src/main/java", "../../src/main/java", "src/test/groovy"});
+    }
+
+    private GroovyDocTool makeXmlTool(ArrayList<LinkArgument> links, Properties props, String[] sources) {
+        return new GroovyDocTool(
+                new FileSystemResourceManager("src"), // template storage
+                sources, // source file dirs
+                new String[]{TEMPLATES_DIR + "/topLevel/rootDocStructuredData.xml"},
+                new String[]{TEMPLATES_DIR + "/packageLevel/packageDocStructuredData.xml"},
+                new String[]{TEMPLATES_DIR + "/classLevel/classDocStructuredData.xml"},
+                links,
+                props
+        );
+    }
+
     private GroovyDocTool makeHtmltool(ArrayList<LinkArgument> links, Properties props) {
         return new GroovyDocTool(
                 new FileSystemResourceManager("src/main/resources"), // template storage
@@ -610,14 +610,12 @@ public class GroovyDocToolTest extends GroovyTestCase {
         GroovyClassDoc classDocDescendantE = getGroovyClassDocByName(root, "DescendantE");
         assertNotNull("Expecting to find DescendantE", classDocDescendantE);
         GroovyClassDoc base = root.classNamed(classDocDescendantE, "Base");
-        // TODO reinstate next two lines or justify why they should be removed
-//        assertNotNull("Expecting to find Base in: " + Arrays.stream(root.classes()).map(GroovyClassDoc::getFullPathName).collect(Collectors.joining(", ")), base);
-//        assertEquals(fullPathBaseC, base.getFullPathName());
+        assertNotNull("Expecting to find Base in: " + Arrays.stream(root.classes()).map(GroovyClassDoc::getFullPathName).collect(Collectors.joining(", ")), base);
+        assertEquals(fullPathBaseC, base.getFullPathName());
 
         GroovyClassDoc classDocDescendantF = getGroovyClassDocByName(root, "DescendantF");
-        assertNotNull("Expecting to find DescendantF", classDocDescendantF);
-        // TODO reinstate next line or justify why it should be removed
-//        assertEquals(fullPathBaseC, root.classNamed(classDocDescendantF, "Base").getFullPathName());
+        assertNotNull("Expecting to find DescendantF in: " + Arrays.stream(root.classes()).map(GroovyClassDoc::getFullPathName).collect(Collectors.joining(", ")), classDocDescendantF);
+        assertEquals(fullPathBaseC, root.classNamed(classDocDescendantF, "Base").getFullPathName());
     }
 
     // GROOVY-5939
@@ -736,7 +734,7 @@ public class GroovyDocToolTest extends GroovyTestCase {
         // and that should link to api/Foo.html, not to lib/Foo.html.
         final Matcher interfacesAndTraits = Pattern.compile(
                 "<dt>All Implemented Interfaces and Traits:</dt>\\s*" +
-                "<dd><a href='[./]*/org/codehaus/groovy/tools/groovydoc/testfiles/alias/(api|lib)/Foo\\.html'>(Foo|FooImpl)</a></dd>"
+                "<dd><a href='[./]*/org/codehaus/groovy/tools/groovydoc/testfiles/alias/(api|lib)/Foo\\.html'>(Foo|FooImpl)</a>"
         ).matcher(fooAdapterDoc);
 
         // Constructor is actually "FooAdapter(FooImpl foo)",
@@ -985,22 +983,24 @@ public class GroovyDocToolTest extends GroovyTestCase {
             "<pre>" +
             "(public )?class (ClassWithMethodComment|DocumentedClass)\n" +
             "extends " + object +
+            "(\nimplements groovy.lang.GroovyObject)?" +
             "</pre>");
         final Pattern derivedClass = Pattern.compile(
             "<pre>" +
             "(public )?abstract class (Java|Groovy)ClassWithMultipleInterfaces\n" +
             "extends " + object + "\n" +
             "implements " + interfaces +
+            "(, groovy.lang.GroovyObject)?" +
             "</pre>");
 
         assertTrue("The Java base interface declaration header should match", baseInterface.matcher(javaBaseInterface).find());
         assertTrue("The Groovy base interface declaration header should match", baseInterface.matcher(groovyBaseInterface).find());
         assertTrue("The Java derived interface declaration header should match", derivedInterface.matcher(javaDerivedInterface).find());
         assertTrue("The Groovy derived interface declaration header should match", derivedInterface.matcher(groovyDerivedInterface).find());
-        assertTrue("The Java base class declaration header should match", baseClass.matcher(javaBaseClass).find());
-        assertTrue("The Groovy base class declaration header should match", baseClass.matcher(groovyBaseClass).find());
-        assertTrue("The Java derived class declaration header should match", derivedClass.matcher(javaDerivedClass).find());
-        assertTrue("The Groovy derived class declaration header should match", derivedClass.matcher(groovyDerivedClass).find());
+        assertTrue("The Java base class declaration header should match in:\n" + javaBaseClass, baseClass.matcher(javaBaseClass).find());
+        assertTrue("The Groovy base class declaration header should match in:\n" + groovyBaseClass, baseClass.matcher(groovyBaseClass).find());
+        assertTrue("The Java derived class declaration header should match in:\n" + javaDerivedClass, derivedClass.matcher(javaDerivedClass).find());
+        assertTrue("The Groovy derived class declaration header should match in:\n" + groovyDerivedClass, derivedClass.matcher(groovyDerivedClass).find());
     }
 
     public void testJavaGenericsTitle() throws Exception {
@@ -1033,12 +1033,12 @@ public class GroovyDocToolTest extends GroovyTestCase {
 
         final String groovydoc = output.getText(MOCK_DIR + "/" + base + "/Groovy.html");
 
-        final Matcher title = Pattern.compile(Pattern.quote(
-                "<h2 title=\"[Groovy] Trait Groovy&lt;N extends Number & Comparable&lt;? extends Number&gt;&gt;\" class=\"title\">"+
-                        "[Groovy] Trait Groovy&lt;N extends Number & Comparable&lt;? extends Number&gt;&gt;</h2>"
-        )).matcher(groovydoc);
+        final Matcher title = Pattern.compile(
+                "<h2 title=\"\\[Groovy] Trait Groovy&lt;N extends (java.lang.)?Number & (java.lang.)?Comparable&lt;\\? extends (java.lang.)?Number&gt;&gt;\" class=\"title\">"+
+                        "\\[Groovy] Trait Groovy&lt;N extends (java.lang.)?Number & (java.lang.)?Comparable&lt;\\? extends (java.lang.)?Number&gt;&gt;</h2>"
+        ).matcher(groovydoc);
 
-        assertTrue("The title should have the generics information", title.find());
+        assertTrue("The title should have the generics information in:\n" + groovydoc, title.find());
     }
 
     public void testParamTagForTypeParams() throws Exception {
@@ -1058,13 +1058,13 @@ public class GroovyDocToolTest extends GroovyTestCase {
                 "<DL><DT><B>Type Parameters:</B></DT><DD><code>N</code> -  Doc.</DD></DL>"
         );
         final Pattern methodTypeParams = Pattern.compile(
-                "<DL><DT><B>Type Parameters:</B></DT><DD><code>A</code> -  Doc.</DD><DD><code>B</code> -  Doc.</DD></DL>"
+                "<DL><DT><B>Type Parameters:</B></DT><DD><code>C</code> -  Doc.</DD><DD><code>D</code> -  Doc.</DD></DL>"
         );
 
-        assertTrue("The Java class doc should have type parameters definitions", classTypeParams.matcher(javadoc).find());
-        assertTrue("The Groovy class doc should have type parameters definitions", classTypeParams.matcher(groovydoc).find());
-        assertTrue("The Java method doc should have type parameters definitions", methodTypeParams.matcher(javadoc).find());
-        assertTrue("The Groovy method doc should have type parameters definitions", methodTypeParams.matcher(groovydoc).find());
+        assertTrue("The Java class doc should have type parameters definitions in:\n" + javadoc, classTypeParams.matcher(javadoc).find());
+        assertTrue("The Groovy class doc should have type parameters definitions in:\n" + groovydoc, classTypeParams.matcher(groovydoc).find());
+        assertTrue("The Java method doc should have type parameters definitions in:\n" + javadoc, methodTypeParams.matcher(javadoc).find());
+        assertTrue("The Groovy method doc should have type parameters definitions in:\n" + groovydoc, methodTypeParams.matcher(groovydoc).find());
     }
 
     public void testMethodTypeParams() throws Exception {
@@ -1081,16 +1081,16 @@ public class GroovyDocToolTest extends GroovyTestCase {
         final String groovydoc = output.getText(MOCK_DIR + "/" + base + "/Groovy.html");
 
         final Pattern methodSummaryTypeParams = Pattern.compile(
-                "<td class=\"colFirst\"><code>&lt;A, B&gt;</code></td>"
+                "<td class=\"colFirst\"><code>&lt;C, D&gt;</code></td>"
         );
         final Pattern methodDetailsTypeParams = Pattern.compile(
-                "<h4>&lt;A, B&gt; (public&nbsp;)?static&nbsp;int <strong>compare</strong>"
+                "<h4>&lt;C, D&gt; .*int <strong>compare</strong>\\("
         );
 
         assertTrue("The Java method summary should have type parameters", methodSummaryTypeParams.matcher(javadoc).find());
         assertTrue("The Groovy method summary should have type parameters", methodSummaryTypeParams.matcher(groovydoc).find());
         assertTrue("The Java method details should have type parameters", methodDetailsTypeParams.matcher(javadoc).find());
-        assertTrue("The Groovy method details should have type parameters", methodDetailsTypeParams.matcher(groovydoc).find());
+        assertTrue("The Groovy method details should have type parameters in:\n" + groovydoc, methodDetailsTypeParams.matcher(groovydoc).find());
     }
 
     public void testMethodParamTypeParams() throws Exception {
@@ -1106,31 +1106,31 @@ public class GroovyDocToolTest extends GroovyTestCase {
         final String javadoc = output.getText(MOCK_DIR + "/" + base + "/Java.html");
         final String groovydoc = output.getText(MOCK_DIR + "/" + base + "/Groovy.html");
 
-        final Pattern methodSummary = Pattern.compile(Pattern.quote(
-                "<code><strong><a href=\"#compare(Class, Class)\">compare</a></strong>"
-                        + "("
-                        + "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a>&lt;A&gt; a, "
-                        + "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a>&lt;B&gt; b"
-                        + ")"
+        final Pattern methodSummary = Pattern.compile(
+                "<code><strong><a href=\"#compare\\((java.lang.)?Class, (java.lang.)?Class\\)\">compare</a></strong>"
+                        + "\\("
+                        + "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a>&lt;C&gt; c, "
+                        + "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a>&lt;D&gt; d"
+                        + "\\)"
                         + "</code>"
-        ));
-        final Pattern methodDetailAnchor = Pattern.compile(Pattern.quote(
-                "<a name=\"compare(Class, Class)\"><!-- --></a>"
-        ));
+        );
+        final Pattern methodDetailAnchor = Pattern.compile(
+                "<a name=\"compare\\((java.lang.)?Class, (java.lang.)?Class\\)\"><!-- --></a>"
+        );
         final Pattern methodDetailTitle = Pattern.compile(Pattern.quote(
                 "<strong>compare</strong>" +
                         "(" +
-                        "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a>&lt;A&gt; a, " +
-                        "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a>&lt;B&gt; b" +
+                        "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a>&lt;C&gt; c, " +
+                        "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html' title='Class'>Class</a>&lt;D&gt; d" +
                         ")"
         ));
 
-        assertTrue("The Java method summary should include type parameters", methodSummary.matcher(javadoc).find());
-        assertTrue("The Java method detail anchor should NOT include type parameters", methodDetailAnchor.matcher(javadoc).find());
-        assertTrue("The Java method detail title should include type parameters", methodDetailTitle.matcher(javadoc).find());
-        assertTrue("The Groovy method summary should include type parameters", methodSummary.matcher(groovydoc).find());
-        assertTrue("The Groovy method detail anchor should NOT include type parameters", methodDetailAnchor.matcher(groovydoc).find());
-        assertTrue("The Groovy method detail title should include type parameters", methodDetailTitle.matcher(groovydoc).find());
+        assertTrue("The Java method summary should include type parameters in:\n" + javadoc, methodSummary.matcher(javadoc).find());
+        assertTrue("The Java method detail anchor should NOT include type parameters in:\n" + javadoc, methodDetailAnchor.matcher(javadoc).find());
+        assertTrue("The Java method detail title should include type parameters in:\n" + javadoc, methodDetailTitle.matcher(javadoc).find());
+        assertTrue("The Groovy method summary should include type parameters in:\n" + groovydoc, methodSummary.matcher(groovydoc).find());
+        assertTrue("The Groovy method detail anchor should NOT include type parameters in:\n" + groovydoc, methodDetailAnchor.matcher(groovydoc).find());
+        assertTrue("The Groovy method detail title should include type parameters in:\n" + groovydoc, methodDetailTitle.matcher(groovydoc).find());
     }
 
     public void testAnnotations() throws Exception {
@@ -1146,62 +1146,75 @@ public class GroovyDocToolTest extends GroovyTestCase {
         final String groovydoc = output.getText(MOCK_DIR + "/" + base + "/Groovy.html");
         final String javadoc = output.getText(MOCK_DIR + "/" + base + "/Java.html");
 
-        assertTrue("The Groovy class declaration header should have the annotation", Pattern.compile(Pattern.quote(
+        assertTrue("The Groovy class declaration header should have the annotation in:\n" + groovydoc, Pattern.compile(Pattern.quote(
                 "<pre>@groovy.transform.EqualsAndHashCode(cache: true)\n" +
                         "class Groovy"
         )).matcher(groovydoc).find());
 
-        assertTrue("The Java class declaration header should have the annotation", Pattern.compile(Pattern.quote(
+        assertTrue("The Java class declaration header should have the annotation in:\n" + javadoc, Pattern.compile(Pattern.quote(
                 "<pre>@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Deprecated.html' title='Deprecated'>Deprecated</a>\n" +
                         "@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/SuppressWarnings.html' title='SuppressWarnings'>SuppressWarnings</a>(\"foo\")\n" +
                         "public class Java"
         )).matcher(javadoc).find());
 
-        assertTrue("The Groovy field details should have the annotation", Pattern.compile(Pattern.quote(
-                "<h4>@groovy.transform.Internal\n" +
+        assertTrue("The Groovy field details should have the annotation in:" + groovydoc, Pattern.compile(Pattern.quote(
+                "<h4>@groovy.transform.Internal<br>" +
                         "public&nbsp;<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/String.html' title='String'>String</a> " +
                         "<strong>annotatedField</strong></h4>"
         )).matcher(groovydoc).find());
 
         assertTrue("The Java field details should have the annotation", Pattern.compile(Pattern.quote(
-                "<h4>@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Deprecated.html' title='Deprecated'>Deprecated</a>\n" +
+                "<h4>@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Deprecated.html' title='Deprecated'>Deprecated</a><br>" +
                         "public&nbsp;<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/String.html' title='String'>String</a> " +
                         "<strong>annotatedField</strong></h4>"
         )).matcher(javadoc).find());
 
         assertTrue("The Groovy property details should have the annotation", Pattern.compile(Pattern.quote(
-            "<h4>@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Deprecated.html' title='Deprecated'>Deprecated</a>\n" +
+            "<h4>@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Deprecated.html' title='Deprecated'>Deprecated</a><br>" +
                 "<a href='https://docs.oracle.com/javase/8/docs/api/java/util/List.html' title='List'>List</a> <strong>annotatedProperty</strong></h4>"
         )).matcher(groovydoc).find());
 
         // Java doesn't have properties section
 
-        assertTrue("The Groovy ctor details should have the annotation", Pattern.compile(Pattern.quote(
-                "<h4>@groovy.transform.NamedVariant\n" +
+        assertTrue("The Groovy ctor details should have the annotation in:\n" + groovydoc, Pattern.compile(Pattern.quote(
+                "<h4>@groovy.transform.NamedVariant<br>" +
                         "<strong>Groovy</strong>(" +
-                        "@groovy.transform.NamedParam " +
                         "<a href='https://docs.oracle.com/javase/8/docs/api/java/util/List.html' title='List'>List</a> ctorParam" +
                         ")</h4>"
         )).matcher(groovydoc).find());
+        assertTrue("The Groovy ctor details should have the annotation in:\n" + groovydoc, Pattern.compile(Pattern.quote(
+                "<h4>@groovy.transform.Generated<br>" +
+                        "<strong>Groovy</strong>(" +
+                        "@groovy.transform.NamedParam(value: ctorParam, type: java.util.List)<br>" +
+                        "<a href='https://docs.oracle.com/javase/8/docs/api/java/util/Map.html' title='Map'>Map</a> namedArgs" +
+                        ")</h4>"
+        )).matcher(groovydoc).find());
 
         assertTrue("The Java ctor details should have the annotation", Pattern.compile(Pattern.quote(
-                "<h4>@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Deprecated.html' title='Deprecated'>Deprecated</a>\n" +
+                "<h4>@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Deprecated.html' title='Deprecated'>Deprecated</a><br>" +
                         "public&nbsp;<strong>Java</strong>()</h4>"
         )).matcher(javadoc).find());
 
         // Note also the param annotation
-        assertTrue("The Groovy method details should have the annotations", Pattern.compile(Pattern.quote(
-                "<h4>@groovy.transform.NamedVariant\n" +
+        assertTrue("The Groovy method details should have the annotations in:\n" + groovydoc, Pattern.compile(Pattern.quote(
+                "<h4>@groovy.transform.NamedVariant<br>" +
                         "void <strong>annotatedMethod</strong>(" +
-                        "@groovy.transform.NamedParam(required: true) " +
                         "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/String.html' title='String'>String</a> methodParam" +
                         ")</h4>"
         )).matcher(groovydoc).find());
+        // Note also the param annotation
+        assertTrue("The Groovy method details should have the annotations in:\n" + groovydoc, Pattern.compile(Pattern.quote(
+                "<h4>@groovy.transform.Generated<br>" +
+                        "void <strong>annotatedMethod</strong>(" +
+                        "@groovy.transform.NamedParam(required: true, value: methodParam, type: java.lang.String)<br>" +
+                        "<a href='https://docs.oracle.com/javase/8/docs/api/java/util/Map.html' title='Map'>Map</a> namedArgs" +
+                        ")</h4>"
+        )).matcher(groovydoc).find());
 
-        assertTrue("The Java method details should have the annotations", Pattern.compile(Pattern.quote(
-                "<h4>@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Deprecated.html' title='Deprecated'>Deprecated</a>\n" +
+        assertTrue("The Java method details should have the annotations in:\n" + javadoc, Pattern.compile(Pattern.quote(
+                "<h4>@<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/Deprecated.html' title='Deprecated'>Deprecated</a><br>" +
                         "public&nbsp;void <strong>annotatedMethod</strong>(" +
-                        "@CommandLine.Parameters(hidden = true) " +
+                        "@CommandLine.Parameters(hidden = true)<br>" +
                         "<a href='https://docs.oracle.com/javase/8/docs/api/java/lang/String.html' title='String'>String</a> annotatedParam" +
                         ")</h4>"
         )).matcher(javadoc).find());
@@ -1337,8 +1350,11 @@ public class GroovyDocToolTest extends GroovyTestCase {
     public void testScript() throws Exception {
         List<String> srcList = new ArrayList<String>();
         srcList.add("org/codehaus/groovy/tools/groovydoc/testfiles/Script.groovy");
-        xmlTool.add(srcList);
 
+        Properties props = new Properties();
+        props.put("packageScope", "true");
+        xmlTool = makeXmlTool(new ArrayList<LinkArgument>(), props);
+        xmlTool.add(srcList);
         MockOutputTool output = new MockOutputTool();
         xmlTool.renderToOutput(output, MOCK_DIR);
         String scriptDoc = output.getText(MOCK_DIR + "/org/codehaus/groovy/tools/groovydoc/testfiles/Script.html");
@@ -1349,7 +1365,7 @@ public class GroovyDocToolTest extends GroovyTestCase {
         assertTrue("There should be a reference to method sayGoodbye", containsTagWithName(scriptDoc, "method", "sayGoodbye"));
         assertTrue("Expecting bid farewell in:\n" + scriptDoc, scriptDoc.contains("Use this to bid farewell"));
 
-        assertTrue("There should be a reference to property instanceProp in:\n" + scriptDoc, containsTagWithName(scriptDoc, "property", "instanceProp"));
+        assertTrue("There should be a reference to property instanceProp in:\n" + scriptDoc, containsTagWithName(scriptDoc, "field", "instanceProp"));
 
         assertTrue("There should be a reference to field staticField", containsTagWithName(scriptDoc, "field", "staticField"));
 
diff --git a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Groovy.groovy b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Groovy.groovy
index 3f781ec264..c19bb5ffd5 100644
--- a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Groovy.groovy
+++ b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Groovy.groovy
@@ -26,7 +26,7 @@ package org.codehaus.groovy.tools.groovydoc.testfiles.generics
 trait Groovy<N extends Number & Comparable<? extends Number>> {
 
     /**
-     * Generic method.
+     * Generic static method.
      *
      * @param <A> Doc.
      * @param <B> Doc.
@@ -34,7 +34,20 @@ trait Groovy<N extends Number & Comparable<? extends Number>> {
      * @param b Doc.
      * @return Doc.
      */
-    static <A, B> int compare(Class<A> a, Class<B> b) {
+    static <A, B> int compareS(Class<A> a, Class<B> b) {
+        0
+    }
+
+    /**
+     * Generic instance method.
+     *
+     * @param <C> Doc.
+     * @param <D> Doc.
+     * @param c Doc.
+     * @param d Doc.
+     * @return Doc.
+     */
+    <C, D> int compare(Class<C> c, Class<D> d) {
         0
     }
 }
diff --git a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Java.java b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Java.java
index d31944f6da..7368723c7f 100644
--- a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Java.java
+++ b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/testfiles/generics/Java.java
@@ -25,7 +25,7 @@ package org.codehaus.groovy.tools.groovydoc.testfiles.generics;
  */
 public abstract class Java<N extends Number & Comparable<? extends Number>> {
     /**
-     * Generic method.
+     * Generic static method.
      *
      * @param <A> Doc.
      * @param <B> Doc.
@@ -33,7 +33,21 @@ public abstract class Java<N extends Number & Comparable<? extends Number>> {
      * @param b Doc.
      * @return Doc.
      */
-    public static <A, B> int compare(Class<A> a, Class<B> b) {
+    public static <A, B> int compareS(Class<A> a, Class<B> b) {
         return 0;
     }
+
+    /**
+     * Generic instance method.
+     *
+     * @param <C> Doc.
+     * @param <D> Doc.
+     * @param c Doc.
+     * @param d Doc.
+     * @return Doc.
+     */
+    public <C, D> int compare(Class<C> c, Class<D> d) {
+        return 0;
+    }
+
 }