changeset 57745:4bb6c4bf72bf

8224052: Javadoc doesn't handle non-public intermediate types well Reviewed-by: jjg
author hannesw
date Thu, 16 Jan 2020 15:50:23 +0100
parents 5bb84e036c14
children 4a0a1b927608
files src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeFieldWriterImpl.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeRequiredMemberWriterImpl.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriterImpl.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriterImpl.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriterImpl.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ReturnTaglet.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletWriter.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ThrowsTaglet.java src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java test/langtools/jdk/javadoc/doclet/testMemberInheritance/TestMemberInheritance.java test/langtools/jdk/javadoc/doclet/testMemberInheritance/pkg2/DocumentedNonGenericChild.java test/langtools/jdk/javadoc/doclet/testMemberInheritance/pkg2/UndocumentedGenericParent.java test/langtools/jdk/javadoc/doclet/testMemberInheritance/pkg3/PrivateGenericParent.java
diffstat 17 files changed, 244 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java	Thu Jan 16 15:50:23 2020 +0100
@@ -35,6 +35,7 @@
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.ArrayType;
 import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.type.TypeVariable;
 import javax.lang.model.util.SimpleTypeVisitor9;
@@ -141,10 +142,10 @@
      * @param isVarArg true if this is a link to var arg.
      * @param tree the content tree to which the parameter information will be added.
      */
-    protected void addParam(ExecutableElement member, VariableElement param,
+    protected void addParam(ExecutableElement member, VariableElement param, TypeMirror paramType,
             boolean isVarArg, Content tree) {
         Content link = writer.getLink(new LinkInfoImpl(configuration, EXECUTABLE_MEMBER_PARAM,
-                param.asType()).varargs(isVarArg));
+                paramType).varargs(isVarArg));
         tree.add(link);
         if(name(param).length() > 0) {
             tree.add(Entity.NO_BREAK_SPACE);
@@ -208,9 +209,11 @@
             sep = "," + DocletConstants.NL;
         }
         int paramstart;
+        ExecutableType instMeth = utils.asInstantiatedMethodType(typeElement, member);
         for (paramstart = 0; paramstart < parameters.size(); paramstart++) {
             paramTree.add(sep);
             VariableElement param = parameters.get(paramstart);
+            TypeMirror paramType = instMeth.getParameterTypes().get(paramstart);
 
             if (param.getKind() != ElementKind.INSTANCE_INIT) {
                 if (includeAnnotations) {
@@ -220,7 +223,7 @@
                         paramTree.add(DocletConstants.NL);
                     }
                 }
-                addParam(member, param,
+                addParam(member, param, paramType,
                     (paramstart == parameters.size() - 1) && member.isVarArgs(), paramTree);
                 break;
             }
@@ -237,7 +240,8 @@
                     paramTree.add(DocletConstants.NL);
                 }
             }
-            addParam(member, parameters.get(i), (i == parameters.size() - 1) && member.isVarArgs(),
+            addParam(member, parameters.get(i), instMeth.getParameterTypes().get(i),
+                    (i == parameters.size() - 1) && member.isVarArgs(),
                     paramTree);
         }
 
@@ -251,7 +255,7 @@
      * @return the content tree containing the exceptions information.
      */
     protected Content getExceptions(ExecutableElement member) {
-        List<? extends TypeMirror> exceptions = member.getThrownTypes();
+        List<? extends TypeMirror> exceptions = utils.asInstantiatedMethodType(typeElement, member).getThrownTypes();
         Content htmltree = new ContentBuilder();
         if (!exceptions.isEmpty()) {
             Content link = writer.getLink(new LinkInfoImpl(configuration, MEMBER, exceptions.get(0)));
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeFieldWriterImpl.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeFieldWriterImpl.java	Thu Jan 16 15:50:23 2020 +0100
@@ -264,7 +264,7 @@
         if (utils.isConstructor(member))
             return null;
         if (utils.isExecutableElement(member))
-            return utils.getReturnType((ExecutableElement)member);
+            return utils.getReturnType(typeElement, (ExecutableElement)member);
         return member.asType();
     }
 }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeRequiredMemberWriterImpl.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeRequiredMemberWriterImpl.java	Thu Jan 16 15:50:23 2020 +0100
