[FIR] Support diagnostic ABSTRACT_SUPER_CALL

This commit is contained in:
Nick
2020-04-11 14:29:50 +03:00
committed by Mikhail Glukhikh
parent 8960829c01
commit c4b7ac994b
12 changed files with 215 additions and 13 deletions
@@ -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<!>
}
}
@@ -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|
}
}
@@ -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);
}
@@ -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
}
@@ -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>(
@@ -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) {
@@ -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<!>()
}
@@ -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() {