[FIR] Implement warnings for java nullability type mismatch on override

#KT-56989
This commit is contained in:
Kirill Rakhman
2023-09-20 17:32:41 +02:00
committed by Space Team
parent a6fdeeb7df
commit 2df1e9dde6
21 changed files with 330 additions and 158 deletions
@@ -5025,6 +5025,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirJvmErrors.WRONG_NULLABILITY_FOR_JAVA_OVERRIDE) { firDiagnostic ->
WrongNullabilityForJavaOverrideImpl(
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a),
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.b),
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirJvmErrors.JAVA_TYPE_MISMATCH) { firDiagnostic ->
JavaTypeMismatchImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
@@ -3495,6 +3495,12 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = JvmInlineWithoutValueClass::class
}
interface WrongNullabilityForJavaOverride : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = WrongNullabilityForJavaOverride::class
val override: KtCallableSymbol
val base: KtCallableSymbol
}
interface JavaTypeMismatch : KtFirDiagnostic<KtExpression> {
override val diagnosticClass get() = JavaTypeMismatch::class
val expectedType: KtType
@@ -4217,6 +4217,13 @@ internal class JvmInlineWithoutValueClassImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.JvmInlineWithoutValueClass
internal class WrongNullabilityForJavaOverrideImpl(
override val override: KtCallableSymbol,
override val base: KtCallableSymbol,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.WrongNullabilityForJavaOverride
internal class JavaTypeMismatchImpl(
override val expectedType: KtType,
override val actualType: KtType,
@@ -39,6 +39,11 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") {
val VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION by error<PsiElement>()
val JVM_INLINE_WITHOUT_VALUE_CLASS by error<PsiElement>()
val WRONG_NULLABILITY_FOR_JAVA_OVERRIDE by warning<PsiElement>(PositioningStrategy.OVERRIDE_MODIFIER) {
parameter<FirCallableSymbol<*>>("override")
parameter<FirCallableSymbol<*>>("base")
}
}
val TYPES by object : DiagnosticGroup("Types") {
@@ -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>()
@@ -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.")
@@ -23,7 +23,8 @@ object JvmDeclarationCheckers : DeclarationCheckers() {
override val classCheckers: Set<FirClassChecker>
get() = setOf(
FirStrictfpApplicabilityChecker
FirStrictfpApplicabilityChecker,
FirOverrideJavaNullabilityWarningChecker,
)
override val regularClassCheckers: Set<FirRegularClassChecker>
@@ -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,
)
}
}
@@ -42,23 +42,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
object FirOverrideChecker : FirClassChecker() {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
val typeCheckerState = context.session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = false
)
val firTypeScope = declaration.unsubstitutedScope(context)
for (it in declaration.declarations) {
if (it is FirSimpleFunction || it is FirProperty) {
val callable = it as FirCallableDeclaration
checkMember(callable.symbol, declaration, reporter, typeCheckerState, firTypeScope, context)
}
}
}
abstract class FirAbstractOverrideChecker : FirClassChecker() {
private fun ConeKotlinType.substituteAllTypeParameters(
overrideDeclaration: FirCallableSymbol<*>,
baseDeclaration: FirCallableSymbol<*>,
@@ -84,6 +68,58 @@ object FirOverrideChecker : FirClassChecker() {
return substitutorByMap(map, context.session).substituteOrSelf(this)
}
// See [OverrideResolver#isReturnTypeOkForOverride]
protected fun FirCallableSymbol<*>.checkReturnType(
overriddenSymbols: List<FirCallableSymbol<*>>,
typeCheckerState: TypeCheckerState,
context: CheckerContext,
): FirCallableSymbol<*>? {
val overridingReturnType = resolvedReturnTypeRef.coneType
// Don't report *_ON_OVERRIDE diagnostics according to an error return type. That should be reported separately.
if (overridingReturnType is ConeErrorType) {
return null
}
val bounds = overriddenSymbols.map { context.returnTypeCalculator.tryCalculateReturnType(it).coneType }
for (it in bounds.indices) {
val overriddenDeclaration = overriddenSymbols[it]
val overriddenReturnType = bounds[it].substituteAllTypeParameters(this, overriddenDeclaration, context)
val isReturnTypeOkForOverride =
if (overriddenDeclaration is FirPropertySymbol && overriddenDeclaration.isVar)
AbstractTypeChecker.equalTypes(typeCheckerState, overridingReturnType, overriddenReturnType)
else
AbstractTypeChecker.isSubtypeOf(typeCheckerState, overridingReturnType, overriddenReturnType)
if (!isReturnTypeOkForOverride) {
return overriddenDeclaration
}
}
return null
}
}
object FirOverrideChecker : FirAbstractOverrideChecker() {
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
val typeCheckerState = context.session.typeContext.newTypeCheckerState(
errorTypesEqualToAnything = false,
stubTypesEqualToAnything = false
)
val firTypeScope = declaration.unsubstitutedScope(context)
for (it in declaration.declarations) {
if (it is FirSimpleFunction || it is FirProperty) {
val callable = it as FirCallableDeclaration
checkMember(callable.symbol, declaration, reporter, typeCheckerState, firTypeScope, context)
}
}
}
private fun checkModality(
overriddenSymbols: List<FirCallableSymbol<*>>,
): FirCallableSymbol<*>? {
@@ -185,40 +221,6 @@ object FirOverrideChecker : FirClassChecker() {
}
}
// See [OverrideResolver#isReturnTypeOkForOverride]
private fun FirCallableSymbol<*>.checkReturnType(
overriddenSymbols: List<FirCallableSymbol<*>>,
typeCheckerState: TypeCheckerState,
context: CheckerContext,
): FirCallableSymbol<*>? {
val overridingReturnType = resolvedReturnTypeRef.coneType
// Don't report *_ON_OVERRIDE diagnostics according to an error return type. That should be reported separately.
if (overridingReturnType is ConeErrorType) {
return null
}
val bounds = overriddenSymbols.map { context.returnTypeCalculator.tryCalculateReturnType(it).coneType }
for (it in bounds.indices) {
val overriddenDeclaration = overriddenSymbols[it]
val overriddenReturnType = bounds[it].substituteAllTypeParameters(this, overriddenDeclaration, context)
val isReturnTypeOkForOverride =
if (overriddenDeclaration is FirPropertySymbol && overriddenDeclaration.isVar)
AbstractTypeChecker.equalTypes(typeCheckerState, overridingReturnType, overriddenReturnType)
else
AbstractTypeChecker.isSubtypeOf(typeCheckerState, overridingReturnType, overriddenReturnType)
if (!isReturnTypeOkForOverride) {
return overriddenDeclaration
}
}
return null
}
@OptIn(SymbolInternals::class)
private fun FirFunctionSymbol<*>.checkDefaultValues(
reporter: DiagnosticReporter,
+2 -2
View File
@@ -1,7 +1,7 @@
compiler/testData/cli/jvm/jspecifyUsage.kt:2:11: warning: Java type mismatch: inferred type is 'kotlin/String', but 'kotlin/Nothing?' was expected.
compiler/testData/cli/jvm/jspecifyUsage.kt:2:11: warning: Java type mismatch: inferred type is 'kotlin.String', but 'kotlin.Nothing?' was expected.
a.foo(null)
^
compiler/testData/cli/jvm/jspecifyUsage.kt:3:5: warning: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'kotlin/String?'.
compiler/testData/cli/jvm/jspecifyUsage.kt:3:5: warning: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'kotlin.String?'.
a.bar().hashCode()
^
OK
+1 -1
View File
@@ -1,5 +1,5 @@
warning: argument -Xjsr305-annotations is deprecated. Please use -Xjsr305 instead
compiler/testData/cli/jvm/jsr305Usage.kt:2:11: warning: Java type mismatch: inferred type is 'kotlin/String', but 'kotlin/Nothing?' was expected.
compiler/testData/cli/jvm/jsr305Usage.kt:2:11: warning: Java type mismatch: inferred type is 'kotlin.String', but 'kotlin.Nothing?' was expected.
a.foo(null)
^
OK
+2 -2
View File
@@ -1,7 +1,7 @@
compiler/testData/cli/jvm/jsr305Migration.kt:2:19: warning: Java type mismatch: inferred type is 'kotlin/String', but 'kotlin/Nothing?' was expected.
compiler/testData/cli/jvm/jsr305Migration.kt:2:19: warning: Java type mismatch: inferred type is 'kotlin.String', but 'kotlin.Nothing?' was expected.
annotated.foo(null)
^
compiler/testData/cli/jvm/jsr305Migration.kt:4:5: warning: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'kotlin/String?'.
compiler/testData/cli/jvm/jsr305Migration.kt:4:5: warning: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'kotlin.String?'.
annotated.nullable().length
^
OK
+2 -2
View File
@@ -1,7 +1,7 @@
compiler/testData/cli/jvm/jsr305Migration.kt:2:19: warning: Java type mismatch: inferred type is 'kotlin/String', but 'kotlin/Nothing?' was expected.
compiler/testData/cli/jvm/jsr305Migration.kt:2:19: warning: Java type mismatch: inferred type is 'kotlin.String', but 'kotlin.Nothing?' was expected.
annotated.foo(null)
^
compiler/testData/cli/jvm/jsr305Migration.kt:4:5: warning: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'kotlin/String?'.
compiler/testData/cli/jvm/jsr305Migration.kt:4:5: warning: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'kotlin.String?'.
annotated.nullable().length
^
OK
+3 -3
View File
@@ -1,10 +1,10 @@
compiler/testData/cli/jvm/jsr305Migration.kt:2:19: warning: Java type mismatch: inferred type is 'kotlin/String', but 'kotlin/Nothing?' was expected.
compiler/testData/cli/jvm/jsr305Migration.kt:2:19: warning: Java type mismatch: inferred type is 'kotlin.String', but 'kotlin.Nothing?' was expected.
annotated.foo(null)
^
compiler/testData/cli/jvm/jsr305Migration.kt:3:19: warning: Java type mismatch: inferred type is 'kotlin/String', but 'kotlin/Nothing?' was expected.
compiler/testData/cli/jvm/jsr305Migration.kt:3:19: warning: Java type mismatch: inferred type is 'kotlin.String', but 'kotlin.Nothing?' was expected.
annotated.bar(null)
^
compiler/testData/cli/jvm/jsr305Migration.kt:4:5: warning: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'kotlin/String?'.
compiler/testData/cli/jvm/jsr305Migration.kt:4:5: warning: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type 'kotlin.String?'.
annotated.nullable().length
^
OK
+1 -1
View File
@@ -1,4 +1,4 @@
compiler/testData/cli/jvm/jsr305Usage.kt:2:11: warning: Java type mismatch: inferred type is 'kotlin/String', but 'kotlin/Nothing?' was expected.
compiler/testData/cli/jvm/jsr305Usage.kt:2:11: warning: Java type mismatch: inferred type is 'kotlin.String', but 'kotlin.Nothing?' was expected.
a.foo(null)
^
OK
+1 -1
View File
@@ -1,4 +1,4 @@
compiler/testData/cli/jvm/jsr305Usage.kt:2:11: warning: Java type mismatch: inferred type is 'kotlin/String', but 'kotlin/Nothing?' was expected.
compiler/testData/cli/jvm/jsr305Usage.kt:2:11: warning: Java type mismatch: inferred type is 'kotlin.String', but 'kotlin.Nothing?' was expected.
a.foo(null)
^
OK
@@ -17,6 +17,8 @@ public class BaseClass {
public @Nullable Foo mixed(Foo x) { return null; }
public Foo explicitlyNullnessUnspecified(@NullnessUnspecified Foo x) { return null; }
public static Foo foo() { return null; }
}
@@ -24,7 +26,11 @@ public class BaseClass {
private val FOO = object : Foo {}
class Correct : BaseClass() {
open class IntermediateClass : BaseClass() {
open fun intermediateNotNull() = BaseClass.foo()
}
class Correct : IntermediateClass() {
override fun everythingNotNullable(x: Foo): Foo {
return FOO
}
@@ -44,24 +50,32 @@ class Correct : BaseClass() {
override fun explicitlyNullnessUnspecified(x: Foo): Foo {
return FOO
}
override fun intermediateNotNull(): Foo {
return FOO
}
}
class WrongReturnTypes : BaseClass() {
override fun everythingNotNullable(x: Foo): Foo? {
class WrongReturnTypes : IntermediateClass() {
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun everythingNotNullable(x: Foo): Foo? {
return null
}
override fun explicitlyNullnessUnspecified(x: Foo): Foo? {
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun explicitlyNullnessUnspecified(x: Foo): Foo? {
return null
}
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun intermediateNotNull(): Foo? {
return null
}
}
class WrongParameter : BaseClass() {
override fun everythingNotNullable(x: Foo?): Foo {
class WrongParameter : IntermediateClass() {
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun everythingNotNullable(x: Foo?): Foo {
return FOO
}
override fun everythingNullable(x: Foo): Foo? {
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun everythingNullable(x: Foo): Foo? {
return null
}
@@ -69,11 +83,11 @@ class WrongParameter : BaseClass() {
return null
}
override fun mixed(x: Foo?): Foo? {
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun mixed(x: Foo?): Foo? {
return null
}
override fun explicitlyNullnessUnspecified(x: Foo?): Foo {
return FOO
}
}
}
@@ -17,6 +17,8 @@ public class BaseClass {
public @Nullable Foo mixed(Foo x) { return null; }
public Foo explicitlyNullnessUnspecified(@NullnessUnspecified Foo x) { return null; }
public static Foo foo() { return null; }
}
@@ -24,7 +26,11 @@ public class BaseClass {
private val FOO = object : Foo {}
class Correct : BaseClass() {
open class IntermediateClass : BaseClass() {
open fun intermediateNotNull() = BaseClass.foo()
}
class Correct : IntermediateClass() {
override fun everythingNotNullable(x: Foo): Foo {
return FOO
}
@@ -44,9 +50,13 @@ class Correct : BaseClass() {
override fun explicitlyNullnessUnspecified(x: Foo): Foo {
return FOO
}
override fun intermediateNotNull(): Foo {
return FOO
}
}
class WrongReturnTypes : BaseClass() {
class WrongReturnTypes : IntermediateClass() {
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun everythingNotNullable(x: Foo): Foo? {
return null
}
@@ -54,9 +64,13 @@ class WrongReturnTypes : BaseClass() {
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun explicitlyNullnessUnspecified(x: Foo): Foo? {
return null
}
override fun intermediateNotNull(): Foo? {
return null
}
}
class WrongParameter : BaseClass() {
class WrongParameter : IntermediateClass() {
<!WRONG_NULLABILITY_FOR_JAVA_OVERRIDE!>override<!> fun everythingNotNullable(x: Foo?): Foo {
return FOO
}
@@ -12,9 +12,12 @@ private val FOO: FOO.`<no name provided>`
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@org.jspecify.annotations.Nullable public open fun mixed(/*0*/ x: Foo!): @org.jspecify.annotations.Nullable Foo!
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
public open fun foo(): Foo!
}
public final class Correct : BaseClass {
public final class Correct : IntermediateClass {
public constructor Correct()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ fun everythingNotNullable(/*0*/ x: Foo): Foo
@@ -22,6 +25,7 @@ public final class Correct : BaseClass {
public open override /*1*/ fun everythingUnknown(/*0*/ x: Foo?): Foo?
public open override /*1*/ fun explicitlyNullnessUnspecified(/*0*/ x: Foo): Foo
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ fun intermediateNotNull(): Foo
public open override /*1*/ fun mixed(/*0*/ x: Foo): Foo?
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -32,7 +36,20 @@ public interface Foo {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class WrongParameter : BaseClass {
public open class IntermediateClass : BaseClass {
public constructor IntermediateClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun everythingNotNullable(/*0*/ x: Foo!): Foo!
@org.jspecify.annotations.Nullable public open override /*1*/ /*fake_override*/ fun everythingNullable(/*0*/ @org.jspecify.annotations.Nullable x: @org.jspecify.annotations.Nullable Foo!): @org.jspecify.annotations.Nullable Foo!
@org.jspecify.annotations.NullnessUnspecified public open override /*1*/ /*fake_override*/ fun everythingUnknown(/*0*/ @org.jspecify.annotations.NullnessUnspecified x: @org.jspecify.annotations.NullnessUnspecified Foo!): @org.jspecify.annotations.NullnessUnspecified Foo!
public open override /*1*/ /*fake_override*/ fun explicitlyNullnessUnspecified(/*0*/ @org.jspecify.annotations.NullnessUnspecified x: @org.jspecify.annotations.NullnessUnspecified Foo!): Foo!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open fun intermediateNotNull(): Foo!
@org.jspecify.annotations.Nullable public open override /*1*/ /*fake_override*/ fun mixed(/*0*/ x: Foo!): @org.jspecify.annotations.Nullable Foo!
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class WrongParameter : IntermediateClass {
public constructor WrongParameter()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ fun everythingNotNullable(/*0*/ x: Foo?): Foo
@@ -40,11 +57,12 @@ public final class WrongParameter : BaseClass {
public open override /*1*/ fun everythingUnknown(/*0*/ x: Foo): Foo?
public open override /*1*/ fun explicitlyNullnessUnspecified(/*0*/ x: Foo?): Foo
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun intermediateNotNull(): Foo!
public open override /*1*/ fun mixed(/*0*/ x: Foo?): Foo?
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class WrongReturnTypes : BaseClass {
public final class WrongReturnTypes : IntermediateClass {
public constructor WrongReturnTypes()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ fun everythingNotNullable(/*0*/ x: Foo): Foo?
@@ -52,6 +70,8 @@ public final class WrongReturnTypes : BaseClass {
@org.jspecify.annotations.NullnessUnspecified public open override /*1*/ /*fake_override*/ fun everythingUnknown(/*0*/ @org.jspecify.annotations.NullnessUnspecified x: @org.jspecify.annotations.NullnessUnspecified Foo!): @org.jspecify.annotations.NullnessUnspecified Foo!
public open override /*1*/ fun explicitlyNullnessUnspecified(/*0*/ x: Foo): Foo?
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ fun intermediateNotNull(): Foo?
@org.jspecify.annotations.Nullable public open override /*1*/ /*fake_override*/ fun mixed(/*0*/ x: Foo!): @org.jspecify.annotations.Nullable Foo!
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -1,79 +0,0 @@
// JSPECIFY_STATE: warn
// FILE: Foo.java
public interface Foo {}
// FILE: BaseClass.java
import org.jspecify.nullness.*;
@NullMarked
public class BaseClass {
public Foo everythingNotNullable(Foo x) { return null; }
public @Nullable Foo everythingNullable(@Nullable Foo x) { return null; }
public @NullnessUnspecified Foo everythingUnknown(@NullnessUnspecified Foo x) { return null; }
public @Nullable Foo mixed(Foo x) { return null; }
public Foo explicitlyNullnessUnspecified(@NullnessUnspecified Foo x) { return null; }
}
// FILE: main.kt
private val FOO = object : Foo {}
class Correct : BaseClass() {
override fun everythingNotNullable(x: Foo): Foo {
return FOO
}
override fun everythingNullable(x: Foo?): Foo? {
return null
}
override fun everythingUnknown(x: Foo?): Foo? {
return null
}
override fun mixed(x: Foo): Foo? {
return null
}
override fun explicitlyNullnessUnspecified(x: Foo): Foo {
return FOO
}
}
class WrongReturnTypes : BaseClass() {
override fun everythingNotNullable(x: Foo): Foo? {
return null
}
override fun explicitlyNullnessUnspecified(x: Foo): Foo? {
return null
}
}
class WrongParameter : BaseClass() {
override fun everythingNotNullable(x: Foo?): Foo {
return FOO
}
override fun everythingNullable(x: Foo): Foo? {
return null
}
override fun everythingUnknown(x: Foo): Foo? {
return null
}
override fun mixed(x: Foo?): Foo? {
return null
}
override fun explicitlyNullnessUnspecified(x: Foo?): Foo {
return FOO
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// JSPECIFY_STATE: warn
// FILE: Foo.java