access can be tightened for inner classes (IDEA-185790)

check enclosing instance for null qualifiers, if accessed via inheritance, then can't be private
This commit is contained in:
Anna Kozlova
2018-01-30 19:38:53 +03:00
parent b2ebc97d71
commit d8f37a3ea8
3 changed files with 28 additions and 9 deletions
@@ -10,9 +10,11 @@ import com.intellij.codeInspection.inheritance.ImplicitSubclassProvider;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
import com.intellij.psi.util.ClassUtil;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.VisibilityUtil;
@@ -255,7 +257,7 @@ class AccessCanBeTightenedInspection extends AbstractBaseJavaLocalInspectionTool
PsiClass innerClass = PsiTreeUtil.getParentOfType(element, PsiClass.class);
boolean isAbstractMember = member.hasModifierProperty(PsiModifier.ABSTRACT);
if (memberClass != null && PsiTreeUtil.isAncestor(innerClass, memberClass, false) ||
innerClass != null && PsiTreeUtil.isAncestor(memberClass, innerClass, false) && !innerClass.hasModifierProperty(PsiModifier.STATIC)) {
innerClass != null && PsiTreeUtil.isAncestor(memberClass, innerClass, false)) {
// access from the same file can be via private
// except when used in annotation:
// @Ann(value = C.VAL) class C { public static final String VAL = "xx"; }
@@ -301,7 +303,10 @@ class AccessCanBeTightenedInspection extends AbstractBaseJavaLocalInspectionTool
private boolean calledOnInheritor(@NotNull PsiElement element, PsiClass memberClass) {
PsiExpression qualifier = getQualifier(element);
if (qualifier == null) return false;
if (qualifier == null) {
PsiClass enclosingInstance = InheritanceUtil.findEnclosingInstanceInScope(memberClass, element, Condition.TRUE, true);
return enclosingInstance != null && enclosingInstance != memberClass;
}
PsiClass qClass = PsiUtil.resolveClassInClassTypeOnly(qualifier.getType());
return qClass != null && qClass.isInheritor(memberClass, true);
}
@@ -142,31 +142,38 @@ public class InheritanceUtil {
PsiElement scope,
Condition<PsiClass> isSuperClassAccepted,
boolean isTypeParamsAccepted) {
return findEnclosingInstanceInScope(aClass, scope, isSuperClassAccepted, isTypeParamsAccepted) != null;
}
public static PsiClass findEnclosingInstanceInScope(PsiClass aClass,
PsiElement scope,
Condition<PsiClass> isSuperClassAccepted,
boolean isTypeParamsAccepted) {
PsiManager manager = aClass.getManager();
PsiElement place = scope;
while (place != null && place != aClass && !(place instanceof PsiFile)) {
while (place != null && !(place instanceof PsiFile)) {
if (place instanceof PsiClass) {
if (isSuperClassAccepted.value((PsiClass)place)) {
if (isInheritorOrSelf((PsiClass)place, aClass, true)) return true;
if (isInheritorOrSelf((PsiClass)place, aClass, true)) return (PsiClass)place;
}
else {
if (manager.areElementsEquivalent(place, aClass)) return true;
if (manager.areElementsEquivalent(place, aClass)) return aClass;
}
if (isTypeParamsAccepted && place instanceof PsiTypeParameter) {
return true;
return (PsiClass)place;
}
}
if (place instanceof PsiModifierListOwner) {
final PsiModifierList modifierList = ((PsiModifierListOwner)place).getModifierList();
if (modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC)) {
return false;
return null;
}
}
place = place.getParent();
}
return place == aClass;
return null;
}
public static boolean processSuperTypes(@NotNull PsiType type, boolean includeSelf, @NotNull Processor<PsiType> processor) {
if (includeSelf && !processor.process(type)) return false;
return processSuperTypes(type, processor, new HashSet<>());
@@ -224,6 +224,13 @@ public class AccessCanBeTightenedInspectionTest extends LightInspectionTestCase
" return myElement;\n" +
" }\n" +
" }\n" +
" <warning descr=\"Access can be private\">protected</warning> String myElement1;\n" +
" class B1 {\n" +
" @Override\n" +
" public String toString() {\n" +
" return myElement1;\n" +
" }\n" +
" }\n" +
"}");
}