diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/FieldVsSyntheticAccessor.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/FieldVsSyntheticAccessor.fir.txt new file mode 100644 index 00000000000..8b0352800b2 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/FieldVsSyntheticAccessor.fir.txt @@ -0,0 +1,19 @@ +FILE: User.kt + public final class User : R|BaseClass| { + public constructor(): R|test/User| { + super() + } + + public final fun foo(tree: R|BaseClass|): R|kotlin/Unit| { + lval ui: R|ft| = R|/tree|.# + } + + public final fun bar(): R|kotlin/Unit| { + lval ui: R|ft| = this@R|test/User|.R|/BaseClass.ui| + } + + public final fun baz(): R|kotlin/Unit| { + lval ui: R|ft| = this@R|test/User|.R|/BaseClass.ui| + } + + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/FieldVsSyntheticAccessor.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/FieldVsSyntheticAccessor.kt new file mode 100644 index 00000000000..568bc1e4369 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/intellij/FieldVsSyntheticAccessor.kt @@ -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.ui + } + + fun bar() { + val ui = ui + } + + fun baz() { + val ui = this.ui + } +} diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index 01cc3e2f555..7f488dcfbd6 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -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 { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index 7e743d10820..c17092eab57 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -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 { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaVisibilityChecker.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaVisibilityChecker.kt index 33a25349c59..a7fa38c3ef3 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaVisibilityChecker.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/FirJavaVisibilityChecker.kt @@ -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 + ) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt index 612c49b9247..2aaf6b9aa94 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt @@ -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, 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 diff --git a/compiler/testData/diagnostics/tests/scopes/kt9430.fir.kt b/compiler/testData/diagnostics/tests/scopes/kt9430.fir.kt index dc6cae66ada..1db50eab125 100644 --- a/compiler/testData/diagnostics/tests/scopes/kt9430.fir.kt +++ b/compiler/testData/diagnostics/tests/scopes/kt9430.fir.kt @@ -6,8 +6,8 @@ class B: A() class C: A() { fun bar() { - A().foo() - B().foo() + A().foo() + B().foo() } } diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.fir.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.fir.kt index d21b87baa85..989b88c592c 100644 --- a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.fir.kt +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/complexCompanion.fir.kt @@ -13,6 +13,6 @@ class B { class C: A() { init { - B.foo() // Error: receiver is not suitable + B.foo() // Error: receiver is not suitable } } diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.fir.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.fir.kt index c569a82c051..13f51aa955d 100644 --- a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.fir.kt +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/kt7971.fir.kt @@ -20,6 +20,6 @@ public class AppServiceModule : AbstractModule() { inline fun AbstractModule.bind() { val x = bind(javaClass()) - x checkType { _() } // check that Class receiver is used instead of extension one + x checkType { _() } // check that Class receiver is used instead of extension one } } diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/syntheticPropertyExtensions.fir.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/syntheticPropertyExtensions.fir.kt index aab40362af0..2b6a674e52d 100644 --- a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/syntheticPropertyExtensions.fir.kt +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/syntheticPropertyExtensions.fir.kt @@ -21,7 +21,7 @@ class B : A() { b.foo b.bar = b.bar + "" - a.foo + a.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.foo // TODO: should be INVISIBLE_SETTER d.x.bar = d.x.bar + "" } diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/syntheticSAMExtensions.fir.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/syntheticSAMExtensions.fir.kt index 442e01b09da..7ef9df01c66 100644 --- a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/syntheticSAMExtensions.fir.kt +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/syntheticSAMExtensions.fir.kt @@ -11,7 +11,7 @@ class Data(var x: A) class B : A() { fun baz(a: A, b: B, d: Data) { - a.foo { } + a.foo { } b.foo { } @@ -20,7 +20,7 @@ class B : A() { } if (d.x is B) { - d.x.foo {} + d.x.foo {} } } } diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.fir.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.fir.kt index 8f0e9eab162..696d0b72931 100644 --- a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.fir.kt +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/unstableSmartCast.fir.kt @@ -11,8 +11,8 @@ fun BaseOuter.foo(): String = "" class Derived : BaseOuter() { fun test(foo: Foo) { if (foo.base is Derived) { - foo.base.foo() checkType { _() } // Resolved to extension - foo.base.bar() + foo.base.foo() checkType { _() } // Resolved to extension + foo.base.bar() } } } diff --git a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.fir.kt b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.fir.kt index e6f83a964cc..0086e6c7c70 100644 --- a/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.fir.kt +++ b/compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.fir.kt @@ -11,10 +11,10 @@ class Derived : Base() { override fun bar() { } protected fun baz(x: Base) { - x.foo() - x.bar() + x.foo() + x.bar() - x.x = x.x + 1 + x.x = x.x + 1 x.y = x.y + 1 if (x is Derived) { diff --git a/compiler/testData/diagnostics/tests/scopes/visibility.fir.kt b/compiler/testData/diagnostics/tests/scopes/visibility.fir.kt index c9c1589337a..75bdd14f6dc 100644 --- a/compiler/testData/diagnostics/tests/scopes/visibility.fir.kt +++ b/compiler/testData/diagnostics/tests/scopes/visibility.fir.kt @@ -72,7 +72,7 @@ class E : C() { class F : C() { fun test8(c: C) { - doSmth(c.i) + doSmth(c.i) } } diff --git a/compiler/testData/diagnostics/tests/syntheticExtensions/samAdapters/Protected.fir.kt b/compiler/testData/diagnostics/tests/syntheticExtensions/samAdapters/Protected.fir.kt index c6fff226bdc..f0444beb6e7 100644 --- a/compiler/testData/diagnostics/tests/syntheticExtensions/samAdapters/Protected.fir.kt +++ b/compiler/testData/diagnostics/tests/syntheticExtensions/samAdapters/Protected.fir.kt @@ -12,7 +12,7 @@ fun foo(javaClass: JavaClass) { class X : JavaClass() { fun foo(other: JavaClass) { doSomething { bar() } - other.doSomething { bar() } + other.doSomething { bar() } } } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java index ae00db6bb32..9595aa0b16e 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java @@ -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 {