FIR: implement protected visibility checker #KT-34788 Fixed

This commit is contained in:
Mikhail Glukhikh
2019-11-14 17:45:31 +03:00
parent ad3659d1d9
commit 802ed9b502
14 changed files with 491 additions and 51 deletions
@@ -28,9 +28,14 @@ abstract class FirProvider : FirSymbolProvider() {
abstract fun getFirClassifierContainerFile(fqName: ClassId): FirFile
abstract fun getFirClassifierContainerFileIfAny(fqName: ClassId): FirFile?
fun getFirClassifierContainerFile(symbol: FirClassLikeSymbol<*>): FirFile =
getFirClassifierContainerFile(symbol.classId)
fun getFirClassifierContainerFileIfAny(symbol: FirClassLikeSymbol<*>): FirFile? =
getFirClassifierContainerFileIfAny(symbol.classId)
abstract fun getFirCallableContainerFile(symbol: FirCallableSymbol<*>): FirFile?
companion object {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.render
@@ -274,16 +275,74 @@ internal object CheckVisibility : CheckerStage() {
}
}
// 'local' isn't taken into account here
private fun ClassId.isSame(other: ClassId): Boolean =
packageFqName == other.packageFqName && relativeClassName == other.relativeClassName
private fun ImplicitReceiverStack.canSeePrivateMemberOf(ownerId: ClassId): Boolean {
for (implicitReceiverValue in receiversAsReversed()) {
if (implicitReceiverValue !is ImplicitDispatchReceiverValue) continue
val boundSymbol = implicitReceiverValue.boundSymbol
if (boundSymbol.classId == ownerId) return true
if (boundSymbol is FirRegularClassSymbol && boundSymbol.fir.companionObject?.symbol?.classId == ownerId) return true
if (boundSymbol.classId.isSame(ownerId)) {
return true
}
if (boundSymbol is FirRegularClassSymbol && boundSymbol.fir.companionObject?.symbol?.classId?.isSame(ownerId) == true) {
return true
}
}
return false
}
private fun FirRegularClassSymbol.canSeeProtectedMemberOf(
ownerId: ClassId,
session: FirSession,
visited: MutableSet<ClassId>
): Boolean {
if (classId in visited) return false
visited += classId
if (classId.isSame(ownerId)) return true
val superTypes = fir.superConeTypes
for (superType in superTypes) {
val superTypeSymbol = superType.lookupTag.toSymbol(session) as? FirRegularClassSymbol ?: continue
if (superTypeSymbol.canSeeProtectedMemberOf(ownerId, session, visited)) return true
}
return false
}
private fun ImplicitReceiverStack.canSeeProtectedMemberOf(ownerId: ClassId, session: FirSession): Boolean {
if (canSeePrivateMemberOf(ownerId)) return true
val visited = mutableSetOf<ClassId>()
for (implicitReceiverValue in receiversAsReversed()) {
if (implicitReceiverValue !is ImplicitDispatchReceiverValue) continue
val boundSymbol = implicitReceiverValue.boundSymbol
val superTypes = boundSymbol.fir.superConeTypes
for (superType in superTypes) {
val superTypeSymbol = superType.lookupTag.toSymbol(session) as? FirRegularClassSymbol ?: continue
if (superTypeSymbol.canSeeProtectedMemberOf(ownerId, session, visited)) return true
}
}
return false
}
private fun AbstractFirBasedSymbol<*>.getOwnerId(): ClassId? {
return when (this) {
is FirClassLikeSymbol<*> -> {
val ownerId = classId.outerClassId
if (classId.isLocal) {
ownerId?.asLocal() ?: classId
} else {
ownerId
}
}
is FirCallableSymbol<*> -> {
callableId.classId
}
else -> {
throw AssertionError("Unsupported owner search for ${fir.javaClass}: ${fir.render()}")
}
}
}
private fun ClassId.asLocal(): ClassId = ClassId(packageFqName, relativeClassName, true)
private suspend fun checkVisibility(
@@ -294,69 +353,42 @@ internal object CheckVisibility : CheckerStage() {
): Boolean {
val useSiteFile = callInfo.containingFile
val implicitReceiverStack = callInfo.implicitReceiverStack
val session = callInfo.session
val provider = session.firProvider
val candidateFile = when (symbol) {
is FirClassLikeSymbol<*> -> provider.getFirClassifierContainerFileIfAny(symbol)
is FirCallableSymbol<*> -> provider.getFirCallableContainerFile(symbol)
else -> null
}
val ownerId = symbol.getOwnerId()
val visible = when (declaration.visibility) {
JavaVisibilities.PACKAGE_VISIBILITY -> {
symbol.packageFqName() == useSiteFile.packageFqName
}
Visibilities.INTERNAL -> {
declaration.session == callInfo.session
}
Visibilities.PRIVATE, Visibilities.PRIVATE_TO_THIS -> {
if (declaration.session == callInfo.session) {
val provider = callInfo.session.firProvider
when (symbol) {
is FirClassLikeSymbol<*> -> {
val classId = symbol.classId
val ownerId = classId.outerClassId
when {
classId.isLocal -> {
// Normally should not be here
implicitReceiverStack.canSeePrivateMemberOf(ownerId?.asLocal() ?: classId)
}
ownerId == null -> {
// Top-level: visible in file
provider.getFirClassifierContainerFile(classId) == useSiteFile
}
else -> {
// Member: visible inside parent class, including all its member classes
implicitReceiverStack.canSeePrivateMemberOf(ownerId)
}
}
}
is FirCallableSymbol<*> -> {
val candidateFile = provider.getFirCallableContainerFile(symbol)
val ownerId = symbol.callableId.classId
when {
candidateFile == null -> {
// Local
ownerId != null && implicitReceiverStack.canSeePrivateMemberOf(ownerId.asLocal())
}
ownerId == null -> {
// Top-level: visible in file
candidateFile == useSiteFile
}
else -> {
// Member: visible inside parent class, including all its member classes
implicitReceiverStack.canSeePrivateMemberOf(ownerId)
}
}
}
else -> {
throw AssertionError("Unsupported visibility check for ${declaration.javaClass}: ${declaration.render()}")
}
if (ownerId == null) {
// Top-level: visible in file
candidateFile == useSiteFile
} else {
// Member: visible inside parent class, including all its member classes
implicitReceiverStack.canSeePrivateMemberOf(ownerId)
}
} else {
false
}
}
Visibilities.INTERNAL -> {
declaration.session == callInfo.session
}
Visibilities.PROTECTED -> {
true // TODO: Support protected visibility
ownerId != null && implicitReceiverStack.canSeeProtectedMemberOf(ownerId, session)
}
JavaVisibilities.PROTECTED_AND_PACKAGE -> {
JavaVisibilities.PROTECTED_AND_PACKAGE, JavaVisibilities.PROTECTED_STATIC_VISIBILITY -> {
if (symbol.packageFqName() == useSiteFile.packageFqName) {
true
} else {
true // TODO: Support protected visibility
ownerId != null && implicitReceiverStack.canSeeProtectedMemberOf(ownerId, session)
}
}
else -> true
@@ -47,6 +47,10 @@ class FirProviderImpl(val session: FirSession) : FirProvider() {
return state.classifierContainerFileMap[fqName] ?: error("Couldn't find container for $fqName")
}
override fun getFirClassifierContainerFileIfAny(fqName: ClassId): FirFile? {
return state.classifierContainerFileMap[fqName]
}
override fun getClassNamesInPackage(fqName: FqName): Set<Name> {
return state.classesInPackage[fqName] ?: emptySet()
}
@@ -0,0 +1,34 @@
open class Outer {
private class PrivateNested
private inner class PrivateInner
protected class ProtectedNested
protected inner class ProtectedInner
public class PublicNested
public inner class PublicInner
}
class Derived : Outer() {
fun foo() {
Outer.<!INAPPLICABLE_CANDIDATE!>PrivateNested<!>()
super.<!INAPPLICABLE_CANDIDATE!>PrivateInner<!>()
Outer.ProtectedNested()
super.ProtectedInner()
Outer.PublicNested()
super.PublicInner()
}
}
fun foo() {
Outer.<!INAPPLICABLE_CANDIDATE!>PrivateNested<!>()
Outer().<!INAPPLICABLE_CANDIDATE!>PrivateInner<!>()
Outer.<!INAPPLICABLE_CANDIDATE!>ProtectedNested<!>()
Outer().<!INAPPLICABLE_CANDIDATE!>ProtectedInner<!>()
Outer.PublicNested()
Outer().PublicInner()
}
@@ -0,0 +1,72 @@
FILE: nestedVisibility.kt
public open class Outer : R|kotlin/Any| {
public constructor(): R|Outer| {
super<R|kotlin/Any|>()
}
private final class PrivateNested : R|kotlin/Any| {
public constructor(): R|Outer.PrivateNested| {
super<R|kotlin/Any|>()
}
}
private final inner class PrivateInner : R|kotlin/Any| {
public constructor(): R|Outer.PrivateInner| {
super<R|kotlin/Any|>()
}
}
protected final class ProtectedNested : R|kotlin/Any| {
public constructor(): R|Outer.ProtectedNested| {
super<R|kotlin/Any|>()
}
}
protected final inner class ProtectedInner : R|kotlin/Any| {
public constructor(): R|Outer.ProtectedInner| {
super<R|kotlin/Any|>()
}
}
public final class PublicNested : R|kotlin/Any| {
public constructor(): R|Outer.PublicNested| {
super<R|kotlin/Any|>()
}
}
public final inner class PublicInner : R|kotlin/Any| {
public constructor(): R|Outer.PublicInner| {
super<R|kotlin/Any|>()
}
}
}
public final class Derived : R|Outer| {
public constructor(): R|Derived| {
super<R|Outer|>()
}
public final fun foo(): R|kotlin/Unit| {
Q|Outer|.<Inapplicable(HIDDEN): [/Outer.PrivateNested.PrivateNested]>#()
super<R|Outer|>.<Inapplicable(HIDDEN): [/Outer.PrivateInner.PrivateInner]>#()
Q|Outer|.R|/Outer.ProtectedNested.ProtectedNested|()
super<R|Outer|>.R|/Outer.ProtectedInner.ProtectedInner|()
Q|Outer|.R|/Outer.PublicNested.PublicNested|()
super<R|Outer|>.R|/Outer.PublicInner.PublicInner|()
}
}
public final fun foo(): R|kotlin/Unit| {
Q|Outer|.<Inapplicable(HIDDEN): [/Outer.PrivateNested.PrivateNested]>#()
R|/Outer.Outer|().<Inapplicable(HIDDEN): [/Outer.PrivateInner.PrivateInner]>#()
Q|Outer|.<Inapplicable(HIDDEN): [/Outer.ProtectedNested.ProtectedNested]>#()
R|/Outer.Outer|().<Inapplicable(HIDDEN): [/Outer.ProtectedInner.ProtectedInner]>#()
Q|Outer|.R|/Outer.PublicNested.PublicNested|()
R|/Outer.Outer|().R|/Outer.PublicInner.PublicInner|()
}
@@ -0,0 +1,54 @@
open class Protected {
protected fun bar() {}
fun baz() {
bar()
Nested().foo()
}
inner class Inner {
fun foo() {
bar()
}
}
protected open class Nested {
fun foo() {
bar()
}
protected fun bar() {}
}
}
class Derived : Protected() {
fun foo() {
bar()
Nested().foo()
Nested().<!INAPPLICABLE_CANDIDATE!>bar<!>() // hidden
}
class NestedDerived : Nested() {
fun use() {
bar()
}
}
}
fun test() {
Protected().baz()
Protected().Inner()
Protected().<!INAPPLICABLE_CANDIDATE!>bar<!>() // hidden
Protected.<!INAPPLICABLE_CANDIDATE!>Nested<!>() // hidden
}
open class Generic<T>(val x: T) {
protected open fun foo(): T = x
}
class DerivedGeneric : Generic<Int>() {
override fun foo(): Int {
return super.foo()
}
}
@@ -0,0 +1,92 @@
FILE: protectedVisibility.kt
public open class Protected : R|kotlin/Any| {
public constructor(): R|Protected| {
super<R|kotlin/Any|>()
}
protected final fun bar(): R|kotlin/Unit| {
}
public final fun baz(): R|kotlin/Unit| {
this@R|/Protected|.R|/Protected.bar|()
R|/Protected.Nested.Nested|().R|/Protected.Nested.foo|()
}
public final inner class Inner : R|kotlin/Any| {
public constructor(): R|Protected.Inner| {
super<R|kotlin/Any|>()
}
public final fun foo(): R|kotlin/Unit| {
this@R|/Protected|.R|/Protected.bar|()
}
}
protected open class Nested : R|kotlin/Any| {
public constructor(): R|Protected.Nested| {
super<R|kotlin/Any|>()
}
public final fun foo(): R|kotlin/Unit| {
this@R|/Protected.Nested|.R|/Protected.Nested.bar|()
}
protected final fun bar(): R|kotlin/Unit| {
}
}
}
public final class Derived : R|Protected| {
public constructor(): R|Derived| {
super<R|Protected|>()
}
public final fun foo(): R|kotlin/Unit| {
this@R|/Protected|.R|/Protected.bar|()
R|/Protected.Nested.Nested|().R|/Protected.Nested.foo|()
R|/Protected.Nested.Nested|().<Inapplicable(HIDDEN): [/Protected.Nested.bar]>#()
}
public final class NestedDerived : R|Protected.Nested| {
public constructor(): R|Derived.NestedDerived| {
super<R|Protected.Nested|>()
}
public final fun use(): R|kotlin/Unit| {
this@R|/Protected.Nested|.R|/Protected.Nested.bar|()
}
}
}
public final fun test(): R|kotlin/Unit| {
R|/Protected.Protected|().R|/Protected.baz|()
R|/Protected.Protected|().R|/Protected.Inner.Inner|()
R|/Protected.Protected|().<Inapplicable(HIDDEN): [/Protected.bar]>#()
Q|Protected|.<Inapplicable(HIDDEN): [/Protected.Nested.Nested]>#()
}
public open class Generic<T> : R|kotlin/Any| {
public constructor<T>(x: R|T|): R|Generic<T>| {
super<R|kotlin/Any|>()
}
public final val x: R|T| = R|<local>/x|
public get(): R|T|
protected open fun foo(): R|T| {
^foo this@R|/Generic|.R|FakeOverride</Generic.x: R|T|>|
}
}
public final class DerivedGeneric : R|Generic<kotlin/Int>| {
public constructor(): R|DerivedGeneric| {
super<R|Generic<kotlin/Int>|>()
}
public final override fun foo(): R|kotlin/Int| {
^foo super<R|Generic<kotlin/Int>|>.R|FakeOverride</Generic.foo: R|kotlin/Int|>|()
}
}
@@ -10,6 +10,8 @@ public class JavaClass {
// FILE: test.kt
package some
fun test(jc: JavaClass) {
jc.<!AMBIGUITY!>field<!>
jc.field
}
@@ -1,4 +1,4 @@
FILE: test.kt
public final fun test(jc: R|JavaClass|): R|kotlin/Unit| {
R|<local>/jc|.<Ambiguity: field, [/JavaClass.field, /JavaClass.field]>#
R|<local>/jc|.R|/JavaClass.field|
}
@@ -0,0 +1,62 @@
// FILE: j/JavaPackageLocal.java
package j;
public class JavaPackageLocal {
static void javaMPackage() {}
static int javaPPackage = 4;
}
// FILE: j/JavaProtected.java
package j;
public class JavaProtected {
protected static void javaMProtectedStatic() {}
protected static int javaPProtectedStatic = 4;
protected final int javaPProtectedPackage = 4;
}
// FILE: k.kt
package k
import j.JavaProtected
import j.JavaPackageLocal
class A {
val p1 = JavaPackageLocal.<!INAPPLICABLE_CANDIDATE!>javaPPackage<!>
val p2 = JavaProtected.<!INAPPLICABLE_CANDIDATE!>javaPProtectedStatic<!>
val p3 = JavaProtected().<!INAPPLICABLE_CANDIDATE!>javaPProtectedPackage<!>
fun test() {
JavaProtected.<!INAPPLICABLE_CANDIDATE!>javaMProtectedStatic<!>()
JavaPackageLocal.<!INAPPLICABLE_CANDIDATE!>javaMPackage<!>()
}
}
class B : JavaProtected() {
val p1 = JavaPackageLocal.<!INAPPLICABLE_CANDIDATE!>javaPPackage<!>
val p2 = JavaProtected.javaPProtectedStatic
val p3 = javaPProtectedPackage
fun test() {
JavaProtected.javaMProtectedStatic()
JavaPackageLocal.<!INAPPLICABLE_CANDIDATE!>javaMPackage<!>()
}
}
// FILE: j.kt
package j
import j.JavaProtected
import j.JavaPackageLocal
class C {
val p1 = JavaPackageLocal.javaPPackage
val p2 = JavaProtected.javaPProtectedStatic
val p3 = JavaProtected().javaPProtectedPackage
fun test() {
JavaProtected.javaMProtectedStatic()
JavaProtected.javaMProtectedStatic()
JavaPackageLocal.javaMPackage()
}
}
@@ -0,0 +1,63 @@
FILE: k.kt
public final class A : R|kotlin/Any| {
public constructor(): R|k/A| {
super<R|kotlin/Any|>()
}
public final val p1: <ERROR TYPE REF: Inapplicable(HIDDEN): [j/JavaPackageLocal.javaPPackage]> = Q|j/JavaPackageLocal|.<Inapplicable(HIDDEN): [j/JavaPackageLocal.javaPPackage]>#
public get(): <ERROR TYPE REF: Inapplicable(HIDDEN): [j/JavaPackageLocal.javaPPackage]>
public final val p2: <ERROR TYPE REF: Inapplicable(HIDDEN): [j/JavaProtected.javaPProtectedStatic]> = Q|j/JavaProtected|.<Inapplicable(HIDDEN): [j/JavaProtected.javaPProtectedStatic]>#
public get(): <ERROR TYPE REF: Inapplicable(HIDDEN): [j/JavaProtected.javaPProtectedStatic]>
public final val p3: <ERROR TYPE REF: Inapplicable(HIDDEN): [j/JavaProtected.javaPProtectedPackage]> = R|j/JavaProtected.JavaProtected|().<Inapplicable(HIDDEN): [j/JavaProtected.javaPProtectedPackage]>#
public get(): <ERROR TYPE REF: Inapplicable(HIDDEN): [j/JavaProtected.javaPProtectedPackage]>
public final fun test(): R|kotlin/Unit| {
Q|j/JavaProtected|.<Inapplicable(HIDDEN): [j/JavaProtected.javaMProtectedStatic]>#()
Q|j/JavaPackageLocal|.<Inapplicable(HIDDEN): [j/JavaPackageLocal.javaMPackage]>#()
}
}
public final class B : R|j/JavaProtected| {
public constructor(): R|k/B| {
super<R|j/JavaProtected|>()
}
public final val p1: <ERROR TYPE REF: Inapplicable(HIDDEN): [j/JavaPackageLocal.javaPPackage]> = Q|j/JavaPackageLocal|.<Inapplicable(HIDDEN): [j/JavaPackageLocal.javaPPackage]>#
public get(): <ERROR TYPE REF: Inapplicable(HIDDEN): [j/JavaPackageLocal.javaPPackage]>
public final val p2: R|kotlin/Int| = Q|j/JavaProtected|.R|j/JavaProtected.javaPProtectedStatic|
public get(): R|kotlin/Int|
public final val p3: R|kotlin/Int| = this@R|j/JavaProtected|.R|j/JavaProtected.javaPProtectedPackage|
public get(): R|kotlin/Int|
public final fun test(): R|kotlin/Unit| {
Q|j/JavaProtected|.R|j/JavaProtected.javaMProtectedStatic|()
Q|j/JavaPackageLocal|.<Inapplicable(HIDDEN): [j/JavaPackageLocal.javaMPackage]>#()
}
}
FILE: j.kt
public final class C : R|kotlin/Any| {
public constructor(): R|j/C| {
super<R|kotlin/Any|>()
}
public final val p1: R|kotlin/Int| = Q|j/JavaPackageLocal|.R|j/JavaPackageLocal.javaPPackage|
public get(): R|kotlin/Int|
public final val p2: R|kotlin/Int| = Q|j/JavaProtected|.R|j/JavaProtected.javaPProtectedStatic|
public get(): R|kotlin/Int|
public final val p3: R|kotlin/Int| = R|j/JavaProtected.JavaProtected|().R|j/JavaProtected.javaPProtectedPackage|
public get(): R|kotlin/Int|
public final fun test(): R|kotlin/Unit| {
Q|j/JavaProtected|.R|j/JavaProtected.javaMProtectedStatic|()
Q|j/JavaProtected|.R|j/JavaProtected.javaMProtectedStatic|()
Q|j/JavaPackageLocal|.R|j/JavaPackageLocal.javaMPackage|()
}
}
@@ -479,6 +479,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/resolve/testData/resolve/expresssions/memberExtension.kt");
}
@TestMetadata("nestedVisibility.kt")
public void testNestedVisibility() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/nestedVisibility.kt");
}
@TestMetadata("objectVsProperty.kt")
public void testObjectVsProperty() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/objectVsProperty.kt");
@@ -504,6 +509,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/resolve/testData/resolve/expresssions/privateVisibility.kt");
}
@TestMetadata("protectedVisibility.kt")
public void testProtectedVisibility() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/protectedVisibility.kt");
}
@TestMetadata("qualifiedExpressions.kt")
public void testQualifiedExpressions() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/qualifiedExpressions.kt");
@@ -396,6 +396,11 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic
runTest("compiler/fir/resolve/testData/resolve/stdlib/j+k/FunctionTypeInJava.kt");
}
@TestMetadata("JavaVisibility2.kt")
public void testJavaVisibility2() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/stdlib/j+k/JavaVisibility2.kt");
}
@TestMetadata("KJKComplexHierarchy.kt")
public void testKJKComplexHierarchy() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/stdlib/j+k/KJKComplexHierarchy.kt");
@@ -76,6 +76,11 @@ class IdeFirProvider(
return cacheProvider.getFirClassifierContainerFile(fqName)
}
override fun getFirClassifierContainerFileIfAny(fqName: ClassId): FirFile? {
getFirClassifierByFqName(fqName)
return cacheProvider.getFirClassifierContainerFileIfAny(fqName)
}
override fun getFirCallableContainerFile(symbol: FirCallableSymbol<*>): FirFile? {
return cacheProvider.getFirCallableContainerFile(symbol)
}