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:
Tianyu Geng
2021-09-07 15:14:58 -07:00
committed by TeamCityServer
parent f27f91b03a
commit f35680f0a4
10 changed files with 151 additions and 16 deletions
@@ -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")
@@ -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")
@@ -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(
@@ -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("")
}
@@ -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("")
}
@@ -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
}
}
@@ -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
@@ -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")