[FE 1.0] Prohibit access to members of companion of enum from enum entry initializer
^KT-49461 Fixed ^KT-49110
This commit is contained in:
committed by
TeamCityServer
parent
4f0c3c3c0d
commit
58889a2b5c
+9
-1
@@ -407,7 +407,15 @@ class ControlFlowInformationProviderImpl private constructor(
|
||||
report(Errors.UNINITIALIZED_ENUM_ENTRY.on(element, classDescriptor), ctxt)
|
||||
ClassKind.OBJECT -> if (classDescriptor.isCompanionObject) {
|
||||
val container = classDescriptor.containingDeclaration
|
||||
if (container is ClassDescriptor && container.kind == ClassKind.ENUM_CLASS) {
|
||||
/*
|
||||
* ProhibitAccessToEnumCompanionMembersInEnumConstructorCall feature enabled then UNINITIALIZED_ENUM_COMPANION
|
||||
* will be reported from EnumCompanionInEnumConstructorCallChecker
|
||||
*/
|
||||
if (
|
||||
container is ClassDescriptor &&
|
||||
container.kind == ClassKind.ENUM_CLASS &&
|
||||
!languageVersionSettings.supportsFeature(LanguageFeature.ProhibitAccessToEnumCompanionMembersInEnumConstructorCall)
|
||||
) {
|
||||
report(Errors.UNINITIALIZED_ENUM_COMPANION.on(element, container), ctxt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -964,7 +964,8 @@ public interface Errors {
|
||||
DiagnosticFactory1<KtSimpleNameExpression, VariableDescriptor> UNINITIALIZED_VARIABLE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<KtSimpleNameExpression, ValueParameterDescriptor> UNINITIALIZED_PARAMETER = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<KtSimpleNameExpression, ClassDescriptor> UNINITIALIZED_ENUM_ENTRY = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<KtSimpleNameExpression, ClassDescriptor> UNINITIALIZED_ENUM_COMPANION = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<KtExpression, ClassDescriptor> UNINITIALIZED_ENUM_COMPANION = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<KtExpression, ClassDescriptor> UNINITIALIZED_ENUM_COMPANION_WARNING = DiagnosticFactory1.create(WARNING);
|
||||
|
||||
DiagnosticFactory1<KtNamedDeclaration, VariableDescriptor> UNUSED_VARIABLE = DiagnosticFactory1.create(WARNING, DECLARATION_NAME);
|
||||
DiagnosticFactory1<KtParameter, VariableDescriptor> UNUSED_PARAMETER = DiagnosticFactory1.create(WARNING, DECLARATION_NAME);
|
||||
|
||||
+1
@@ -346,6 +346,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(UNINITIALIZED_PARAMETER, "Parameter ''{0}'' is uninitialized here", NAME);
|
||||
MAP.put(UNINITIALIZED_ENUM_ENTRY, "Enum entry ''{0}'' is uninitialized here", NAME);
|
||||
MAP.put(UNINITIALIZED_ENUM_COMPANION, "Companion object of enum class ''{0}'' is uninitialized here", NAME);
|
||||
MAP.put(UNINITIALIZED_ENUM_COMPANION_WARNING, "Companion object of enum class ''{0}'' is uninitialized here. This warning will became error in future releases", NAME);
|
||||
MAP.put(UNUSED_VARIABLE, "Variable ''{0}'' is never used", NAME);
|
||||
MAP.put(UNUSED_PARAMETER, "Parameter ''{0}'' is never used", NAME);
|
||||
MAP.put(UNUSED_ANONYMOUS_PARAMETER, "Parameter ''{0}'' is never used, could be renamed to _", NAME);
|
||||
|
||||
@@ -46,6 +46,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
SealedInheritorInSameModuleChecker,
|
||||
SealedInterfaceAllowedChecker,
|
||||
SuspendFunctionAsSupertypeChecker,
|
||||
EnumCompanionInEnumConstructorCallChecker,
|
||||
)
|
||||
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.resolve.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassValueReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
object EnumCompanionInEnumConstructorCallChecker : DeclarationChecker {
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (declaration !is KtEnumEntry || descriptor !is ClassDescriptor) return
|
||||
if (descriptor.kind != ClassKind.ENUM_ENTRY) return
|
||||
val enumDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return
|
||||
val enumCompanion = enumDescriptor.companionObjectDescriptor ?: return
|
||||
val initializer = declaration.initializerList?.initializers?.firstIsInstanceOrNull<KtSuperTypeCallEntry>() ?: return
|
||||
val bindingTrace = context.trace
|
||||
val visitor = Visitor(
|
||||
enumDescriptor,
|
||||
enumCompanion,
|
||||
bindingTrace.bindingContext,
|
||||
bindingTrace,
|
||||
reportError = context.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitAccessToEnumCompanionMembersInEnumConstructorCall)
|
||||
)
|
||||
initializer.acceptChildren(visitor)
|
||||
}
|
||||
|
||||
private class Visitor(
|
||||
val enumDescriptor: ClassDescriptor,
|
||||
val companionDescriptor: ClassDescriptor,
|
||||
val context: BindingContext,
|
||||
val reporter: DiagnosticSink,
|
||||
val reportError: Boolean
|
||||
) : KtVisitorVoid() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
|
||||
override fun visitExpression(expression: KtExpression) {
|
||||
val needAnalyzeReceiver = analyzeExpression(expression)
|
||||
if (needAnalyzeReceiver) {
|
||||
expression.acceptChildren(this)
|
||||
} else if (expression is KtCallExpression) {
|
||||
expression.valueArgumentList?.acceptChildren(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun analyzeExpression(expression: KtExpression): Boolean {
|
||||
if (expression.parent is KtCallExpression) return true
|
||||
val resolvedCall = expression.getResolvedCall(context) ?: return true
|
||||
|
||||
val dispatchDescriptor = resolvedCall.dispatchReceiver.resolvedDescriptor
|
||||
val extensionDescriptor = resolvedCall.extensionReceiver.resolvedDescriptor
|
||||
val dispatchIsCompanion = dispatchDescriptor == companionDescriptor
|
||||
val extensionIsCompanion = extensionDescriptor == companionDescriptor
|
||||
|
||||
val dispatchIsImplicit = resolvedCall.dispatchReceiver is ImplicitClassReceiver
|
||||
val extensionIsImplicit = resolvedCall.extensionReceiver is ImplicitClassReceiver
|
||||
|
||||
/*
|
||||
* ControlFlowInformationProviderImpl already reports UNINITIALIZED_ENUM_COMPANION for extension function calls
|
||||
* with implicit companion receiver, so we should skip reporting a warning
|
||||
*
|
||||
* If feature is enabled then ControlFlowInformationProviderImpl won't report an error, to keep all checks
|
||||
* in one place (in this checker)
|
||||
*/
|
||||
if (
|
||||
!reportError &&
|
||||
expression is KtCallExpression &&
|
||||
(dispatchIsCompanion && dispatchIsImplicit || extensionIsCompanion && extensionIsImplicit)
|
||||
) return false
|
||||
|
||||
if (dispatchIsCompanion || extensionIsCompanion) {
|
||||
val reportOn = when (val receiverExpression = (expression as? KtQualifiedExpression)?.receiverExpression) {
|
||||
is KtSimpleNameExpression -> receiverExpression
|
||||
is KtQualifiedExpression -> receiverExpression.selectorExpression
|
||||
else -> null
|
||||
} ?: expression
|
||||
val factory = if (reportError) {
|
||||
Errors.UNINITIALIZED_ENUM_COMPANION
|
||||
} else {
|
||||
Errors.UNINITIALIZED_ENUM_COMPANION_WARNING
|
||||
}
|
||||
reporter.report(factory.on(reportOn, enumDescriptor))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private val ReceiverValue?.resolvedDescriptor: DeclarationDescriptor?
|
||||
get() {
|
||||
if (this !is ClassValueReceiver && this !is ImplicitClassReceiver) return null
|
||||
return this.type.unwrap().constructor.declarationDescriptor
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user