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");
|
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
|
@Nested
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
|||||||
+6
@@ -22527,6 +22527,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt");
|
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
|
@Nested
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
|||||||
+6
@@ -22527,6 +22527,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt");
|
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
|
@Nested
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
|||||||
@@ -5,14 +5,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.java
|
package org.jetbrains.kotlin.fir.java
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.descriptors.Visibility
|
import org.jetbrains.kotlin.descriptors.Visibility
|
||||||
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
|
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
|
||||||
import org.jetbrains.kotlin.fir.*
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
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.declarations.utils.visibility
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticPropertySymbol
|
||||||
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.*
|
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||||
|
|
||||||
@NoMutableState
|
@NoMutableState
|
||||||
object FirJavaVisibilityChecker : FirVisibilityChecker() {
|
object FirJavaVisibilityChecker : FirVisibilityChecker() {
|
||||||
@@ -22,18 +26,25 @@ object FirJavaVisibilityChecker : FirVisibilityChecker() {
|
|||||||
useSiteFile: FirFile,
|
useSiteFile: FirFile,
|
||||||
containingDeclarations: List<FirDeclaration>,
|
containingDeclarations: List<FirDeclaration>,
|
||||||
dispatchReceiver: ReceiverValue?,
|
dispatchReceiver: ReceiverValue?,
|
||||||
session: FirSession
|
session: FirSession,
|
||||||
|
isCallToPropertySetter: Boolean,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (declarationVisibility) {
|
return when (declarationVisibility) {
|
||||||
JavaVisibilities.ProtectedAndPackage, JavaVisibilities.ProtectedStaticVisibility -> {
|
JavaVisibilities.ProtectedAndPackage, JavaVisibilities.ProtectedStaticVisibility -> {
|
||||||
if (symbol.packageFqName() == useSiteFile.packageFqName) {
|
if (symbol.packageFqName() == useSiteFile.packageFqName) {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
val ownerLookupTag = symbol.getOwnerLookupTag()
|
val ownerLookupTag = symbol.getOwnerLookupTag() ?: return false
|
||||||
ownerLookupTag != null && canSeeProtectedMemberOf(
|
if (canSeeProtectedMemberOf(
|
||||||
containingDeclarations, dispatchReceiver, ownerLookupTag, session,
|
containingDeclarations, dispatchReceiver, ownerLookupTag, session,
|
||||||
isVariableOrNamedFunction = symbol is FirVariableSymbol || symbol is FirNamedFunctionSymbol
|
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.*
|
||||||
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.expressions.FirPropertyAccessExpression
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.*
|
import org.jetbrains.kotlin.fir.resolve.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
|
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.resolve.calls.ReceiverValue
|
||||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
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.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.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||||
@@ -47,7 +48,8 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
useSiteFile: FirFile,
|
useSiteFile: FirFile,
|
||||||
containingDeclarations: List<FirDeclaration>,
|
containingDeclarations: List<FirDeclaration>,
|
||||||
dispatchReceiver: ReceiverValue?,
|
dispatchReceiver: ReceiverValue?,
|
||||||
session: FirSession
|
session: FirSession,
|
||||||
|
isCallToPropertySetter: Boolean,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -90,7 +92,14 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
return true
|
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()
|
val backingField = declaration.getBackingFieldIfApplicable()
|
||||||
|
|
||||||
if (visible && backingField != null) {
|
if (visible && backingField != null) {
|
||||||
@@ -100,6 +109,7 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
useSiteFile,
|
useSiteFile,
|
||||||
containingDeclarations,
|
containingDeclarations,
|
||||||
candidate.dispatchReceiverValue,
|
candidate.dispatchReceiverValue,
|
||||||
|
candidate.callInfo.callSite is FirVariableAssignment,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +121,8 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
session: FirSession,
|
session: FirSession,
|
||||||
useSiteFile: FirFile,
|
useSiteFile: FirFile,
|
||||||
containingDeclarations: List<FirDeclaration>,
|
containingDeclarations: List<FirDeclaration>,
|
||||||
dispatchReceiver: ReceiverValue?
|
dispatchReceiver: ReceiverValue?,
|
||||||
|
isCallToPropertySetter: Boolean = false,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
require(declaration is FirDeclaration)
|
require(declaration is FirDeclaration)
|
||||||
val provider = session.firProvider
|
val provider = session.firProvider
|
||||||
@@ -166,7 +177,8 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
useSiteFile,
|
useSiteFile,
|
||||||
containingDeclarations,
|
containingDeclarations,
|
||||||
dispatchReceiver,
|
dispatchReceiver,
|
||||||
session
|
session,
|
||||||
|
isCallToPropertySetter,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -177,7 +189,8 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
|||||||
useSiteFile: FirFile,
|
useSiteFile: FirFile,
|
||||||
containingDeclarations: List<FirDeclaration>,
|
containingDeclarations: List<FirDeclaration>,
|
||||||
dispatchReceiver: ReceiverValue?,
|
dispatchReceiver: ReceiverValue?,
|
||||||
session: FirSession
|
session: FirSession,
|
||||||
|
isCallToPropertySetter: Boolean,
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
private fun canSeePrivateMemberOf(
|
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) {
|
fun foo(javaClass: JavaClass) {
|
||||||
val v = javaClass.<!INVISIBLE_REFERENCE!>something<!>
|
val v = javaClass.<!INVISIBLE_REFERENCE!>something<!>
|
||||||
javaClass.<!INVISIBLE_REFERENCE!>something<!> = 1
|
javaClass.something = 1
|
||||||
javaClass.<!INVISIBLE_REFERENCE, INVISIBLE_REFERENCE!>something<!>++
|
javaClass.<!INVISIBLE_REFERENCE!>something<!>++
|
||||||
}
|
}
|
||||||
|
|
||||||
// FILE: JavaClass.java
|
// FILE: JavaClass.java
|
||||||
|
|||||||
Generated
+6
@@ -22533,6 +22533,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/properties/lateinitOnTopLevel.kt");
|
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
|
@Nested
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
@TestMetadata("compiler/testData/diagnostics/tests/properties/backingField")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
|||||||
Reference in New Issue
Block a user