FIR: add proper receiver check for protected visibility #KT-48378 Fixed

This commit is contained in:
Mikhail Glukhikh
2021-08-24 12:30:53 +03:00
committed by TeamCityServer
parent 1c1b9547c1
commit 08e498f1f2
16 changed files with 140 additions and 30 deletions
@@ -0,0 +1,19 @@
FILE: User.kt
public final class User : R|BaseClass| {
public constructor(): R|test/User| {
super<R|BaseClass|>()
}
public final fun foo(tree: R|BaseClass|): R|kotlin/Unit| {
lval ui: R|ft<kotlin/String, kotlin/String?>| = R|<local>/tree|.<HIDDEN: /BaseClass.ui is invisible>#
}
public final fun bar(): R|kotlin/Unit| {
lval ui: R|ft<kotlin/String, kotlin/String?>| = this@R|test/User|.R|/BaseClass.ui|
}
public final fun baz(): R|kotlin/Unit| {
lval ui: R|ft<kotlin/String, kotlin/String?>| = this@R|test/User|.R|/BaseClass.ui|
}
}
@@ -0,0 +1,23 @@
// FILE: BaseClass.java
public class BaseClass {
protected String ui = "";
}
// FILE: User.kt
package test
class User : BaseClass() {
fun foo(tree: BaseClass) {
val ui = tree.<!INVISIBLE_REFERENCE!>ui<!>
}
fun bar() {
val ui = ui
}
fun baz() {
val ui = this.ui
}
}
@@ -5051,6 +5051,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/EnumWithToString.kt"); runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/EnumWithToString.kt");
} }
@Test
@TestMetadata("FieldVsSyntheticAccessor.kt")
public void testFieldVsSyntheticAccessor() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/FieldVsSyntheticAccessor.kt");
}
@Test @Test
@TestMetadata("IntersectionWithJavaString.kt") @TestMetadata("IntersectionWithJavaString.kt")
public void testIntersectionWithJavaString() throws Exception { public void testIntersectionWithJavaString() throws Exception {
@@ -5051,6 +5051,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/EnumWithToString.kt"); runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/EnumWithToString.kt");
} }
@Test
@TestMetadata("FieldVsSyntheticAccessor.kt")
public void testFieldVsSyntheticAccessor() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/FieldVsSyntheticAccessor.kt");
}
@Test @Test
@TestMetadata("IntersectionWithJavaString.kt") @TestMetadata("IntersectionWithJavaString.kt")
public void testIntersectionWithJavaString() throws Exception { public void testIntersectionWithJavaString() throws Exception {
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
@NoMutableState @NoMutableState
object FirJavaVisibilityChecker : FirVisibilityChecker() { object FirJavaVisibilityChecker : FirVisibilityChecker() {
@@ -29,7 +30,10 @@ object FirJavaVisibilityChecker : FirVisibilityChecker() {
true true
} else { } else {
val ownerId = symbol.getOwnerId() val ownerId = symbol.getOwnerId()
ownerId != null && canSeeProtectedMemberOf(containingDeclarations, dispatchReceiver, ownerId, session) ownerId != null && canSeeProtectedMemberOf(
containingDeclarations, dispatchReceiver, ownerId, session,
isVariableOrNamedFunction = symbol is FirVariableSymbol || symbol is FirNamedFunctionSymbol
)
} }
} }
@@ -9,18 +9,16 @@ import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.calls.ExpressionReceiverValue
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
import org.jetbrains.kotlin.fir.resolve.firProvider
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -116,7 +114,10 @@ abstract class FirVisibilityChecker : FirSessionComponent {
Visibilities.Protected -> { Visibilities.Protected -> {
val ownerId = symbol.getOwnerId() val ownerId = symbol.getOwnerId()
ownerId != null && canSeeProtectedMemberOf(containingDeclarations, dispatchReceiver, ownerId, session) ownerId != null && canSeeProtectedMemberOf(
containingDeclarations, dispatchReceiver, ownerId, session,
isVariableOrNamedFunction = symbol is FirVariableSymbol || symbol is FirNamedFunctionSymbol
)
} }
else -> platformVisibilityCheck( else -> platformVisibilityCheck(
@@ -173,14 +174,57 @@ abstract class FirVisibilityChecker : FirSessionComponent {
private fun canSeeProtectedMemberOf( private fun canSeeProtectedMemberOf(
containingUseSiteClass: FirClass, containingUseSiteClass: FirClass,
dispatchReceiver: ReceiverValue?, dispatchReceiver: ReceiverValue?,
ownerId: ClassId, session: FirSession ownerId: ClassId,
session: FirSession,
isVariableOrNamedFunction: Boolean
): Boolean { ): Boolean {
dispatchReceiver?.ownerIfCompanion(session)?.let { companionOwnerClassId -> dispatchReceiver?.ownerIfCompanion(session)?.let { companionOwnerClassId ->
if (containingUseSiteClass.isSubClass(companionOwnerClassId, session)) return true if (containingUseSiteClass.isSubClass(companionOwnerClassId, session)) return true
} }
// TODO: Add check for receiver, see org.jetbrains.kotlin.descriptors.Visibility#doesReceiverFitForProtectedVisibility return when {
return containingUseSiteClass.isSubClass(ownerId, session) !containingUseSiteClass.isSubClass(ownerId, session) -> false
isVariableOrNamedFunction -> doesReceiverFitForProtectedVisibility(dispatchReceiver, containingUseSiteClass, session)
else -> true
}
}
private fun doesReceiverFitForProtectedVisibility(
dispatchReceiver: ReceiverValue?,
containingUseSiteClass: FirClass,
session: FirSession
): Boolean {
if (dispatchReceiver == null) return true
var dispatchReceiverType = dispatchReceiver.type
if (dispatchReceiver is ExpressionReceiverValue) {
val explicitReceiver = dispatchReceiver.explicitReceiver
if (explicitReceiver is FirPropertyAccessExpression && explicitReceiver.calleeReference is FirSuperReference) {
// Special 'super' case: type of this, not of super, should be taken for the check below
dispatchReceiverType = explicitReceiver.dispatchReceiver.typeRef.coneType
}
}
return dispatchReceiverType.fullyExpandedType(session).isSubtypeOfClass(containingUseSiteClass.classId, session)
}
private fun ConeKotlinType.isSubtypeOfClass(ownerId: ClassId, session: FirSession): Boolean {
return when (this) {
is ConeClassLikeType -> {
val dispatchReceiverClass = lookupTag.toSymbol(session)?.fir as? FirClass
dispatchReceiverClass?.isSubClass(ownerId, session) == true
}
is ConeTypeParameterType -> {
this.lookupTag.typeParameterSymbol.fir.bounds.any {
it.coneType.isSubtypeOfClass(ownerId, session)
}
}
is ConeFlexibleType -> {
lowerBound.isSubtypeOfClass(ownerId, session)
}
is ConeDefinitelyNotNullType -> {
original.isSubtypeOfClass(ownerId, session)
}
else -> false
}
} }
private fun FirClass.isSubClass(ownerId: ClassId, session: FirSession): Boolean { private fun FirClass.isSubClass(ownerId: ClassId, session: FirSession): Boolean {
@@ -207,14 +251,16 @@ abstract class FirVisibilityChecker : FirSessionComponent {
protected fun canSeeProtectedMemberOf( protected fun canSeeProtectedMemberOf(
containingDeclarationOfUseSite: List<FirDeclaration>, containingDeclarationOfUseSite: List<FirDeclaration>,
dispatchReceiver: ReceiverValue?, dispatchReceiver: ReceiverValue?,
ownerId: ClassId, session: FirSession ownerId: ClassId,
session: FirSession,
isVariableOrNamedFunction: Boolean
): Boolean { ): Boolean {
if (canSeePrivateMemberOf(containingDeclarationOfUseSite, ownerId, session)) return true if (canSeePrivateMemberOf(containingDeclarationOfUseSite, ownerId, session)) return true
for (containingDeclaration in containingDeclarationOfUseSite) { for (containingDeclaration in containingDeclarationOfUseSite) {
if (containingDeclaration !is FirClass) continue if (containingDeclaration !is FirClass) continue
val boundSymbol = containingDeclaration.symbol val boundSymbol = containingDeclaration.symbol
if (canSeeProtectedMemberOf(boundSymbol.fir, dispatchReceiver, ownerId, session)) return true if (canSeeProtectedMemberOf(boundSymbol.fir, dispatchReceiver, ownerId, session, isVariableOrNamedFunction)) return true
} }
return false return false
+2 -2
View File
@@ -6,8 +6,8 @@ class B: A()
class C: A() { class C: A() {
fun bar() { fun bar() {
A().foo() A().<!INVISIBLE_REFERENCE!>foo<!>()
B().foo() B().<!INVISIBLE_REFERENCE!>foo<!>()
} }
} }
@@ -13,6 +13,6 @@ class B {
class C: A() { class C: A() {
init { init {
B.foo() // Error: receiver is not suitable B.<!INVISIBLE_REFERENCE!>foo<!>() // Error: receiver is not suitable
} }
} }
@@ -20,6 +20,6 @@ public class AppServiceModule : AbstractModule<String>() {
inline fun <reified T> AbstractModule<Int>.bind() { inline fun <reified T> AbstractModule<Int>.bind() {
val x = <!PROTECTED_CALL_FROM_PUBLIC_INLINE!>bind<!>(javaClass<T>()) val x = <!PROTECTED_CALL_FROM_PUBLIC_INLINE!>bind<!>(javaClass<T>())
x checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><String>() } // check that Class receiver is used instead of extension one x checkType { _<String>() } // check that Class receiver is used instead of extension one
} }
} }
@@ -21,7 +21,7 @@ class B : A() {
b.foo b.foo
b.bar = b.bar + "" b.bar = b.bar + ""
a.foo a.<!INVISIBLE_REFERENCE!>foo<!>
// TODO: should be INVISIBLE_SETTER // TODO: should be INVISIBLE_SETTER
a.bar = a.bar + "" a.bar = a.bar + ""
@@ -32,7 +32,7 @@ class B : A() {
if (d.x is B) { if (d.x is B) {
d.x.abc // Ok d.x.abc // Ok
d.x.foo d.x.<!INVISIBLE_REFERENCE!>foo<!>
// TODO: should be INVISIBLE_SETTER // TODO: should be INVISIBLE_SETTER
d.x.bar = d.x.bar + "" d.x.bar = d.x.bar + ""
} }
@@ -11,7 +11,7 @@ class Data(var x: A)
class B : A() { class B : A() {
fun baz(a: A, b: B, d: Data) { fun baz(a: A, b: B, d: Data) {
a.foo { } a.<!INVISIBLE_REFERENCE!>foo<!> { }
b.foo { } b.foo { }
@@ -20,7 +20,7 @@ class B : A() {
} }
if (d.x is B) { if (d.x is B) {
d.x.foo {} d.x.<!INVISIBLE_REFERENCE!>foo<!> {}
} }
} }
} }
@@ -11,8 +11,8 @@ fun BaseOuter.foo(): String = ""
class Derived : BaseOuter() { class Derived : BaseOuter() {
fun test(foo: Foo) { fun test(foo: Foo) {
if (foo.base is Derived) { if (foo.base is Derived) {
foo.base.foo() checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><String>() } // Resolved to extension foo.base.foo() checkType { _<String>() } // Resolved to extension
foo.base.bar() foo.base.<!INVISIBLE_REFERENCE!>bar<!>()
} }
} }
} }
@@ -11,10 +11,10 @@ class Derived : Base() {
override fun bar() { } override fun bar() { }
protected fun baz(x: Base) { protected fun baz(x: Base) {
x.foo() x.<!INVISIBLE_REFERENCE!>foo<!>()
x.bar() x.<!INVISIBLE_REFERENCE!>bar<!>()
x.x = x.x + 1 x.<!INVISIBLE_REFERENCE!>x<!> = x.<!INVISIBLE_REFERENCE!>x<!> + 1
x.y = x.y + 1 x.y = x.y + 1
if (x is Derived) { if (x is Derived) {
@@ -72,7 +72,7 @@ class E : C() {
class F : C() { class F : C() {
fun test8(c: C) { fun test8(c: C) {
doSmth(c.i) doSmth(c.<!INVISIBLE_REFERENCE!>i<!>)
} }
} }
@@ -12,7 +12,7 @@ fun foo(javaClass: JavaClass) {
class X : JavaClass() { class X : JavaClass() {
fun foo(other: JavaClass) { fun foo(other: JavaClass) {
doSomething { bar() } doSomething { bar() }
other.doSomething { bar() } other.<!INVISIBLE_REFERENCE!>doSomething<!> { bar() }
} }
} }
@@ -5051,6 +5051,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/EnumWithToString.kt"); runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/EnumWithToString.kt");
} }
@Test
@TestMetadata("FieldVsSyntheticAccessor.kt")
public void testFieldVsSyntheticAccessor() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/FieldVsSyntheticAccessor.kt");
}
@Test @Test
@TestMetadata("IntersectionWithJavaString.kt") @TestMetadata("IntersectionWithJavaString.kt")
public void testIntersectionWithJavaString() throws Exception { public void testIntersectionWithJavaString() throws Exception {