@@ -266,7 +266,7 @@
 
     private TypeMirror getType(Element member) {
         return utils.isExecutableElement(member)
-                ? utils.getReturnType((ExecutableElement) member)
+                ? utils.getReturnType(typeElement, (ExecutableElement) member)
                 : member.asType();
     }
 }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java	Thu Jan 16 15:50:23 2020 +0100
@@ -207,6 +207,14 @@
     /**
      * {@inheritDoc}
      */
+    @Override
+    protected TypeElement getCurrentPageElement() {
+        return typeElement;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     @Override @SuppressWarnings("preview")
     public void addClassSignature(String modifiers, Content classInfoTree) {
         Content hr = new HtmlTree(HtmlTag.HR);
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriterImpl.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FieldWriterImpl.java	Thu Jan 16 15:50:23 2020 +0100
@@ -115,7 +115,7 @@
     @Override
     public Content getSignature(VariableElement field) {
         return new MemberSignature(field)
-                .addType(field.asType())
+                .addType(utils.asInstantiatedFieldType(typeElement, field))
                 .toContent();
     }
 
@@ -262,7 +262,7 @@
      */
     @Override
     protected void addSummaryType(Element member, Content tdSummaryType) {
-        addModifierAndType(member, member.asType(), tdSummaryType);
+        addModifierAndType(member, utils.asInstantiatedFieldType(typeElement, (VariableElement)member), tdSummaryType);
     }
 
     /**
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java	Thu Jan 16 15:50:23 2020 +0100
@@ -861,6 +861,15 @@
     }
 
     /**
+     * Return the main type element of the current page or null for pages that don't have one.
+     *
+     * @return the type element of the current page.
+     */
+    protected TypeElement getCurrentPageElement() {
+        return null;
+    }
+
+    /**
      * Add the class link, with only class name as the strong link and prefixing
      * plain package name.
      *
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriterImpl.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriterImpl.java	Thu Jan 16 15:50:23 2020 +0100
@@ -290,7 +290,7 @@
     @Override
     protected void addSummaryType(Element member, Content tdSummaryType) {
         ExecutableElement meth = (ExecutableElement)member;
-        addModifierAndType(meth, utils.getReturnType(meth), tdSummaryType);
+        addModifierAndType(meth, utils.getReturnType(typeElement, meth), tdSummaryType);
     }
 
     /**
@@ -386,7 +386,7 @@
      * @return content containing the return type
      */
     protected Content getReturnType(ExecutableElement method) {
-        TypeMirror type = utils.getReturnType(method);
+        TypeMirror type = utils.getReturnType(typeElement, method);
         if (type != null) {
             return writer.getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.RETURN_TYPE, type));
         }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriterImpl.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PropertyWriterImpl.java	Thu Jan 16 15:50:23 2020 +0100
@@ -109,7 +109,7 @@
     @Override
     public Content getSignature(ExecutableElement property) {
         return new MemberSignature(property)
-                .addType(utils.getReturnType(property))
+                .addType(utils.getReturnType(typeElement, property))
                 .toContent();
     }
 
@@ -282,7 +282,7 @@
      */
     @Override
     protected void addSummaryType(Element member, Content tdSummaryType) {
-        addModifierAndType(member, utils.getReturnType((ExecutableElement)member), tdSummaryType);
+        addModifierAndType(member, utils.getReturnType(typeElement, (ExecutableElement)member), tdSummaryType);
     }
 
     /**
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/TagletWriterImpl.java	Thu Jan 16 15:50:23 2020 +0100
@@ -341,12 +341,15 @@
     /**
      * {@inheritDoc}
      */
-    public Content throwsTagOutput(Element element, DocTree throwsTag) {
+    public Content throwsTagOutput(Element element, DocTree throwsTag, TypeMirror substituteType) {
         ContentBuilder body = new ContentBuilder();
         CommentHelper ch = utils.getCommentHelper(element);
         Element exception = ch.getException(configuration, throwsTag);
         Content excName;
-        if (exception == null) {
+        if (substituteType != null) {
+           excName = htmlWriter.getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.MEMBER,
+                   substituteType));
+        } else if (exception == null) {
             excName = new RawHtml(ch.getExceptionName(throwsTag).toString());
         } else if (exception.asType() == null) {
             excName = new RawHtml(utils.getFullyQualifiedName(exception));
@@ -415,6 +418,13 @@
         return configuration;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    protected TypeElement getCurrentPageElement() {
+        return htmlWriter.getCurrentPageElement();
+    }
+
     @SuppressWarnings("preview")
     private Content createAnchorAndSearchIndex(Element element, String tagText, String desc, boolean isSystemProperty) {
         Content result = null;
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ReturnTaglet.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ReturnTaglet.java	Thu Jan 16 15:50:23 2020 +0100
@@ -74,7 +74,7 @@
     public Content getTagletOutput(Element holder, TagletWriter writer) {
         Messages messages = writer.configuration().getMessages();
         Utils utils = writer.configuration().utils;
-        TypeMirror returnType = utils.getReturnType((ExecutableElement)holder);
+        TypeMirror returnType = utils.getReturnType(writer.getCurrentPageElement(), (ExecutableElement)holder);
         List<? extends DocTree> tags = utils.getBlockTags(holder, name);
 
         //Make sure we are not using @return tag on method with void return type.
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletWriter.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/TagletWriter.java	Thu Jan 16 15:50:23 2020 +0100
@@ -27,6 +27,7 @@
 
 import java.util.List;
 import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.TypeMirror;
 
@@ -189,9 +190,10 @@
      *
      * @param element
      * @param throwsTag the throws tag.
+     * @param substituteType instantiated type of a generic type-variable, or null.
      * @return the output of the throws tag.
      */
-    protected abstract Content throwsTagOutput(Element element, DocTree throwsTag);
+    protected abstract Content throwsTagOutput(Element element, DocTree throwsTag, TypeMirror substituteType);
 
     /**
      * Return the output for the throws tag.
@@ -214,6 +216,13 @@
         String constantVal, boolean includeLink);
 
     /**
+     * Return the main type element of the current page or null for pages that don't have one.
+     *
+     * @return the type element of the current page or null.
+     */
+    protected abstract TypeElement getCurrentPageElement();
+
+    /**
      * Given an output object, append to it the tag documentation for
      * the given member.
      *
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ThrowsTaglet.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ThrowsTaglet.java	Thu Jan 16 15:50:23 2020 +0100
@@ -31,6 +31,7 @@
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ExecutableType;
 import javax.lang.model.type.TypeMirror;
 
 import com.sun.source.doctree.DocTree;
@@ -99,8 +100,8 @@
         for (TypeMirror declaredExceptionType : declaredExceptionTypes) {
             TypeElement klass = utils.asTypeElement(declaredExceptionType);
             if (klass != null &&
-                !alreadyDocumented.contains(utils.getSimpleName(klass)) &&
-                !alreadyDocumented.contains(utils.getFullyQualifiedName(klass))) {
+                !alreadyDocumented.contains(declaredExceptionType.toString()) &&
+                !alreadyDocumented.contains(utils.getFullyQualifiedName(klass, false))) {
                 if (alreadyDocumented.isEmpty()) {
                     result.add(writer.getThrowsHeader());
                 }
@@ -117,7 +118,7 @@
      */
     private Content inheritThrowsDocumentation(Element holder,
             List<? extends TypeMirror> declaredExceptionTypes, Set<String> alreadyDocumented,
-            TagletWriter writer) {
+            Map<String, TypeMirror> typeSubstitutions, TagletWriter writer) {
         Utils utils = writer.configuration().utils;
         Content result = writer.getOutputInstance();
         if (utils.isExecutableElement(holder)) {
@@ -138,7 +139,8 @@
                     declaredExceptionTags.put(inheritedDoc.tagList, (ExecutableElement)inheritedDoc.holder);
                 }
             }
-            result.add(throwsTagsOutput(declaredExceptionTags, writer, alreadyDocumented, false));
+            result.add(throwsTagsOutput(declaredExceptionTags, writer, alreadyDocumented,
+                    typeSubstitutions, false));
         }
         return result;
     }
@@ -149,17 +151,21 @@
     public Content getTagletOutput(Element holder, TagletWriter writer) {
         Utils utils = writer.configuration().utils;
         ExecutableElement execHolder = (ExecutableElement) holder;
+        ExecutableType instantiatedType = utils.asInstantiatedMethodType(
+                writer.getCurrentPageElement(), (ExecutableElement)holder);
+        List<? extends TypeMirror> thrownTypes = instantiatedType.getThrownTypes();
+        Map<String, TypeMirror> typeSubstitutions = getSubstitutedThrownTypes(
+                ((ExecutableElement) holder).getThrownTypes(), thrownTypes);
         Map<List<? extends DocTree>, ExecutableElement> tagsMap = new LinkedHashMap<>();
         tagsMap.put(utils.getThrowsTrees(execHolder), execHolder);
         Content result = writer.getOutputInstance();
         HashSet<String> alreadyDocumented = new HashSet<>();
         if (!tagsMap.isEmpty()) {
-            result.add(throwsTagsOutput(tagsMap, writer, alreadyDocumented, true));
+            result.add(throwsTagsOutput(tagsMap, writer, alreadyDocumented, typeSubstitutions, true));
         }
         result.add(inheritThrowsDocumentation(holder,
-            execHolder.getThrownTypes(), alreadyDocumented, writer));
-        result.add(linkToUndocumentedDeclaredExceptions(
-            execHolder.getThrownTypes(), alreadyDocumented, writer));
+                thrownTypes, alreadyDocumented, typeSubstitutions, writer));
+        result.add(linkToUndocumentedDeclaredExceptions(thrownTypes, alreadyDocumented, writer));
         return result;
     }
 
@@ -174,7 +180,8 @@
      * @return the Content representation of this <code>Tag</code>.
      */
     protected Content throwsTagsOutput(Map<List<? extends DocTree>, ExecutableElement> throwTags,
-        TagletWriter writer, Set<String> alreadyDocumented, boolean allowDups) {
+                                       TagletWriter writer, Set<String> alreadyDocumented,
+                                       Map<String,TypeMirror> typeSubstitutions, boolean allowDups) {
         Utils utils = writer.configuration().utils;
         Content result = writer.getOutputInstance();
         if (!throwTags.isEmpty()) {
@@ -184,21 +191,52 @@
                 for (DocTree dt : entry.getKey()) {
                     Element te = ch.getException(utils.configuration, dt);
                     String excName = ch.getExceptionName(dt).toString();
+                    TypeMirror substituteType = typeSubstitutions.get(excName);
                     if ((!allowDups) &&
                         (alreadyDocumented.contains(excName) ||
-                        (te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te))))) {
+                        (te != null && alreadyDocumented.contains(utils.getFullyQualifiedName(te, false)))) ||
+                        (substituteType != null && alreadyDocumented.contains(substituteType.toString()))) {
                         continue;
                     }
                     if (alreadyDocumented.isEmpty()) {
                         result.add(writer.getThrowsHeader());
                     }
-                    result.add(writer.throwsTagOutput(e, dt));
-                    alreadyDocumented.add(te != null
-                            ? utils.getFullyQualifiedName(te)
-                            : excName);
+                    result.add(writer.throwsTagOutput(e, dt, substituteType));
+                    if (substituteType != null) {
+                        alreadyDocumented.add(substituteType.toString());
+                    } else {
+                        alreadyDocumented.add(te != null
+                                ? utils.getFullyQualifiedName(te, false)
+                                : excName);
+                    }
                 }
             }
         }
         return result;
     }
