From c4b7ac994b9f5a3098f106cf85ebbdd06b1afe24 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 11 Apr 2020 14:29:50 +0300 Subject: [PATCH] [FIR] Support diagnostic ABSTRACT_SUPER_CALL --- .../resolve/diagnostics/abstractSuperCall.kt | 41 +++++++++++++ .../resolve/diagnostics/abstractSuperCall.txt | 61 +++++++++++++++++++ .../fir/FirDiagnosticsTestGenerated.java | 5 ++ ...DiagnosticsWithLightTreeTestGenerated.java | 5 ++ .../fir/analysis/checkers/FirHelpers.kt | 35 ++++++++++- .../analysis/checkers/call/CallCheckers.kt | 3 +- .../call/FirAbstractSuperCallChecker.kt | 55 +++++++++++++++++ .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../tests/declarationChecks/kt559.fir.kt | 4 +- .../tests/defaultArguments/superCall.fir.kt | 4 +- ...tendExpectedClassWithAbstractMember.fir.kt | 10 +-- ...unqualifiedSuperWithAbstractMembers.fir.kt | 4 +- 12 files changed, 215 insertions(+), 13 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.txt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirAbstractSuperCallChecker.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.kt new file mode 100644 index 00000000000..4c7e16469db --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.kt @@ -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.f() + super.t() + + super.x + super.y + } +} + +abstract class J : A() { + fun r() { + super.f() + super.t() + + super.x + super.y + } +} diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.txt new file mode 100644 index 00000000000..3820afc9ac4 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/abstractSuperCall.txt @@ -0,0 +1,61 @@ +FILE: abstractSuperCall.kt + public open class C : R|kotlin/Any| { + public constructor(): R|C| { + super() + } + + 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() + } + + 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.h|() + super.R|/C.x| + } + + } + public final class B : R|A| { + public constructor(): R|B| { + super() + } + + public final override fun f(): R|kotlin/Unit| { + } + + public final fun g(): R|kotlin/Unit| { + super.R|/A.f|() + super.R|/A.t|() + super.R|/A.x| + super.R|/A.y| + } + + } + public abstract class J : R|A| { + public constructor(): R|J| { + super() + } + + public final fun r(): R|kotlin/Unit| { + super.R|/A.f|() + super.R|/A.t|() + super.R|/A.x| + super.R|/A.y| + } + + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 5dde4adb6bd..d65832ea4ce 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -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); } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index ec722e37edb..40c3a3c6d4f 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -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); } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index 2fd5f48e264..7e37757f876 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -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()?.type?.toRegularClass(session) -} \ No newline at end of file +} + +/** + * Returns FirSimpleFunction based on the given FirFunctionCall + */ +inline fun FirQualifiedAccessExpression.getDeclaration(): T? { + return this.calleeReference.safeAs() + ?.resolvedSymbol + ?.fir.safeAs() +} + +/** + * 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>() + ?.callableId + ?.classId + ?: return null + + if (!classId.isLocal) { + return context.session.firSymbolProvider.getClassLikeSymbolByFqName(classId)?.fir + } + + return null +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/CallCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/CallCheckers.kt index dd1c6f9388e..d57657f5180 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/CallCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/CallCheckers.kt @@ -13,7 +13,8 @@ object CallCheckers { val QUALIFIED_ACCESS: List = listOf( FirSuperNotAvailableChecker, FirNotASupertypeChecker, - FirSuperclassNotAccessibleFromInterfaceChecker + FirSuperclassNotAccessibleFromInterfaceChecker, + FirAbstractSuperCallChecker ) + EXPRESSIONS val FUNCTION_CALLS: List = listOf( diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirAbstractSuperCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirAbstractSuperCallChecker.kt new file mode 100644 index 00000000000..f0409e9acfc --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirAbstractSuperCallChecker.kt @@ -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() + ?.calleeReference.safeAs() + ?: return + + val closestClass = context.findClosest() + ?: return + + if (closestClass.classKind == ClassKind.CLASS) { + // handles all the FirSimpleFunction/FirProperty/etc. + val item = functionCall.getDeclaration>() + ?: return + + val declaration = item.getContainingClass(context).safeAs() + ?: 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)) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index a656de9e22b..4583aa64f5a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -43,6 +43,7 @@ object FirErrors { val SUPER_NOT_AVAILABLE by error0() val NOT_A_SUPERTYPE by error0() val SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE by error0() + val ABSTRACT_SUPER_CALL by error0() val INAPPLICABLE_INFIX_MODIFIER by existing(Errors.INAPPLICABLE_INFIX_MODIFIER) val CONSTRUCTOR_IN_OBJECT by existing(Errors.CONSTRUCTOR_IN_OBJECT) diff --git a/compiler/testData/diagnostics/tests/declarationChecks/kt559.fir.kt b/compiler/testData/diagnostics/tests/declarationChecks/kt559.fir.kt index 9a9657ce04a..8ad56c8ea24 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/kt559.fir.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/kt559.fir.kt @@ -22,9 +22,9 @@ class C() : D() { class B() : A() { override fun foo(): Int { - super.i + super.i super.fff() //everything is ok - return super.foo() //no error!! + return super.foo() } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/defaultArguments/superCall.fir.kt b/compiler/testData/diagnostics/tests/defaultArguments/superCall.fir.kt index 77305e62156..d61a4e53aae 100644 --- a/compiler/testData/diagnostics/tests/defaultArguments/superCall.fir.kt +++ b/compiler/testData/diagnostics/tests/defaultArguments/superCall.fir.kt @@ -18,8 +18,8 @@ open class B : A() { super.foo2("123") super.foo2() - super.foo3("123") - super.foo3() + super.foo3("123") + super.foo3() } override fun foo3(a: String) { diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.fir.kt index 58af3847cb9..6903d6fe9d8 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/extendExpectedClassWithAbstractMember.fir.kt @@ -9,7 +9,7 @@ expect open class BaseAImpl() : BaseA class DerivedA1 : BaseAImpl() class DerivedA2 : BaseAImpl() { - override fun foo() = super.foo() + override fun foo() = super.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.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.foo() } @@ -42,7 +42,7 @@ expect interface BaseD { fun foo() } abstract class BaseDImpl() : BaseD { - fun bar() = super.foo() + fun bar() = super.foo() } @@ -51,7 +51,7 @@ expect interface BaseE { fun foo() } sealed class BaseEImpl() : BaseE { - fun bar() = super.foo() + fun bar() = super.foo() } diff --git a/compiler/testData/diagnostics/tests/thisAndSuper/unqualifiedSuper/unqualifiedSuperWithAbstractMembers.fir.kt b/compiler/testData/diagnostics/tests/thisAndSuper/unqualifiedSuper/unqualifiedSuperWithAbstractMembers.fir.kt index 8a0e37f2e25..a3435563189 100644 --- a/compiler/testData/diagnostics/tests/thisAndSuper/unqualifiedSuper/unqualifiedSuperWithAbstractMembers.fir.kt +++ b/compiler/testData/diagnostics/tests/thisAndSuper/unqualifiedSuper/unqualifiedSuperWithAbstractMembers.fir.kt @@ -25,8 +25,8 @@ class B : A(), I { override val y: Int = super.y override fun foo(): Int { - super.foo() - return super.x + super.foo() + return super.x } override fun bar() {