[FIR] Support diagnostic ABSTRACT_SUPER_CALL
This commit is contained in:
+41
@@ -0,0 +1,41 @@
|
||||
open class C {
|
||||
open val x: Int = 10
|
||||
fun h() {}
|
||||
}
|
||||
|
||||
abstract class A : C() {
|
||||
override val x: Int = 20
|
||||
|
||||
abstract val y: Int
|
||||
|
||||
abstract fun f()
|
||||
|
||||
fun t() {
|
||||
super.h()
|
||||
super.x
|
||||
}
|
||||
}
|
||||
|
||||
class B : A() {
|
||||
override fun f() {
|
||||
|
||||
}
|
||||
|
||||
fun g() {
|
||||
super.<!ABSTRACT_SUPER_CALL!>f<!>()
|
||||
super.t()
|
||||
|
||||
super.x
|
||||
super.<!ABSTRACT_SUPER_CALL!>y<!>
|
||||
}
|
||||
}
|
||||
|
||||
abstract class J : A() {
|
||||
fun r() {
|
||||
super.<!ABSTRACT_SUPER_CALL!>f<!>()
|
||||
super.t()
|
||||
|
||||
super.x
|
||||
super.<!ABSTRACT_SUPER_CALL!>y<!>
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
FILE: abstractSuperCall.kt
|
||||
public open class C : R|kotlin/Any| {
|
||||
public constructor(): R|C| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public open val x: R|kotlin/Int| = Int(10)
|
||||
public get(): R|kotlin/Int|
|
||||
|
||||
public final fun h(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
}
|
||||
public abstract class A : R|C| {
|
||||
public constructor(): R|A| {
|
||||
super<R|C|>()
|
||||
}
|
||||
|
||||
public open override val x: R|kotlin/Int| = Int(20)
|
||||
public get(): R|kotlin/Int|
|
||||
|
||||
public abstract val y: R|kotlin/Int|
|
||||
public get(): R|kotlin/Int|
|
||||
|
||||
public abstract fun f(): R|kotlin/Unit|
|
||||
|
||||
public final fun t(): R|kotlin/Unit| {
|
||||
super<R|C|>.R|/C.h|()
|
||||
super<R|C|>.R|/C.x|
|
||||
}
|
||||
|
||||
}
|
||||
public final class B : R|A| {
|
||||
public constructor(): R|B| {
|
||||
super<R|A|>()
|
||||
}
|
||||
|
||||
public final override fun f(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
public final fun g(): R|kotlin/Unit| {
|
||||
super<R|A|>.R|/A.f|()
|
||||
super<R|A|>.R|/A.t|()
|
||||
super<R|A|>.R|/A.x|
|
||||
super<R|A|>.R|/A.y|
|
||||
}
|
||||
|
||||
}
|
||||
public abstract class J : R|A| {
|
||||
public constructor(): R|J| {
|
||||
super<R|A|>()
|
||||
}
|
||||
|
||||
public final fun r(): R|kotlin/Unit| {
|
||||
super<R|A|>.R|/A.f|()
|
||||
super<R|A|>.R|/A.t|()
|
||||
super<R|A|>.R|/A.x|
|
||||
super<R|A|>.R|/A.y|
|
||||
}
|
||||
|
||||
}
|
||||
Generated
+5
@@ -787,6 +787,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("abstractSuperCall.kt")
|
||||
public void testAbstractSuperCall() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.kt");
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDiagnostics() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/diagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
+5
@@ -787,6 +787,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("abstractSuperCall.kt")
|
||||
public void testAbstractSuperCall() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.kt");
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDiagnostics() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/diagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,17 @@ package org.jetbrains.kotlin.fir.analysis.checkers
|
||||
|
||||
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.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
@@ -105,4 +112,30 @@ fun ConeKotlinType.toRegularClass(session: FirSession): FirRegularClass? {
|
||||
*/
|
||||
fun FirTypeRef.toRegularClass(session: FirSession): FirRegularClass? {
|
||||
return safeAs<FirResolvedTypeRef>()?.type?.toRegularClass(session)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns FirSimpleFunction based on the given FirFunctionCall
|
||||
*/
|
||||
inline fun <reified T : Any> FirQualifiedAccessExpression.getDeclaration(): T? {
|
||||
return this.calleeReference.safeAs<FirResolvedNamedReference>()
|
||||
?.resolvedSymbol
|
||||
?.fir.safeAs<T>()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ClassLikeDeclaration where the Fir object has been defined
|
||||
* or null if no proper declaration has been found.
|
||||
*/
|
||||
fun FirSymbolOwner<*>.getContainingClass(context: CheckerContext): FirClassLikeDeclaration<*>? {
|
||||
val classId = this.symbol.safeAs<FirCallableSymbol<*>>()
|
||||
?.callableId
|
||||
?.classId
|
||||
?: return null
|
||||
|
||||
if (!classId.isLocal) {
|
||||
return context.session.firSymbolProvider.getClassLikeSymbolByFqName(classId)?.fir
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@ object CallCheckers {
|
||||
val QUALIFIED_ACCESS: List<FirQualifiedAccessChecker> = listOf(
|
||||
FirSuperNotAvailableChecker,
|
||||
FirNotASupertypeChecker,
|
||||
FirSuperclassNotAccessibleFromInterfaceChecker
|
||||
FirSuperclassNotAccessibleFromInterfaceChecker,
|
||||
FirAbstractSuperCallChecker
|
||||
) + EXPRESSIONS
|
||||
|
||||
val FUNCTION_CALLS: List<FirFunctionCallChecker> = listOf<FirFunctionCallChecker>(
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getDeclaration
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.modality
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
object FirAbstractSuperCallChecker : FirQualifiedAccessChecker() {
|
||||
override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
// require the receiver to be the super reference
|
||||
functionCall.explicitReceiver.safeAs<FirQualifiedAccessExpression>()
|
||||
?.calleeReference.safeAs<FirSuperReference>()
|
||||
?: return
|
||||
|
||||
val closestClass = context.findClosest<FirRegularClass>()
|
||||
?: return
|
||||
|
||||
if (closestClass.classKind == ClassKind.CLASS) {
|
||||
// handles all the FirSimpleFunction/FirProperty/etc.
|
||||
val item = functionCall.getDeclaration<FirCallableMemberDeclaration<*>>()
|
||||
?: return
|
||||
|
||||
val declaration = item.getContainingClass(context).safeAs<FirRegularClass>()
|
||||
?: return
|
||||
|
||||
if (
|
||||
declaration.modality == Modality.ABSTRACT &&
|
||||
item.modality == Modality.ABSTRACT
|
||||
) {
|
||||
reporter.report(functionCall.calleeReference.source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DiagnosticReporter.report(source: FirSourceElement?) {
|
||||
source?.let {
|
||||
report(FirErrors.ABSTRACT_SUPER_CALL.on(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ object FirErrors {
|
||||
val SUPER_NOT_AVAILABLE by error0<FirSourceElement, PsiElement>()
|
||||
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 INAPPLICABLE_INFIX_MODIFIER by existing<FirSourceElement, PsiElement, String>(Errors.INAPPLICABLE_INFIX_MODIFIER)
|
||||
val CONSTRUCTOR_IN_OBJECT by existing<FirSourceElement, KtDeclaration>(Errors.CONSTRUCTOR_IN_OBJECT)
|
||||
|
||||
@@ -22,9 +22,9 @@ class C() : D() {
|
||||
|
||||
class B() : A() {
|
||||
override fun foo(): Int {
|
||||
super.i
|
||||
super.<!ABSTRACT_SUPER_CALL!>i<!>
|
||||
|
||||
super.fff() //everything is ok
|
||||
return super.foo() //no error!!
|
||||
return super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ open class B : A() {
|
||||
super.foo2("123")
|
||||
super.foo2()
|
||||
|
||||
super.foo3("123")
|
||||
super.foo3()
|
||||
super.<!ABSTRACT_SUPER_CALL!>foo3<!>("123")
|
||||
super.<!ABSTRACT_SUPER_CALL!>foo3<!>()
|
||||
}
|
||||
|
||||
override fun foo3(a: String) {
|
||||
|
||||
+5
-5
@@ -9,7 +9,7 @@ expect open class BaseAImpl() : BaseA
|
||||
|
||||
class DerivedA1 : BaseAImpl()
|
||||
class DerivedA2 : BaseAImpl() {
|
||||
override fun foo() = super.foo()
|
||||
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ expect open class BaseBImpl() : BaseB
|
||||
|
||||
class DerivedB1 : BaseBImpl()
|
||||
class DerivedB2 : BaseBImpl() {
|
||||
override fun foo() = super.foo()
|
||||
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ expect abstract class BaseCImpl() : BaseC
|
||||
|
||||
class DerivedC1 : BaseCImpl()
|
||||
class DerivedC2 : BaseCImpl() {
|
||||
override fun foo() = super.foo()
|
||||
override fun foo() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ expect interface BaseD {
|
||||
fun foo()
|
||||
}
|
||||
abstract class BaseDImpl() : BaseD {
|
||||
fun bar() = super.foo()
|
||||
fun bar() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ expect interface BaseE {
|
||||
fun foo()
|
||||
}
|
||||
sealed class BaseEImpl() : BaseE {
|
||||
fun bar() = super.foo()
|
||||
fun bar() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -25,8 +25,8 @@ class B : A(), I {
|
||||
override val y: Int = super.y
|
||||
|
||||
override fun foo(): Int {
|
||||
super.foo()
|
||||
return super.x
|
||||
super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||
return super.<!ABSTRACT_SUPER_CALL!>x<!>
|
||||
}
|
||||
|
||||
override fun bar() {
|
||||
|
||||
Reference in New Issue
Block a user