kotlin: fix false positives for 'suspicious package-private access' inspection nested object literal expressions in Kotlin

This is an improvement for cdd6055 which also fixes IDEA-210253. JavaResolveUtil.canAccessProtectedMember doesn't work properly for Kotlin because of KT-30759 and KT-30752, so its code is rewritten to use UAST to find outer class.
This commit is contained in:
nik
2019-04-03 14:13:45 +03:00
parent 57b6442fe1
commit c0198a15fe
6 changed files with 99 additions and 13 deletions
@@ -0,0 +1,7 @@
package xxx
open class ProtectedMembersKotlin {
protected val property: String = ""
protected fun foo() {}
}
@@ -61,3 +61,17 @@ class AccessingProtectedConstructorFromSubclass : ProtectedConstructors(1)
val objectAccessingDefaultProtectedConstructorFromSubclass = object : ProtectedConstructors() {}
val objectAccessingProtectedConstructorFromSubclass = object : ProtectedConstructors(1) {}
class AccessingProtectedMembersFromObjectLiteral {
fun bar1() {
object : ProtectedMembers() {
fun bar2() {
object : Runnable {
override fun run() {
method()
}
}
}
}
}
}
@@ -0,0 +1,31 @@
package xxx
@Suppress("UNUSED_VARIABLE")
class AccessingProtectedKotlinMembersFromObjectLiteral {
fun bar1() {
object : ProtectedMembersKotlin() {
fun bar2() {
object : Runnable {
override fun run() {
foo()
val s2 = property
}
}
}
}
}
}
@Suppress("UNUSED_VARIABLE")
class AccessingProtectedMembersFromKotlin : ProtectedMembersKotlin() {
fun bar() {
foo()
val s = property
object : Runnable {
override fun run() {
foo()
val s2 = property
}
}
}
}
@@ -14,5 +14,9 @@ class KtSuspiciousPackagePrivateAccessInspectionTest : SuspiciousPackagePrivateA
doTestWithDependency()
}
fun testAccessingProtectedMembersFromKotlin() {
doTestWithDependency()
}
override fun getBasePath() = "${JvmAnalysisKtTestsUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH}/codeInspection/suspiciousPackagePrivateAccess"
}
@@ -15,7 +15,7 @@ import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.refactoring.util.RefactoringUIUtil;
@@ -232,24 +232,38 @@ public class SuspiciousPackagePrivateAccessInspection extends AbstractBaseUastLo
sourceClass);
}
private static boolean canAccessProtectedMember(PsiMember member, PsiClass memberClass, PsiClass accessObjectType,
boolean isMemberStatic, UClass sourceClass) {
if (JavaResolveUtil.canAccessProtectedMember(member, memberClass, accessObjectType, sourceClass.getJavaPsi(), isMemberStatic)) {
return true;
}
if (sourceClass instanceof UAnonymousClass && sourceClass.getJavaPsi().getContext() == null) {
//workaround for KT-30752: KtLightClassForAnonymousDeclaration::getContext returns null for object literal expressions in some member initializers
UElement uastParent = sourceClass.getUastParent();
if (uastParent != null) {
UClass parentClass = UastUtils.findContaining(uastParent.getSourcePsi(), UClass.class);
if (parentClass != null) {
return canAccessProtectedMember(member, memberClass, accessObjectType, isMemberStatic, parentClass);
/**
* The implementation was copied from {@link com.intellij.psi.impl.source.resolve.JavaResolveUtil#canAccessProtectedMember} but uses UAST
* to find outer class as a workaround for bugs in Kotlin Light PSI (KT-30759, KT-30752)
*/
private static boolean canAccessProtectedMember(@NotNull PsiMember member, @NotNull PsiClass memberClass,
@Nullable PsiClass accessObjectClass, boolean isStatic, @Nullable UClass contextClass) {
while (contextClass != null) {
PsiClass javaPsiClass = contextClass.getJavaPsi();
if (InheritanceUtil.isInheritorOrSelf(javaPsiClass, memberClass, true)) {
if (member instanceof PsiClass || isStatic || accessObjectClass == null
|| InheritanceUtil.isInheritorOrSelf(accessObjectClass, javaPsiClass, true)) {
return true;
}
}
contextClass = getOuterClass(contextClass);
}
return false;
}
private static UClass getOuterClass(@NotNull UClass aClass) {
UElement uastParent = aClass.getUastParent();
if (uastParent == null) return null;
PsiElement sourcePsi = uastParent.getSourcePsi();
while (sourcePsi == null) {
uastParent = uastParent.getUastParent();
if (uastParent == null) return null;
sourcePsi = uastParent.getSourcePsi();
}
return UastUtils.findContaining(sourcePsi, UClass.class);
}
private boolean isPackageLocalAccessSuspicious(Module sourceModule, Module targetModule) {
if (targetModule == null || sourceModule == null || targetModule.equals(sourceModule)) {
return false;
@@ -10,6 +10,15 @@ class AccessingProtectedMembersNotFromSubclass {
new ProtectedConstructors() {};
new ProtectedConstructors(1) {};
}
void baz() {
class LocalSubclass extends ProtectedMembers {
void bar() {
method();
staticMethod();
}
}
}
}
class AccessingProtectedMembersFromSubclass extends ProtectedMembers {
@@ -32,6 +41,13 @@ class AccessingProtectedMembersFromSubclass extends ProtectedMembers {
staticMethod();
}
};
class LocalClass {
void baz() {
method();
staticMethod();
}
}
}
public static class StaticInnerImpl1 extends ProtectedMembers.StaticInner {