FIR checker: resolve property with protected getter and public setter
Assuming Java class `Super` has a protected `getName` method and a public `setName` method. Then, in a subclass of `Super`, FE1.0 allows calls to `setName` via the property syntax if the property is protected and invisible due to dispatch receiver is not `this` or `super`.
This commit is contained in:
committed by
TeamCityServer
parent
f27f91b03a
commit
f35680f0a4
+6
@@ -22527,6 +22527,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("protectedGetterWithPublicSetter.kt")
|
||||
public void testProtectedGetterWithPublicSetter() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/properties/protectedGetterWithPublicSetter.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+6
@@ -22527,6 +22527,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("protectedGetterWithPublicSetter.kt")
|
||||
public void testProtectedGetterWithPublicSetter() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/properties/protectedGetterWithPublicSetter.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+6
@@ -22527,6 +22527,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("protectedGetterWithPublicSetter.kt")
|
||||
public void testProtectedGetterWithPublicSetter() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/properties/protectedGetterWithPublicSetter.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
@@ -5,14 +5,18 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.java
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
|
||||
@NoMutableState
|
||||
object FirJavaVisibilityChecker : FirVisibilityChecker() {
|
||||
@@ -22,18 +26,25 @@ object FirJavaVisibilityChecker : FirVisibilityChecker() {
|
||||
useSiteFile: FirFile,
|
||||
containingDeclarations: List<FirDeclaration>,
|
||||
dispatchReceiver: ReceiverValue?,
|
||||
session: FirSession
|
||||
session: FirSession,
|
||||
isCallToPropertySetter: Boolean,
|
||||
): Boolean {
|
||||
return when (declarationVisibility) {
|
||||
JavaVisibilities.ProtectedAndPackage, JavaVisibilities.ProtectedStaticVisibility -> {
|
||||
if (symbol.packageFqName() == useSiteFile.packageFqName) {
|
||||
true
|
||||
} else {
|
||||
val ownerLookupTag = symbol.getOwnerLookupTag()
|
||||
ownerLookupTag != null && canSeeProtectedMemberOf(
|
||||
containingDeclarations, dispatchReceiver, ownerLookupTag, session,
|
||||
isVariableOrNamedFunction = symbol is FirVariableSymbol || symbol is FirNamedFunctionSymbol
|
||||
)
|
||||
val ownerLookupTag = symbol.getOwnerLookupTag() ?: return false
|
||||
if (canSeeProtectedMemberOf(
|
||||
containingDeclarations, dispatchReceiver, ownerLookupTag, session,
|
||||
isVariableOrNamedFunction = symbol is FirVariableSymbol || symbol is FirNamedFunctionSymbol
|
||||
)
|
||||
) return true
|
||||
|
||||
// FE1.0 allows calling public setters with property assignment syntax if the getter is protected.
|
||||
if (!isCallToPropertySetter || symbol !is FirSyntheticPropertySymbol) return false
|
||||
val setterVisibility = symbol.setterSymbol?.visibility
|
||||
setterVisibility != null && setterVisibility == Visibilities.Public
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ 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.expressions.FirVariableAssignment
|
||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||
import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
|
||||
@@ -18,9 +19,9 @@ import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
@@ -47,7 +48,8 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
useSiteFile: FirFile,
|
||||
containingDeclarations: List<FirDeclaration>,
|
||||
dispatchReceiver: ReceiverValue?,
|
||||
session: FirSession
|
||||
session: FirSession,
|
||||
isCallToPropertySetter: Boolean,
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
@@ -90,7 +92,14 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
return true
|
||||
}
|
||||
|
||||
val visible = isVisible(declaration, session, useSiteFile, containingDeclarations, candidate.dispatchReceiverValue)
|
||||
val visible = isVisible(
|
||||
declaration,
|
||||
session,
|
||||
useSiteFile,
|
||||
containingDeclarations,
|
||||
candidate.dispatchReceiverValue,
|
||||
candidate.callInfo.callSite is FirVariableAssignment
|
||||
)
|
||||
val backingField = declaration.getBackingFieldIfApplicable()
|
||||
|
||||
if (visible && backingField != null) {
|
||||
@@ -100,6 +109,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
useSiteFile,
|
||||
containingDeclarations,
|
||||
candidate.dispatchReceiverValue,
|
||||
candidate.callInfo.callSite is FirVariableAssignment,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -111,7 +121,8 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
session: FirSession,
|
||||
useSiteFile: FirFile,
|
||||
containingDeclarations: List<FirDeclaration>,
|
||||
dispatchReceiver: ReceiverValue?
|
||||
dispatchReceiver: ReceiverValue?,
|
||||
isCallToPropertySetter: Boolean = false,
|
||||
): Boolean {
|
||||
require(declaration is FirDeclaration)
|
||||
val provider = session.firProvider
|
||||
@@ -166,7 +177,8 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
useSiteFile,
|
||||
containingDeclarations,
|
||||
dispatchReceiver,
|
||||
session
|
||||
session,
|
||||
isCallToPropertySetter,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -177,7 +189,8 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
useSiteFile: FirFile,
|
||||
containingDeclarations: List<FirDeclaration>,
|
||||
dispatchReceiver: ReceiverValue?,
|
||||
session: FirSession
|
||||
session: FirSession,
|
||||
isCallToPropertySetter: Boolean,
|
||||
): Boolean
|
||||
|
||||
private fun canSeePrivateMemberOf(
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// FILE: j/Super.java
|
||||
package j
|
||||
|
||||
public abstract class Super {
|
||||
protected abstract String getName();
|
||||
public abstract void setName(String s);
|
||||
}
|
||||
|
||||
// FILE: k/test.kt
|
||||
|
||||
package k
|
||||
import j.Super
|
||||
|
||||
abstract class Sub: Super() {
|
||||
fun test(s: Super) {
|
||||
s.<!INVISIBLE_REFERENCE!>name<!>
|
||||
s.<!INVISIBLE_REFERENCE!>getName<!>()
|
||||
s.name = ""
|
||||
s.name = s.<!INVISIBLE_REFERENCE!>name<!>
|
||||
s.setName("")
|
||||
}
|
||||
}
|
||||
|
||||
fun test(s: Super) {
|
||||
s.<!INVISIBLE_REFERENCE!>name<!>
|
||||
s.<!INVISIBLE_REFERENCE!>getName<!>()
|
||||
s.name = ""
|
||||
s.name = s.<!INVISIBLE_REFERENCE!>name<!>
|
||||
s.setName("")
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// FILE: j/Super.java
|
||||
package j
|
||||
|
||||
public abstract class Super {
|
||||
protected abstract String getName();
|
||||
public abstract void setName(String s);
|
||||
}
|
||||
|
||||
// FILE: k/test.kt
|
||||
|
||||
package k
|
||||
import j.Super
|
||||
|
||||
abstract class Sub: Super() {
|
||||
fun test(s: Super) {
|
||||
s.<!INVISIBLE_MEMBER!>name<!>
|
||||
s.<!INVISIBLE_MEMBER!>getName<!>()
|
||||
s.name = ""
|
||||
s.name = s.<!INVISIBLE_MEMBER!>name<!>
|
||||
s.setName("")
|
||||
}
|
||||
}
|
||||
|
||||
fun test(s: Super) {
|
||||
s.<!INVISIBLE_MEMBER!>name<!>
|
||||
s.<!INVISIBLE_MEMBER!>getName<!>()
|
||||
s.<!INVISIBLE_MEMBER!>name<!> = ""
|
||||
s.<!INVISIBLE_MEMBER!>name<!> = s.<!INVISIBLE_MEMBER!>name<!>
|
||||
s.setName("")
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package
|
||||
|
||||
package j {
|
||||
|
||||
public abstract class Super {
|
||||
public constructor Super()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
protected/*protected and package*/ abstract fun getName(): kotlin.String!
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public abstract fun setName(/*0*/ s: kotlin.String!): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
package k {
|
||||
public fun test(/*0*/ s: j.Super): kotlin.Unit
|
||||
|
||||
public abstract class Sub : j.Super {
|
||||
public constructor Sub()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
protected/*protected and package*/ abstract override /*1*/ /*fake_override*/ fun getName(): kotlin.String!
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public abstract override /*1*/ /*fake_override*/ fun setName(/*0*/ s: kotlin.String!): kotlin.Unit
|
||||
public final fun test(/*0*/ s: j.Super): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
Vendored
+2
-2
@@ -5,8 +5,8 @@ import JavaClass
|
||||
|
||||
fun foo(javaClass: JavaClass) {
|
||||
val v = javaClass.<!INVISIBLE_REFERENCE!>something<!>
|
||||
javaClass.<!INVISIBLE_REFERENCE!>something<!> = 1
|
||||
javaClass.<!INVISIBLE_REFERENCE, INVISIBLE_REFERENCE!>something<!>++
|
||||
javaClass.something = 1
|
||||
javaClass.<!INVISIBLE_REFERENCE!>something<!>++
|
||||
}
|
||||
|
||||
// FILE: JavaClass.java
|
||||
|
||||
Generated
+6
@@ -22533,6 +22533,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("protectedGetterWithPublicSetter.kt")
|
||||
public void testProtectedGetterWithPublicSetter() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/properties/protectedGetterWithPublicSetter.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
Reference in New Issue
Block a user