[FIR] Support diagnostic QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE
This commit is contained in:
+34
@@ -0,0 +1,34 @@
|
||||
interface IBase<T> {
|
||||
fun foo() {}
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
typealias AliasedIBase1 = IBase<String>
|
||||
typealias AliasedIBase = AliasedIBase1
|
||||
|
||||
interface IDerived<T> : IBase<T> {
|
||||
override fun foo() {}
|
||||
fun qux() {}
|
||||
}
|
||||
|
||||
class Test : IDerived<String>, IBase<String> {
|
||||
fun test() {
|
||||
super<<!QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE!>IBase<!>>.foo()
|
||||
super<<!QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE!>IBase<!>>.bar()
|
||||
super<IDerived>.foo()
|
||||
super<IDerived>.bar()
|
||||
super<IDerived>.qux()
|
||||
}
|
||||
}
|
||||
|
||||
class Test2 : IDerived<String>, AliasedIBase {
|
||||
fun test() {
|
||||
super<<!QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE!>IBase<!>>.foo()
|
||||
super<<!QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE!>IBase<!>>.bar()
|
||||
super<<!QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE!>AliasedIBase<!>>.foo()
|
||||
super<<!QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE!>AliasedIBase<!>>.bar()
|
||||
super<IDerived>.foo()
|
||||
super<IDerived>.bar()
|
||||
super<IDerived>.qux()
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
FILE: qualifiedSupertypeExtendedByOtherSupertype.kt
|
||||
public abstract interface IBase<T> : R|kotlin/Any| {
|
||||
public open fun foo(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
public open fun bar(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
}
|
||||
public final typealias AliasedIBase1 = R|IBase<kotlin/String>|
|
||||
public final typealias AliasedIBase = R|AliasedIBase1|
|
||||
public abstract interface IDerived<T> : R|IBase<T>| {
|
||||
public open override fun foo(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
public open fun qux(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
}
|
||||
public final class Test : R|IDerived<kotlin/String>|, R|IBase<kotlin/String>| {
|
||||
public constructor(): R|Test| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun test(): R|kotlin/Unit| {
|
||||
super<R|IBase|>.R|/IBase.foo|()
|
||||
super<R|IBase|>.R|/IBase.bar|()
|
||||
super<R|IDerived|>.R|/IDerived.foo|()
|
||||
super<R|IDerived|>.R|/IBase.bar|()
|
||||
super<R|IDerived|>.R|/IDerived.qux|()
|
||||
}
|
||||
|
||||
}
|
||||
public final class Test2 : R|IDerived<kotlin/String>|, R|AliasedIBase| {
|
||||
public constructor(): R|Test2| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun test(): R|kotlin/Unit| {
|
||||
super<R|IBase|>.R|/IBase.foo|()
|
||||
super<R|IBase|>.R|/IBase.bar|()
|
||||
super<R|AliasedIBase|>.R|/IBase.foo|()
|
||||
super<R|AliasedIBase|>.R|/IBase.bar|()
|
||||
super<R|IDerived|>.R|/IDerived.foo|()
|
||||
super<R|IDerived|>.R|/IBase.bar|()
|
||||
super<R|IDerived|>.R|/IDerived.qux|()
|
||||
}
|
||||
|
||||
}
|
||||
Generated
+5
@@ -826,6 +826,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("qualifiedSupertypeExtendedByOtherSupertype.kt")
|
||||
public void testQualifiedSupertypeExtendedByOtherSupertype() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/qualifiedSupertypeExtendedByOtherSupertype.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("redundantModifier.kt")
|
||||
public void testRedundantModifier() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/redundantModifier.kt");
|
||||
|
||||
+5
@@ -826,6 +826,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("qualifiedSupertypeExtendedByOtherSupertype.kt")
|
||||
public void testQualifiedSupertypeExtendedByOtherSupertype() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/qualifiedSupertypeExtendedByOtherSupertype.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("redundantModifier.kt")
|
||||
public void testRedundantModifier() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/redundantModifier.kt");
|
||||
|
||||
+56
-10
@@ -9,9 +9,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
||||
@@ -34,7 +32,9 @@ fun FirClass<*>.isSuperclassOf(other: FirClass<*>): Boolean {
|
||||
*/
|
||||
fun FirClass<*>.isSuperclassOf(other: FirClass<*>, exclude: MutableSet<FirClass<*>>): Boolean {
|
||||
for (it in other.superTypeRefs) {
|
||||
val that = it.firClassLike(session) as? FirClass<*>
|
||||
val that = it.firClassLike(session)
|
||||
?.followAllAlias(session)
|
||||
?.safeAs<FirClass<*>>()
|
||||
?: continue
|
||||
|
||||
if (that in exclude) {
|
||||
@@ -58,7 +58,7 @@ fun FirClass<*>.isSuperclassOf(other: FirClass<*>): Boolean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is a superclass of other.
|
||||
* Returns true if this is a supertype of other.
|
||||
*/
|
||||
fun FirClass<*>.isSupertypeOf(other: FirClass<*>): Boolean {
|
||||
/**
|
||||
@@ -66,20 +66,22 @@ fun FirClass<*>.isSupertypeOf(other: FirClass<*>): Boolean {
|
||||
*/
|
||||
fun FirClass<*>.isSupertypeOf(other: FirClass<*>, exclude: MutableSet<FirClass<*>>): Boolean {
|
||||
for (it in other.superTypeRefs) {
|
||||
val that = it.firClassLike(session) as? FirClass<*>
|
||||
val candidate = it.firClassLike(session)
|
||||
?.followAllAlias(session)
|
||||
?.safeAs<FirClass<*>>()
|
||||
?: continue
|
||||
|
||||
if (that in exclude) {
|
||||
if (candidate in exclude) {
|
||||
continue
|
||||
}
|
||||
|
||||
exclude.add(that)
|
||||
exclude.add(candidate)
|
||||
|
||||
if (that == this) {
|
||||
if (candidate == this) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (this.isSupertypeOf(that, exclude)) {
|
||||
if (this.isSupertypeOf(candidate, exclude)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -139,3 +141,47 @@ fun FirSymbolOwner<*>.getContainingClass(context: CheckerContext): FirClassLikeD
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FirClassLikeDeclaration the type alias is pointing
|
||||
* to provided `this` is a FirTypeAlias. Returns this otherwise.
|
||||
*/
|
||||
fun FirClassLikeDeclaration<*>.followAlias(session: FirSession): FirClassLikeDeclaration<*>? {
|
||||
return this.safeAs<FirTypeAlias>()
|
||||
?.expandedTypeRef
|
||||
?.firClassLike(session)
|
||||
?: return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FirClassLikeDeclaration that the
|
||||
* sequence of FirTypeAlias'es points to starting
|
||||
* with `this`. Or null if something goes wrong.
|
||||
*/
|
||||
fun FirClassLikeDeclaration<*>.followAllAlias(session: FirSession): FirClassLikeDeclaration<*>? {
|
||||
var it: FirClassLikeDeclaration<*>? = this
|
||||
|
||||
while (it is FirTypeAlias) {
|
||||
it = it.expandedTypeRef.firClassLike(session)
|
||||
}
|
||||
|
||||
return it
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest to the end of context.containingDeclarations
|
||||
* item like FirRegularClass or FirAnonymousObject
|
||||
* or null if no such item could be found.
|
||||
*/
|
||||
fun CheckerContext.findClosestClassOrObject(): FirClass<*>? {
|
||||
for (it in containingDeclarations.reversed()) {
|
||||
if (
|
||||
it is FirRegularClass ||
|
||||
it is FirAnonymousObject
|
||||
) {
|
||||
return it as FirClass<*>
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
+2
-1
@@ -14,7 +14,8 @@ object CallCheckers {
|
||||
FirSuperNotAvailableChecker,
|
||||
FirNotASupertypeChecker,
|
||||
FirSuperclassNotAccessibleFromInterfaceChecker,
|
||||
FirAbstractSuperCallChecker
|
||||
FirAbstractSuperCallChecker,
|
||||
FirQualifiedSupertypeExtendedByOtherSupertypeChecker,
|
||||
) + EXPRESSIONS
|
||||
|
||||
val FUNCTION_CALLS: List<FirFunctionCallChecker> = listOf<FirFunctionCallChecker>(
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.fir.analysis.checkers.call
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.findClosestClassOrObject
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.followAllAlias
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isSupertypeOf
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
object FirQualifiedSupertypeExtendedByOtherSupertypeChecker : FirQualifiedAccessChecker() {
|
||||
override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
// require to be called over a super reference
|
||||
val superReference = functionCall.calleeReference.safeAs<FirSuperReference>()
|
||||
?: return
|
||||
|
||||
val explicitType = superReference.superTypeRef.safeAs<FirResolvedTypeRef>()
|
||||
?.firClassLike(context.session)
|
||||
?.followAllAlias(context.session).safeAs<FirClass<*>>()
|
||||
?: return
|
||||
|
||||
val surroundingType = context.findClosestClassOrObject()
|
||||
?: return
|
||||
|
||||
// how many supertypes of `surroundingType`
|
||||
// have `explicitType` as their supertype or
|
||||
// equal to it
|
||||
var count = 0
|
||||
|
||||
for (it in surroundingType.superTypeRefs) {
|
||||
val that = it.firClassLike(context.session)
|
||||
?.followAllAlias(context.session).safeAs<FirClass<*>>()
|
||||
?: continue
|
||||
|
||||
if (explicitType == that || explicitType.isSupertypeOf(that)) {
|
||||
count += 1
|
||||
|
||||
if (count >= 2) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count >= 2) {
|
||||
reporter.report(superReference.superTypeRef.source)
|
||||
}
|
||||
}
|
||||
|
||||
private fun DiagnosticReporter.report(source: FirSourceElement?) {
|
||||
source?.let {
|
||||
report(FirErrors.QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE.on(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ object FirErrors {
|
||||
val NOT_A_SUPERTYPE by error0<FirSourceElement, PsiElement>()
|
||||
val SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE by error0<FirSourceElement, PsiElement>()
|
||||
val ABSTRACT_SUPER_CALL by error0<FirSourceElement, PsiElement>()
|
||||
val QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE by error0<FirSourceElement, PsiElement>()
|
||||
|
||||
val INAPPLICABLE_INFIX_MODIFIER by existing<FirSourceElement, PsiElement, String>(Errors.INAPPLICABLE_INFIX_MODIFIER)
|
||||
val CONSTRUCTOR_IN_OBJECT by existing<FirSourceElement, KtDeclaration>(Errors.CONSTRUCTOR_IN_OBJECT)
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
interface IBase<T> {
|
||||
fun foo() {}
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
interface IDerived<T> : IBase<T> {
|
||||
override fun foo() {}
|
||||
fun qux() {}
|
||||
}
|
||||
|
||||
class Test : IDerived<String>, IBase<String> {
|
||||
fun test() {
|
||||
super<IBase>.foo()
|
||||
super<IBase>.bar()
|
||||
super<IDerived>.foo()
|
||||
super<IDerived>.bar()
|
||||
super<IDerived>.qux()
|
||||
}
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
interface IBase<T> {
|
||||
fun foo() {}
|
||||
fun bar() {}
|
||||
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
interface IBase {
|
||||
fun foo() {}
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
interface IDerived1 : IBase {
|
||||
override fun foo() {}
|
||||
fun qux() {}
|
||||
}
|
||||
|
||||
interface IDerived2 : IBase {
|
||||
override fun foo() {}
|
||||
}
|
||||
|
||||
class Test : IDerived1, IBase, IDerived2 {
|
||||
override fun foo() {}
|
||||
|
||||
fun test() {
|
||||
super<IBase>.foo()
|
||||
super<IBase>.bar()
|
||||
|
||||
super<IDerived1>.foo()
|
||||
super<IDerived1>.bar()
|
||||
super<IDerived1>.qux()
|
||||
|
||||
super<IDerived2>.foo()
|
||||
super<IDerived2>.bar()
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
interface IBase {
|
||||
fun foo() {}
|
||||
fun bar() {}
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ class TestSuperForBase : B() {
|
||||
typealias MyBase = B
|
||||
|
||||
override fun foo() {
|
||||
<!NOT_A_SUPERTYPE!>super<Base><!>.foo()
|
||||
super<Base>.foo()
|
||||
super<B>.foo()
|
||||
super<MyBase>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||
super<U>.foo()
|
||||
@@ -32,7 +32,7 @@ class TestSuperForGenericBase<T> : GB<T>() {
|
||||
typealias MyBaseInt = GB<Int>
|
||||
|
||||
override fun foo() {
|
||||
<!NOT_A_SUPERTYPE!>super<GenericBase><!>.foo()
|
||||
super<GenericBase>.foo()
|
||||
super<GB>.foo()
|
||||
super<MyBase>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||
super<MyBaseInt>.<!UNRESOLVED_REFERENCE!>foo<!>() // Type arguments don't matter here
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ open class Bar2 : Bar() {
|
||||
class ManySupers: Foo2(), B {
|
||||
fun foo() {
|
||||
super<Foo2>.test()
|
||||
super<B>.test()
|
||||
super<<!QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE!>B<!>>.test()
|
||||
super.test()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user