[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);
|
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 {
|
public void testAllFilesPresentInDiagnostics() throws Exception {
|
||||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/diagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
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);
|
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 {
|
public void testAllFilesPresentInDiagnostics() throws Exception {
|
||||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/diagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
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.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
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.FirClass
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
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.toSymbol
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
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.symbols.impl.FirRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
@@ -105,4 +112,30 @@ fun ConeKotlinType.toRegularClass(session: FirSession): FirRegularClass? {
|
|||||||
*/
|
*/
|
||||||
fun FirTypeRef.toRegularClass(session: FirSession): FirRegularClass? {
|
fun FirTypeRef.toRegularClass(session: FirSession): FirRegularClass? {
|
||||||
return safeAs<FirResolvedTypeRef>()?.type?.toRegularClass(session)
|
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(
|
val QUALIFIED_ACCESS: List<FirQualifiedAccessChecker> = listOf(
|
||||||
FirSuperNotAvailableChecker,
|
FirSuperNotAvailableChecker,
|
||||||
FirNotASupertypeChecker,
|
FirNotASupertypeChecker,
|
||||||
FirSuperclassNotAccessibleFromInterfaceChecker
|
FirSuperclassNotAccessibleFromInterfaceChecker,
|
||||||
|
FirAbstractSuperCallChecker
|
||||||
) + EXPRESSIONS
|
) + EXPRESSIONS
|
||||||
|
|
||||||
val FUNCTION_CALLS: List<FirFunctionCallChecker> = listOf<FirFunctionCallChecker>(
|
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 SUPER_NOT_AVAILABLE by error0<FirSourceElement, PsiElement>()
|
||||||
val NOT_A_SUPERTYPE by error0<FirSourceElement, PsiElement>()
|
val NOT_A_SUPERTYPE by error0<FirSourceElement, PsiElement>()
|
||||||
val SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE 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 INAPPLICABLE_INFIX_MODIFIER by existing<FirSourceElement, PsiElement, String>(Errors.INAPPLICABLE_INFIX_MODIFIER)
|
||||||
val CONSTRUCTOR_IN_OBJECT by existing<FirSourceElement, KtDeclaration>(Errors.CONSTRUCTOR_IN_OBJECT)
|
val CONSTRUCTOR_IN_OBJECT by existing<FirSourceElement, KtDeclaration>(Errors.CONSTRUCTOR_IN_OBJECT)
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ class C() : D() {
|
|||||||
|
|
||||||
class B() : A() {
|
class B() : A() {
|
||||||
override fun foo(): Int {
|
override fun foo(): Int {
|
||||||
super.i
|
super.<!ABSTRACT_SUPER_CALL!>i<!>
|
||||||
|
|
||||||
super.fff() //everything is ok
|
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("123")
|
||||||
super.foo2()
|
super.foo2()
|
||||||
|
|
||||||
super.foo3("123")
|
super.<!ABSTRACT_SUPER_CALL!>foo3<!>("123")
|
||||||
super.foo3()
|
super.<!ABSTRACT_SUPER_CALL!>foo3<!>()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun foo3(a: String) {
|
override fun foo3(a: String) {
|
||||||
|
|||||||
+5
-5
@@ -9,7 +9,7 @@ expect open class BaseAImpl() : BaseA
|
|||||||
|
|
||||||
class DerivedA1 : BaseAImpl()
|
class DerivedA1 : BaseAImpl()
|
||||||
class DerivedA2 : 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 DerivedB1 : BaseBImpl()
|
||||||
class DerivedB2 : 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 DerivedC1 : BaseCImpl()
|
||||||
class DerivedC2 : 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()
|
fun foo()
|
||||||
}
|
}
|
||||||
abstract class BaseDImpl() : BaseD {
|
abstract class BaseDImpl() : BaseD {
|
||||||
fun bar() = super.foo()
|
fun bar() = super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ expect interface BaseE {
|
|||||||
fun foo()
|
fun foo()
|
||||||
}
|
}
|
||||||
sealed class BaseEImpl() : BaseE {
|
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 val y: Int = super.y
|
||||||
|
|
||||||
override fun foo(): Int {
|
override fun foo(): Int {
|
||||||
super.foo()
|
super.<!ABSTRACT_SUPER_CALL!>foo<!>()
|
||||||
return super.x
|
return super.<!ABSTRACT_SUPER_CALL!>x<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bar() {
|
override fun bar() {
|
||||||
|
|||||||
Reference in New Issue
Block a user