提交 490ef210 编写于 作者: D Dmitriy Novozhilov

[FIR] Support sealed class inheritors in multiple files

上级 c8f9cc33
......@@ -29,7 +29,7 @@ sealed class P {
class K : P()
<!REDECLARATION!>object B<!> {
class I : <!SEALED_SUPERTYPE!>P<!>()
class I : P()
}
fun test() {
......
......@@ -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()
......
/*
* 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)
}
}
/*
* 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,59 +11,30 @@ 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?>() {
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
return element.compose()
}
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirDeclaration> {
return (file.transformChildren(this, data) as FirFile).compose()
}
override fun transformRegularClass(regularClass: FirRegularClass, data: Nothing?): CompositeTransformResult<FirStatement> {
if (regularClass.modality == Modality.SEALED) {
val inheritors = inheritorsMap.remove(regularClass)
if (inheritors != null) {
regularClass.sealedInheritors = inheritors
}
}
if (inheritorsMap.isEmpty()) return regularClass.compose()
return (regularClass.transformChildren(this, data) as FirRegularClass).compose()
}
}
private object InheritorsCollector : FirDefaultVisitor<Unit, MutableMap<FirRegularClass, MutableList<ClassId>>>() {
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>>) {
......@@ -81,6 +52,8 @@ class FirSealedClassInheritorsTransformer : FirTransformer<Nothing?>() {
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
}
......@@ -99,6 +72,28 @@ class FirSealedClassInheritorsTransformer : FirTransformer<Nothing?>() {
}
}
}
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()
}
override fun transformFile(file: FirFile, data: Nothing?): CompositeTransformResult<FirDeclaration> {
return (file.transformChildren(this, data) as FirFile).compose()
}
override fun transformRegularClass(regularClass: FirRegularClass, data: Nothing?): CompositeTransformResult<FirStatement> {
if (regularClass.modality == Modality.SEALED) {
val inheritors = inheritorsMap.remove(regularClass)
if (inheritors != null) {
regularClass.sealedInheritors = inheritors
}
}
if (inheritorsMap.isEmpty()) return regularClass.compose()
return (regularClass.transformChildren(this, data) as FirRegularClass).compose()
}
}
}
object SealedClassInheritorsKey : FirDeclarationDataKey()
......
......@@ -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()
}
}
}
\ No newline at end of file
}
// ISSUE: KT-13495
// IGNORE_BACKEND_FIR: JVM_IR
// !LANGUAGE: +AllowSealedInheritorsInDifferentFilesOfSamePackage
// FILE: Base.kt
......
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<!>()
// 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
}
}
// 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<!>()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册