From b38d30bab001120c447576adf218bfee88b8d08c Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 27 Mar 2020 03:18:03 +0300 Subject: [PATCH] [FIR] Support several super-related diagnostics --- .../resolve/diagnostics/notASupertype.kt | 10 ++ .../resolve/diagnostics/notASupertype.txt | 21 ++++ .../resolve/diagnostics/superNotAvailable.kt | 20 ++++ .../resolve/diagnostics/superNotAvailable.txt | 24 ++++ .../superclassNotAccessibleFromInterface.kt | 9 ++ .../superclassNotAccessibleFromInterface.txt | 16 +++ .../fir/FirDiagnosticsTestGenerated.java | 15 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 15 +++ .../fir/analysis/checkers/FirHelpers.kt | 108 ++++++++++++++++++ .../analysis/checkers/call/CallCheckers.kt | 12 +- .../checkers/call/FirNotASupertypeChecker.kt | 58 ++++++++++ .../call/FirSuperNotAvailableChecker.kt | 33 ++++++ ...rclassNotAccessibleFromInterfaceChecker.kt | 62 ++++++++++ .../checkers/context/CheckerContext.kt | 34 +++++- .../collectors/AbstractDiagnosticCollector.kt | 3 +- .../DeclarationCheckersDiagnosticComponent.kt | 4 + .../fir/analysis/diagnostics/FirErrors.kt | 3 + .../tests/thisAndSuper/Super.fir.kt | 12 +- .../notAccessibleSuperInTrait.fir.kt | 2 +- .../superInExtensionFunction.fir.kt | 4 +- .../superInToplevelFunction.fir.kt | 6 +- .../superIsNotAnExpression.fir.kt | 8 +- .../typeAliasAsSuperQualifier.fir.kt | 4 +- 23 files changed, 456 insertions(+), 27 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.txt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirNotASupertypeChecker.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirSuperNotAvailableChecker.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirSuperclassNotAccessibleFromInterfaceChecker.kt diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt new file mode 100644 index 00000000000..014517dd296 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt @@ -0,0 +1,10 @@ +class A { + fun f() {} +} + +class B : A { + fun g() { + super.f() + super.f() + } +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.txt new file mode 100644 index 00000000000..5e174020775 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.txt @@ -0,0 +1,21 @@ +FILE: notASupertype.kt + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public final fun f(): R|kotlin/Unit| { + } + + } + public final class B : R|A| { + public constructor(): R|B| { + super() + } + + public final fun g(): R|kotlin/Unit| { + super.#() + super.R|/A.f|() + } + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.kt new file mode 100644 index 00000000000..2afd9af2a99 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.kt @@ -0,0 +1,20 @@ +fun String.f() { + super@f.compareTo("") + super.compareTo("") +} + +fun foo() { + super + super.foo() + super.foo() +} + +class A { + fun act() { + println("Test") + } + + fun String.fact() { + println("Fest") + } +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.txt new file mode 100644 index 00000000000..ae86accc976 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.txt @@ -0,0 +1,24 @@ +FILE: superNotAvailable.kt + public final fun R|kotlin/String|.f(): R|kotlin/Unit| { + super<>.#(String()) + super<>.#(String()) + } + public final fun foo(): R|kotlin/Unit| { + super<> + super<>.#() + super.#() + } + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public final fun act(): R|kotlin/Unit| { + #(String(Test)) + } + + public final fun R|kotlin/String|.fact(): R|kotlin/Unit| { + #(String(Fest)) + } + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt new file mode 100644 index 00000000000..120ad12201b --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt @@ -0,0 +1,9 @@ +open class A { + open fun foo() {} +} + +interface ATrait : A { + override fun foo() { + super.foo() + } +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.txt new file mode 100644 index 00000000000..7d7157c95d9 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.txt @@ -0,0 +1,16 @@ +FILE: superclassNotAccessibleFromInterface.kt + public open class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public open fun foo(): R|kotlin/Unit| { + } + + } + public abstract interface ATrait : R|A| { + public open override fun foo(): R|kotlin/Unit| { + super.R|/A.foo|() + } + + } 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 7732cdd80c7..a9a530a794f 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 @@ -750,6 +750,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/infixFunctions.kt"); } + @TestMetadata("notASupertype.kt") + public void testNotASupertype() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt"); + } + @TestMetadata("redundantModifier.kt") public void testRedundantModifier() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/redundantModifier.kt"); @@ -764,6 +769,16 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { public void testSuperIsNotAnExpression() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superIsNotAnExpression.kt"); } + + @TestMetadata("superNotAvailable.kt") + public void testSuperNotAvailable() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.kt"); + } + + @TestMetadata("superclassNotAccessibleFromInterface.kt") + public void testSuperclassNotAccessibleFromInterface() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt"); + } } @TestMetadata("compiler/fir/analysis-tests/testData/resolve/expresssions") 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 49c5aa6c09c..6d232619983 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 @@ -750,6 +750,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/infixFunctions.kt"); } + @TestMetadata("notASupertype.kt") + public void testNotASupertype() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/notASupertype.kt"); + } + @TestMetadata("redundantModifier.kt") public void testRedundantModifier() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/redundantModifier.kt"); @@ -764,6 +769,16 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos public void testSuperIsNotAnExpression() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superIsNotAnExpression.kt"); } + + @TestMetadata("superNotAvailable.kt") + public void testSuperNotAvailable() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superNotAvailable.kt"); + } + + @TestMetadata("superclassNotAccessibleFromInterface.kt") + public void testSuperclassNotAccessibleFromInterface() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superclassNotAccessibleFromInterface.kt"); + } } @TestMetadata("compiler/fir/analysis-tests/testData/resolve/expresssions") 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 new file mode 100644 index 00000000000..2fd5f48e264 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -0,0 +1,108 @@ +/* + * 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 + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +/** + * Returns true if this is a superclass of other. + */ +fun FirClass<*>.isSuperclassOf(other: FirClass<*>): Boolean { + /** + * Hides additional parameters. + */ + fun FirClass<*>.isSuperclassOf(other: FirClass<*>, exclude: MutableSet>): Boolean { + for (it in other.superTypeRefs) { + val that = it.firClassLike(session) as? FirClass<*> + ?: continue + + if (that in exclude) { + continue + } + + if (that.classKind == ClassKind.CLASS) { + if (that == this) { + return true + } + + exclude.add(that) + return this.isSuperclassOf(that, exclude) + } + } + + return false + } + + return isSuperclassOf(other, mutableSetOf()) +} + +/** + * Returns true if this is a superclass of other. + */ +fun FirClass<*>.isSupertypeOf(other: FirClass<*>): Boolean { + /** + * Hides additional parameters. + */ + fun FirClass<*>.isSupertypeOf(other: FirClass<*>, exclude: MutableSet>): Boolean { + for (it in other.superTypeRefs) { + val that = it.firClassLike(session) as? FirClass<*> + ?: continue + + if (that in exclude) { + continue + } + + exclude.add(that) + + if (that == this) { + return true + } + + if (this.isSupertypeOf(that, exclude)) { + return true + } + } + + return false + } + + return isSupertypeOf(other, mutableSetOf()) +} + +/** + * Returns the FirRegularClass associated with this + * or null of something goes wrong. + */ +fun ConeClassLikeType.toRegularClass(session: FirSession): FirRegularClass? { + return lookupTag.toSymbol(session).safeAs()?.fir +} + +/** + * Returns the FirRegularClass associated with this + * or null of something goes wrong. + */ +fun ConeKotlinType.toRegularClass(session: FirSession): FirRegularClass? { + return safeAs()?.toRegularClass(session) +} + +/** + * Returns the FirRegularClass associated with this + * or null of something goes wrong. + */ +fun FirTypeRef.toRegularClass(session: FirSession): FirRegularClass? { + return safeAs()?.type?.toRegularClass(session) +} \ No newline at end of file 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 c7c42fdf641..dd1c6f9388e 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 @@ -9,6 +9,14 @@ import org.jetbrains.kotlin.fir.expressions.FirExpression object CallCheckers { val EXPRESSIONS: List> = listOf() - val QUALIFIED_ACCESS: List = listOf() + EXPRESSIONS - val FUNCTION_CALLS: List = listOf() + QUALIFIED_ACCESS + + val QUALIFIED_ACCESS: List = listOf( + FirSuperNotAvailableChecker, + FirNotASupertypeChecker, + FirSuperclassNotAccessibleFromInterfaceChecker + ) + EXPRESSIONS + + val FUNCTION_CALLS: List = listOf( + + ) + QUALIFIED_ACCESS } \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirNotASupertypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirNotASupertypeChecker.kt new file mode 100644 index 00000000000..58d584613bf --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirNotASupertypeChecker.kt @@ -0,0 +1,58 @@ +/* + * 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.isSupertypeOf +import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClass +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject +import org.jetbrains.kotlin.fir.declarations.FirClass +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.references.FirSuperReference +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +object FirNotASupertypeChecker : FirQualifiedAccessChecker() { + override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + val surrounding = context.findClosestClass() ?: return + + val targetClass = functionCall.calleeReference.safeAs() + ?.superTypeRef + ?.toRegularClass(context.session) + ?: return + + if (!targetClass.isSupertypeOf(surrounding)) { + reporter.report(functionCall.source) + } + } + + /** + * Returns the closest to the end of context.containingDeclarations + * item like FirRegularClass or FirAnonymousObject + * or null if no such item could be found. + */ + private fun CheckerContext.findClosestClass(): FirClass<*>? { + for (it in containingDeclarations.reversed()) { + if ( + it is FirRegularClass || + it is FirAnonymousObject + ) { + return it as FirClass<*> + } + } + + return null + } + + private fun DiagnosticReporter.report(source: FirSourceElement?) { + source?.let { + report(FirErrors.NOT_A_SUPERTYPE.on(it)) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirSuperNotAvailableChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirSuperNotAvailableChecker.kt new file mode 100644 index 00000000000..773bc6bac81 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirSuperNotAvailableChecker.kt @@ -0,0 +1,33 @@ +/* + * 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.KtNodeTypes.* +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression + +object FirSuperNotAvailableChecker : FirQualifiedAccessChecker() { + override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + if (functionCall.source?.elementType == SUPER_EXPRESSION) { + val isInsideClass = context.containingDeclarations.any { + it.source?.elementType == CLASS || it.source?.elementType == OBJECT_LITERAL + } + + if (!isInsideClass) { + reporter.report(functionCall.source) + } + } + } + + private fun DiagnosticReporter.report(source: FirSourceElement?) { + source?.let { + report(FirErrors.SUPER_NOT_AVAILABLE.on(it)) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirSuperclassNotAccessibleFromInterfaceChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirSuperclassNotAccessibleFromInterfaceChecker.kt new file mode 100644 index 00000000000..a2150197fe2 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/call/FirSuperclassNotAccessibleFromInterfaceChecker.kt @@ -0,0 +1,62 @@ +/* + * 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.fir.FirSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.isSuperclassOf +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +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.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +object FirSuperclassNotAccessibleFromInterfaceChecker : FirQualifiedAccessChecker() { + override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + val closestClass = context.findClosest() ?: return + + if (closestClass.classKind == ClassKind.INTERFACE) { + val origin = getClassLikeDeclaration(functionCall, context) + ?.symbol.safeAs() + ?.fir + ?: return + + if (origin.source != null && origin.isSuperclassOf(closestClass)) { + reporter.report(functionCall.explicitReceiver?.source) + } + } + } + + /** + * Returns the ClassLikeDeclaration where the function has been defined + * or null if no proper declaration has been found. + */ + private fun getClassLikeDeclaration(functionCall: FirQualifiedAccessExpression, context: CheckerContext): FirClassLikeDeclaration<*>? { + val classId = functionCall.calleeReference.safeAs() + ?.resolvedSymbol.safeAs() + ?.callableId + ?.classId + ?: return null + + if (!classId.isLocal) { + return context.session.firSymbolProvider.getClassLikeSymbolByFqName(classId)?.fir + } + + return null + } + + private fun DiagnosticReporter.report(source: FirSourceElement?) { + source?.let { + report(FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE.on(it)) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt index ce9f69101c0..50addb1041a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/context/CheckerContext.kt @@ -7,36 +7,58 @@ package org.jetbrains.kotlin.fir.analysis.checkers.context import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStack +import org.jetbrains.kotlin.fir.resolve.SessionHolder import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue import org.jetbrains.kotlin.name.Name abstract class CheckerContext { abstract val implicitReceiverStack: ImplicitReceiverStack abstract val containingDeclarations: List + abstract val sessionHolder: SessionHolder + + val session: FirSession + get() = sessionHolder.session + + /** + * Returns the closest to the end of context.containingDeclarations + * T instance or null if no such item could be found. + */ + inline fun findClosest(): T? { + for (it in containingDeclarations.reversed()) { + return it as? T ?: continue + } + + return null + } } class PersistentCheckerContext( - override val implicitReceiverStack: PersistentImplicitReceiverStack, - override val containingDeclarations: PersistentList + override val implicitReceiverStack: PersistentImplicitReceiverStack = PersistentImplicitReceiverStack(), + override val containingDeclarations: PersistentList = persistentListOf(), + override val sessionHolder: SessionHolder ) : CheckerContext() { - constructor() : this( + constructor(sessionHolder: SessionHolder) : this( PersistentImplicitReceiverStack(), - persistentListOf() + persistentListOf(), + sessionHolder ) fun addImplicitReceiver(name: Name?, value: ImplicitReceiverValue<*>): PersistentCheckerContext { return PersistentCheckerContext( implicitReceiverStack.add(name, value), - containingDeclarations + containingDeclarations, + sessionHolder ) } fun addDeclaration(declaration: FirDeclaration): PersistentCheckerContext { return PersistentCheckerContext( implicitReceiverStack, - containingDeclarations.add(declaration) + containingDeclarations.add(declaration), + sessionHolder ) } } \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt index 299c96d21ee..de61757da2a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt @@ -48,7 +48,8 @@ abstract class AbstractDiagnosticCollector( private var componentsInitialized = false private val visitor = Visitor() - private var context = PersistentCheckerContext() + @Suppress("LeakingThis") + private var context = PersistentCheckerContext(this) fun initializeComponents(vararg components: AbstractDiagnosticCollectorComponent) { if (componentsInitialized) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/DeclarationCheckersDiagnosticComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/DeclarationCheckersDiagnosticComponent.kt index 8069014a826..872821f74a8 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/DeclarationCheckersDiagnosticComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/DeclarationCheckersDiagnosticComponent.kt @@ -57,6 +57,10 @@ class DeclarationCheckersDiagnosticComponent(collector: AbstractDiagnosticCollec runCheck { DeclarationCheckers.DECLARATIONS.check(enumEntry, data, it) } } + override fun visitAnonymousObject(anonymousObject: FirAnonymousObject, data: CheckerContext) { + runCheck { DeclarationCheckers.DECLARATIONS.check(anonymousObject, data, it) } + } + private fun List>.check( declaration: D, context: CheckerContext, 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 8a759f40cf6..7b0c0863c2c 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 @@ -37,6 +37,9 @@ object FirErrors { val VARIABLE_EXPECTED by error0() val RETURN_NOT_ALLOWED by error0() val SUPER_IS_NOT_AN_EXPRESSION by error0() + val SUPER_NOT_AVAILABLE by error0() + val NOT_A_SUPERTYPE by error0() + val SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE 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/thisAndSuper/Super.fir.kt b/compiler/testData/diagnostics/tests/thisAndSuper/Super.fir.kt index d8d01cb2bc7..ea027ec6114 100644 --- a/compiler/testData/diagnostics/tests/thisAndSuper/Super.fir.kt +++ b/compiler/testData/diagnostics/tests/thisAndSuper/Super.fir.kt @@ -19,10 +19,10 @@ class A() : C(), T { super@A.bar() super.bar() super@A.bar() - super.foo() + super.foo() super<>.foo() - super<() -> Unit>.foo() - super.foo() + super<() -> Unit>.foo() + super.foo() super@B.foo() super@B.bar() } @@ -30,11 +30,11 @@ class A() : C(), T { inner class B : T { fun test() { super.foo(); - super.bar() - super@A.bar() + super.bar() + super@A.bar() super@A.foo() super@B.foo() - super@B.foo() + super@B.foo() super.foo() super super diff --git a/compiler/testData/diagnostics/tests/thisAndSuper/notAccessibleSuperInTrait.fir.kt b/compiler/testData/diagnostics/tests/thisAndSuper/notAccessibleSuperInTrait.fir.kt index 4d8bd0dc92f..92e994aabcc 100644 --- a/compiler/testData/diagnostics/tests/thisAndSuper/notAccessibleSuperInTrait.fir.kt +++ b/compiler/testData/diagnostics/tests/thisAndSuper/notAccessibleSuperInTrait.fir.kt @@ -5,6 +5,6 @@ open class A { interface ATrait : A { override fun foo() { - super.foo() + super.foo() } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/thisAndSuper/superInExtensionFunction.fir.kt b/compiler/testData/diagnostics/tests/thisAndSuper/superInExtensionFunction.fir.kt index 6bb82a500f7..5793824a7e6 100644 --- a/compiler/testData/diagnostics/tests/thisAndSuper/superInExtensionFunction.fir.kt +++ b/compiler/testData/diagnostics/tests/thisAndSuper/superInExtensionFunction.fir.kt @@ -1,4 +1,4 @@ fun String.f() { - super@f.compareTo("") - super.compareTo("") + super@f.compareTo("") + super.compareTo("") } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/thisAndSuper/superInToplevelFunction.fir.kt b/compiler/testData/diagnostics/tests/thisAndSuper/superInToplevelFunction.fir.kt index 7ff075f2363..b5b95fd190d 100644 --- a/compiler/testData/diagnostics/tests/thisAndSuper/superInToplevelFunction.fir.kt +++ b/compiler/testData/diagnostics/tests/thisAndSuper/superInToplevelFunction.fir.kt @@ -1,5 +1,5 @@ fun foo() { - super - super.foo() - super.foo() + super + super.foo() + super.foo() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/thisAndSuper/superIsNotAnExpression.fir.kt b/compiler/testData/diagnostics/tests/thisAndSuper/superIsNotAnExpression.fir.kt index 4a2dac7c651..6315871138c 100644 --- a/compiler/testData/diagnostics/tests/thisAndSuper/superIsNotAnExpression.fir.kt +++ b/compiler/testData/diagnostics/tests/thisAndSuper/superIsNotAnExpression.fir.kt @@ -1,11 +1,11 @@ fun any(a : Any) {} fun notAnExpression() { - any(super) // not an expression - if (super) {} else {} // not an expression - val x = super // not an expression + any(super) // not an expression + if (super) {} else {} // not an expression + val x = super // not an expression when (1) { - super -> 1 // not an expression + super -> 1 // not an expression else -> {} } diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasAsSuperQualifier.fir.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasAsSuperQualifier.fir.kt index 39534bbba1b..6bb4a6c32c7 100644 --- a/compiler/testData/diagnostics/tests/typealias/typeAliasAsSuperQualifier.fir.kt +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasAsSuperQualifier.fir.kt @@ -20,7 +20,7 @@ class TestSuperForBase : B() { typealias MyBase = B override fun foo() { - super.foo() + super.foo() super.foo() super.foo() super.foo() @@ -32,7 +32,7 @@ class TestSuperForGenericBase : GB() { typealias MyBaseInt = GB override fun foo() { - super.foo() + super.foo() super.foo() super.foo() super.foo() // Type arguments don't matter here