K2: implement FirLateinitIntrinsicApplicabilityChecker ^KT-55495 Fixed
This commit is contained in:
committed by
Space Team
parent
cfe8bc682c
commit
721a37f7e7
+25
@@ -2877,6 +2877,31 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL) { firDiagnostic ->
|
||||||
|
LateinitIntrinsicCallOnNonLiteralImpl(
|
||||||
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
add(FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT) { firDiagnostic ->
|
||||||
|
LateinitIntrinsicCallOnNonLateinitImpl(
|
||||||
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
add(FirErrors.LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION) { firDiagnostic ->
|
||||||
|
LateinitIntrinsicCallInInlineFunctionImpl(
|
||||||
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
add(FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY) { firDiagnostic ->
|
||||||
|
LateinitIntrinsicCallOnNonAccessiblePropertyImpl(
|
||||||
|
firSymbolBuilder.buildSymbol(firDiagnostic.a),
|
||||||
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.EXPECTED_DECLARATION_WITH_BODY) { firDiagnostic ->
|
add(FirErrors.EXPECTED_DECLARATION_WITH_BODY) { firDiagnostic ->
|
||||||
ExpectedDeclarationWithBodyImpl(
|
ExpectedDeclarationWithBodyImpl(
|
||||||
firDiagnostic as KtPsiDiagnostic,
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
|||||||
+17
@@ -2031,6 +2031,23 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
override val diagnosticClass get() = ExplicitTypeArgumentsInPropertyAccess::class
|
override val diagnosticClass get() = ExplicitTypeArgumentsInPropertyAccess::class
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class LateinitIntrinsicCallOnNonLiteral : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = LateinitIntrinsicCallOnNonLiteral::class
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class LateinitIntrinsicCallOnNonLateinit : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = LateinitIntrinsicCallOnNonLateinit::class
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class LateinitIntrinsicCallInInlineFunction : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = LateinitIntrinsicCallInInlineFunction::class
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class LateinitIntrinsicCallOnNonAccessibleProperty : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = LateinitIntrinsicCallOnNonAccessibleProperty::class
|
||||||
|
abstract val declaration: KtSymbol
|
||||||
|
}
|
||||||
|
|
||||||
abstract class ExpectedDeclarationWithBody : KtFirDiagnostic<KtDeclaration>() {
|
abstract class ExpectedDeclarationWithBody : KtFirDiagnostic<KtDeclaration>() {
|
||||||
override val diagnosticClass get() = ExpectedDeclarationWithBody::class
|
override val diagnosticClass get() = ExpectedDeclarationWithBody::class
|
||||||
}
|
}
|
||||||
|
|||||||
+21
@@ -2446,6 +2446,27 @@ internal class ExplicitTypeArgumentsInPropertyAccessImpl(
|
|||||||
override val token: KtLifetimeToken,
|
override val token: KtLifetimeToken,
|
||||||
) : KtFirDiagnostic.ExplicitTypeArgumentsInPropertyAccess(), KtAbstractFirDiagnostic<KtExpression>
|
) : KtFirDiagnostic.ExplicitTypeArgumentsInPropertyAccess(), KtAbstractFirDiagnostic<KtExpression>
|
||||||
|
|
||||||
|
internal class LateinitIntrinsicCallOnNonLiteralImpl(
|
||||||
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
|
override val token: KtLifetimeToken,
|
||||||
|
) : KtFirDiagnostic.LateinitIntrinsicCallOnNonLiteral(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
|
internal class LateinitIntrinsicCallOnNonLateinitImpl(
|
||||||
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
|
override val token: KtLifetimeToken,
|
||||||
|
) : KtFirDiagnostic.LateinitIntrinsicCallOnNonLateinit(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
|
internal class LateinitIntrinsicCallInInlineFunctionImpl(
|
||||||
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
|
override val token: KtLifetimeToken,
|
||||||
|
) : KtFirDiagnostic.LateinitIntrinsicCallInInlineFunction(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
|
internal class LateinitIntrinsicCallOnNonAccessiblePropertyImpl(
|
||||||
|
override val declaration: KtSymbol,
|
||||||
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
|
override val token: KtLifetimeToken,
|
||||||
|
) : KtFirDiagnostic.LateinitIntrinsicCallOnNonAccessibleProperty(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
internal class ExpectedDeclarationWithBodyImpl(
|
internal class ExpectedDeclarationWithBodyImpl(
|
||||||
override val firDiagnostic: KtPsiDiagnostic,
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
override val token: KtLifetimeToken,
|
override val token: KtLifetimeToken,
|
||||||
|
|||||||
+7
@@ -1018,6 +1018,13 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING by warning<KtProperty>(PositioningStrategy.TYPE_PARAMETERS_LIST)
|
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING by warning<KtProperty>(PositioningStrategy.TYPE_PARAMETERS_LIST)
|
||||||
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS by error<KtProperty>(PositioningStrategy.TYPE_PARAMETERS_LIST)
|
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS by error<KtProperty>(PositioningStrategy.TYPE_PARAMETERS_LIST)
|
||||||
val EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS by error<KtExpression>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
val EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS by error<KtExpression>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED)
|
||||||
|
|
||||||
|
val LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL by error<PsiElement>()
|
||||||
|
val LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT by error<PsiElement>()
|
||||||
|
val LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION by error<PsiElement>()
|
||||||
|
val LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY by error<PsiElement>() {
|
||||||
|
parameter<Symbol>("declaration")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val MPP_PROJECTS by object : DiagnosticGroup("Multi-platform projects") {
|
val MPP_PROJECTS by object : DiagnosticGroup("Multi-platform projects") {
|
||||||
|
|||||||
@@ -557,6 +557,10 @@ object FirErrors {
|
|||||||
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING by warning0<KtProperty>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
|
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS_WARNING by warning0<KtProperty>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
|
||||||
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS by error0<KtProperty>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
|
val LOCAL_VARIABLE_WITH_TYPE_PARAMETERS by error0<KtProperty>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
|
||||||
val EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS by error0<KtExpression>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
val EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS by error0<KtExpression>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||||
|
val LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL by error0<PsiElement>()
|
||||||
|
val LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT by error0<PsiElement>()
|
||||||
|
val LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION by error0<PsiElement>()
|
||||||
|
val LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY by error1<PsiElement, FirBasedSymbol<*>>()
|
||||||
|
|
||||||
// Multi-platform projects
|
// Multi-platform projects
|
||||||
val EXPECTED_DECLARATION_WITH_BODY by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
|
val EXPECTED_DECLARATION_WITH_BODY by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
|
||||||
|
|||||||
+1
@@ -46,6 +46,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
|||||||
FirFunInterfaceConstructorReferenceChecker,
|
FirFunInterfaceConstructorReferenceChecker,
|
||||||
FirReifiedChecker,
|
FirReifiedChecker,
|
||||||
FirSuspendCallChecker,
|
FirSuspendCallChecker,
|
||||||
|
FirLateinitIntrinsicApplicabilityChecker,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val callCheckers: Set<FirCallChecker>
|
override val callCheckers: Set<FirCallChecker>
|
||||||
|
|||||||
+80
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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.expression
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.utils.isInline
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.utils.isLateInit
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||||
|
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
|
import org.jetbrains.kotlin.name.StandardClassIds.Annotations
|
||||||
|
|
||||||
|
object FirLateinitIntrinsicApplicabilityChecker : FirQualifiedAccessExpressionChecker() {
|
||||||
|
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
|
|
||||||
|
val resolvedSymbol = expression.calleeReference.toResolvedPropertySymbol() ?: return
|
||||||
|
|
||||||
|
// An optimization
|
||||||
|
if (resolvedSymbol.name.identifierOrNullIfSpecial != "isInitialized") return
|
||||||
|
|
||||||
|
val receiverParameter = resolvedSymbol.receiverParameter ?: return
|
||||||
|
|
||||||
|
if (!receiverParameter.hasAnnotation(Annotations.AccessibleLateinitPropertyLiteral, context.session)) return
|
||||||
|
|
||||||
|
val source = expression.calleeReference.source
|
||||||
|
|
||||||
|
val extensionReceiver = expression.extensionReceiver
|
||||||
|
if (extensionReceiver !is FirCallableReferenceAccess) {
|
||||||
|
reporter.reportOn(source, FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL, context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val calleePropertySymbol = extensionReceiver.calleeReference.toResolvedPropertySymbol() ?: return
|
||||||
|
|
||||||
|
if (!calleePropertySymbol.isLateInit) {
|
||||||
|
reporter.reportOn(source, FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT, context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// property must be declared in one of the outer lexical scopes
|
||||||
|
if (calleePropertySymbol.containingClassOrFile(context) !in context.containingDeclarations) {
|
||||||
|
reporter.reportOn(
|
||||||
|
source,
|
||||||
|
FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY,
|
||||||
|
calleePropertySymbol,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val closestOwnFunction = context.containingDeclarations.lastOrNull()
|
||||||
|
if (closestOwnFunction is FirFunction && closestOwnFunction.isInline) {
|
||||||
|
reporter.reportOn(source, FirErrors.LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the containing class or file if the property is top-level.
|
||||||
|
*/
|
||||||
|
@OptIn(SymbolInternals::class)
|
||||||
|
private fun FirPropertySymbol.containingClassOrFile(
|
||||||
|
context: CheckerContext
|
||||||
|
): FirDeclaration? {
|
||||||
|
return getContainingClassSymbol(context.session)?.fir
|
||||||
|
?: context.session.firProvider.getFirCallableContainerFile(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -289,6 +289,10 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_MISSING
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_ON_NULLABLE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_ON_NULLABLE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_FIELD_IN_VAL_PROPERTY
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_FIELD_IN_VAL_PROPERTY
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_NULLABLE_BACKING_FIELD
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_NULLABLE_BACKING_FIELD
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_PROPERTY_FIELD_DECLARATION_WITH_INITIALIZER
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LATEINIT_PROPERTY_FIELD_DECLARATION_WITH_INITIALIZER
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LEAKED_IN_PLACE_LAMBDA
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LEAKED_IN_PLACE_LAMBDA
|
||||||
@@ -1646,6 +1650,11 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
|||||||
map.put(LOCAL_VARIABLE_WITH_TYPE_PARAMETERS, "Local variables are not allowed to have type parameters")
|
map.put(LOCAL_VARIABLE_WITH_TYPE_PARAMETERS, "Local variables are not allowed to have type parameters")
|
||||||
map.put(EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS, "A property access cannot have explicit type arguments")
|
map.put(EXPLICIT_TYPE_ARGUMENTS_IN_PROPERTY_ACCESS, "A property access cannot have explicit type arguments")
|
||||||
|
|
||||||
|
map.put(LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL, "This declaration can only be called on a property literal (e.g. 'Foo::bar')")
|
||||||
|
map.put(LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT, "This declaration can only be called on a reference to a lateinit property")
|
||||||
|
map.put(LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION, "This declaration can not be used inside an inline function")
|
||||||
|
map.put(LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY, "Backing field of ''{0}'' is not accessible at this point", SYMBOL)
|
||||||
|
|
||||||
map.put(CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT, "Const 'val' are only allowed on top level, in named objects, or in companion objects")
|
map.put(CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT, "Const 'val' are only allowed on top level, in named objects, or in companion objects")
|
||||||
map.put(CONST_VAL_WITH_GETTER, "Const 'val' should not have a getter")
|
map.put(CONST_VAL_WITH_GETTER, "Const 'val' should not have a getter")
|
||||||
map.put(CONST_VAL_WITH_DELEGATE, "Const 'val' should not have a delegate")
|
map.put(CONST_VAL_WITH_DELEGATE, "Const 'val' should not have a delegate")
|
||||||
|
|||||||
@@ -113,6 +113,10 @@ fun FirBasedSymbol<*>.hasAnnotation(classId: ClassId, session: FirSession): Bool
|
|||||||
return resolvedAnnotationsWithClassIds.hasAnnotation(classId, session)
|
return resolvedAnnotationsWithClassIds.hasAnnotation(classId, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun FirAnnotationContainer.hasAnnotation(classId: ClassId, session: FirSession): Boolean {
|
||||||
|
return annotations.hasAnnotation(classId, session)
|
||||||
|
}
|
||||||
|
|
||||||
fun List<FirAnnotation>.hasAnnotation(classId: ClassId, session: FirSession): Boolean {
|
fun List<FirAnnotation>.hasAnnotation(classId: ClassId, session: FirSession): Boolean {
|
||||||
return this.any { it.toAnnotationClassId(session) == classId }
|
return this.any { it.toAnnotationClassId(session) == classId }
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -17,30 +17,28 @@
|
|||||||
package org.jetbrains.kotlin.resolve.calls.checkers
|
package org.jetbrains.kotlin.resolve.calls.checkers
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.builtins.StandardNames.FqNames
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||||
|
|
||||||
class LateinitIntrinsicApplicabilityChecker(val isWarningInPre19: Boolean) : CallChecker {
|
class LateinitIntrinsicApplicabilityChecker(val isWarningInPre19: Boolean) : CallChecker {
|
||||||
private val ACCESSIBLE_LATEINIT_PROPERTY_LITERAL = FqName("kotlin.internal.AccessibleLateinitPropertyLiteral")
|
|
||||||
|
|
||||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||||
val descriptor = resolvedCall.resultingDescriptor
|
val descriptor = resolvedCall.resultingDescriptor
|
||||||
|
|
||||||
// An optimization
|
// An optimization
|
||||||
if (descriptor.name.asString() != "isInitialized") return
|
if (descriptor.name.asString() != "isInitialized") return
|
||||||
|
|
||||||
if (descriptor.extensionReceiverParameter?.annotations?.hasAnnotation(ACCESSIBLE_LATEINIT_PROPERTY_LITERAL) != true) return
|
if (descriptor.extensionReceiverParameter?.annotations?.hasAnnotation(FqNames.accessibleLateinitPropertyLiteral) != true) return
|
||||||
|
|
||||||
val expression = (resolvedCall.extensionReceiver as? ExpressionReceiver)?.expression?.let(KtPsiUtil::safeDeparenthesize)
|
val expression = (resolvedCall.extensionReceiver as? ExpressionReceiver)?.expression?.let(KtPsiUtil::safeDeparenthesize)
|
||||||
fun <T> chooseDiagnostic(ifWarning: T, ifError: T) =
|
fun <T> chooseDiagnostic(ifWarning: T, ifError: T) =
|
||||||
|
|||||||
+2
@@ -1,6 +1,8 @@
|
|||||||
// WITH_STDLIB
|
// WITH_STDLIB
|
||||||
// On JVM, isInitialized on a property from another file is forbidden because lateinit-ness is not included in the an ABI of a property.
|
// On JVM, isInitialized on a property from another file is forbidden because lateinit-ness is not included in the an ABI of a property.
|
||||||
// IGNORE_BACKEND_K1: JVM, JVM_IR
|
// IGNORE_BACKEND_K1: JVM, JVM_IR
|
||||||
|
// In K2, isInitialized on a property from another file is forbidden for all backends.
|
||||||
|
// IGNORE_BACKEND_K2: JVM_IR
|
||||||
// IGNORE_LIGHT_ANALYSIS
|
// IGNORE_LIGHT_ANALYSIS
|
||||||
|
|
||||||
// FILE: A.kt
|
// FILE: A.kt
|
||||||
|
|||||||
@@ -51,15 +51,15 @@ open class Foo : Base {
|
|||||||
|
|
||||||
fun onLiteral() {
|
fun onLiteral() {
|
||||||
val p = this::x
|
val p = this::x
|
||||||
p.isInitialized
|
p.<!LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL!>isInitialized<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNonLateinit() {
|
fun onNonLateinit() {
|
||||||
this::nonLateInit.isInitialized
|
this::nonLateInit.<!LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT!>isInitialized<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun inlineFun() {
|
inline fun inlineFun() {
|
||||||
this::x.isInitialized
|
this::x.<!LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION!>isInitialized<!>
|
||||||
|
|
||||||
object {
|
object {
|
||||||
val z = this@Foo::x.isInitialized
|
val z = this@Foo::x.isInitialized
|
||||||
@@ -78,22 +78,22 @@ open class Foo : Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onNonAccessible() {
|
fun onNonAccessible() {
|
||||||
Foo()::x.isInitialized
|
Foo()::x.<!LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY!>isInitialized<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNonLateinit() {
|
fun onNonLateinit() {
|
||||||
Foo()::nonLateInit.isInitialized
|
Foo()::nonLateInit.<!LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT!>isInitialized<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
object Unrelated {
|
object Unrelated {
|
||||||
fun onNonAccessible() {
|
fun onNonAccessible() {
|
||||||
Foo()::x.isInitialized
|
Foo()::x.<!LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY!>isInitialized<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FooImpl : Foo() {
|
class FooImpl : Foo() {
|
||||||
fun onNonAccessible() {
|
fun onNonAccessible() {
|
||||||
this::x.isInitialized
|
this::x.<!LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY!>isInitialized<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +101,6 @@ class FooImpl : Foo() {
|
|||||||
|
|
||||||
class OtherFooImpl : Foo() {
|
class OtherFooImpl : Foo() {
|
||||||
fun onNonAccessible() {
|
fun onNonAccessible() {
|
||||||
this::x.isInitialized
|
this::x.<!LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY!>isInitialized<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -NOTHING_TO_INLINE
|
|
||||||
// !LANGUAGE: -NativeJsProhibitLateinitIsInitalizedIntrinsicWithoutPrivateAccess
|
|
||||||
// FILE: stdlibInternal.kt
|
|
||||||
|
|
||||||
package kotlin.internal
|
|
||||||
|
|
||||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
|
||||||
@Retention(AnnotationRetention.BINARY)
|
|
||||||
internal annotation class AccessibleLateinitPropertyLiteral
|
|
||||||
|
|
||||||
// FILE: stdlib.kt
|
|
||||||
package kotlin
|
|
||||||
|
|
||||||
import kotlin.internal.AccessibleLateinitPropertyLiteral
|
|
||||||
import kotlin.reflect.KProperty0
|
|
||||||
|
|
||||||
inline val @receiver:AccessibleLateinitPropertyLiteral KProperty0<*>.isInitialized: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
|
|
||||||
// FILE: test.kt
|
|
||||||
|
|
||||||
interface Base {
|
|
||||||
var x: String
|
|
||||||
}
|
|
||||||
|
|
||||||
open class Foo : Base {
|
|
||||||
override lateinit var x: String
|
|
||||||
private lateinit var y: String
|
|
||||||
|
|
||||||
var nonLateInit: Int = 1
|
|
||||||
|
|
||||||
fun ok() {
|
|
||||||
val b: Boolean = this::x.isInitialized
|
|
||||||
|
|
||||||
val otherInstance = Foo()
|
|
||||||
otherInstance::x.isInitialized
|
|
||||||
|
|
||||||
(this::x).isInitialized
|
|
||||||
(@Suppress("ALL") (this::x)).isInitialized
|
|
||||||
|
|
||||||
object {
|
|
||||||
fun local() {
|
|
||||||
class Local {
|
|
||||||
val xx = this@Foo::x.isInitialized
|
|
||||||
val yy = this@Foo::y.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onLiteral() {
|
|
||||||
val p = this::x
|
|
||||||
p.isInitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onNonLateinit() {
|
|
||||||
this::nonLateInit.isInitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun inlineFun() {
|
|
||||||
this::x.isInitialized
|
|
||||||
|
|
||||||
object {
|
|
||||||
val z = this@Foo::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class InnerSubclass : Foo() {
|
|
||||||
fun innerOk() {
|
|
||||||
// This is access to Foo.x declared lexically above
|
|
||||||
this@Foo::x.isInitialized
|
|
||||||
|
|
||||||
// This is access to InnerSubclass.x which is inherited from Foo.x
|
|
||||||
this::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onNonAccessible() {
|
|
||||||
Foo()::x.isInitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onNonLateinit() {
|
|
||||||
Foo()::nonLateInit.isInitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
object Unrelated {
|
|
||||||
fun onNonAccessible() {
|
|
||||||
Foo()::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FooImpl : Foo() {
|
|
||||||
fun onNonAccessible() {
|
|
||||||
this::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FILE: other.kt
|
|
||||||
|
|
||||||
class OtherFooImpl : Foo() {
|
|
||||||
fun onNonAccessible() {
|
|
||||||
this::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -NOTHING_TO_INLINE
|
// !DIAGNOSTICS: -UNUSED_VARIABLE -NOTHING_TO_INLINE
|
||||||
// !LANGUAGE: +NativeJsProhibitLateinitIsInitalizedIntrinsicWithoutPrivateAccess
|
// !LANGUAGE: +NativeJsProhibitLateinitIsInitalizedIntrinsicWithoutPrivateAccess
|
||||||
// FILE: stdlibInternal.kt
|
// FILE: stdlibInternal.kt
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -NOTHING_TO_INLINE
|
|
||||||
// FILE: test.kt
|
|
||||||
|
|
||||||
interface Base {
|
|
||||||
var x: String
|
|
||||||
}
|
|
||||||
|
|
||||||
open class Foo : Base {
|
|
||||||
override lateinit var x: String
|
|
||||||
private lateinit var y: String
|
|
||||||
|
|
||||||
var nonLateInit: Int = 1
|
|
||||||
|
|
||||||
fun ok() {
|
|
||||||
val b: Boolean = this::x.isInitialized
|
|
||||||
|
|
||||||
val otherInstance = Foo()
|
|
||||||
otherInstance::x.isInitialized
|
|
||||||
|
|
||||||
(this::x).isInitialized
|
|
||||||
(@Suppress("ALL") (this::x)).isInitialized
|
|
||||||
|
|
||||||
object {
|
|
||||||
fun local() {
|
|
||||||
class Local {
|
|
||||||
val xx = this@Foo::x.isInitialized
|
|
||||||
val yy = this@Foo::y.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onLiteral() {
|
|
||||||
val p = this::x
|
|
||||||
p.isInitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onNonLateinit() {
|
|
||||||
this::nonLateInit.isInitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun inlineFun() {
|
|
||||||
this::x.isInitialized
|
|
||||||
|
|
||||||
object {
|
|
||||||
val z = this@Foo::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class InnerSubclass : Foo() {
|
|
||||||
fun innerOk() {
|
|
||||||
// This is access to Foo.x declared lexically above
|
|
||||||
this@Foo::x.isInitialized
|
|
||||||
|
|
||||||
// This is access to InnerSubclass.x which is inherited from Foo.x
|
|
||||||
this::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onNonAccessible() {
|
|
||||||
Foo()::x.isInitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onNonLateinit() {
|
|
||||||
Foo()::nonLateInit.isInitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
object Unrelated {
|
|
||||||
fun onNonAccessible() {
|
|
||||||
Foo()::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FooImpl : Foo() {
|
|
||||||
fun onNonAccessible() {
|
|
||||||
this::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FILE: other.kt
|
|
||||||
|
|
||||||
class OtherFooImpl : Foo() {
|
|
||||||
fun onNonAccessible() {
|
|
||||||
this::x.isInitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -NOTHING_TO_INLINE
|
// !DIAGNOSTICS: -UNUSED_VARIABLE -NOTHING_TO_INLINE
|
||||||
// FILE: test.kt
|
// FILE: test.kt
|
||||||
|
|
||||||
|
lateinit var topLevel: String
|
||||||
|
|
||||||
interface Base {
|
interface Base {
|
||||||
var x: String
|
var x: String
|
||||||
}
|
}
|
||||||
@@ -12,6 +15,7 @@ open class Foo : Base {
|
|||||||
var nonLateInit: Int = 1
|
var nonLateInit: Int = 1
|
||||||
|
|
||||||
fun ok() {
|
fun ok() {
|
||||||
|
::topLevel.isInitialized
|
||||||
val b: Boolean = this::x.isInitialized
|
val b: Boolean = this::x.isInitialized
|
||||||
|
|
||||||
val otherInstance = Foo()
|
val otherInstance = Foo()
|
||||||
@@ -83,5 +87,6 @@ class FooImpl : Foo() {
|
|||||||
class OtherFooImpl : Foo() {
|
class OtherFooImpl : Foo() {
|
||||||
fun onNonAccessible() {
|
fun onNonAccessible() {
|
||||||
this::x.<!LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY!>isInitialized<!>
|
this::x.<!LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY!>isInitialized<!>
|
||||||
|
::topLevel.<!LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY!>isInitialized<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package
|
package
|
||||||
|
|
||||||
|
public lateinit var topLevel: kotlin.String
|
||||||
public fun onNonAccessible(): kotlin.Unit
|
public fun onNonAccessible(): kotlin.Unit
|
||||||
public fun onNonLateinit(): kotlin.Unit
|
public fun onNonLateinit(): kotlin.Unit
|
||||||
|
|
||||||
@@ -76,3 +77,4 @@ public object Unrelated {
|
|||||||
public final fun onNonAccessible(): kotlin.Unit
|
public final fun onNonAccessible(): kotlin.Unit
|
||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ object StandardNames {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val TEXT_PACKAGE_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("text"))
|
val TEXT_PACKAGE_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("text"))
|
||||||
|
|
||||||
|
@JvmField
|
||||||
val KOTLIN_INTERNAL_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("internal"))
|
val KOTLIN_INTERNAL_FQ_NAME = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier("internal"))
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@@ -137,6 +138,7 @@ object StandardNames {
|
|||||||
@JvmField val mustBeDocumented: FqName = annotationName("MustBeDocumented")
|
@JvmField val mustBeDocumented: FqName = annotationName("MustBeDocumented")
|
||||||
@JvmField val unsafeVariance: FqName = fqName("UnsafeVariance")
|
@JvmField val unsafeVariance: FqName = fqName("UnsafeVariance")
|
||||||
@JvmField val publishedApi: FqName = fqName("PublishedApi")
|
@JvmField val publishedApi: FqName = fqName("PublishedApi")
|
||||||
|
@JvmField val accessibleLateinitPropertyLiteral: FqName = internalName("AccessibleLateinitPropertyLiteral")
|
||||||
|
|
||||||
@JvmField val iterator: FqName = collectionsFqName("Iterator")
|
@JvmField val iterator: FqName = collectionsFqName("Iterator")
|
||||||
@JvmField val iterable: FqName = collectionsFqName("Iterable")
|
@JvmField val iterable: FqName = collectionsFqName("Iterable")
|
||||||
@@ -228,6 +230,10 @@ object StandardNames {
|
|||||||
private fun annotationName(simpleName: String): FqName {
|
private fun annotationName(simpleName: String): FqName {
|
||||||
return ANNOTATION_PACKAGE_FQ_NAME.child(Name.identifier(simpleName))
|
return ANNOTATION_PACKAGE_FQ_NAME.child(Name.identifier(simpleName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun internalName(simpleName: String): FqName {
|
||||||
|
return KOTLIN_INTERNAL_FQ_NAME.child(Name.identifier(simpleName))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|||||||
@@ -171,6 +171,8 @@ object StandardClassIds {
|
|||||||
|
|
||||||
val WasExperimental = "WasExperimental".baseId()
|
val WasExperimental = "WasExperimental".baseId()
|
||||||
|
|
||||||
|
val AccessibleLateinitPropertyLiteral = "AccessibleLateinitPropertyLiteral".internalId()
|
||||||
|
|
||||||
object Java {
|
object Java {
|
||||||
val Deprecated = "Deprecated".javaLangId()
|
val Deprecated = "Deprecated".javaLangId()
|
||||||
val Repeatable = "Repeatable".javaAnnotationId()
|
val Repeatable = "Repeatable".javaAnnotationId()
|
||||||
@@ -226,8 +228,8 @@ object StandardClassIds {
|
|||||||
StandardClassIds.MapEntry to StandardClassIds.MutableMapEntry
|
StandardClassIds.MapEntry to StandardClassIds.MutableMapEntry
|
||||||
)
|
)
|
||||||
|
|
||||||
val mutableCollectionToBaseCollection: Map<ClassId, ClassId> =
|
val mutableCollectionToBaseCollection: Map<ClassId, ClassId> =
|
||||||
baseCollectionToMutableEquivalent.entries.associateBy({ it.value }) { it.key }
|
baseCollectionToMutableEquivalent.entries.associateBy({ it.value }) { it.key }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user