[FE] Prohibit inheritors of sealed classes which are declared in different package
#KT-13495
This commit is contained in:
committed by
TeamCityServer
parent
e76acc8ee0
commit
d605c7e491
@@ -426,6 +426,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtCallExpression> SEALED_CLASS_CONSTRUCTOR_CALL = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtTypeReference> SEALED_SUPERTYPE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtTypeReference> SEALED_SUPERTYPE_IN_LOCAL_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory2<KtTypeReference, FqName, FqName> SEALED_INHERITOR_IN_DIFFERENT_PACKAGE = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
// Companion objects
|
||||
|
||||
|
||||
+1
@@ -633,6 +633,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(DATA_CLASS_CANNOT_HAVE_CLASS_SUPERTYPES, "Data class inheritance from other classes is forbidden");
|
||||
MAP.put(SEALED_SUPERTYPE, "This type is sealed, so it can be inherited by only its own nested classes or objects");
|
||||
MAP.put(SEALED_SUPERTYPE_IN_LOCAL_CLASS, "Local class cannot extend a sealed class");
|
||||
MAP.put(SEALED_INHERITOR_IN_DIFFERENT_PACKAGE, "Inheritor of sealed class or interface declared in package {1} but it must be in package {2} where base class is declared", TO_STRING, TO_STRING);
|
||||
MAP.put(SINGLETON_IN_SUPERTYPE, "Cannot inherit from a singleton");
|
||||
MAP.put(CLASS_CANNOT_BE_EXTENDED_DIRECTLY, "Class {0} cannot be extended directly", NAME);
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
FunInterfaceDeclarationChecker(),
|
||||
DeprecatedSinceKotlinAnnotationChecker,
|
||||
ContractDescriptionBlockChecker,
|
||||
PrivateInlineFunctionsReturningAnonymousObjectsChecker
|
||||
PrivateInlineFunctionsReturningAnonymousObjectsChecker,
|
||||
SealedInheritorInSamePackageChecker,
|
||||
)
|
||||
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.containingPackage
|
||||
import org.jetbrains.kotlin.descriptors.isSealed
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
|
||||
|
||||
object SealedInheritorInSamePackageChecker : DeclarationChecker {
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.FreedomForSealedClasses)) return
|
||||
if (descriptor !is ClassDescriptor || declaration !is KtClassOrObject) return
|
||||
val classPackage = descriptor.containingPackage() ?: return // local class, SEALED_SUPERTYPE already reported
|
||||
for (superTypeListEntry in declaration.superTypeListEntries) {
|
||||
val typeReference = superTypeListEntry.typeReference ?: continue
|
||||
val superType = typeReference.getAbbreviatedTypeOrType(context.trace.bindingContext)?.unwrap() ?: continue
|
||||
val superClass = superType.constructor.declarationDescriptor ?: continue
|
||||
if (!superClass.isSealed()) continue
|
||||
val superClassPackage = superClass.containingPackage() ?: continue
|
||||
if (classPackage != superClassPackage) {
|
||||
context.trace.report(Errors.SEALED_INHERITOR_IN_DIFFERENT_PACKAGE.on(typeReference, classPackage, superClassPackage))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,22 @@
|
||||
|
||||
// FILE: a.kt
|
||||
|
||||
package foo
|
||||
|
||||
sealed class Base {
|
||||
class A : Base()
|
||||
}
|
||||
|
||||
// FILE: b.kt
|
||||
|
||||
package foo
|
||||
|
||||
class B : <!HIDDEN!>Base<!>()
|
||||
|
||||
// FILE: c.kt
|
||||
|
||||
package foo
|
||||
|
||||
class Container {
|
||||
class C : <!HIDDEN, SEALED_SUPERTYPE!>Base<!>()
|
||||
|
||||
@@ -24,3 +30,11 @@ class Container {
|
||||
class LocalClass : <!HIDDEN, SEALED_SUPERTYPE_IN_LOCAL_CLASS!>Base<!>() {} // Should be an error
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: E.kt
|
||||
|
||||
package bar
|
||||
|
||||
import foo.Base
|
||||
|
||||
class E : <!HIDDEN!>Base<!>()
|
||||
|
||||
@@ -3,16 +3,22 @@
|
||||
|
||||
// FILE: a.kt
|
||||
|
||||
package foo
|
||||
|
||||
sealed class Base {
|
||||
class A : Base()
|
||||
}
|
||||
|
||||
// FILE: b.kt
|
||||
|
||||
package foo
|
||||
|
||||
class B : Base()
|
||||
|
||||
// FILE: c.kt
|
||||
|
||||
package foo
|
||||
|
||||
class Container {
|
||||
class C : Base()
|
||||
|
||||
@@ -24,3 +30,11 @@ class Container {
|
||||
class LocalClass : <!SEALED_SUPERTYPE!>Base<!>() {} // Should be an error
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: E.kt
|
||||
|
||||
package bar
|
||||
|
||||
import foo.Base
|
||||
|
||||
class E : <!SEALED_INHERITOR_IN_DIFFERENT_PACKAGE!>Base<!>()
|
||||
|
||||
@@ -1,45 +1,58 @@
|
||||
package
|
||||
|
||||
public final class B : Base {
|
||||
public constructor B()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
package bar {
|
||||
|
||||
public sealed class Base {
|
||||
internal constructor Base()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public final class A : Base {
|
||||
public constructor A()
|
||||
public final class E : foo.Base {
|
||||
public constructor E()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
public final class Container {
|
||||
public constructor Container()
|
||||
public final val anon: Base
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final fun someFun(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
package foo {
|
||||
|
||||
public final class C : Base {
|
||||
public constructor C()
|
||||
public final class B : foo.Base {
|
||||
public constructor B()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final inner class D : Base {
|
||||
public constructor D()
|
||||
public sealed class Base {
|
||||
internal constructor Base()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public final class A : foo.Base {
|
||||
public constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
public final class Container {
|
||||
public constructor Container()
|
||||
public final val anon: foo.Base
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final fun someFun(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public final class C : foo.Base {
|
||||
public constructor C()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final inner class D : foo.Base {
|
||||
public constructor D()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,14 @@ import org.jetbrains.kotlin.builtins.StandardNames.CONTINUATION_INTERFACE_FQ_NAM
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
fun ModuleDescriptor.resolveClassByFqName(fqName: FqName, lookupLocation: LookupLocation): ClassDescriptor? {
|
||||
if (fqName.isRoot) return null
|
||||
@@ -56,3 +59,21 @@ fun DeclarationDescriptor.isTopLevelInPackage(name: String, packageName: String)
|
||||
}
|
||||
|
||||
fun CallableDescriptor.isSupportedForCallableReference() = this is PropertyDescriptor || this is FunctionDescriptor
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun DeclarationDescriptor.isSealed(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isSealed is ClassDescriptor)
|
||||
}
|
||||
return DescriptorUtils.isSealedClass(this)
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.containingPackage(): FqName? {
|
||||
var container = containingDeclaration
|
||||
while (true) {
|
||||
if (container == null || container is PackageFragmentDescriptor) break
|
||||
container = container.containingDeclaration
|
||||
}
|
||||
require(container is PackageFragmentDescriptor?)
|
||||
return container?.fqName
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user