[FIR] Implement warnings for java nullability type mismatch on override
#KT-56989
This commit is contained in:
committed by
Space Team
parent
a6fdeeb7df
commit
2df1e9dde6
+1
@@ -41,6 +41,7 @@ object FirJvmErrors {
|
||||
val FUNCTION_DELEGATE_MEMBER_NAME_CLASH by error0<PsiElement>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
val VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION by error0<PsiElement>()
|
||||
val JVM_INLINE_WITHOUT_VALUE_CLASS by error0<PsiElement>()
|
||||
val WRONG_NULLABILITY_FOR_JAVA_OVERRIDE by warning2<PsiElement, FirCallableSymbol<*>, FirCallableSymbol<*>>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER)
|
||||
|
||||
// Types
|
||||
val JAVA_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>()
|
||||
|
||||
+9
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.CONCURRENT_HASH_MAP_CONTAINS_OPERATOR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.DELEGATION_BY_IN_JVM_RECORD
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.DEPRECATED_JAVA_ANNOTATION
|
||||
@@ -80,6 +81,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZ
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_ON_SUSPEND
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.UPPER_BOUND_CANNOT_BE_ARRAY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.WRONG_NULLABILITY_FOR_JAVA_OVERRIDE
|
||||
|
||||
object FirJvmErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
|
||||
@@ -99,6 +101,13 @@ object FirJvmErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
NOT_RENDERED
|
||||
)
|
||||
|
||||
map.put(
|
||||
WRONG_NULLABILITY_FOR_JAVA_OVERRIDE,
|
||||
"Override ''{0}'' has incorrect nullability in its signature compared to the overridden declaration ''{1}''.",
|
||||
SYMBOL,
|
||||
SYMBOL,
|
||||
)
|
||||
|
||||
map.put(UPPER_BOUND_CANNOT_BE_ARRAY, "Upper bound of type parameter cannot be an array.")
|
||||
map.put(STRICTFP_ON_CLASS, "'@Strictfp' annotation on classes is not yet supported.")
|
||||
map.put(SYNCHRONIZED_ON_ABSTRACT, "'@Synchronized' annotation cannot be used on abstract functions.")
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ object JvmDeclarationCheckers : DeclarationCheckers() {
|
||||
|
||||
override val classCheckers: Set<FirClassChecker>
|
||||
get() = setOf(
|
||||
FirStrictfpApplicabilityChecker
|
||||
FirStrictfpApplicabilityChecker,
|
||||
FirOverrideJavaNullabilityWarningChecker,
|
||||
)
|
||||
|
||||
override val regularClassCheckers: Set<FirRegularClassChecker>
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.jvm.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirAbstractOverrideChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.java.enhancement.EnhancedForWarningConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.scopes.firOverrideChecker
|
||||
import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenFunctions
|
||||
import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenProperties
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirFakeOverrideGenerator
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.typeContext
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
|
||||
object FirOverrideJavaNullabilityWarningChecker : FirAbstractOverrideChecker() {
|
||||
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val substitutor = EnhancedForWarningConeSubstitutor(context.session.typeContext)
|
||||
val scope = declaration.unsubstitutedScope(context)
|
||||
val typeCheckerState = context.session.typeContext.newTypeCheckerState(
|
||||
errorTypesEqualToAnything = false,
|
||||
stubTypesEqualToAnything = false
|
||||
)
|
||||
|
||||
for (member in declaration.declarations) {
|
||||
var anyBaseEnhanced = false
|
||||
|
||||
if (member is FirSimpleFunction) {
|
||||
val enhancedOverrides = scope
|
||||
.getDirectOverriddenFunctions(member.symbol)
|
||||
.map {
|
||||
@OptIn(SymbolInternals::class)
|
||||
val substitutedBase = it.fir.substituteOrNull(substitutor, context) ?: return@map it
|
||||
anyBaseEnhanced = true
|
||||
|
||||
if (!context.session.firOverrideChecker.isOverriddenFunction(member, substitutedBase)) {
|
||||
reporter.reportOn(
|
||||
member.source,
|
||||
FirJvmErrors.WRONG_NULLABILITY_FOR_JAVA_OVERRIDE,
|
||||
member.symbol,
|
||||
substitutedBase.symbol,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
substitutedBase.symbol
|
||||
}
|
||||
|
||||
if (anyBaseEnhanced) {
|
||||
member.symbol.checkReturnType(enhancedOverrides, typeCheckerState, context)?.let {
|
||||
reporter.reportOn(
|
||||
member.source, FirJvmErrors.WRONG_NULLABILITY_FOR_JAVA_OVERRIDE, member.symbol, it, context
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (member is FirProperty) {
|
||||
val enhancedOverrides = scope
|
||||
.getDirectOverriddenProperties(member.symbol)
|
||||
.map {
|
||||
@OptIn(SymbolInternals::class)
|
||||
val substitutedBase = it.fir.substituteOrNull(substitutor, context) ?: return@map it
|
||||
anyBaseEnhanced = true
|
||||
|
||||
if (!context.session.firOverrideChecker.isOverriddenProperty(member, substitutedBase)) {
|
||||
reporter.reportOn(
|
||||
member.source,
|
||||
FirJvmErrors.WRONG_NULLABILITY_FOR_JAVA_OVERRIDE,
|
||||
member.symbol,
|
||||
substitutedBase.symbol,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
substitutedBase.symbol
|
||||
}
|
||||
|
||||
if (anyBaseEnhanced) {
|
||||
member.symbol.checkReturnType(enhancedOverrides, typeCheckerState, context)?.let {
|
||||
reporter.reportOn(
|
||||
member.source, FirJvmErrors.WRONG_NULLABILITY_FOR_JAVA_OVERRIDE, member.symbol, it, context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.jetbrains.kotlin.fir.scopes.impl.FirClassSubstitutionScope.createSubstitutionOverrideFunction
|
||||
* @see org.jetbrains.kotlin.fir.scopes.impl.FirClassSubstitutionScope.createSubstitutedData
|
||||
*/
|
||||
private fun FirSimpleFunction.substituteOrNull(
|
||||
substitutor: EnhancedForWarningConeSubstitutor,
|
||||
context: CheckerContext,
|
||||
): FirSimpleFunction? {
|
||||
symbol.lazyResolveToPhase(FirResolvePhase.TYPES)
|
||||
var isEnhanced = false
|
||||
|
||||
val newParameterTypes = valueParameters.map { substitutor.substituteOrNull(it.returnTypeRef.coneType)?.also { isEnhanced = true } }
|
||||
val newContextReceiverTypes = contextReceivers.map { substitutor.substituteOrNull(it.typeRef.coneType)?.also { isEnhanced = true } }
|
||||
val newReturnType = substitutor.substituteOrNull(context.returnTypeCalculator.tryCalculateReturnType(this).coneType)?.also { isEnhanced = true }
|
||||
val newExtensionReceiverType =
|
||||
receiverParameter?.typeRef?.coneType?.let { substitutor.substituteOrNull(it) }?.also { isEnhanced = true }
|
||||
|
||||
return runIf(isEnhanced) {
|
||||
FirFakeOverrideGenerator.createCopyForFirFunction(
|
||||
FirFakeOverrideGenerator.createSymbolForSubstitutionOverride(symbol),
|
||||
this,
|
||||
null,
|
||||
context.session,
|
||||
FirDeclarationOrigin.Enhancement,
|
||||
newDispatchReceiverType = null,
|
||||
newParameterTypes = newParameterTypes,
|
||||
newReturnType = newReturnType,
|
||||
newContextReceiverTypes = newContextReceiverTypes,
|
||||
newReceiverType = newExtensionReceiverType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.jetbrains.kotlin.fir.scopes.impl.FirClassSubstitutionScope.createSubstitutionOverrideProperty
|
||||
* @see org.jetbrains.kotlin.fir.scopes.impl.FirClassSubstitutionScope.createSubstitutedData
|
||||
*/
|
||||
private fun FirProperty.substituteOrNull(
|
||||
substitutor: EnhancedForWarningConeSubstitutor,
|
||||
context: CheckerContext,
|
||||
): FirProperty? {
|
||||
if (!isJavaOrEnhancement) return null
|
||||
symbol.lazyResolveToPhase(FirResolvePhase.TYPES)
|
||||
var isEnhanced = false
|
||||
|
||||
val newContextReceiverTypes = contextReceivers.map { substitutor.substituteOrNull(it.typeRef.coneType)?.also { isEnhanced = true } }
|
||||
val newReturnType = substitutor.substituteOrNull(context.returnTypeCalculator.tryCalculateReturnType(this).coneType)?.also { isEnhanced = true }
|
||||
val newExtensionReceiverType =
|
||||
receiverParameter?.typeRef?.coneType?.let { substitutor.substituteOrNull(it) }?.also { isEnhanced = true }
|
||||
|
||||
return runIf(isEnhanced) {
|
||||
FirFakeOverrideGenerator.createCopyForFirProperty(
|
||||
FirFakeOverrideGenerator.createSymbolForSubstitutionOverride(symbol),
|
||||
this,
|
||||
null,
|
||||
context.session,
|
||||
FirDeclarationOrigin.Enhancement,
|
||||
newDispatchReceiverType = null,
|
||||
newReturnType = newReturnType,
|
||||
newContextReceiverTypes = newContextReceiverTypes,
|
||||
newReceiverType = newExtensionReceiverType,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user