diff --git a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java index 88d6516685b9..a72617b9b2da 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java @@ -15,10 +15,7 @@ */ package com.intellij.codeInsight.javadoc; -import com.intellij.codeInsight.AnnotationUtil; -import com.intellij.codeInsight.CodeInsightBundle; -import com.intellij.codeInsight.ExternalAnnotationsManager; -import com.intellij.codeInsight.InferredAnnotationsManager; +import com.intellij.codeInsight.*; import com.intellij.codeInsight.documentation.DocumentationManagerProtocol; import com.intellij.codeInsight.documentation.DocumentationManagerUtil; import com.intellij.javadoc.JavadocGeneratorRunProfile; @@ -1723,11 +1720,36 @@ public class JavaDocInfoGenerator { private void generateThrowsSection(StringBuilder buffer, PsiMethod method, PsiDocComment comment) { PsiDocTag[] localTags = getThrowsTags(comment); + PsiDocTag[] thrownTags = localTags; + JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(method.getProject()); + + Set reported = new HashSet<>(); + for (HierarchicalMethodSignature signature : method.getHierarchicalMethodSignature().getSuperSignatures()) { + PsiMethod superMethod = ObjectUtils.tryCast(signature.getMethod().getNavigationElement(), PsiMethod.class); + PsiDocComment docComment = superMethod != null ? superMethod.getDocComment() : null; + if (docComment != null) { + PsiDocTag[] uncheckedExceptions = Arrays.stream(getThrowsTags(docComment)).filter(tag -> { + PsiDocTagValue valueElement = tag.getValueElement(); + if (valueElement == null) return false; + if (Arrays.stream(localTags) + .map(PsiDocTag::getValueElement) + .map(ObjectUtils::notNull) + .anyMatch(docTagValue -> areWeakEqual(docTagValue.getText(), valueElement.getText()))) { + return false; + } + PsiClass exClass = psiFacade.getResolveHelper().resolveReferencedClass(valueElement.getText(), docComment); + if (exClass == null) return false; + return ExceptionUtil.isUncheckedException(exClass) && reported.add(exClass); + }).toArray(PsiDocTag[]::new); + thrownTags = ArrayUtil.mergeArrays(thrownTags, uncheckedExceptions); + } + } + LinkedList>> collectedTags = new LinkedList<>(); List declaredThrows = new ArrayList<>(Arrays.asList(method.getThrowsList().getReferencedTypes())); - for (int i = localTags.length - 1; i > -1; i--) { - PsiDocTagValue valueElement = localTags[i].getValueElement(); + for (int i = thrownTags.length - 1; i > -1; i--) { + PsiDocTagValue valueElement = thrownTags[i].getValueElement(); if (valueElement != null) { for (Iterator iterator = declaredThrows.iterator(); iterator.hasNext();) { @@ -1740,7 +1762,7 @@ public class JavaDocInfoGenerator { } Pair> tag = findInheritDocTag(method, exceptionLocator(valueElement.getText())); - collectedTags.addFirst(new Pair<>(localTags[i], new InheritDocProvider() { + collectedTags.addFirst(new Pair<>(thrownTags[i], new InheritDocProvider() { @Override public Pair> getInheritDoc() { return tag; @@ -1759,7 +1781,7 @@ public class JavaDocInfoGenerator { String paramName = trouser.getCanonicalText(); Pair> parmTag = null; - for (PsiDocTag localTag : localTags) { + for (PsiDocTag localTag : thrownTags) { PsiDocTagValue value = localTag.getValueElement(); if (value != null) { String tagName = value.getText(); @@ -1779,7 +1801,7 @@ public class JavaDocInfoGenerator { } else { try { - PsiDocTag tag = JavaPsiFacade.getInstance(method.getProject()).getElementFactory().createDocTagFromText("@exception " + paramName); + PsiDocTag tag = psiFacade.getElementFactory().createDocTagFromText("@exception " + paramName); collectedTags.addLast(Pair.create(tag, ourEmptyProvider)); } catch (IncorrectOperationException e) { diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java index 2b8ad55504bc..d387e66f8e18 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java @@ -705,6 +705,11 @@ public class ExceptionUtil { return InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION) || InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ERROR); } + public static boolean isUncheckedException(@NotNull PsiClass psiClass) { + return InheritanceUtil.isInheritor(psiClass, CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION) || + InheritanceUtil.isInheritor(psiClass, CommonClassNames.JAVA_LANG_ERROR); + } + public static boolean isUncheckedExceptionOrSuperclass(@NotNull final PsiClassType type) { return isGeneralExceptionType(type) || isUncheckedException(type); } diff --git a/java/java-tests/testData/codeInsight/javadocIG/documentationForUncheckedExceptionsInSupers.html b/java/java-tests/testData/codeInsight/javadocIG/documentationForUncheckedExceptionsInSupers.html new file mode 100644 index 000000000000..61a8c084c3de --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/documentationForUncheckedExceptionsInSupers.html @@ -0,0 +1,7 @@ + java.util.Collection
public abstract boolean contains(Object o)
+ Returns true if this collection contains the specified + element. More formally, returns true if and only if this + collection contains at least one element e such that + (o==null ? e==null : o.equals(e)). + +
Parameters:
o - element whose presence in this collection is to be tested.
Returns:
true if this collection contains the specified element
Throws:
ClassCastException - if the type of the specified element is incompatible with this collection (optional).
NullPointerException - if the specified element is null and this collection does not support null elements (optional).
\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/documentationForUncheckedExceptionsInSupers.java b/java/java-tests/testData/codeInsight/javadocIG/documentationForUncheckedExceptionsInSupers.java new file mode 100644 index 000000000000..34f042686d51 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/documentationForUncheckedExceptionsInSupers.java @@ -0,0 +1,13 @@ +interface I { + /** + * @throws NullPointerException blah-blah + */ + boolean contains(Object o) {} +} +interface My extends java.util.Collection, I {} +class C { + { + My m = null; + m.contains(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java index abcf4d87de03..a42914a98b47 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java @@ -401,6 +401,10 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase { doTestAtCaret(); } + public void testDocumentationForUncheckedExceptionsInSupers() throws Exception { + doTestAtCaret(); + } + public void testDumbMode() throws Exception { DumbServiceImpl.getInstance(myProject).setDumb(true); try {