FIR: add proper receiver check for protected visibility #KT-48378 Fixed
This commit is contained in:
committed by
TeamCityServer
parent
1c1b9547c1
commit
08e498f1f2
Vendored
+19
@@ -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|
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+23
@@ -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
|
||||
}
|
||||
}
|
||||
+6
@@ -5051,6 +5051,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
|
||||
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
|
||||
@TestMetadata("IntersectionWithJavaString.kt")
|
||||
public void testIntersectionWithJavaString() throws Exception {
|
||||
|
||||
+6
@@ -5051,6 +5051,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
||||
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
|
||||
@TestMetadata("IntersectionWithJavaString.kt")
|
||||
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.resolve.calls.ReceiverValue
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
|
||||
@NoMutableState
|
||||
object FirJavaVisibilityChecker : FirVisibilityChecker() {
|
||||
@@ -29,7 +30,10 @@ object FirJavaVisibilityChecker : FirVisibilityChecker() {
|
||||
true
|
||||
} else {
|
||||
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.fir.declarations.*
|
||||
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.ExpressionReceiverValue
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol
|
||||
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.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
@@ -116,7 +114,10 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
|
||||
Visibilities.Protected -> {
|
||||
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(
|
||||
@@ -173,14 +174,57 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
private fun canSeeProtectedMemberOf(
|
||||
containingUseSiteClass: FirClass,
|
||||
dispatchReceiver: ReceiverValue?,
|
||||
ownerId: ClassId, session: FirSession
|
||||
ownerId: ClassId,
|
||||
session: FirSession,
|
||||
isVariableOrNamedFunction: Boolean
|
||||
): Boolean {
|
||||
dispatchReceiver?.ownerIfCompanion(session)?.let { companionOwnerClassId ->
|
||||
if (containingUseSiteClass.isSubClass(companionOwnerClassId, session)) return true
|
||||
}
|
||||
|
||||
// TODO: Add check for receiver, see org.jetbrains.kotlin.descriptors.Visibility#doesReceiverFitForProtectedVisibility
|
||||
return containingUseSiteClass.isSubClass(ownerId, session)
|
||||
return when {
|
||||
!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 {
|
||||
@@ -207,14 +251,16 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
protected fun canSeeProtectedMemberOf(
|
||||
containingDeclarationOfUseSite: List<FirDeclaration>,
|
||||
dispatchReceiver: ReceiverValue?,
|
||||
ownerId: ClassId, session: FirSession
|
||||
ownerId: ClassId,
|
||||
session: FirSession,
|
||||
isVariableOrNamedFunction: Boolean
|
||||
): Boolean {
|
||||
if (canSeePrivateMemberOf(containingDeclarationOfUseSite, ownerId, session)) return true
|
||||
|
||||
for (containingDeclaration in containingDeclarationOfUseSite) {
|
||||
if (containingDeclaration !is FirClass) continue
|
||||
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
|
||||
|
||||
@@ -6,8 +6,8 @@ class B: A()
|
||||
|
||||
class C: A() {
|
||||
fun bar() {
|
||||
A().foo()
|
||||
B().foo()
|
||||
A().<!INVISIBLE_REFERENCE!>foo<!>()
|
||||
B().<!INVISIBLE_REFERENCE!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -13,6 +13,6 @@ class B {
|
||||
|
||||
class C: A() {
|
||||
init {
|
||||
B.foo() // Error: receiver is not suitable
|
||||
B.<!INVISIBLE_REFERENCE!>foo<!>() // Error: receiver is not suitable
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,6 +20,6 @@ public class AppServiceModule : AbstractModule<String>() {
|
||||
inline fun <reified T> AbstractModule<Int>.bind() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -21,7 +21,7 @@ class B : A() {
|
||||
b.foo
|
||||
b.bar = b.bar + ""
|
||||
|
||||
a.foo
|
||||
a.<!INVISIBLE_REFERENCE!>foo<!>
|
||||
// TODO: should be INVISIBLE_SETTER
|
||||
a.bar = a.bar + ""
|
||||
|
||||
@@ -32,7 +32,7 @@ class B : A() {
|
||||
|
||||
if (d.x is B) {
|
||||
d.x.abc // Ok
|
||||
d.x.foo
|
||||
d.x.<!INVISIBLE_REFERENCE!>foo<!>
|
||||
// TODO: should be INVISIBLE_SETTER
|
||||
d.x.bar = d.x.bar + ""
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -11,7 +11,7 @@ class Data(var x: A)
|
||||
|
||||
class B : A() {
|
||||
fun baz(a: A, b: B, d: Data) {
|
||||
a.foo { }
|
||||
a.<!INVISIBLE_REFERENCE!>foo<!> { }
|
||||
|
||||
b.foo { }
|
||||
|
||||
@@ -20,7 +20,7 @@ class B : A() {
|
||||
}
|
||||
|
||||
if (d.x is B) {
|
||||
d.x.foo {}
|
||||
d.x.<!INVISIBLE_REFERENCE!>foo<!> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@ fun BaseOuter.foo(): String = ""
|
||||
class Derived : BaseOuter() {
|
||||
fun test(foo: Foo) {
|
||||
if (foo.base is Derived) {
|
||||
foo.base.foo() checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><String>() } // Resolved to extension
|
||||
foo.base.bar()
|
||||
foo.base.foo() checkType { _<String>() } // Resolved to extension
|
||||
foo.base.<!INVISIBLE_REFERENCE!>bar<!>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -11,10 +11,10 @@ class Derived : Base() {
|
||||
override fun bar() { }
|
||||
|
||||
protected fun baz(x: Base) {
|
||||
x.foo()
|
||||
x.bar()
|
||||
x.<!INVISIBLE_REFERENCE!>foo<!>()
|
||||
x.<!INVISIBLE_REFERENCE!>bar<!>()
|
||||
|
||||
x.x = x.x + 1
|
||||
x.<!INVISIBLE_REFERENCE!>x<!> = x.<!INVISIBLE_REFERENCE!>x<!> + 1
|
||||
x.y = x.y + 1
|
||||
|
||||
if (x is Derived) {
|
||||
|
||||
@@ -72,7 +72,7 @@ class E : C() {
|
||||
|
||||
class F : C() {
|
||||
fun test8(c: C) {
|
||||
doSmth(c.i)
|
||||
doSmth(c.<!INVISIBLE_REFERENCE!>i<!>)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ fun foo(javaClass: JavaClass) {
|
||||
class X : JavaClass() {
|
||||
fun foo(other: JavaClass) {
|
||||
doSomething { bar() }
|
||||
other.doSomething { bar() }
|
||||
other.<!INVISIBLE_REFERENCE!>doSomething<!> { bar() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -5051,6 +5051,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis
|
||||
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
|
||||
@TestMetadata("IntersectionWithJavaString.kt")
|
||||
public void testIntersectionWithJavaString() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user