[FIR] Prohibit access to members of companion of enum from enum entry initializer
^KT-49110
This commit is contained in:
committed by
TeamCityServer
parent
58889a2b5c
commit
877b53201b
+12
@@ -5751,6 +5751,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("uninitializedCompanionOfEnum_after.kt")
|
||||
public void testUninitializedCompanionOfEnum_after() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("uninitializedCompanionOfEnum_before.kt")
|
||||
public void testUninitializedCompanionOfEnum_before() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_before.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("UninitializedEnumCompanionVals.kt")
|
||||
public void testUninitializedEnumCompanionVals() throws Exception {
|
||||
|
||||
+12
@@ -5751,6 +5751,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/tryWithAssignmentUsedInCatch.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("uninitializedCompanionOfEnum_after.kt")
|
||||
public void testUninitializedCompanionOfEnum_after() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_after.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("uninitializedCompanionOfEnum_before.kt")
|
||||
public void testUninitializedCompanionOfEnum_before() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedCompanionOfEnum_before.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("UninitializedEnumCompanionVals.kt")
|
||||
public void testUninitializedEnumCompanionVals() throws Exception {
|
||||
|
||||
+1
-1
@@ -1023,7 +1023,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val UNINITIALIZED_ENUM_ENTRY by error<KtSimpleNameExpression> {
|
||||
parameter<FirEnumEntrySymbol>("enumEntry")
|
||||
}
|
||||
val UNINITIALIZED_ENUM_COMPANION by error<KtSimpleNameExpression>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
val UNINITIALIZED_ENUM_COMPANION by error<KtExpression>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
|
||||
parameter<FirRegularClassSymbol>("enumClass")
|
||||
}
|
||||
val VAL_REASSIGNMENT by error<KtExpression> {
|
||||
|
||||
+1
-1
@@ -559,7 +559,7 @@ object FirErrors {
|
||||
val UNINITIALIZED_VARIABLE by error1<KtExpression, FirPropertySymbol>()
|
||||
val UNINITIALIZED_PARAMETER by error1<KtSimpleNameExpression, FirValueParameterSymbol>()
|
||||
val UNINITIALIZED_ENUM_ENTRY by error1<KtSimpleNameExpression, FirEnumEntrySymbol>()
|
||||
val UNINITIALIZED_ENUM_COMPANION by error1<KtSimpleNameExpression, FirRegularClassSymbol>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val UNINITIALIZED_ENUM_COMPANION by error1<KtExpression, FirRegularClassSymbol>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val VAL_REASSIGNMENT by error1<KtExpression, FirVariableSymbol<*>>()
|
||||
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by deprecationError1<KtExpression, FirBackingFieldSymbol>(RestrictionOfValReassignmentViaBackingField)
|
||||
val CAPTURED_VAL_INITIALIZATION by error1<KtExpression, FirPropertySymbol>()
|
||||
|
||||
+5
@@ -156,4 +156,9 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
get() = setOf(
|
||||
FirAnonymousInitializerInInterfaceChecker
|
||||
)
|
||||
|
||||
override val enumEntryCheckers: Set<FirEnumEntryChecker>
|
||||
get() = setOf(
|
||||
FirEnumCompanionInEnumConstructorCallChecker,
|
||||
)
|
||||
}
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.declaration
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.primaryConstructorIfAny
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
|
||||
import org.jetbrains.kotlin.fir.visitors.acceptAllElements
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
|
||||
|
||||
object FirEnumCompanionInEnumConstructorCallChecker : FirEnumEntryChecker() {
|
||||
override fun check(declaration: FirEnumEntry, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val enumClass = context.containingDeclarations.lastIsInstanceOrNull<FirRegularClass>() ?: return
|
||||
if (enumClass.classKind != ClassKind.ENUM_CLASS) return
|
||||
val companionOfEnumSymbol = enumClass.companionObject?.symbol ?: return
|
||||
val initializerObject = (declaration.initializer as? FirAnonymousObjectExpression)?.anonymousObject ?: return
|
||||
val delegatingConstructorCall = initializerObject.primaryConstructorIfAny(context.session)?.resolvedDelegatedConstructorCall ?: return
|
||||
val visitor = Visitor(context, reporter, companionOfEnumSymbol)
|
||||
delegatingConstructorCall.argumentList.acceptChildren(visitor)
|
||||
}
|
||||
|
||||
private class Visitor(
|
||||
val context: CheckerContext,
|
||||
val reporter: DiagnosticReporter,
|
||||
val companionSymbol: FirRegularClassSymbol
|
||||
) : FirVisitorVoid() {
|
||||
override fun visitElement(element: FirElement) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitFunctionCall(functionCall: FirFunctionCall) {
|
||||
val needVisitReceiver = checkQualifiedAccess(functionCall)
|
||||
functionCall.argumentList.acceptChildren(this)
|
||||
if (needVisitReceiver) {
|
||||
functionCall.explicitReceiver?.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression) {
|
||||
val needVisitReceiver = checkQualifiedAccess(propertyAccessExpression)
|
||||
if (needVisitReceiver) {
|
||||
propertyAccessExpression.explicitReceiver?.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun checkQualifiedAccess(expression: FirQualifiedAccessExpression): Boolean {
|
||||
val extensionReceiver = expression.extensionReceiver
|
||||
if (extensionReceiver !is FirResolvedQualifier && extensionReceiver !is FirThisReceiverExpression) return true
|
||||
|
||||
val receiverSymbol = expression.toResolvedCallableSymbol()
|
||||
?.resolvedReceiverTypeRef
|
||||
?.toRegularClassSymbol(context.session)
|
||||
?: return true
|
||||
|
||||
if (receiverSymbol == companionSymbol) {
|
||||
val source = expression.extensionReceiver.source ?: expression.source
|
||||
reporter.reportOn(source, FirErrors.UNINITIALIZED_ENUM_COMPANION, companionSymbol, context)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,3 +39,11 @@ inline fun <T : FirElement, D> MutableList<T>.transformInplace(transformer: FirT
|
||||
iterator.set(result)
|
||||
}
|
||||
}
|
||||
|
||||
fun <R, D> List<FirElement>.acceptAllElements(visitor: FirVisitor<R, D>, data: D) {
|
||||
forEach { it.accept(visitor, data) }
|
||||
}
|
||||
|
||||
fun List<FirElement>.acceptAllElements(visitor: FirVisitorVoid) {
|
||||
forEach { it.accept(visitor) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user