+
+    /**
+     * Returns a map of substitutions for a list of thrown types with the original type-variable
+     * name as key and the instantiated type as value. If no types need to be substituted
+     * an empty map is returned.
+     * @param declaredThrownTypes the originally declared thrown types.
+     * @param instantiatedThrownTypes the thrown types in the context of the current type.
+     * @return map of declared to instantiated thrown types or an empty map.
+     */
+    private Map<String, TypeMirror> getSubstitutedThrownTypes(List<? extends TypeMirror> declaredThrownTypes,
+                                                              List<? extends TypeMirror> instantiatedThrownTypes) {
+        if (!instantiatedThrownTypes.equals(declaredThrownTypes)) {
+            Map<String, TypeMirror> map = new HashMap<>();
+            Iterator<? extends TypeMirror> i1 = instantiatedThrownTypes.iterator();
+            Iterator<? extends TypeMirror> i2 = declaredThrownTypes.iterator();
+            while (i1.hasNext() && i2.hasNext()) {
+                TypeMirror t1 = i1.next();
+                TypeMirror t2 = i2.next();
+                if (!t1.equals(t2))
+                    map.put(t2.toString(), t1);
+            }
+            return map;
+        }
+        return Collections.emptyMap();
+    }
 }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java	Thu Jan 16 15:50:23 2020 +0100
