diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.dot b/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.dot index 6fbd681984e..9636ca4f760 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.dot +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.dot @@ -492,11 +492,11 @@ digraph nullability_kt { 176 [label="Access variable R|/Q.data|"]; 177 [label="Access variable R|/q|"]; 178 [label="Access variable R|/Q.data|"]; - 179 [label="Access variable #"]; + 179 [label="Access variable #"]; 180 [label="Access variable R|/q|"]; 181 [label="Access variable R|/Q.data|"]; - 182 [label="Access variable #"]; - 183 [label="Function call: R|/q|.R|/Q.data|.#.R|kotlin/Int.inc|()"]; + 182 [label="Access variable #"]; + 183 [label="Function call: R|/q|.R|/Q.data|.#.R|kotlin/Int.inc|()"]; 184 [label="Exit block"]; } 185 [label="Exit when branch result"]; @@ -566,11 +566,11 @@ digraph nullability_kt { 208 [label="Access variable R|/Q.data|"]; 209 [label="Access variable R|/q|"]; 210 [label="Access variable R|/Q.data|"]; - 211 [label="Access variable #"]; + 211 [label="Access variable #"]; 212 [label="Access variable R|/q|"]; 213 [label="Access variable R|/Q.data|"]; - 214 [label="Access variable #"]; - 215 [label="Function call: R|/q|.R|/Q.data|.#.R|kotlin/Int.inc|()"]; + 214 [label="Access variable #"]; + 215 [label="Function call: R|/q|.R|/Q.data|.#.R|kotlin/Int.inc|()"]; 216 [label="Exit block"]; } 217 [label="Exit function test_6" style="filled" fillcolor=red]; @@ -1280,11 +1280,11 @@ digraph nullability_kt { 490 [label="Access variable R|/QImplWithCustomGetter.data|"]; 491 [label="Access variable R|/q|"]; 492 [label="Access variable R|/QImplWithCustomGetter.data|"]; - 493 [label="Access variable #"]; + 493 [label="Access variable #"]; 494 [label="Access variable R|/q|"]; 495 [label="Access variable R|/QImplWithCustomGetter.data|"]; - 496 [label="Access variable #"]; - 497 [label="Function call: R|/q|.R|/QImplWithCustomGetter.data|.#.R|kotlin/Int.inc|()"]; + 496 [label="Access variable #"]; + 497 [label="Function call: R|/q|.R|/QImplWithCustomGetter.data|.#.R|kotlin/Int.inc|()"]; 498 [label="Exit block"]; } 499 [label="Exit when branch result"]; @@ -1363,11 +1363,11 @@ digraph nullability_kt { 524 [label="Access variable R|/QImplMutable.data|"]; 525 [label="Access variable R|/q|"]; 526 [label="Access variable R|/QImplMutable.data|"]; - 527 [label="Access variable #"]; + 527 [label="Access variable #"]; 528 [label="Access variable R|/q|"]; 529 [label="Access variable R|/QImplMutable.data|"]; - 530 [label="Access variable #"]; - 531 [label="Function call: R|/q|.R|/QImplMutable.data|.#.R|kotlin/Int.inc|()"]; + 530 [label="Access variable #"]; + 531 [label="Function call: R|/q|.R|/QImplMutable.data|.#.R|kotlin/Int.inc|()"]; 532 [label="Exit block"]; } 533 [label="Exit when branch result"]; diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.fir.txt b/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.fir.txt index cfb6f6d0941..c9de93e9311 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.fir.txt @@ -102,8 +102,8 @@ FILE: nullability.kt when () { !=(R|/q|?.{ $subj$.R|/Q.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> { R|/q|.R|/Q.data| - R|/q|.R|/Q.data|.# - R|/q|.R|/Q.data|.#.R|kotlin/Int.inc|() + R|/q|.R|/Q.data|.# + R|/q|.R|/Q.data|.#.R|kotlin/Int.inc|() } } @@ -111,8 +111,8 @@ FILE: nullability.kt public final fun test_6(q: R|Q?|): R|kotlin/Unit| { R|/q|?.{ $subj$.R|/Q.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() } ?: ^test_6 Unit R|/q|.R|/Q.data| - R|/q|.R|/Q.data|.# - R|/q|.R|/Q.data|.#.R|kotlin/Int.inc|() + R|/q|.R|/Q.data|.# + R|/q|.R|/Q.data|.#.R|kotlin/Int.inc|() } public final fun test_7(q: R|Q?|): R|kotlin/Unit| { when () { @@ -216,8 +216,8 @@ FILE: nullability.kt when () { !=(R|/q|?.{ $subj$.R|/QImplWithCustomGetter.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> { R|/q|.R|/QImplWithCustomGetter.data| - R|/q|.R|/QImplWithCustomGetter.data|.# - R|/q|.R|/QImplWithCustomGetter.data|.#.R|kotlin/Int.inc|() + R|/q|.R|/QImplWithCustomGetter.data|.# + R|/q|.R|/QImplWithCustomGetter.data|.#.R|kotlin/Int.inc|() } } @@ -226,8 +226,8 @@ FILE: nullability.kt when () { !=(R|/q|?.{ $subj$.R|/QImplMutable.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> { R|/q|.R|/QImplMutable.data| - R|/q|.R|/QImplMutable.data|.# - R|/q|.R|/QImplMutable.data|.#.R|kotlin/Int.inc|() + R|/q|.R|/QImplMutable.data|.# + R|/q|.R|/QImplMutable.data|.#.R|kotlin/Int.inc|() } } diff --git a/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.kt b/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.kt index 3f5877d797c..7347a02d70f 100644 --- a/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.kt +++ b/compiler/fir/analysis-tests/testData/resolve/smartcasts/nullability.kt @@ -67,8 +67,8 @@ fun test_5(q: Q?) { // `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData. if (q?.data?.s?.inc() != null) { q.data // good - q.data.s // should be bad - q.data.s.inc() // should be bad + q.data.s // should be bad + q.data.s.inc() // should be bad } } @@ -76,8 +76,8 @@ fun test_6(q: Q?) { // `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData. q?.data?.s?.inc() ?: return q.data // good - q.data.s // should be bad - q.data.s.inc() // should be bad + q.data.s // should be bad + q.data.s.inc() // should be bad } fun test_7(q: Q?) { @@ -162,8 +162,8 @@ fun test_12(q: QImplWithCustomGetter?) { // `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData. if (q?.data?.s?.inc() != null) { q.data // good - q.data.s // should be bad - q.data.s.inc() // should be bad + q.data.s // should be bad + q.data.s.inc() // should be bad } } @@ -171,7 +171,7 @@ fun test_13(q: QImplMutable?) { // `q.data` is a property that is mutable, so we can NOT smartcast it to non-nullable MyData. if (q?.data?.s?.inc() != null) { q.data // good - q.data.s // should be bad - q.data.s.inc() // should be bad + q.data.s // should be bad + q.data.s.inc() // should be bad } } diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 27921a773d3..fd7388e037c 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -26663,6 +26663,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt"); } + + @Test + @TestMetadata("unneededUnstableSmartcast.kt") + public void testUnneededUnstableSmartcast() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt"); + } } @Nested diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index c4eeee2eb6a..e75bb33a7b0 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -26663,6 +26663,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt"); } + + @Test + @TestMetadata("unneededUnstableSmartcast.kt") + public void testUnneededUnstableSmartcast() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt"); + } } @Nested diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 5c4bccdef78..07918f7f32a 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -475,6 +475,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("variance") parameter("containingType") } + + val SMARTCAST_IMPOSSIBLE by error { + parameter("desiredType") + parameter("subject") + parameter("description") + } } val REFLECTION by object : DiagnosticGroup("Reflection") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 60d43095529..24dd1e2e902 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -309,6 +309,7 @@ object FirErrors { val INCOMPATIBLE_TYPES_WARNING by warning2() val TYPE_VARIANCE_CONFLICT by error4(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) val TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE by error4(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT) + val SMARTCAST_IMPOSSIBLE by error3() // Reflection val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt index 503eeee20d5..735a4d02f65 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirFunctionReturnTypeMismatchChecker.kt @@ -10,13 +10,16 @@ import org.jetbrains.kotlin.fir.analysis.checkers.isSubtypeForTypeMismatch import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULL_FOR_NONNULL_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction +import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast import org.jetbrains.kotlin.fir.expressions.FirReturnExpression import org.jetbrains.kotlin.fir.expressions.FirWhenExpression import org.jetbrains.kotlin.fir.expressions.isExhaustive import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.types.SmartcastStability object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() { override fun check(expression: FirReturnExpression, context: CheckerContext, reporter: DiagnosticReporter) { @@ -36,10 +39,23 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() { if (resultExpression.isNullLiteral && functionReturnType.nullability == ConeNullability.NOT_NULL) { reporter.reportOn(resultExpression.source, NULL_FOR_NONNULL_TYPE, context) } else { - reporter.report( - RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement), - context - ) + if (resultExpression is FirExpressionWithSmartcast && resultExpression.smartcastStability != SmartcastStability.STABLE_VALUE && + isSubtypeForTypeMismatch(typeContext, subtype = resultExpression.smartcastType.coneType, supertype = functionReturnType) + ) { + reporter.reportOn( + returnExpressionSource, + SMARTCAST_IMPOSSIBLE, + functionReturnType, + resultExpression, + resultExpression.smartcastStability.description, + context + ) + } else { + reporter.report( + RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement), + context + ) + } } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index df8b5bb3512..0102195034f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -282,6 +282,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE_ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SINGLETON_IN_SUPERTYPE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPES_FOR_ANNOTATION_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_APPEARS_TWICE @@ -654,6 +655,13 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { map.put(UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, "Extension function type can not be used as an upper bound") map.put(INCOMPATIBLE_TYPES, "Incompatible types: {0} and {1}", RENDER_TYPE, RENDER_TYPE) map.put(INCOMPATIBLE_TYPES_WARNING, "Potentially incompatible types: {0} and {1}", RENDER_TYPE, RENDER_TYPE) + map.put( + SMARTCAST_IMPOSSIBLE, + "Smart cast to ''{0}'' is impossible, because ''{1}'' is a {2}", + RENDER_TYPE, + FIR, + TO_STRING + ) map.put( TYPE_VARIANCE_CONFLICT, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index 5644e0da6b1..6f50e18654d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -47,6 +47,15 @@ private fun ConeDiagnostic.toFirDiagnostic( val candidate = candidates.first { it.currentApplicability == CandidateApplicability.UNSAFE_CALL } val unsafeCall = candidate.diagnostics.firstIsInstance() mapUnsafeCallError(candidate, unsafeCall, source, qualifiedAccessSource) + } else if (this.applicability == CandidateApplicability.UNSTABLE_SMARTCAST) { + val unstableSmartcast = + this.candidates.first { it.currentApplicability == CandidateApplicability.UNSTABLE_SMARTCAST }.diagnostics.firstIsInstance() + FirErrors.SMARTCAST_IMPOSSIBLE.on( + unstableSmartcast.argument.source, + unstableSmartcast.targetType, + unstableSmartcast.argument, + unstableSmartcast.argument.smartcastStability.description + ) } else { FirErrors.NONE_APPLICABLE.on(source, this.candidates.map { it.symbol }) } @@ -142,8 +151,8 @@ private fun mapInapplicableCandidateError( source: FirSourceElement, qualifiedAccessSource: FirSourceElement?, ): List> { - // TODO: Need to distinguish SMARTCAST_IMPOSSIBLE - return diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause -> + val genericDiagnostic = FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol) + val diagnostics = diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause -> when (rootCause) { is VarargArgumentOutsideParentheses -> FirErrors.VARARG_OUTSIDE_PARENTHESES.on( rootCause.argument.source ?: qualifiedAccessSource @@ -174,9 +183,21 @@ private fun mapInapplicableCandidateError( is InfixCallOfNonInfixFunction -> FirErrors.INFIX_MODIFIER_REQUIRED.on(source, rootCause.function) is OperatorCallOfNonOperatorFunction -> FirErrors.OPERATOR_MODIFIER_REQUIRED.on(source, rootCause.function, rootCause.function.fir.name.asString()) - else -> null + is UnstableSmartCast -> FirErrors.SMARTCAST_IMPOSSIBLE.on( + rootCause.argument.source, + rootCause.targetType, + rootCause.argument, + rootCause.argument.smartcastStability.description + ) + else -> genericDiagnostic } - }.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) } + }.distinct() + return if (diagnostics.size > 1) { + // If there are more specific diagnostics, filter out the generic diagnostic. + diagnostics.filter { it != genericDiagnostic } + } else { + diagnostics + } } @OptIn(ExperimentalStdlibApi::class) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt index 560d369abb6..47d6176ae67 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt @@ -10,9 +10,11 @@ import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolved import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator +import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope import org.jetbrains.kotlin.fir.scopes.FirTypeScope import org.jetbrains.kotlin.fir.scopes.impl.FirScopeWithFakeOverrideTypeCalculator import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker @@ -25,6 +27,25 @@ import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.types.SmartcastStability + +fun FirExpressionWithSmartcast.smartcastScope( + useSiteSession: FirSession, + scopeSession: ScopeSession +): FirTypeScope? { + val smartcastType = smartcastType.coneType + val smartcastScope = smartcastType.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing) + if (smartcastStability == SmartcastStability.STABLE_VALUE) { + return smartcastScope + } + val originalScope = originalType.coneType.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing) + ?: return smartcastScope + + if (smartcastScope == null) { + return originalScope + } + return FirUnstableSmartcastTypeScope(smartcastScope, originalScope) +} fun ConeKotlinType.scope( useSiteSession: FirSession, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index 55a636e0315..2bab2ce415b 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -30,9 +30,9 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible -import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.SmartcastStability import org.jetbrains.kotlin.types.model.CaptureStatus import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext import org.jetbrains.kotlin.utils.addToStdlib.runIf @@ -358,17 +358,7 @@ private fun checkApplicabilityForArgumentType( ) { if (expectedType == null) return - fun unstableSmartCastOrSubtypeError( - unstableType: ConeKotlinType?, - actualExpectedType: ConeKotlinType, - position: ConstraintPosition - ): ResolutionDiagnostic { - if (unstableType != null) { - if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, actualExpectedType, position)) { - return UnstableSmartCast.ResolutionError(argument, unstableType) - } - } - + fun subtypeError(actualExpectedType: ConeKotlinType): ResolutionDiagnostic { if (argument.isNullLiteral && actualExpectedType.nullability == ConeNullability.NOT_NULL) { return NullForNotNullType(argument) } @@ -406,14 +396,17 @@ private fun checkApplicabilityForArgumentType( } if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) { + val smartcastExpression = argument as? FirExpressionWithSmartcast + if (smartcastExpression != null && smartcastExpression.smartcastStability != SmartcastStability.STABLE_VALUE) { + val unstableType = smartcastExpression.smartcastType.coneType + if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, expectedType, position)) { + sink.reportDiagnostic(UnstableSmartCast(smartcastExpression, expectedType)) + return + } + } + if (!isReceiver) { - sink.reportDiagnosticIfNotNull( - unstableSmartCastOrSubtypeError( - unstableType = null, // TODO: handle unstable smartcasts - expectedType, - position - ) - ) + sink.reportDiagnosticIfNotNull(subtypeError(expectedType)) return } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt index 295a7d69eeb..ac21f72d6ec 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirReceivers.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionWithSmartcast import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression @@ -16,6 +17,7 @@ import org.jetbrains.kotlin.fir.renderWithType import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.constructType import org.jetbrains.kotlin.fir.resolve.scope +import org.jetbrains.kotlin.fir.resolve.smartcastScope import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator import org.jetbrains.kotlin.fir.scopes.FirTypeScope @@ -57,7 +59,11 @@ abstract class AbstractExplicitReceiverValue : AbstractExplic class ExpressionReceiverValue( override val explicitReceiver: FirExpression -) : AbstractExplicitReceiverValue(), ReceiverValue +) : AbstractExplicitReceiverValue(), ReceiverValue { + override fun scope(useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope? = + (receiverExpression as? FirExpressionWithSmartcast)?.smartcastScope(useSiteSession, scopeSession) + ?: type.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing) +} sealed class ImplicitReceiverValue>( val boundSymbol: S, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index 301e342c9d8..3a7f9658e10 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType @@ -74,21 +75,7 @@ object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(RES object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED) -sealed class UnstableSmartCast( - val argument: FirExpression, - val targetType: ConeKotlinType, - applicability: CandidateApplicability -) : ResolutionDiagnostic(applicability) { - class ResolutionError( - argument: FirExpression, - targetType: ConeKotlinType, - ) : UnstableSmartCast(argument, targetType, MAY_THROW_RUNTIME_ERROR) - - class DiagnosticError( - argument: FirExpression, - targetType: ConeKotlinType, - ) : UnstableSmartCast(argument, targetType, RESOLVED_WITH_ERROR) -} +class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType) : ResolutionDiagnostic(UNSTABLE_SMARTCAST) class ArgumentTypeMismatch( val expectedType: ConeKotlinType, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index a462399fe23..62ef3164425 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.resolve.inference.* import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol @@ -26,6 +27,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* import org.jetbrains.kotlin.types.AbstractNullabilityChecker +import org.jetbrains.kotlin.types.SmartcastStability abstract class ResolutionStage { abstract suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) @@ -106,8 +108,17 @@ object CheckDispatchReceiver : ResolutionStage() { } val dispatchReceiverValueType = candidate.dispatchReceiverValue?.type ?: return + val isReceiverNullable = !AbstractNullabilityChecker.isSubtypeOfAny(context.session.typeContext, dispatchReceiverValueType) - if (!AbstractNullabilityChecker.isSubtypeOfAny(context.session.typeContext, dispatchReceiverValueType)) { + val isCandidateFromUnstableSmartcast = + (candidate.originScope as? FirUnstableSmartcastTypeScope)?.isSymbolFromUnstableSmartcast(candidate.symbol) == true + + if (explicitReceiverExpression is FirExpressionWithSmartcast && + explicitReceiverExpression.smartcastStability != SmartcastStability.STABLE_VALUE && + (isCandidateFromUnstableSmartcast || isReceiverNullable) + ) { + sink.yieldDiagnostic(UnstableSmartCast(explicitReceiverExpression, explicitReceiverExpression.smartcastType.coneType)) + } else if (isReceiverNullable) { sink.yieldDiagnostic(UnsafeCall(dispatchReceiverValueType)) } } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirUnstableSmartcastTypeScope.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirUnstableSmartcastTypeScope.kt new file mode 100644 index 00000000000..1f04a108bc3 --- /dev/null +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/scopes/FirUnstableSmartcastTypeScope.kt @@ -0,0 +1,111 @@ +/* + * 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.scopes + +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.name.Name + +/** + * Special type scope for unstable smartcast. The purpose of this scope is only to report "SMARTCAST_IMPOSSIBLE" diagnostics. + * + * This scope will serve all candidates available in the original scope. In addition, it also serve all additional members that are + * available from the smartcast type. This way, these additional members can be resolved. Later in + * [org.jetbrains.kotlin.fir.resolve.calls.CheckDispatchReceiver], these additional members are rejected with "UnstableSmartcast" + * diagnostic, which surfaces as "SMARTCAST_IMPOSSIBLE" diagnostic. + */ +class FirUnstableSmartcastTypeScope( + private val smartcastScope: FirTypeScope, + private val originalScope: FirTypeScope +) : FirTypeScope(), FirContainingNamesAwareScope { + private val scopes = listOf(originalScope, smartcastScope) + private val symbolsFromUnstableSmartcast = mutableSetOf>() + override fun processClassifiersByNameWithSubstitution( + name: Name, + processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit + ) { + for (scope in scopes) { + scope.processClassifiersByNameWithSubstitution(name, processor) + } + } + + private inline fun > processComposite( + process: FirTypeScope.(Name, (T) -> Unit) -> Unit, + name: Name, + noinline processor: (T) -> Unit + ) { + val unique = mutableSetOf() + originalScope.process(name) { + unique += it + processor(it) + } + smartcastScope.process(name) { + if (it !in unique) { + symbolsFromUnstableSmartcast += it + processor(it) + } + } + } + + override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) { + return processComposite(FirScope::processFunctionsByName, name, processor) + } + + override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) { + return processComposite(FirScope::processPropertiesByName, name, processor) + } + + private inline fun > processTypedComposite( + process: FirTypeScope.(N, (T, FirTypeScope) -> ProcessorAction) -> ProcessorAction, + name: N, + noinline processor: (T, FirTypeScope) -> ProcessorAction + ): ProcessorAction { + val unique = mutableSetOf() + originalScope.process(name) { symbol, firTypeScope -> + unique += symbol + processor(symbol, firTypeScope) + }.let { if (it == ProcessorAction.STOP) return ProcessorAction.STOP } + + smartcastScope.process(name) { symbol, firTypeScope -> + if (symbol !in unique) { + symbolsFromUnstableSmartcast += symbol + processor(symbol, firTypeScope) + } else { + ProcessorAction.NEXT + } + }.let { if (it == ProcessorAction.STOP) return ProcessorAction.STOP } + return ProcessorAction.NEXT + } + + fun isSymbolFromUnstableSmartcast(symbol: AbstractFirBasedSymbol<*>) = symbol in symbolsFromUnstableSmartcast + + override fun processDirectOverriddenFunctionsWithBaseScope( + functionSymbol: FirNamedFunctionSymbol, + processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction + ): ProcessorAction { + return processTypedComposite(FirTypeScope::processDirectOverriddenFunctionsWithBaseScope, functionSymbol, processor) + } + + override fun processDirectOverriddenPropertiesWithBaseScope( + propertySymbol: FirPropertySymbol, + processor: (FirPropertySymbol, FirTypeScope) -> ProcessorAction + ): ProcessorAction { + return processTypedComposite(FirTypeScope::processDirectOverriddenPropertiesWithBaseScope, propertySymbol, processor) + } + + override fun getCallableNames(): Set { + return scopes.flatMapTo(hashSetOf()) { it.getContainingCallableNamesIfPresent() } + } + + override fun getClassifierNames(): Set { + return scopes.flatMapTo(hashSetOf()) { it.getContainingClassifierNamesIfPresent() } + } + + override val scopeOwnerLookupNames: List by lazy(LazyThreadSafetyMode.PUBLICATION) { + scopes.flatMap { it.scopeOwnerLookupNames } + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt index ea4b8224f7d..29c053bde4b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/DiagnosticReporterByTrackingStrategy.kt @@ -180,8 +180,8 @@ class DiagnosticReporterByTrackingStrategy( } } - ArgumentTypeMismatchDiagnostic::class.java -> { - require(diagnostic is ArgumentTypeMismatchDiagnostic) + ArgumentNullabilityMismatchDiagnostic::class.java -> { + require(diagnostic is ArgumentNullabilityMismatchDiagnostic) val expression = callArgument.safeAs()?.valueArgument?.getArgumentExpression()?.let { KtPsiUtil.deparenthesize(it) ?: it } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/tower/CandidateApplicability.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/tower/CandidateApplicability.kt index d6c6dc02548..08c6b7240ab 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/tower/CandidateApplicability.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/tower/CandidateApplicability.kt @@ -14,8 +14,8 @@ enum class CandidateApplicability { INAPPLICABLE_MODIFIER, // no expected modifier (eg infix call on non-infix function) IMPOSSIBLE_TO_GENERATE, // access to outer class from nested RUNTIME_ERROR, // problems with visibility - UNSAFE_CALL, // receiver nullability doesn't match - MAY_THROW_RUNTIME_ERROR, // unstable smart cast + UNSAFE_CALL, // receiver or argument nullability doesn't match + UNSTABLE_SMARTCAST, // unstable smart cast CONVENTION_ERROR, // missing infix, operator etc RESOLVED_LOW_PRIORITY, RESOLVED_NEED_PRESERVE_COMPATIBILITY, // call resolved successfully, but using new features that changes resolve diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt index 821f90df96f..cf1ffb638d5 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/SimpleArgumentsChecks.kt @@ -85,7 +85,7 @@ private fun checkExpressionArgument( if (argumentType.isMarkedNullable) { if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, actualExpectedType, position)) return null if (csBuilder.addSubtypeConstraintIfCompatible(argumentType.makeNotNullable(), actualExpectedType, position)) { - return ArgumentTypeMismatchDiagnostic(actualExpectedType, argumentType, expressionArgument) + return ArgumentNullabilityMismatchDiagnostic(actualExpectedType, argumentType, expressionArgument) } } @@ -97,7 +97,7 @@ private fun checkExpressionArgument( // Used only for arguments with @NotNull annotation if (expectedType is NotNullTypeVariable && argumentType.isMarkedNullable) { - diagnosticsHolder.addDiagnostic(ArgumentTypeMismatchDiagnostic(expectedType, argumentType, expressionArgument)) + diagnosticsHolder.addDiagnostic(ArgumentNullabilityMismatchDiagnostic(expectedType, argumentType, expressionArgument)) } if (expressionArgument.isSafeCall) { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt index 11a592577f3..44fb70366e1 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/model/KotlinCallDiagnostics.kt @@ -161,7 +161,7 @@ sealed class UnstableSmartCast( class UnstableSmartCastResolutionError( argument: ExpressionKotlinCallArgument, targetType: UnwrappedType, -) : UnstableSmartCast(argument, targetType, MAY_THROW_RUNTIME_ERROR) +) : UnstableSmartCast(argument, targetType, UNSTABLE_SMARTCAST) class UnstableSmartCastDiagnosticError( argument: ExpressionKotlinCallArgument, @@ -214,11 +214,11 @@ class NonApplicableCallForBuilderInferenceDiagnostic(val kotlinCall: KotlinCall) } } -class ArgumentTypeMismatchDiagnostic( +class ArgumentNullabilityMismatchDiagnostic( val expectedType: UnwrappedType, val actualType: UnwrappedType, val expressionArgument: ExpressionKotlinCallArgument -) : KotlinCallDiagnostic(MAY_THROW_RUNTIME_ERROR) { +) : KotlinCallDiagnostic(UNSAFE_CALL) { override fun report(reporter: DiagnosticReporter) { reporter.onCallArgument(expressionArgument, this) } diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt index 418d191202a..2f70131750e 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/ImplicitScopeTower.kt @@ -120,7 +120,7 @@ object ErrorDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED) // todo discus object LowPriorityDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY) object DynamicDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY) object ResolvedUsingNewFeatures : ResolutionDiagnostic(RESOLVED_NEED_PRESERVE_COMPATIBILITY) -object UnstableSmartCastDiagnostic : ResolutionDiagnostic(MAY_THROW_RUNTIME_ERROR) +object UnstableSmartCastDiagnostic : ResolutionDiagnostic(UNSTABLE_SMARTCAST) object HiddenExtensionRelatedToDynamicTypes : ResolutionDiagnostic(HIDDEN) object HiddenDescriptor : ResolutionDiagnostic(HIDDEN) diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.fir.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.fir.kt deleted file mode 100644 index eef4481310c..00000000000 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.fir.kt +++ /dev/null @@ -1,15 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER - -interface Ctx -class CtxImpl : Ctx { - fun doJob(a: Int) {} - fun doJob(s: String) {} -} - -open class Test(open val ctx: Ctx) { - fun test() { - when (ctx) { - is CtxImpl -> ctx.doJob(2) - } - } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.kt index 9470f9633c6..838a09497f6 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.kt +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWhenOpenGetterWithOverloading.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER interface Ctx diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.fir.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.fir.kt deleted file mode 100644 index 8aa89b44288..00000000000 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.fir.kt +++ /dev/null @@ -1,14 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER - -class A -class B - -fun A.foo(a: A) {} -fun A.foo(b: B) {} -var a: A? = null - -fun smartCastInterference(b: B) { - if (a != null) { - a.foo(b) - } -} diff --git a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.kt b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.kt index 8a05ee7fba5..dd7243cff22 100644 --- a/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.kt +++ b/compiler/testData/diagnostics/tests/nullabilityAndSmartCasts/unstableSmartcastWithOverloadedExtensions.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER class A diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010.fir.kt deleted file mode 100644 index 1c01fc7674d..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -class A { - fun foo(): E = TODO() -} - -class B(var a: A<*>?) { - fun bar() { - if (a != null) { - a.foo() - } - } -} diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010.kt index 79b4b03581d..b9398fa2c9f 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL class A { fun foo(): E = TODO() } diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010_2.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010_2.fir.kt deleted file mode 100644 index 29b5304ca7b..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010_2.fir.kt +++ /dev/null @@ -1,16 +0,0 @@ -open class A { -} - -class B : A() { - fun foo() {} -} - -interface KI { - val a: A<*> -} - -fun KI.bar() { - if (a is B) { - a.foo() - } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010_2.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010_2.kt index 92ad9e96636..ecdf447cc75 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010_2.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/inference/kt39010_2.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL open class A { } diff --git a/compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt b/compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt new file mode 100644 index 00000000000..5da10816d2f --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt @@ -0,0 +1,8 @@ +// FIR_IDENTICAL +class Test(var c: Collection) { + fun test() { + if (c is List<*>) { + c.size // smartcast is unstable, but `size` is also available from `Collection`, so there should not be any error + } + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.kt b/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.kt index e8db585a64d..bc5813c837f 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/intersectionScope/unstableSmartCast.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !CHECK_TYPE interface A { diff --git a/compiler/testData/diagnostics/tests/smartCasts/localDelegatedPropertyAfter.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/localDelegatedPropertyAfter.fir.kt index 086c4e05ef7..934a279a0bb 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/localDelegatedPropertyAfter.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/localDelegatedPropertyAfter.fir.kt @@ -10,6 +10,6 @@ fun failsWithClassCastException() { val sometimesNotInt: Any? by AlternatingDelegate() if (sometimesNotInt is Int) { - sometimesNotInt.inc() + sometimesNotInt.inc() } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/localDelegatedPropertyBefore.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/localDelegatedPropertyBefore.fir.kt index 8876767b0ec..13b63862b6b 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/localDelegatedPropertyBefore.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/localDelegatedPropertyBefore.fir.kt @@ -10,6 +10,6 @@ fun failsWithClassCastException() { val sometimesNotInt: Any? by AlternatingDelegate() if (sometimesNotInt is Int) { - sometimesNotInt.inc() + sometimesNotInt.inc() } } diff --git a/compiler/testData/diagnostics/tests/smartCasts/openInSealed.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/openInSealed.fir.kt index e8928c7d8f1..70a19484fba 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/openInSealed.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/openInSealed.fir.kt @@ -2,7 +2,7 @@ sealed class My(open val x: Int?) { init { if (x != null) { // Should be error: property is open - x.hashCode() + x.hashCode() } } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/propertyToNotNull.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/propertyToNotNull.fir.kt index f14a192a0df..cd502255331 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/propertyToNotNull.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/propertyToNotNull.fir.kt @@ -7,7 +7,7 @@ class Immutable(val x: String?) { class Mutable(var y: String?) { fun foo(): String { - if (y != null) return y + if (y != null) return y return "" } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.fir.kt deleted file mode 100644 index 6cbdc76ca22..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.fir.kt +++ /dev/null @@ -1,13 +0,0 @@ -public class X { - private val x : String? = null - public val y: CharSequence? - get() = x?.subSequence(0, 1) - public fun fn(): Int { - if (y != null) - // With non-default getter smartcast is not possible - return y.length - else - return 0 - } -} - diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.kt index 645344871e1..61f6ed0bc73 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVals/customGetter.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL public class X { private val x : String? = null public val y: CharSequence? diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.fir.kt deleted file mode 100644 index 4c34ecac3aa..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -public open class A() { - public open val foo: Int? = 1 -} - -infix fun Int.bar(i: Int) = i - -fun test() { - val p = A() - // For open value properties, smart casts should not work - if (p.foo is Int) p.foo bar 11 -} diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.kt index 770e1fcca6a..3bfb8482af7 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVals/open.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL public open class A() { public open val foo: Int? = 1 } diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.fir.kt deleted file mode 100644 index 10449588bdd..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.fir.kt +++ /dev/null @@ -1,35 +0,0 @@ -// MODULE: m1 -// FILE: a.kt - -package a - -public class X { - public val x : String? = null -} - -// MODULE: m2(m1) -// FILE: b.kt - -package b - -import a.X - -public fun X.gav(): Int { - if (x != null) - // Smart cast is not possible if definition is in another module - return x.length - else - return 0 -} - -// FILE: c.kt - -package a - -public fun X.gav(): Int { - if (x != null) - // Even if it's in the same package - return x.length - else - return 0 -} diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.kt index c5496f9ab03..ab085b21210 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVals/otherModule.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // MODULE: m1 // FILE: a.kt diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.fir.kt deleted file mode 100644 index 8b8e840c20a..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.fir.kt +++ /dev/null @@ -1,15 +0,0 @@ -public class X { - public var x : String? = null - private var y: String? = "abc" - public fun fn(): Int { - if (x != null) - // Smartcast is not possible for variable properties - return x.length - else if (y != null) - // Even if they are private - return y.length - else - return 0 - } -} - diff --git a/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.kt b/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.kt index c8ca4850b83..94cb475bdd9 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/publicVals/var.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL public class X { public var x : String? = null private var y: String? = "abc" diff --git a/compiler/testData/diagnostics/tests/smartCasts/unstableToStable.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/unstableToStable.fir.kt deleted file mode 100644 index 472d38cca3b..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/unstableToStable.fir.kt +++ /dev/null @@ -1,18 +0,0 @@ -class Foo(var x: Int?) { - init { - if (x != null) { - val y = x - // Error: x is not stable, Type(y) = Int? - x.hashCode() - y.hashCode() - if (y == x) { - // Still error - y.hashCode() - } - if (x == null && y != x) { - // Still error - y.hashCode() - } - } - } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/unstableToStable.kt b/compiler/testData/diagnostics/tests/smartCasts/unstableToStable.kt index 53baa677a91..5216bc63586 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/unstableToStable.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/unstableToStable.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL class Foo(var x: Int?) { init { if (x != null) { diff --git a/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.fir.kt deleted file mode 100644 index 4702c095847..00000000000 --- a/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.fir.kt +++ /dev/null @@ -1,22 +0,0 @@ -class Bar { - fun bar() {} -} - -class Foo(var x: Any) { - init { - if (x is Bar) { - val y = x - // Error: x is not stable, Type(y) = Any - x.bar() - y.bar() - if (y == x) { - // Still error - y.bar() - } - if (x !is Bar && y != x) { - // Still error - y.bar() - } - } - } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.kt b/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.kt index 5119d5d11b1..1a872d2f169 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/unstableToStableTypes.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL class Bar { fun bar() {} } diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/receiver.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/receiver.fir.kt index bf485ab0d1e..6fb91017f33 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/receiver.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/smartcasts/receiver.fir.kt @@ -28,7 +28,7 @@ class UnstableReceiver { x.inc() } else { - x.dec() + x.dec() } } } diff --git a/compiler/testData/resolvedCalls/enhancedSignatures/map/mapMerge.txt b/compiler/testData/resolvedCalls/enhancedSignatures/map/mapMerge.txt index 3e3b57214ae..48dac6e4303 100644 --- a/compiler/testData/resolvedCalls/enhancedSignatures/map/mapMerge.txt +++ b/compiler/testData/resolvedCalls/enhancedSignatures/map/mapMerge.txt @@ -11,7 +11,7 @@ fun valuesNullable(map: MutableMap) { // ORIGINAL: fun merge(K, V, BiFunction): V? defined in kotlin.collections.MutableMap // SUBSTITUTED: fun merge(Int, String, BiFunction): String? defined in kotlin.collections.MutableMap map.merge(1, null) { old, new -> old + new } - // OTHER_ERROR + // UNSAFE_CALL_ERROR // ORIGINAL: fun merge(K, V, BiFunction): V? defined in kotlin.collections.MutableMap // SUBSTITUTED: fun merge(Int, String, BiFunction): String? defined in kotlin.collections.MutableMap } @@ -32,7 +32,7 @@ fun valuesTNotNull(map: MutableMap, newValue: T) { fun valuesTNullable(map: MutableMap, newValue: T?) { map.merge(1, newValue) { old, new -> new } - // OTHER_ERROR + // UNSAFE_CALL_ERROR // ORIGINAL: fun merge(K, V, BiFunction): V? defined in kotlin.collections.MutableMap // SUBSTITUTED: fun merge(Int, T, BiFunction): T? defined in kotlin.collections.MutableMap map.merge(1, newValue!!) { old, new -> new } diff --git a/compiler/testData/resolvedCalls/enhancedSignatures/optional/optionalOf.txt b/compiler/testData/resolvedCalls/enhancedSignatures/optional/optionalOf.txt index a1486a87f9f..7638f81b666 100644 --- a/compiler/testData/resolvedCalls/enhancedSignatures/optional/optionalOf.txt +++ b/compiler/testData/resolvedCalls/enhancedSignatures/optional/optionalOf.txt @@ -3,7 +3,7 @@ import java.util.* fun use() { val x: String? = "x" Optional.of(x) - // OTHER_ERROR + // UNSAFE_CALL_ERROR // ORIGINAL: fun of(T): Optional defined in java.util.Optional // SUBSTITUTED: fun of(String): Optional defined in java.util.Optional diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index ee8ec51e27d..b80421756e6 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -26753,6 +26753,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt"); } + + @Test + @TestMetadata("unneededUnstableSmartcast.kt") + public void testUnneededUnstableSmartcast() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt"); + } } @Nested diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt index e49ce937d6f..6fe6e2bacc6 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt @@ -9,7 +9,6 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1 import org.jetbrains.kotlin.checkers.utils.TypeOfCall -import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.rendering.Renderers import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.diagnostics.* @@ -43,6 +42,7 @@ import org.jetbrains.kotlin.test.model.TestFile import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.services.* import org.jetbrains.kotlin.test.utils.AbstractTwoAttributesMetaInfoProcessor +import org.jetbrains.kotlin.types.SmartcastStability import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addIfNotNull @@ -176,7 +176,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes diagnosedRangesToDiagnosticNames: Map> ): FirDiagnosticWithParameters1? = DebugInfoDiagnosticFactory1.EXPRESSION_TYPE.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) { - element.typeRef.renderAsString((element as? FirExpressionWithSmartcast)?.originalType) + element.typeRef.renderAsString((element as? FirExpressionWithSmartcast)?.takeIf { it.smartcastStability == SmartcastStability.STABLE_VALUE }?.originalType) } private fun FirTypeRef.renderAsString(originalTypeRef: FirTypeRef?): String { diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt index 84ee533bd4c..501fe32d597 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/53.fir.kt @@ -33,7 +33,7 @@ fun case_3(x: Int) = "" fun case_3(x: Int?) = 10 fun case_3() { if (case_3_prop != null) { - val z = case_3(case_3_prop) + val z = case_3(case_3_prop) z } } @@ -89,7 +89,7 @@ fun case_7(x: Int) = "" fun case_7(x: Int?) = 10 fun case_7() { if (case_7_prop != null) { - val z = case_7(case_7_prop) + val z = case_7(case_7_prop) z } } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index ea156b69c2e..1c7387b2ff3 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -23320,6 +23320,11 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt"); } + + @TestMetadata("unneededUnstableSmartcast.kt") + public void testUnneededUnstableSmartcast() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt"); + } } @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/intersectionScope") diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 2cdcc4be4f1..9e6eed5546e 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -1411,6 +1411,15 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.SMARTCAST_IMPOSSIBLE) { firDiagnostic -> + SmartcastImpossibleImpl( + firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), + firDiagnostic.b.source!!.psi as KtExpression, + firDiagnostic.c, + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic -> ExtensionInClassReferenceNotAllowedImpl( firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 20295301d9e..cb46409f8e5 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -999,6 +999,13 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val containingType: KtType } + abstract class SmartcastImpossible : KtFirDiagnostic() { + override val diagnosticClass get() = SmartcastImpossible::class + abstract val desiredType: KtType + abstract val subject: KtExpression + abstract val description: String + } + abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic() { override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class abstract val referencedDeclaration: KtCallableSymbol diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 093828a68c7..5d49163206f 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1612,6 +1612,16 @@ internal class TypeVarianceConflictInExpandedTypeImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class SmartcastImpossibleImpl( + override val desiredType: KtType, + override val subject: KtExpression, + override val description: String, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.SmartcastImpossible(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class ExtensionInClassReferenceNotAllowedImpl( override val referencedDeclaration: KtCallableSymbol, firDiagnostic: FirPsiDiagnostic<*>, diff --git a/idea/testData/checker/infos/SmartCasts.fir.kt b/idea/testData/checker/infos/SmartCasts.fir.kt index 503884ed227..e927bee71fa 100644 --- a/idea/testData/checker/infos/SmartCasts.fir.kt +++ b/idea/testData/checker/infos/SmartCasts.fir.kt @@ -234,13 +234,13 @@ class Mutable(var x: String?) { fun foo(): String { if (x is String) { - return x + return x } if (x != null) { - return x + return x } if (xx is String) { - return xx + return xx } return "" } @@ -248,7 +248,7 @@ class Mutable(var x: String?) { fun bar(other: Mutable): String { var y = other if (y.x is String) { - return y.x + return y.x } return "" }