diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java index 9605ff13669a..172c4d47021b 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.psi.impl; import com.intellij.codeInsight.AnnotationTargetUtil; @@ -394,7 +394,8 @@ public final class PsiImplUtil { PsiModifierList modifierList = member.getModifierList(); int accessLevel = modifierList == null ? PsiUtil.ACCESS_LEVEL_PUBLIC : PsiUtil.getAccessLevel(modifierList); - if (accessLevel == PsiUtil.ACCESS_LEVEL_PUBLIC || accessLevel == PsiUtil.ACCESS_LEVEL_PROTECTED) { + if (accessLevel == PsiUtil.ACCESS_LEVEL_PUBLIC || + accessLevel == PsiUtil.ACCESS_LEVEL_PROTECTED) { SearchScope classScope = getClassUseScopeIfApplicable(member, aClass, accessLevel); return (classScope != null) ? classScope : maximalUseScope; } @@ -418,13 +419,51 @@ public final class PsiImplUtil { if (aClass == null) return null; final PsiModifierList classModifierList = aClass.getModifierList(); if (classModifierList == null) return null; - if (!classModifierList.hasModifierProperty(PsiModifier.FINAL) && - !(member instanceof PsiMethod && ((PsiMethod)member).isConstructor())) { - // class use scope doesn't matter, since another very visible class can inherit from aClass - return null; + if (classModifierList.hasModifierProperty(PsiModifier.FINAL) || + member instanceof PsiMethod && ((PsiMethod)member).isConstructor()) { + // constructors and members of final classes cannot be accessed via a subclass so their use scope can't be wider than their class's + return (PsiUtil.getAccessLevel(classModifierList) < accessLevel) ? aClass.getUseScope() : null; } - // constructors and members of final classes cannot be accessed via a subclass so their use scope can't be wider than their class's - return (PsiUtil.getAccessLevel(classModifierList) < accessLevel) ? aClass.getUseScope() : null; + else if (classModifierList.hasModifierProperty(PsiModifier.PRIVATE) && !hasSubclassInContainingContext(aClass)) { + return aClass.getUseScope(); + } + // class use scope doesn't matter, since another very visible class can inherit from aClass + return null; + } + + private static boolean hasSubclassInContainingContext(PsiClass aClass) { + if (aClass instanceof PsiCompiledElement) return true; // don't check library code + class LocalInheritorVisitor extends JavaRecursiveElementWalkingVisitor { + private boolean extended = false; + @Override + public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement reference) { + if (extended) return; + final PsiElement parent = reference.getParent(); + if (!(parent instanceof PsiReferenceList)) return; + final PsiElement grandParent = parent.getParent(); + if (!(grandParent instanceof PsiClass)) return; + if (reference.isReferenceTo(aClass)) { + extended = true; + stopWalking(); + } + } + + @Override + public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {} + + public boolean isExtended() { + return extended; + } + } + PsiClass containingClass = aClass.getContainingClass(); + PsiClass context = aClass; + while (containingClass != null) { + context = containingClass; + containingClass = containingClass.getContainingClass(); + } + final LocalInheritorVisitor visitor = new LocalInheritorVisitor(); + context.accept(visitor); + return visitor.isExtended(); } public static boolean isInServerPage(@Nullable final PsiElement element) {