@@ -219,7 +219,7 @@
      * @return true if t1 is a superclass of t2.
      */
     public boolean isSubclassOf(TypeElement t1, TypeElement t2) {
-        return typeUtils.isSubtype(t1.asType(), t2.asType());
+        return typeUtils.isSubtype(typeUtils.erasure(t1.asType()), typeUtils.erasure(t2.asType()));
     }
 
     /**
@@ -565,7 +565,8 @@
     }
 
     public boolean isUndocumentedEnclosure(TypeElement enclosingTypeElement) {
-        return isPackagePrivate(enclosingTypeElement) && !isLinkable(enclosingTypeElement);
+        return (isPackagePrivate(enclosingTypeElement) || isPrivate(enclosingTypeElement))
+                && !isLinkable(enclosingTypeElement);
     }
 
     public boolean isError(TypeElement te) {
@@ -796,13 +797,51 @@
     }
 
     /**
-     * Returns the TypeMirror of the ExecutableElement for all methods,
-     * a null if constructor.
+     * Returns the TypeMirror of the ExecutableElement if it is a method, or null
+     * if it is a constructor.
+     * @param site the contextual type
      * @param ee the ExecutableElement
-     * @return
+     * @return the return type
+     */
+    public TypeMirror getReturnType(TypeElement site, ExecutableElement ee) {
+        return ee.getKind() == CONSTRUCTOR ? null : asInstantiatedMethodType(site, ee).getReturnType();
+    }
+
+    /**
+     * Returns the ExecutableType corresponding to the type of the method declaration seen as a
+     * member of a given declared type. This might cause type-variable substitution to kick in.
+     * @param site the contextual type.
+     * @param ee the method declaration.
+     * @return the instantiated method type.
      */
