[FIR] Support sealed class inheritors in multiple files
This commit is contained in:
+1
-1
@@ -29,7 +29,7 @@ sealed class P {
|
||||
class K : P()
|
||||
|
||||
<!REDECLARATION!>object B<!> {
|
||||
class I : <!SEALED_SUPERTYPE!>P<!>()
|
||||
class I : P()
|
||||
}
|
||||
|
||||
fun test() {
|
||||
|
||||
+8
-35
@@ -20,26 +20,18 @@ object FirSealedSupertypeChecker : FirMemberDeclarationChecker() {
|
||||
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (declaration is FirClass<*>) {
|
||||
// only the file declaration is present
|
||||
when {
|
||||
context.containingDeclarations.size == 1 -> {
|
||||
checkTopLevelDeclaration(declaration, context, reporter)
|
||||
}
|
||||
declaration.classId.isLocal -> {
|
||||
checkLocalDeclaration(declaration, context, reporter)
|
||||
}
|
||||
else -> {
|
||||
checkInnerDeclaration(declaration, context, reporter)
|
||||
}
|
||||
if (declaration.classId.isLocal) {
|
||||
checkLocalDeclaration(declaration, context, reporter)
|
||||
} else {
|
||||
checkGlobalDeclaration(declaration, context, reporter)
|
||||
}
|
||||
} else if (declaration is FirProperty) {
|
||||
val initializer = declaration.initializer.safeAs<FirClass<*>>()
|
||||
?: return
|
||||
|
||||
val initializer = declaration.initializer.safeAs<FirClass<*>>() ?: return
|
||||
checkLocalDeclaration(initializer, context, reporter)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkTopLevelDeclaration(declaration: FirClass<*>, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
private fun checkGlobalDeclaration(declaration: FirClass<*>, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
for (it in declaration.superTypeRefs) {
|
||||
val classId = it.coneType.classId ?: continue
|
||||
|
||||
@@ -51,9 +43,9 @@ object FirSealedSupertypeChecker : FirMemberDeclarationChecker() {
|
||||
?.fir.safeAs<FirRegularClass>()
|
||||
?: continue
|
||||
|
||||
if (fir.status.modality == Modality.SEALED && classId.outerClassId != null) {
|
||||
if (fir.status.modality == Modality.SEALED && declaration.classId.packageFqName != fir.classId.packageFqName) {
|
||||
reporter.reportOn(it.source, FirErrors.SEALED_SUPERTYPE, context)
|
||||
return
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,23 +68,4 @@ object FirSealedSupertypeChecker : FirMemberDeclarationChecker() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkInnerDeclaration(declaration: FirClass<*>, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
for (it in declaration.superTypeRefs) {
|
||||
val classId = it.coneType.classId ?: continue
|
||||
|
||||
if (classId.isLocal) {
|
||||
continue
|
||||
}
|
||||
|
||||
val fir = context.session.symbolProvider.getClassLikeSymbolByFqName(classId)
|
||||
?.fir.safeAs<FirRegularClass>()
|
||||
?: continue
|
||||
|
||||
if (fir.status.modality == Modality.SEALED && !context.containingDeclarations.contains(fir)) {
|
||||
reporter.reportOn(it.source, FirErrors.SEALED_SUPERTYPE, context)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,17 +65,24 @@ abstract class FirVisibilityChecker : FirSessionComponent {
|
||||
Visibilities.Private, Visibilities.PrivateToThis -> {
|
||||
val ownerId = symbol.getOwnerId()
|
||||
if (declaration.session == session) {
|
||||
if (ownerId == null || declaration is FirConstructor && declaration.isFromSealedClass) {
|
||||
val candidateFile = when (symbol) {
|
||||
is FirClassLikeSymbol<*> -> provider.getFirClassifierContainerFileIfAny(symbol)
|
||||
is FirCallableSymbol<*> -> provider.getFirCallableContainerFile(symbol)
|
||||
else -> null
|
||||
when {
|
||||
ownerId == null -> {
|
||||
val candidateFile = when (symbol) {
|
||||
is FirClassLikeSymbol<*> -> provider.getFirClassifierContainerFileIfAny(symbol)
|
||||
is FirCallableSymbol<*> -> provider.getFirCallableContainerFile(symbol)
|
||||
else -> null
|
||||
}
|
||||
// Top-level: visible in file
|
||||
candidateFile == useSiteFile
|
||||
}
|
||||
declaration is FirConstructor && declaration.isFromSealedClass -> {
|
||||
// Sealed class constructor: visible in same package
|
||||
declaration.symbol.callableId.packageName == useSiteFile.packageFqName
|
||||
}
|
||||
else -> {
|
||||
// Member: visible inside parent class, including all its member classes
|
||||
canSeePrivateMemberOf(containingDeclarations, ownerId, session)
|
||||
}
|
||||
// Top-level: visible in file
|
||||
candidateFile == useSiteFile
|
||||
} else {
|
||||
// Member: visible inside parent class, including all its member classes
|
||||
canSeePrivateMemberOf(containingDeclarations, ownerId, session)
|
||||
}
|
||||
} else {
|
||||
declaration is FirSimpleFunction && declaration.isAllowedToBeAccessedFromOutside()
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.transformers
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.compose
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
/*
|
||||
* This processor is needed only for IDE until there won't be proper IDE implementation
|
||||
* for detecting sealed inheritors in multiple files
|
||||
*/
|
||||
class FirLegacySealedClassInheritorsProcessor(session: FirSession, scopeSession: ScopeSession) : FirTransformerBasedResolveProcessor(session, scopeSession) {
|
||||
override val transformer = FirLegacySealedClassInheritorsTransformer()
|
||||
}
|
||||
|
||||
class FirLegacySealedClassInheritorsTransformer : FirTransformer<Nothing?>() {
|
||||
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
|
||||
throw IllegalStateException("Should not be there")
|
||||
}
|
||||
|
||||
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirDeclaration> {
|
||||
val sealedClassInheritorsMap = mutableMapOf<FirRegularClass, MutableList<ClassId>>()
|
||||
file.accept(FirSealedClassInheritorsProcessor.InheritorsCollector, sealedClassInheritorsMap)
|
||||
if (sealedClassInheritorsMap.isEmpty()) return file.compose()
|
||||
return file.transform(FirSealedClassInheritorsProcessor.InheritorsTransformer(sealedClassInheritorsMap), null)
|
||||
}
|
||||
}
|
||||
+52
-57
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -11,38 +11,69 @@ import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.firProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.getSymbolByLookupTag
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeLookupTagBasedType
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
||||
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
|
||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||
import org.jetbrains.kotlin.fir.visitors.compose
|
||||
import org.jetbrains.kotlin.fir.visitors.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
class FirSealedClassInheritorsProcessor(session: FirSession, scopeSession: ScopeSession) : FirTransformerBasedResolveProcessor(session, scopeSession) {
|
||||
override val transformer = FirSealedClassInheritorsTransformer()
|
||||
}
|
||||
|
||||
class FirSealedClassInheritorsTransformer : FirTransformer<Nothing?>() {
|
||||
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
|
||||
throw IllegalStateException("Should not be there")
|
||||
}
|
||||
|
||||
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirDeclaration> {
|
||||
class FirSealedClassInheritorsProcessor(
|
||||
session: FirSession,
|
||||
scopeSession: ScopeSession
|
||||
) : FirGlobalResolveProcessor(session, scopeSession) {
|
||||
override fun process(files: Collection<FirFile>) {
|
||||
val sealedClassInheritorsMap = mutableMapOf<FirRegularClass, MutableList<ClassId>>()
|
||||
file.accept(InheritorsCollector, sealedClassInheritorsMap)
|
||||
if (sealedClassInheritorsMap.isEmpty()) return file.compose()
|
||||
return file.transform(InheritorsTransformer(sealedClassInheritorsMap), null)
|
||||
files.forEach { it.accept(InheritorsCollector, sealedClassInheritorsMap) }
|
||||
files.forEach { it.transformSingle(InheritorsTransformer(sealedClassInheritorsMap), null) }
|
||||
}
|
||||
|
||||
private class InheritorsTransformer(private val inheritorsMap: MutableMap<FirRegularClass, MutableList<ClassId>>) : FirTransformer<Nothing?>() {
|
||||
object InheritorsCollector : FirDefaultVisitor<Unit, MutableMap<FirRegularClass, MutableList<ClassId>>>() {
|
||||
override fun visitElement(element: FirElement, data: MutableMap<FirRegularClass, MutableList<ClassId>>) {}
|
||||
|
||||
override fun visitFile(file: FirFile, data: MutableMap<FirRegularClass, MutableList<ClassId>>) {
|
||||
file.declarations.forEach { it.accept(this, data) }
|
||||
}
|
||||
|
||||
override fun visitRegularClass(regularClass: FirRegularClass, data: MutableMap<FirRegularClass, MutableList<ClassId>>) {
|
||||
regularClass.declarations.forEach { it.accept(this, data) }
|
||||
|
||||
if (regularClass.modality == Modality.SEALED) {
|
||||
data.computeIfAbsent(regularClass) { mutableListOf() }
|
||||
}
|
||||
|
||||
val symbolProvider = regularClass.session.symbolProvider
|
||||
|
||||
for (typeRef in regularClass.superTypeRefs) {
|
||||
val parent = extractClassFromTypeRef(symbolProvider, typeRef).takeIf { it?.modality == Modality.SEALED } ?: continue
|
||||
// Inheritors of sealed class are allowed only in same package
|
||||
if (parent.classId.packageFqName != regularClass.classId.packageFqName) continue
|
||||
val inheritors = data.computeIfAbsent(parent) { mutableListOf() }
|
||||
inheritors += regularClass.symbol.classId
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractClassFromTypeRef(symbolProvider: FirSymbolProvider, typeRef: FirTypeRef): FirRegularClass? {
|
||||
val lookupTag = (typeRef.coneType as? ConeLookupTagBasedType)?.lookupTag ?: return null
|
||||
val classLikeSymbol: FirClassifierSymbol<*> = symbolProvider.getSymbolByLookupTag(lookupTag) ?: return null
|
||||
return when (classLikeSymbol) {
|
||||
is FirRegularClassSymbol -> classLikeSymbol.fir
|
||||
is FirTypeAliasSymbol -> {
|
||||
classLikeSymbol.ensureResolved(FirResolvePhase.SUPER_TYPES, symbolProvider.session)
|
||||
extractClassFromTypeRef(symbolProvider, classLikeSymbol.fir.expandedTypeRef)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InheritorsTransformer(private val inheritorsMap: MutableMap<FirRegularClass, MutableList<ClassId>>) : FirTransformer<Nothing?>() {
|
||||
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
|
||||
return element.compose()
|
||||
}
|
||||
@@ -63,42 +94,6 @@ class FirSealedClassInheritorsTransformer : FirTransformer<Nothing?>() {
|
||||
}
|
||||
}
|
||||
|
||||
private object InheritorsCollector : FirDefaultVisitor<Unit, MutableMap<FirRegularClass, MutableList<ClassId>>>() {
|
||||
override fun visitElement(element: FirElement, data: MutableMap<FirRegularClass, MutableList<ClassId>>) {}
|
||||
|
||||
override fun visitFile(file: FirFile, data: MutableMap<FirRegularClass, MutableList<ClassId>>) {
|
||||
file.declarations.forEach { it.accept(this, data) }
|
||||
}
|
||||
|
||||
override fun visitRegularClass(regularClass: FirRegularClass, data: MutableMap<FirRegularClass, MutableList<ClassId>>) {
|
||||
regularClass.declarations.forEach { it.accept(this, data) }
|
||||
|
||||
if (regularClass.modality == Modality.SEALED) {
|
||||
data.computeIfAbsent(regularClass) { mutableListOf() }
|
||||
}
|
||||
|
||||
val symbolProvider = regularClass.session.symbolProvider
|
||||
|
||||
for (typeRef in regularClass.superTypeRefs) {
|
||||
val parent = extractClassFromTypeRef(symbolProvider, typeRef).takeIf { it?.modality == Modality.SEALED } ?: continue
|
||||
val inheritors = data.computeIfAbsent(parent) { mutableListOf() }
|
||||
inheritors += regularClass.symbol.classId
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractClassFromTypeRef(symbolProvider: FirSymbolProvider, typeRef: FirTypeRef): FirRegularClass? {
|
||||
val lookupTag = (typeRef.coneType as? ConeLookupTagBasedType)?.lookupTag ?: return null
|
||||
val classLikeSymbol: FirClassifierSymbol<*> = symbolProvider.getSymbolByLookupTag(lookupTag) ?: return null
|
||||
return when (classLikeSymbol) {
|
||||
is FirRegularClassSymbol -> classLikeSymbol.fir
|
||||
is FirTypeAliasSymbol -> {
|
||||
classLikeSymbol.ensureResolved(FirResolvePhase.SUPER_TYPES, symbolProvider.session)
|
||||
extractClassFromTypeRef(symbolProvider, classLikeSymbol.fir.expandedTypeRef)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object SealedClassInheritorsKey : FirDeclarationDataKey()
|
||||
+2
-2
@@ -50,7 +50,7 @@ fun FirResolvePhase.createTransformerBasedProcessorByPhase(
|
||||
CLASS_GENERATION -> FirDummyTransformerBasedProcessor(session, scopeSession) // TODO: remove
|
||||
IMPORTS -> FirImportResolveProcessor(session, scopeSession)
|
||||
SUPER_TYPES -> FirSupertypeResolverProcessor(session, scopeSession)
|
||||
SEALED_CLASS_INHERITORS -> FirSealedClassInheritorsProcessor(session, scopeSession)
|
||||
SEALED_CLASS_INHERITORS -> FirLegacySealedClassInheritorsProcessor(session, scopeSession)
|
||||
TYPES -> FirTypeResolveProcessor(session, scopeSession)
|
||||
ARGUMENTS_OF_PLUGIN_ANNOTATIONS -> FirAnnotationArgumentsResolveProcessor(session, scopeSession)
|
||||
EXTENSION_STATUS_UPDATE -> FirTransformerBasedExtensionStatusProcessor(session, scopeSession)
|
||||
@@ -74,4 +74,4 @@ private class FirDummyTransformerBasedProcessor(
|
||||
return element.compose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// ISSUE: KT-13495
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// !LANGUAGE: +AllowSealedInheritorsInDifferentFilesOfSamePackage
|
||||
|
||||
// FILE: Base.kt
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
sealed class Base
|
||||
|
||||
class Derived: Base() {
|
||||
class Derived2: <!SEALED_SUPERTYPE!>Base<!>()
|
||||
class Derived2: Base()
|
||||
}
|
||||
|
||||
fun test() {
|
||||
|
||||
@@ -10,14 +10,14 @@ sealed class Base {
|
||||
|
||||
// FILE: B.kt
|
||||
|
||||
class B : <!HIDDEN!>Base<!>()
|
||||
class B : Base()
|
||||
|
||||
// FILE: Container.kt
|
||||
|
||||
class Containter {
|
||||
class C : <!HIDDEN, SEALED_SUPERTYPE!>Base<!>()
|
||||
class C : Base()
|
||||
|
||||
inner class D : <!HIDDEN, SEALED_SUPERTYPE!>Base<!>()
|
||||
inner class D : Base()
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
@@ -13,21 +13,21 @@ sealed class Base {
|
||||
|
||||
package foo
|
||||
|
||||
class B : <!HIDDEN!>Base<!>()
|
||||
class B : Base()
|
||||
|
||||
// FILE: c.kt
|
||||
|
||||
package foo
|
||||
|
||||
class Container {
|
||||
class C : <!HIDDEN, SEALED_SUPERTYPE!>Base<!>()
|
||||
class C : Base()
|
||||
|
||||
inner class D : <!HIDDEN, SEALED_SUPERTYPE!>Base<!>()
|
||||
inner class D : Base()
|
||||
|
||||
val anon = object : <!HIDDEN, SEALED_SUPERTYPE_IN_LOCAL_CLASS!>Base<!>() {} // Should be an error
|
||||
val anon = object : <!SEALED_SUPERTYPE_IN_LOCAL_CLASS!>Base<!>() {} // Should be an error
|
||||
|
||||
fun someFun() {
|
||||
class LocalClass : <!HIDDEN, SEALED_SUPERTYPE_IN_LOCAL_CLASS!>Base<!>() {} // Should be an error
|
||||
class LocalClass : <!SEALED_SUPERTYPE_IN_LOCAL_CLASS!>Base<!>() {} // Should be an error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,4 +37,4 @@ package bar
|
||||
|
||||
import foo.Base
|
||||
|
||||
class E : <!HIDDEN!>Base<!>()
|
||||
class E : <!HIDDEN, SEALED_SUPERTYPE!>Base<!>()
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
// ISSUE: KT-13495
|
||||
// !LANGUAGE: +AllowSealedInheritorsInDifferentFilesOfSamePackage
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
// FILE: base.kt
|
||||
|
||||
package foo
|
||||
|
||||
class Container {
|
||||
sealed class Base
|
||||
}
|
||||
|
||||
// FILE: a.kt
|
||||
|
||||
package foo
|
||||
|
||||
class A : <!HIDDEN, SEALED_SUPERTYPE!>Container.Base<!>()
|
||||
|
||||
// FILE: b.kt
|
||||
|
||||
package foo
|
||||
|
||||
class BContainer {
|
||||
class B : <!HIDDEN, SEALED_SUPERTYPE!>Container.Base<!>()
|
||||
|
||||
inner class C : <!HIDDEN, SEALED_SUPERTYPE!>Container.Base<!>()
|
||||
}
|
||||
|
||||
// FILE: test.kt
|
||||
|
||||
package foo
|
||||
|
||||
fun test(base: Container.Base) {
|
||||
val x = when (base) {
|
||||
is A -> 1
|
||||
is BContainer.B -> 2
|
||||
is BContainer.C -> 3
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// ISSUE: KT-13495
|
||||
// !LANGUAGE: +AllowSealedInheritorsInDifferentFilesOfSamePackage
|
||||
// !DIAGNOSTICS: -UNUSED_VARIABLE
|
||||
|
||||
@@ -2,7 +2,7 @@ class A {
|
||||
sealed class Base
|
||||
}
|
||||
|
||||
class Derived : <!SEALED_SUPERTYPE!>A.Base<!>()
|
||||
class Derived : A.Base()
|
||||
|
||||
fun test() {
|
||||
class DerivedLocal : <!SEALED_SUPERTYPE_IN_LOCAL_CLASS!>A.Base<!>()
|
||||
|
||||
Reference in New Issue
Block a user