javadoc references must resolve despite current resolve scope (Too often javadoc for interface references implementation which should not show up red)

This commit is contained in:
Alexey Kudravtsev
2018-08-10 15:41:23 +03:00
parent 2c7a0ea8fd
commit 34f65ccec8
6 changed files with 54 additions and 12 deletions

View File

@@ -8,10 +8,13 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.StringTokenizer;
@@ -70,12 +73,12 @@ public class JavaDocUtil {
}
@Nullable
public static PsiElement findReferenceTarget(PsiManager manager, String refText, PsiElement context) {
public static PsiElement findReferenceTarget(@NotNull PsiManager manager, @NotNull String refText, PsiElement context) {
return findReferenceTarget(manager, refText, context, true);
}
@Nullable
public static PsiElement findReferenceTarget(PsiManager manager, String refText, PsiElement context, boolean useNavigationElement) {
public static PsiElement findReferenceTarget(@NotNull PsiManager manager, @NotNull String refText, PsiElement context, boolean useNavigationElement) {
LOG.assertTrue(context == null || context.isValid());
if (context != null) {
context = context.getNavigationElement();
@@ -84,9 +87,7 @@ public class JavaDocUtil {
int poundIndex = refText.indexOf('#');
final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
if (poundIndex < 0) {
PsiClass aClass = facade.getResolveHelper().resolveReferencedClass(refText, context);
if (aClass == null) aClass = facade.findClass(refText, context.getResolveScope());
PsiClass aClass = findClassFromRef(manager, facade, refText, context);
if (aClass != null) {
return useNavigationElement ? aClass.getNavigationElement() : aClass;
@@ -98,9 +99,7 @@ public class JavaDocUtil {
else {
String classRef = refText.substring(0, poundIndex).trim();
if (!classRef.isEmpty()) {
PsiClass aClass = facade.getResolveHelper().resolveReferencedClass(classRef, context);
if (aClass == null) aClass = facade.findClass(classRef, context.getResolveScope());
PsiClass aClass = findClassFromRef(manager, facade, classRef, context);
if (aClass == null) return null;
PsiElement member = findReferencedMember(aClass, refText.substring(poundIndex + 1), context);
@@ -110,7 +109,7 @@ public class JavaDocUtil {
String memberRefText = refText.substring(1);
PsiElement scope = context;
while (true) {
if (scope instanceof PsiFile) break;
if (scope instanceof PsiFile || scope == null) break;
if (scope instanceof PsiClass) {
PsiElement member = findReferencedMember((PsiClass)scope, memberRefText, context);
if (member != null) {
@@ -124,6 +123,23 @@ public class JavaDocUtil {
}
}
private static PsiClass findClassFromRef(@NotNull PsiManager manager,
@NotNull JavaPsiFacade facade,
@NotNull String refText, PsiElement context) {
PsiClass aClass = facade.getResolveHelper().resolveReferencedClass(refText, context);
GlobalSearchScope projectScope = GlobalSearchScope.projectScope(manager.getProject());
if (aClass == null) aClass = facade.findClass(refText, projectScope);
if (aClass == null && refText.indexOf('.') == -1 && context != null) {
// find short-named class in the same package (maybe in the different module)
PsiFile file = context.getContainingFile();
PsiDirectory directory = file == null ? null : file.getContainingDirectory();
PsiPackage aPackage = directory == null ? null : JavaDirectoryService.getInstance().getPackage(directory);
aClass = aPackage == null ? null : ArrayUtil.getFirstElement(aPackage.findClassByShortName(refText, projectScope));
}
return aClass;
}
@Nullable
private static PsiElement findReferencedMember(PsiClass aClass, String memberRefText, PsiElement context) {
int parenthIndex = memberRefText.indexOf('(');

View File

@@ -289,7 +289,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya
@NotNull ResolveState state,
PsiElement lastParent,
@NotNull PsiElement place) {
GlobalSearchScope scope = place.getResolveScope();
GlobalSearchScope scope = PsiUtil.isInsideJavadocComment(place) ? allScope() : place.getResolveScope();
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this);
ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
@@ -336,7 +336,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya
private static boolean processClasses(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@NotNull PsiClass[] classes,
@NotNull Condition<String> nameCondition) {
@NotNull Condition<? super String> nameCondition) {
for (PsiClass aClass : classes) {
String name = aClass.getName();
if (name != null && nameCondition.value(name)) {

View File

@@ -314,7 +314,8 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
if (processor instanceof ClassResolverProcessor &&
isPhysical() &&
(getUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING) == Boolean.TRUE || myResolveCache.hasUpToDateValue())) {
(getUserData(PsiFileEx.BATCH_REFERENCE_PROCESSING) == Boolean.TRUE || myResolveCache.hasUpToDateValue()) &&
!PsiUtil.isInsideJavadocComment(place)) {
final ClassResolverProcessor hint = (ClassResolverProcessor)processor;
String name = hint.getName(state);
MostlySingularMultiMap<String, SymbolCollectingProcessor.ResultWithContext> cache = myResolveCache.getValue();

View File

@@ -0,0 +1,14 @@
package pkg;
/**
* @see pkg1.PackageLocal - package local class in other package should resolve
* @see java.io.ObjectStreamClass.WeakClassKey - and JDK package local class too
* @see java.io.ObjectStreamClass.Caches - and even JDK private classes
*
* - but don't go over the top, of course:
* @see java.io.ObjectStreamClass.<error descr="Cannot resolve symbol 'java.io.ObjectStreamClass.XXXXXX'">XXXXXX</error>
*/
class JavadocMustResolveEvenOtherPackageLocalClasses {
}

View File

@@ -0,0 +1,3 @@
package pkg1;
class PackageLocal {}

View File

@@ -4,7 +4,9 @@ package com.intellij.java.codeInsight.daemon;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.codeInspection.javaDoc.JavaDocLocalInspection;
import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.testFramework.IdeaTestUtil;
public class JavadocResolveTest extends DaemonAnalyzerTestCase {
private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/javaDoc/resolve";
@@ -16,6 +18,7 @@ public class JavadocResolveTest extends DaemonAnalyzerTestCase {
public void testPackageInfo() { doTest("/pkg/package-info.java"); }
public void testBrokenPackageInfo() { doTest("/pkg1/package-info.java"); }
public void testModuleInfo() { setLanguageLevel(LanguageLevel.JDK_1_9); doTest("/module-info.java"); }
public void testOtherPackageLocal() { doTest(); }
private void doTest() {
doTest("/pkg/" + getTestName(false) + ".java");
@@ -28,4 +31,9 @@ public class JavadocResolveTest extends DaemonAnalyzerTestCase {
}
catch (Exception e) { throw new RuntimeException(e); }
}
@Override
protected Sdk getTestProjectJdk() {
return IdeaTestUtil.getMockJdk18();
}
}