-    public TypeMirror getReturnType(ExecutableElement ee) {
-        return ee.getKind() == CONSTRUCTOR ? null : ee.getReturnType();
+    public ExecutableType asInstantiatedMethodType(TypeElement site, ExecutableElement ee) {
+        return shouldInstantiate(site, ee) ?
+                (ExecutableType)typeUtils.asMemberOf((DeclaredType)site.asType(), ee) :
+                (ExecutableType)ee.asType();
+    }
+
+    /**
+     * Returns the TypeMirror corresponding to the type of the field declaration seen as a
+     * member of a given declared type. This might cause type-variable substitution to kick in.
+     * @param site the contextual type.
+     * @param ve the field declaration.
+     * @return the instantiated field type.
+     */
+    public TypeMirror asInstantiatedFieldType(TypeElement site, VariableElement ve) {
+        return shouldInstantiate(site, ve) ?
+                typeUtils.asMemberOf((DeclaredType)site.asType(), ve) :
+                ve.asType();
+    }
+
+    /*
+     * We should not instantiate if (i) there's no contextual type declaration, (ii) the declaration
+     * to which the member belongs to is the same as the one under consideration, (iii) if the
+     * delcaration to which the member belongs to is not generic.
+     */
+    private boolean shouldInstantiate(TypeElement site, Element e) {
+        return site != null &&
+                site != e.getEnclosingElement() &&
+               !((DeclaredType)e.getEnclosingElement().asType()).getTypeArguments().isEmpty();
     }
 
     /**
--- a/test/langtools/jdk/javadoc/doclet/testMemberInheritance/TestMemberInheritance.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/test/langtools/jdk/javadoc/doclet/testMemberInheritance/TestMemberInheritance.java	Thu Jan 16 15:50:23 2020 +0100
@@ -24,7 +24,7 @@
 /*
  * @test
  * @bug 4638588 4635809 6256068 6270645 8025633 8026567 8162363 8175200
- *      8192850 8182765 8220217
+ *      8192850 8182765 8220217 8224052
  * @summary Test to make sure that members are inherited properly in the Javadoc.
  *          Verify that inheritance labels are correct.
  * @library ../../lib
@@ -46,7 +46,7 @@
     public void test() {
         javadoc("-d", "out",
                 "-sourcepath", testSrc,
-                "pkg", "diamond", "inheritDist", "pkg1", "pkg2");
+                "pkg", "diamond", "inheritDist", "pkg1", "pkg2", "pkg3");
         checkExit(Exit.OK);
 
         checkOutput("pkg/SubClass.html", true,
@@ -114,16 +114,54 @@
         checkOutput("pkg2/DocumentedNonGenericChild.html", true,
                 "<td class=\"colFirst\"><code>protected abstract java.lang.String</code></td>\n"
                 + "<th class=\"colSecond\" scope=\"row\"><code><span class=\"memberNameLink\">"
-                + "<a href=\"#parentMethod()\">parentMethod</a></span>()</code></th>\n"
+                + "<a href=\"#parentMethod(T)\">parentMethod</a></span>&#8203;"
+                + "(java.lang.String&nbsp;t)</code></th>\n"
                 + "<td class=\"colLast\">\n"
                 + "<div class=\"block\">Returns some value.</div>\n"
                 + "</td>\n");
 
         checkOutput("pkg2/DocumentedNonGenericChild.html", true,
-                "<h3><a id=\"parentMethod()\">parentMethod</a></h3>\n"
+                "</a><a id=\"parentMethod(T)\">parentMethod</a></h3>\n"
                 + "<div class=\"memberSignature\"><span class=\"modifiers\">protected abstract</span>"
                 + "&nbsp;<span class=\"returnType\">java.lang.String</span>&nbsp;"
-                + "<span class=\"memberName\">parentMethod</span>()</div>");
+                + "<span class=\"memberName\">parentMethod</span>&#8203;"
+                + "(<span class=\"arguments\">java.lang.String&nbsp;t)</span>\n"
+                + "                                          "
+                + "throws <span class=\"exceptions\">java.lang.IllegalArgumentException,\n"
+                + "java.lang.InterruptedException,\n"
+                + "java.lang.IllegalStateException</span></div>");
+
+        checkOutput("pkg2/DocumentedNonGenericChild.html", true,
+                "<dt><span class=\"throwsLabel\">Throws:</span></dt>\n"
+                + "<dd><code>java.lang.InterruptedException</code> - a generic error</dd>\n"
+                + "<dd><code>java.lang.IllegalStateException</code> - illegal state</dd>\n"
+                + "<dd><code>java.lang.IllegalArgumentException</code></dd>");
+
+        checkOutput("pkg2/DocumentedNonGenericChild.html", true,
+                "<td class=\"colFirst\"><code>java.lang.String</code></td>\n"
+                + "<th class=\"colSecond\" scope=\"row\"><code><span class=\"memberNameLink\">"
+                + "<a href=\"#f\">f</a></span></code></th>\n"
+                + "<td class=\"colLast\">\n"
+                + "<div class=\"block\">A field.</div>",
+                "<section class=\"detail\">\n"
+                + "<h3><a id=\"f\">f</a></h3>\n"
+                + "<div class=\"memberSignature\"><span class=\"modifiers\">public</span>&nbsp;"
+                + "<span class=\"returnType\">java.lang.String</span>&nbsp;<span class=\"memberName\">f</span></div>\n"
+                + "<div class=\"block\">A field.</div>\n"
+                + "</section>");
+
+        checkOutput("pkg3/PrivateGenericParent.PublicChild.html", true,
+                "<td class=\"colFirst\"><code>java.lang.String</code></td>\n"
+                + "<th class=\"colSecond\" scope=\"row\"><code><span class=\"memberNameLink\">"
+                + "<a href=\"#method(T)\">method</a></span>&#8203;(java.lang.String&nbsp;t)</code></th>",
+                "<section class=\"detail\">\n"
+                + "<h3><a id=\"method(java.lang.Object)\">\n"
+                + "<!--   -->\n"
+                + "</a><a id=\"method(T)\">method</a></h3>\n"
+                + "<div class=\"memberSignature\"><span class=\"modifiers\">public</span>&nbsp;"
+                + "<span class=\"returnType\">java.lang.String</span>&nbsp;<span class=\"memberName\">"
+                + "method</span>&#8203;(<span class=\"arguments\">java.lang.String&nbsp;t)</span></div>\n"
+                + "</section>");
 
     }
 }
--- a/test/langtools/jdk/javadoc/doclet/testMemberInheritance/pkg2/DocumentedNonGenericChild.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/test/langtools/jdk/javadoc/doclet/testMemberInheritance/pkg2/DocumentedNonGenericChild.java	Thu Jan 16 15:50:23 2020 +0100
@@ -23,6 +23,6 @@
 
 package pkg2;
 
-public abstract class DocumentedNonGenericChild extends UndocumentedGenericParent<String> {
+public abstract class DocumentedNonGenericChild extends UndocumentedGenericParent<String, InterruptedException, IllegalArgumentException> {
 
 }
--- a/test/langtools/jdk/javadoc/doclet/testMemberInheritance/pkg2/UndocumentedGenericParent.java	Fri Jan 10 17:50:51 2020 +0100
+++ b/test/langtools/jdk/javadoc/doclet/testMemberInheritance/pkg2/UndocumentedGenericParent.java	Thu Jan 16 15:50:23 2020 +0100
@@ -23,11 +23,19 @@
 
 package pkg2;
 
-abstract class UndocumentedGenericParent<T> {
+abstract class UndocumentedGenericParent<T, E extends Throwable, F extends Throwable> {
+    /**
+     * A field.
+     */
+    public T f;
+
     /**
      * Returns some value.
      *
+     * @param t a parameter
      * @return some value
+     * @throws E a generic error
+     * @throws IllegalStateException illegal state
      */
-    protected abstract String parentMethod();
+    protected abstract T parentMethod(T t) throws F, E, IllegalStateException;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/jdk/javadoc/doclet/testMemberInheritance/pkg3/PrivateGenericParent.java	Thu Jan 16 15:50:23 2020 +0100
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+package pkg3;
+
+public class PrivateGenericParent {
+
+    private static class PrivateParent<T> {
+        public T method(T t) {
+            return t;
+        }
+    }
+
+    public class PublicChild extends PrivateParent<String> {}
+}