FIR checkers: report SMARTCAST_IMPOSSIBLE
This commit is contained in:
committed by
TeamCityServer
parent
2bb7ef9747
commit
ce767046eb
@@ -492,11 +492,11 @@ digraph nullability_kt {
|
|||||||
176 [label="Access variable R|/Q.data|"];
|
176 [label="Access variable R|/Q.data|"];
|
||||||
177 [label="Access variable R|<local>/q|"];
|
177 [label="Access variable R|<local>/q|"];
|
||||||
178 [label="Access variable R|/Q.data|"];
|
178 [label="Access variable R|/Q.data|"];
|
||||||
179 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
179 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||||
180 [label="Access variable R|<local>/q|"];
|
180 [label="Access variable R|<local>/q|"];
|
||||||
181 [label="Access variable R|/Q.data|"];
|
181 [label="Access variable R|/Q.data|"];
|
||||||
182 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
182 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||||
183 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
|
183 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||||
184 [label="Exit block"];
|
184 [label="Exit block"];
|
||||||
}
|
}
|
||||||
185 [label="Exit when branch result"];
|
185 [label="Exit when branch result"];
|
||||||
@@ -566,11 +566,11 @@ digraph nullability_kt {
|
|||||||
208 [label="Access variable R|/Q.data|"];
|
208 [label="Access variable R|/Q.data|"];
|
||||||
209 [label="Access variable R|<local>/q|"];
|
209 [label="Access variable R|<local>/q|"];
|
||||||
210 [label="Access variable R|/Q.data|"];
|
210 [label="Access variable R|/Q.data|"];
|
||||||
211 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
211 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||||
212 [label="Access variable R|<local>/q|"];
|
212 [label="Access variable R|<local>/q|"];
|
||||||
213 [label="Access variable R|/Q.data|"];
|
213 [label="Access variable R|/Q.data|"];
|
||||||
214 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
214 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||||
215 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
|
215 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||||
216 [label="Exit block"];
|
216 [label="Exit block"];
|
||||||
}
|
}
|
||||||
217 [label="Exit function test_6" style="filled" fillcolor=red];
|
217 [label="Exit function test_6" style="filled" fillcolor=red];
|
||||||
@@ -1280,11 +1280,11 @@ digraph nullability_kt {
|
|||||||
490 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
490 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
||||||
491 [label="Access variable R|<local>/q|"];
|
491 [label="Access variable R|<local>/q|"];
|
||||||
492 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
492 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
||||||
493 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
493 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||||
494 [label="Access variable R|<local>/q|"];
|
494 [label="Access variable R|<local>/q|"];
|
||||||
495 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
495 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
||||||
496 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
496 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||||
497 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
|
497 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||||
498 [label="Exit block"];
|
498 [label="Exit block"];
|
||||||
}
|
}
|
||||||
499 [label="Exit when branch result"];
|
499 [label="Exit when branch result"];
|
||||||
@@ -1363,11 +1363,11 @@ digraph nullability_kt {
|
|||||||
524 [label="Access variable R|/QImplMutable.data|"];
|
524 [label="Access variable R|/QImplMutable.data|"];
|
||||||
525 [label="Access variable R|<local>/q|"];
|
525 [label="Access variable R|<local>/q|"];
|
||||||
526 [label="Access variable R|/QImplMutable.data|"];
|
526 [label="Access variable R|/QImplMutable.data|"];
|
||||||
527 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
527 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||||
528 [label="Access variable R|<local>/q|"];
|
528 [label="Access variable R|<local>/q|"];
|
||||||
529 [label="Access variable R|/QImplMutable.data|"];
|
529 [label="Access variable R|/QImplMutable.data|"];
|
||||||
530 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
530 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||||
531 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
|
531 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||||
532 [label="Exit block"];
|
532 [label="Exit block"];
|
||||||
}
|
}
|
||||||
533 [label="Exit when branch result"];
|
533 [label="Exit when branch result"];
|
||||||
|
|||||||
+8
-8
@@ -102,8 +102,8 @@ FILE: nullability.kt
|
|||||||
when () {
|
when () {
|
||||||
!=(R|<local>/q|?.{ $subj$.R|/Q.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> {
|
!=(R|<local>/q|?.{ $subj$.R|/Q.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> {
|
||||||
R|<local>/q|.R|/Q.data|
|
R|<local>/q|.R|/Q.data|
|
||||||
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#
|
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#
|
||||||
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()
|
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,8 +111,8 @@ FILE: nullability.kt
|
|||||||
public final fun test_6(q: R|Q?|): R|kotlin/Unit| {
|
public final fun test_6(q: R|Q?|): R|kotlin/Unit| {
|
||||||
R|<local>/q|?.{ $subj$.R|/Q.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() } ?: ^test_6 Unit
|
R|<local>/q|?.{ $subj$.R|/Q.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() } ?: ^test_6 Unit
|
||||||
R|<local>/q|.R|/Q.data|
|
R|<local>/q|.R|/Q.data|
|
||||||
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#
|
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#
|
||||||
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()
|
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()
|
||||||
}
|
}
|
||||||
public final fun test_7(q: R|Q?|): R|kotlin/Unit| {
|
public final fun test_7(q: R|Q?|): R|kotlin/Unit| {
|
||||||
when () {
|
when () {
|
||||||
@@ -216,8 +216,8 @@ FILE: nullability.kt
|
|||||||
when () {
|
when () {
|
||||||
!=(R|<local>/q|?.{ $subj$.R|/QImplWithCustomGetter.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> {
|
!=(R|<local>/q|?.{ $subj$.R|/QImplWithCustomGetter.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> {
|
||||||
R|<local>/q|.R|/QImplWithCustomGetter.data|
|
R|<local>/q|.R|/QImplWithCustomGetter.data|
|
||||||
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#
|
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#
|
||||||
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()
|
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,8 +226,8 @@ FILE: nullability.kt
|
|||||||
when () {
|
when () {
|
||||||
!=(R|<local>/q|?.{ $subj$.R|/QImplMutable.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> {
|
!=(R|<local>/q|?.{ $subj$.R|/QImplMutable.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> {
|
||||||
R|<local>/q|.R|/QImplMutable.data|
|
R|<local>/q|.R|/QImplMutable.data|
|
||||||
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#
|
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#
|
||||||
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()
|
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
// `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) {
|
if (q?.data?.s?.inc() != null) {
|
||||||
q.data // good
|
q.data // good
|
||||||
q.data<!UNSAFE_CALL!>.<!>s // should be bad
|
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // should be bad
|
||||||
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
|
<!SMARTCAST_IMPOSSIBLE!>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` 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?.s?.inc() ?: return
|
||||||
q.data // good
|
q.data // good
|
||||||
q.data<!UNSAFE_CALL!>.<!>s // should be bad
|
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // should be bad
|
||||||
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
|
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s.inc() // should be bad
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test_7(q: Q?) {
|
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.
|
// `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) {
|
if (q?.data?.s?.inc() != null) {
|
||||||
q.data // good
|
q.data // good
|
||||||
q.data<!UNSAFE_CALL!>.<!>s // should be bad
|
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // should be bad
|
||||||
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
|
<!SMARTCAST_IMPOSSIBLE!>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.
|
// `q.data` is a property that is mutable, so we can NOT smartcast it to non-nullable MyData.
|
||||||
if (q?.data?.s?.inc() != null) {
|
if (q?.data?.s?.inc() != null) {
|
||||||
q.data // good
|
q.data // good
|
||||||
q.data<!UNSAFE_CALL!>.<!>s // should be bad
|
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // should be bad
|
||||||
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
|
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s.inc() // should be bad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -26663,6 +26663,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+6
@@ -26663,6 +26663,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+6
@@ -475,6 +475,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
parameter<Variance>("variance")
|
parameter<Variance>("variance")
|
||||||
parameter<ConeKotlinType>("containingType")
|
parameter<ConeKotlinType>("containingType")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val SMARTCAST_IMPOSSIBLE by error<KtExpression> {
|
||||||
|
parameter<ConeKotlinType>("desiredType")
|
||||||
|
parameter<FirExpression>("subject")
|
||||||
|
parameter<String>("description")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val REFLECTION by object : DiagnosticGroup("Reflection") {
|
val REFLECTION by object : DiagnosticGroup("Reflection") {
|
||||||
|
|||||||
@@ -309,6 +309,7 @@ object FirErrors {
|
|||||||
val INCOMPATIBLE_TYPES_WARNING by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
|
val INCOMPATIBLE_TYPES_WARNING by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||||
val TYPE_VARIANCE_CONFLICT by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
val TYPE_VARIANCE_CONFLICT by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||||
val TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
val TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
|
||||||
|
val SMARTCAST_IMPOSSIBLE by error3<KtExpression, ConeKotlinType, FirExpression, String>()
|
||||||
|
|
||||||
// Reflection
|
// Reflection
|
||||||
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||||
|
|||||||
+20
-4
@@ -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.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULL_FOR_NONNULL_TYPE
|
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.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.analysis.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
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.FirReturnExpression
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
|
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
|
||||||
import org.jetbrains.kotlin.fir.expressions.isExhaustive
|
import org.jetbrains.kotlin.fir.expressions.isExhaustive
|
||||||
import org.jetbrains.kotlin.fir.typeContext
|
import org.jetbrains.kotlin.fir.typeContext
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
|
import org.jetbrains.kotlin.types.SmartcastStability
|
||||||
|
|
||||||
object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
|
object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
|
||||||
override fun check(expression: FirReturnExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
override fun check(expression: FirReturnExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
@@ -36,10 +39,23 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
|
|||||||
if (resultExpression.isNullLiteral && functionReturnType.nullability == ConeNullability.NOT_NULL) {
|
if (resultExpression.isNullLiteral && functionReturnType.nullability == ConeNullability.NOT_NULL) {
|
||||||
reporter.reportOn(resultExpression.source, NULL_FOR_NONNULL_TYPE, context)
|
reporter.reportOn(resultExpression.source, NULL_FOR_NONNULL_TYPE, context)
|
||||||
} else {
|
} else {
|
||||||
reporter.report(
|
if (resultExpression is FirExpressionWithSmartcast && resultExpression.smartcastStability != SmartcastStability.STABLE_VALUE &&
|
||||||
RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement),
|
isSubtypeForTypeMismatch(typeContext, subtype = resultExpression.smartcastType.coneType, supertype = functionReturnType)
|
||||||
context
|
) {
|
||||||
)
|
reporter.reportOn(
|
||||||
|
returnExpressionSource,
|
||||||
|
SMARTCAST_IMPOSSIBLE,
|
||||||
|
functionReturnType,
|
||||||
|
resultExpression,
|
||||||
|
resultExpression.smartcastStability.description,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
reporter.report(
|
||||||
|
RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement),
|
||||||
|
context
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -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.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.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.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.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.SUPERTYPES_FOR_ANNOTATION_CLASS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_APPEARS_TWICE
|
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(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, "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(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(
|
map.put(
|
||||||
TYPE_VARIANCE_CONFLICT,
|
TYPE_VARIANCE_CONFLICT,
|
||||||
|
|||||||
+25
-4
@@ -47,6 +47,15 @@ private fun ConeDiagnostic.toFirDiagnostic(
|
|||||||
val candidate = candidates.first { it.currentApplicability == CandidateApplicability.UNSAFE_CALL }
|
val candidate = candidates.first { it.currentApplicability == CandidateApplicability.UNSAFE_CALL }
|
||||||
val unsafeCall = candidate.diagnostics.firstIsInstance<UnsafeCall>()
|
val unsafeCall = candidate.diagnostics.firstIsInstance<UnsafeCall>()
|
||||||
mapUnsafeCallError(candidate, unsafeCall, source, qualifiedAccessSource)
|
mapUnsafeCallError(candidate, unsafeCall, source, qualifiedAccessSource)
|
||||||
|
} else if (this.applicability == CandidateApplicability.UNSTABLE_SMARTCAST) {
|
||||||
|
val unstableSmartcast =
|
||||||
|
this.candidates.first { it.currentApplicability == CandidateApplicability.UNSTABLE_SMARTCAST }.diagnostics.firstIsInstance<UnstableSmartCast>()
|
||||||
|
FirErrors.SMARTCAST_IMPOSSIBLE.on(
|
||||||
|
unstableSmartcast.argument.source,
|
||||||
|
unstableSmartcast.targetType,
|
||||||
|
unstableSmartcast.argument,
|
||||||
|
unstableSmartcast.argument.smartcastStability.description
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
FirErrors.NONE_APPLICABLE.on(source, this.candidates.map { it.symbol })
|
FirErrors.NONE_APPLICABLE.on(source, this.candidates.map { it.symbol })
|
||||||
}
|
}
|
||||||
@@ -142,8 +151,8 @@ private fun mapInapplicableCandidateError(
|
|||||||
source: FirSourceElement,
|
source: FirSourceElement,
|
||||||
qualifiedAccessSource: FirSourceElement?,
|
qualifiedAccessSource: FirSourceElement?,
|
||||||
): List<FirDiagnostic<FirSourceElement>> {
|
): List<FirDiagnostic<FirSourceElement>> {
|
||||||
// TODO: Need to distinguish SMARTCAST_IMPOSSIBLE
|
val genericDiagnostic = FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)
|
||||||
return diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause ->
|
val diagnostics = diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause ->
|
||||||
when (rootCause) {
|
when (rootCause) {
|
||||||
is VarargArgumentOutsideParentheses -> FirErrors.VARARG_OUTSIDE_PARENTHESES.on(
|
is VarargArgumentOutsideParentheses -> FirErrors.VARARG_OUTSIDE_PARENTHESES.on(
|
||||||
rootCause.argument.source ?: qualifiedAccessSource
|
rootCause.argument.source ?: qualifiedAccessSource
|
||||||
@@ -174,9 +183,21 @@ private fun mapInapplicableCandidateError(
|
|||||||
is InfixCallOfNonInfixFunction -> FirErrors.INFIX_MODIFIER_REQUIRED.on(source, rootCause.function)
|
is InfixCallOfNonInfixFunction -> FirErrors.INFIX_MODIFIER_REQUIRED.on(source, rootCause.function)
|
||||||
is OperatorCallOfNonOperatorFunction ->
|
is OperatorCallOfNonOperatorFunction ->
|
||||||
FirErrors.OPERATOR_MODIFIER_REQUIRED.on(source, rootCause.function, rootCause.function.fir.name.asString())
|
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)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject
|
|||||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
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.substitution.substitutorByMap
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolved
|
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolved
|
||||||
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
|
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.FirTypeScope
|
||||||
import org.jetbrains.kotlin.fir.scopes.impl.FirScopeWithFakeOverrideTypeCalculator
|
import org.jetbrains.kotlin.fir.scopes.impl.FirScopeWithFakeOverrideTypeCalculator
|
||||||
import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker
|
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.ConeClassLikeTypeImpl
|
||||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
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(
|
fun ConeKotlinType.scope(
|
||||||
useSiteSession: FirSession,
|
useSiteSession: FirSession,
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.name.StandardClassIds
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
|
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.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
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.CaptureStatus
|
||||||
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
|
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
@@ -358,17 +358,7 @@ private fun checkApplicabilityForArgumentType(
|
|||||||
) {
|
) {
|
||||||
if (expectedType == null) return
|
if (expectedType == null) return
|
||||||
|
|
||||||
fun unstableSmartCastOrSubtypeError(
|
fun subtypeError(actualExpectedType: ConeKotlinType): ResolutionDiagnostic {
|
||||||
unstableType: ConeKotlinType?,
|
|
||||||
actualExpectedType: ConeKotlinType,
|
|
||||||
position: ConstraintPosition
|
|
||||||
): ResolutionDiagnostic {
|
|
||||||
if (unstableType != null) {
|
|
||||||
if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, actualExpectedType, position)) {
|
|
||||||
return UnstableSmartCast.ResolutionError(argument, unstableType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argument.isNullLiteral && actualExpectedType.nullability == ConeNullability.NOT_NULL) {
|
if (argument.isNullLiteral && actualExpectedType.nullability == ConeNullability.NOT_NULL) {
|
||||||
return NullForNotNullType(argument)
|
return NullForNotNullType(argument)
|
||||||
}
|
}
|
||||||
@@ -406,14 +396,17 @@ private fun checkApplicabilityForArgumentType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) {
|
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) {
|
if (!isReceiver) {
|
||||||
sink.reportDiagnosticIfNotNull(
|
sink.reportDiagnosticIfNotNull(subtypeError(expectedType))
|
||||||
unstableSmartCastOrSubtypeError(
|
|
||||||
unstableType = null, // TODO: handle unstable smartcasts
|
|
||||||
expectedType,
|
|
||||||
position
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.calls
|
|||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
|
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
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.FirThisReceiverExpression
|
||||||
import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionWithSmartcast
|
import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionWithSmartcast
|
||||||
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression
|
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.ScopeSession
|
||||||
import org.jetbrains.kotlin.fir.resolve.constructType
|
import org.jetbrains.kotlin.fir.resolve.constructType
|
||||||
import org.jetbrains.kotlin.fir.resolve.scope
|
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.resolvedTypeFromPrototype
|
||||||
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
|
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
|
||||||
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||||
@@ -57,7 +59,11 @@ abstract class AbstractExplicitReceiverValue<E : FirExpression> : AbstractExplic
|
|||||||
|
|
||||||
class ExpressionReceiverValue(
|
class ExpressionReceiverValue(
|
||||||
override val explicitReceiver: FirExpression
|
override val explicitReceiver: FirExpression
|
||||||
) : AbstractExplicitReceiverValue<FirExpression>(), ReceiverValue
|
) : AbstractExplicitReceiverValue<FirExpression>(), ReceiverValue {
|
||||||
|
override fun scope(useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope? =
|
||||||
|
(receiverExpression as? FirExpressionWithSmartcast)?.smartcastScope(useSiteSession, scopeSession)
|
||||||
|
?: type.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
|
||||||
|
}
|
||||||
|
|
||||||
sealed class ImplicitReceiverValue<S : AbstractFirBasedSymbol<*>>(
|
sealed class ImplicitReceiverValue<S : AbstractFirBasedSymbol<*>>(
|
||||||
val boundSymbol: S,
|
val boundSymbol: S,
|
||||||
|
|||||||
+2
-15
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.calls
|
|||||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
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.expressions.FirNamedArgumentExpression
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
@@ -74,21 +75,7 @@ object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(RES
|
|||||||
|
|
||||||
object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED)
|
object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED)
|
||||||
|
|
||||||
sealed class UnstableSmartCast(
|
class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType) : ResolutionDiagnostic(UNSTABLE_SMARTCAST)
|
||||||
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 ArgumentTypeMismatch(
|
class ArgumentTypeMismatch(
|
||||||
val expectedType: ConeKotlinType,
|
val expectedType: ConeKotlinType,
|
||||||
|
|||||||
+12
-1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.expressions.*
|
|||||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.inference.*
|
import org.jetbrains.kotlin.fir.resolve.inference.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
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.SyntheticSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
|
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
|
||||||
import org.jetbrains.kotlin.types.AbstractNullabilityChecker
|
import org.jetbrains.kotlin.types.AbstractNullabilityChecker
|
||||||
|
import org.jetbrains.kotlin.types.SmartcastStability
|
||||||
|
|
||||||
abstract class ResolutionStage {
|
abstract class ResolutionStage {
|
||||||
abstract suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext)
|
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 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))
|
sink.yieldDiagnostic(UnsafeCall(dispatchReceiverValueType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+111
@@ -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<FirCallableSymbol<*>>()
|
||||||
|
override fun processClassifiersByNameWithSubstitution(
|
||||||
|
name: Name,
|
||||||
|
processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit
|
||||||
|
) {
|
||||||
|
for (scope in scopes) {
|
||||||
|
scope.processClassifiersByNameWithSubstitution(name, processor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <T : FirCallableSymbol<*>> processComposite(
|
||||||
|
process: FirTypeScope.(Name, (T) -> Unit) -> Unit,
|
||||||
|
name: Name,
|
||||||
|
noinline processor: (T) -> Unit
|
||||||
|
) {
|
||||||
|
val unique = mutableSetOf<T>()
|
||||||
|
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 <N, T : FirCallableSymbol<*>> processTypedComposite(
|
||||||
|
process: FirTypeScope.(N, (T, FirTypeScope) -> ProcessorAction) -> ProcessorAction,
|
||||||
|
name: N,
|
||||||
|
noinline processor: (T, FirTypeScope) -> ProcessorAction
|
||||||
|
): ProcessorAction {
|
||||||
|
val unique = mutableSetOf<T>()
|
||||||
|
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<Name> {
|
||||||
|
return scopes.flatMapTo(hashSetOf()) { it.getContainingCallableNamesIfPresent() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getClassifierNames(): Set<Name> {
|
||||||
|
return scopes.flatMapTo(hashSetOf()) { it.getContainingClassifierNamesIfPresent() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val scopeOwnerLookupNames: List<String> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
|
scopes.flatMap { it.scopeOwnerLookupNames }
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -180,8 +180,8 @@ class DiagnosticReporterByTrackingStrategy(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArgumentTypeMismatchDiagnostic::class.java -> {
|
ArgumentNullabilityMismatchDiagnostic::class.java -> {
|
||||||
require(diagnostic is ArgumentTypeMismatchDiagnostic)
|
require(diagnostic is ArgumentNullabilityMismatchDiagnostic)
|
||||||
val expression = callArgument.safeAs<PSIKotlinCallArgument>()?.valueArgument?.getArgumentExpression()?.let {
|
val expression = callArgument.safeAs<PSIKotlinCallArgument>()?.valueArgument?.getArgumentExpression()?.let {
|
||||||
KtPsiUtil.deparenthesize(it) ?: it
|
KtPsiUtil.deparenthesize(it) ?: it
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -14,8 +14,8 @@ enum class CandidateApplicability {
|
|||||||
INAPPLICABLE_MODIFIER, // no expected modifier (eg infix call on non-infix function)
|
INAPPLICABLE_MODIFIER, // no expected modifier (eg infix call on non-infix function)
|
||||||
IMPOSSIBLE_TO_GENERATE, // access to outer class from nested
|
IMPOSSIBLE_TO_GENERATE, // access to outer class from nested
|
||||||
RUNTIME_ERROR, // problems with visibility
|
RUNTIME_ERROR, // problems with visibility
|
||||||
UNSAFE_CALL, // receiver nullability doesn't match
|
UNSAFE_CALL, // receiver or argument nullability doesn't match
|
||||||
MAY_THROW_RUNTIME_ERROR, // unstable smart cast
|
UNSTABLE_SMARTCAST, // unstable smart cast
|
||||||
CONVENTION_ERROR, // missing infix, operator etc
|
CONVENTION_ERROR, // missing infix, operator etc
|
||||||
RESOLVED_LOW_PRIORITY,
|
RESOLVED_LOW_PRIORITY,
|
||||||
RESOLVED_NEED_PRESERVE_COMPATIBILITY, // call resolved successfully, but using new features that changes resolve
|
RESOLVED_NEED_PRESERVE_COMPATIBILITY, // call resolved successfully, but using new features that changes resolve
|
||||||
|
|||||||
+2
-2
@@ -85,7 +85,7 @@ private fun checkExpressionArgument(
|
|||||||
if (argumentType.isMarkedNullable) {
|
if (argumentType.isMarkedNullable) {
|
||||||
if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, actualExpectedType, position)) return null
|
if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, actualExpectedType, position)) return null
|
||||||
if (csBuilder.addSubtypeConstraintIfCompatible(argumentType.makeNotNullable(), actualExpectedType, position)) {
|
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
|
// Used only for arguments with @NotNull annotation
|
||||||
if (expectedType is NotNullTypeVariable && argumentType.isMarkedNullable) {
|
if (expectedType is NotNullTypeVariable && argumentType.isMarkedNullable) {
|
||||||
diagnosticsHolder.addDiagnostic(ArgumentTypeMismatchDiagnostic(expectedType, argumentType, expressionArgument))
|
diagnosticsHolder.addDiagnostic(ArgumentNullabilityMismatchDiagnostic(expectedType, argumentType, expressionArgument))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expressionArgument.isSafeCall) {
|
if (expressionArgument.isSafeCall) {
|
||||||
|
|||||||
+3
-3
@@ -161,7 +161,7 @@ sealed class UnstableSmartCast(
|
|||||||
class UnstableSmartCastResolutionError(
|
class UnstableSmartCastResolutionError(
|
||||||
argument: ExpressionKotlinCallArgument,
|
argument: ExpressionKotlinCallArgument,
|
||||||
targetType: UnwrappedType,
|
targetType: UnwrappedType,
|
||||||
) : UnstableSmartCast(argument, targetType, MAY_THROW_RUNTIME_ERROR)
|
) : UnstableSmartCast(argument, targetType, UNSTABLE_SMARTCAST)
|
||||||
|
|
||||||
class UnstableSmartCastDiagnosticError(
|
class UnstableSmartCastDiagnosticError(
|
||||||
argument: ExpressionKotlinCallArgument,
|
argument: ExpressionKotlinCallArgument,
|
||||||
@@ -214,11 +214,11 @@ class NonApplicableCallForBuilderInferenceDiagnostic(val kotlinCall: KotlinCall)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArgumentTypeMismatchDiagnostic(
|
class ArgumentNullabilityMismatchDiagnostic(
|
||||||
val expectedType: UnwrappedType,
|
val expectedType: UnwrappedType,
|
||||||
val actualType: UnwrappedType,
|
val actualType: UnwrappedType,
|
||||||
val expressionArgument: ExpressionKotlinCallArgument
|
val expressionArgument: ExpressionKotlinCallArgument
|
||||||
) : KotlinCallDiagnostic(MAY_THROW_RUNTIME_ERROR) {
|
) : KotlinCallDiagnostic(UNSAFE_CALL) {
|
||||||
override fun report(reporter: DiagnosticReporter) {
|
override fun report(reporter: DiagnosticReporter) {
|
||||||
reporter.onCallArgument(expressionArgument, this)
|
reporter.onCallArgument(expressionArgument, this)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -120,7 +120,7 @@ object ErrorDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED) // todo discus
|
|||||||
object LowPriorityDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
|
object LowPriorityDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
|
||||||
object DynamicDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
|
object DynamicDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
|
||||||
object ResolvedUsingNewFeatures : ResolutionDiagnostic(RESOLVED_NEED_PRESERVE_COMPATIBILITY)
|
object ResolvedUsingNewFeatures : ResolutionDiagnostic(RESOLVED_NEED_PRESERVE_COMPATIBILITY)
|
||||||
object UnstableSmartCastDiagnostic : ResolutionDiagnostic(MAY_THROW_RUNTIME_ERROR)
|
object UnstableSmartCastDiagnostic : ResolutionDiagnostic(UNSTABLE_SMARTCAST)
|
||||||
object HiddenExtensionRelatedToDynamicTypes : ResolutionDiagnostic(HIDDEN)
|
object HiddenExtensionRelatedToDynamicTypes : ResolutionDiagnostic(HIDDEN)
|
||||||
object HiddenDescriptor : ResolutionDiagnostic(HIDDEN)
|
object HiddenDescriptor : ResolutionDiagnostic(HIDDEN)
|
||||||
|
|
||||||
|
|||||||
-15
@@ -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.<!UNRESOLVED_REFERENCE!>doJob<!>(2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
interface Ctx
|
interface Ctx
|
||||||
|
|||||||
-14
@@ -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<!UNSAFE_CALL!>.<!>foo(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
|
||||||
class A
|
class A
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
class A<E> {
|
|
||||||
fun foo(): E = TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
class B(var a: A<*>?) {
|
|
||||||
fun bar() {
|
|
||||||
if (a != null) {
|
|
||||||
a<!UNSAFE_CALL!>.<!>foo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
class A<E> {
|
class A<E> {
|
||||||
fun foo(): E = TODO()
|
fun foo(): E = TODO()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
open class A<E> {
|
|
||||||
}
|
|
||||||
|
|
||||||
class B : A<String>() {
|
|
||||||
fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface KI {
|
|
||||||
val a: A<*>
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KI.bar() {
|
|
||||||
if (a is B) {
|
|
||||||
a.<!UNRESOLVED_REFERENCE!>foo<!>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
open class A<E> {
|
open class A<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
class Test<E>(var c: Collection<E>) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !CHECK_TYPE
|
// !CHECK_TYPE
|
||||||
|
|
||||||
interface A {
|
interface A {
|
||||||
|
|||||||
+1
-1
@@ -10,6 +10,6 @@ fun failsWithClassCastException() {
|
|||||||
val sometimesNotInt: Any? by AlternatingDelegate()
|
val sometimesNotInt: Any? by AlternatingDelegate()
|
||||||
|
|
||||||
if (sometimesNotInt is Int) {
|
if (sometimesNotInt is Int) {
|
||||||
sometimesNotInt.<!UNRESOLVED_REFERENCE!>inc<!>()
|
<!SMARTCAST_IMPOSSIBLE!>sometimesNotInt<!>.inc()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -10,6 +10,6 @@ fun failsWithClassCastException() {
|
|||||||
val sometimesNotInt: Any? by AlternatingDelegate()
|
val sometimesNotInt: Any? by AlternatingDelegate()
|
||||||
|
|
||||||
if (sometimesNotInt is Int) {
|
if (sometimesNotInt is Int) {
|
||||||
sometimesNotInt.<!UNRESOLVED_REFERENCE!>inc<!>()
|
<!SMARTCAST_IMPOSSIBLE!>sometimesNotInt<!>.inc()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ sealed class My(open val x: Int?) {
|
|||||||
init {
|
init {
|
||||||
if (x != null) {
|
if (x != null) {
|
||||||
// Should be error: property is open
|
// Should be error: property is open
|
||||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
<!SMARTCAST_IMPOSSIBLE!>x<!>.hashCode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ class Immutable(val x: String?) {
|
|||||||
|
|
||||||
class Mutable(var y: String?) {
|
class Mutable(var y: String?) {
|
||||||
fun foo(): String {
|
fun foo(): String {
|
||||||
if (y != null) return <!RETURN_TYPE_MISMATCH!>y<!>
|
if (y != null) return <!SMARTCAST_IMPOSSIBLE!>y<!>
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-13
@@ -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<!UNSAFE_CALL!>.<!>length
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
public class X {
|
public class X {
|
||||||
private val x : String? = null
|
private val x : String? = null
|
||||||
public val y: CharSequence?
|
public val y: CharSequence?
|
||||||
|
|||||||
@@ -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 <!UNSAFE_INFIX_CALL!>bar<!> 11
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
public open class A() {
|
public open class A() {
|
||||||
public open val foo: Int? = 1
|
public open val foo: Int? = 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<!UNSAFE_CALL!>.<!>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<!UNSAFE_CALL!>.<!>length
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// MODULE: m1
|
// MODULE: m1
|
||||||
// FILE: a.kt
|
// FILE: a.kt
|
||||||
|
|
||||||
|
|||||||
@@ -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<!UNSAFE_CALL!>.<!>length
|
|
||||||
else if (y != null)
|
|
||||||
// Even if they are private
|
|
||||||
return y<!UNSAFE_CALL!>.<!>length
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
public class X {
|
public class X {
|
||||||
public var x : String? = null
|
public var x : String? = null
|
||||||
private var y: String? = "abc"
|
private var y: String? = "abc"
|
||||||
|
|||||||
@@ -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<!UNSAFE_CALL!>.<!>hashCode()
|
|
||||||
y<!UNSAFE_CALL!>.<!>hashCode()
|
|
||||||
if (y == x) {
|
|
||||||
// Still error
|
|
||||||
y<!UNSAFE_CALL!>.<!>hashCode()
|
|
||||||
}
|
|
||||||
if (x == null && y != x) {
|
|
||||||
// Still error
|
|
||||||
y<!UNSAFE_CALL!>.<!>hashCode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
class Foo(var x: Int?) {
|
class Foo(var x: Int?) {
|
||||||
init {
|
init {
|
||||||
if (x != null) {
|
if (x != 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.<!UNRESOLVED_REFERENCE!>bar<!>()
|
|
||||||
y.<!UNRESOLVED_REFERENCE!>bar<!>()
|
|
||||||
if (y == x) {
|
|
||||||
// Still error
|
|
||||||
y.<!UNRESOLVED_REFERENCE!>bar<!>()
|
|
||||||
}
|
|
||||||
if (x !is Bar && y != x) {
|
|
||||||
// Still error
|
|
||||||
y.<!UNRESOLVED_REFERENCE!>bar<!>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
class Bar {
|
class Bar {
|
||||||
fun bar() {}
|
fun bar() {}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ class UnstableReceiver {
|
|||||||
x<!UNSAFE_CALL!>.<!>inc()
|
x<!UNSAFE_CALL!>.<!>inc()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
x<!UNSAFE_CALL!>.<!>dec()
|
<!SMARTCAST_IMPOSSIBLE!>x<!>.dec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ fun valuesNullable(map: MutableMap<Int, String?>) {
|
|||||||
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out V?>): V? defined in kotlin.collections.MutableMap
|
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out V?>): V? defined in kotlin.collections.MutableMap
|
||||||
// SUBSTITUTED: fun merge(Int, String, BiFunction<in String, in String, out String?>): String? defined in kotlin.collections.MutableMap
|
// SUBSTITUTED: fun merge(Int, String, BiFunction<in String, in String, out String?>): String? defined in kotlin.collections.MutableMap
|
||||||
map.merge(1, null) { old, new -> old + new }
|
map.merge(1, null) { old, new -> old + new }
|
||||||
// OTHER_ERROR
|
// UNSAFE_CALL_ERROR
|
||||||
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out V?>): V? defined in kotlin.collections.MutableMap
|
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out V?>): V? defined in kotlin.collections.MutableMap
|
||||||
// SUBSTITUTED: fun merge(Int, String, BiFunction<in String, in String, out String?>): String? defined in kotlin.collections.MutableMap
|
// SUBSTITUTED: fun merge(Int, String, BiFunction<in String, in String, out String?>): String? defined in kotlin.collections.MutableMap
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ fun <T : Any> valuesTNotNull(map: MutableMap<Int, T>, newValue: T) {
|
|||||||
|
|
||||||
fun <T : Any> valuesTNullable(map: MutableMap<Int, T?>, newValue: T?) {
|
fun <T : Any> valuesTNullable(map: MutableMap<Int, T?>, newValue: T?) {
|
||||||
map.merge(1, newValue) { old, new -> new }
|
map.merge(1, newValue) { old, new -> new }
|
||||||
// OTHER_ERROR
|
// UNSAFE_CALL_ERROR
|
||||||
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out V?>): V? defined in kotlin.collections.MutableMap
|
// ORIGINAL: fun merge(K, V, BiFunction<in V, in V, out V?>): V? defined in kotlin.collections.MutableMap
|
||||||
// SUBSTITUTED: fun merge(Int, T, BiFunction<in T, in T, out T?>): T? defined in kotlin.collections.MutableMap
|
// SUBSTITUTED: fun merge(Int, T, BiFunction<in T, in T, out T?>): T? defined in kotlin.collections.MutableMap
|
||||||
map.merge(1, newValue!!) { old, new -> new }
|
map.merge(1, newValue!!) { old, new -> new }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import java.util.*
|
|||||||
fun use() {
|
fun use() {
|
||||||
val x: String? = "x"
|
val x: String? = "x"
|
||||||
Optional.of(x)
|
Optional.of(x)
|
||||||
// OTHER_ERROR
|
// UNSAFE_CALL_ERROR
|
||||||
// ORIGINAL: fun <T : Any!> of(T): Optional<T> defined in java.util.Optional
|
// ORIGINAL: fun <T : Any!> of(T): Optional<T> defined in java.util.Optional
|
||||||
// SUBSTITUTED: fun <T : Any!> of(String): Optional<String> defined in java.util.Optional
|
// SUBSTITUTED: fun <T : Any!> of(String): Optional<String> defined in java.util.Optional
|
||||||
|
|
||||||
|
|||||||
Generated
+6
@@ -26753,6 +26753,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+2
-2
@@ -9,7 +9,6 @@ import com.intellij.psi.PsiElement
|
|||||||
import org.jetbrains.kotlin.KtNodeTypes
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1
|
import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1
|
||||||
import org.jetbrains.kotlin.checkers.utils.TypeOfCall
|
import org.jetbrains.kotlin.checkers.utils.TypeOfCall
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
|
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
|
||||||
import org.jetbrains.kotlin.fir.*
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
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.model.TestModule
|
||||||
import org.jetbrains.kotlin.test.services.*
|
import org.jetbrains.kotlin.test.services.*
|
||||||
import org.jetbrains.kotlin.test.utils.AbstractTwoAttributesMetaInfoProcessor
|
import org.jetbrains.kotlin.test.utils.AbstractTwoAttributesMetaInfoProcessor
|
||||||
|
import org.jetbrains.kotlin.types.SmartcastStability
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
|
|||||||
diagnosedRangesToDiagnosticNames: Map<IntRange, Set<String>>
|
diagnosedRangesToDiagnosticNames: Map<IntRange, Set<String>>
|
||||||
): FirDiagnosticWithParameters1<FirSourceElement, String>? =
|
): FirDiagnosticWithParameters1<FirSourceElement, String>? =
|
||||||
DebugInfoDiagnosticFactory1.EXPRESSION_TYPE.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) {
|
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 {
|
private fun FirTypeRef.renderAsString(originalTypeRef: FirTypeRef?): String {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ fun case_3(x: Int) = ""
|
|||||||
fun case_3(x: Int?) = 10
|
fun case_3(x: Int?) = 10
|
||||||
fun case_3() {
|
fun case_3() {
|
||||||
if (case_3_prop != null) {
|
if (case_3_prop != null) {
|
||||||
val z = case_3(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int?")!>case_3_prop<!>)
|
val z = case_3(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>case_3_prop<!>)
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>z<!>
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>z<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ fun case_7(x: Int) = ""
|
|||||||
fun case_7(x: Int?) = 10
|
fun case_7(x: Int?) = 10
|
||||||
fun case_7() {
|
fun case_7() {
|
||||||
if (case_7_prop != null) {
|
if (case_7_prop != null) {
|
||||||
val z = case_7(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int?")!>case_7_prop<!>)
|
val z = case_7(<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>case_7_prop<!>)
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>z<!>
|
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int")!>z<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -23320,6 +23320,11 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt");
|
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")
|
@TestMetadata("compiler/testData/diagnostics/tests/smartCasts/intersectionScope")
|
||||||
|
|||||||
+9
@@ -1411,6 +1411,15 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
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 ->
|
add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic ->
|
||||||
ExtensionInClassReferenceNotAllowedImpl(
|
ExtensionInClassReferenceNotAllowedImpl(
|
||||||
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration),
|
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration),
|
||||||
|
|||||||
+7
@@ -999,6 +999,13 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
abstract val containingType: KtType
|
abstract val containingType: KtType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class SmartcastImpossible : KtFirDiagnostic<KtExpression>() {
|
||||||
|
override val diagnosticClass get() = SmartcastImpossible::class
|
||||||
|
abstract val desiredType: KtType
|
||||||
|
abstract val subject: KtExpression
|
||||||
|
abstract val description: String
|
||||||
|
}
|
||||||
|
|
||||||
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
|
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
|
||||||
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
|
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
|
||||||
abstract val referencedDeclaration: KtCallableSymbol
|
abstract val referencedDeclaration: KtCallableSymbol
|
||||||
|
|||||||
+10
@@ -1612,6 +1612,16 @@ internal class TypeVarianceConflictInExpandedTypeImpl(
|
|||||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
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<KtExpression> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
internal class ExtensionInClassReferenceNotAllowedImpl(
|
internal class ExtensionInClassReferenceNotAllowedImpl(
|
||||||
override val referencedDeclaration: KtCallableSymbol,
|
override val referencedDeclaration: KtCallableSymbol,
|
||||||
firDiagnostic: FirPsiDiagnostic<*>,
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
|
|||||||
+4
-4
@@ -234,13 +234,13 @@ class Mutable(var x: String?) {
|
|||||||
|
|
||||||
fun foo(): String {
|
fun foo(): String {
|
||||||
if (x is String) {
|
if (x is String) {
|
||||||
return <error descr="[RETURN_TYPE_MISMATCH] Return type mismatch: expected kotlin/String, actual kotlin/String?">x</error>
|
return <error descr="[SMARTCAST_IMPOSSIBLE] Smart cast to 'kotlin/String' is impossible, because 'this@R|/Mutable|.R|/Mutable.x|' is a mutable property that could have been changed by this time">x</error>
|
||||||
}
|
}
|
||||||
if (x != null) {
|
if (x != null) {
|
||||||
return <error descr="[RETURN_TYPE_MISMATCH] Return type mismatch: expected kotlin/String, actual kotlin/String?">x</error>
|
return <error descr="[SMARTCAST_IMPOSSIBLE] Smart cast to 'kotlin/String' is impossible, because 'this@R|/Mutable|.R|/Mutable.x|' is a mutable property that could have been changed by this time">x</error>
|
||||||
}
|
}
|
||||||
if (xx is String) {
|
if (xx is String) {
|
||||||
return <error descr="[RETURN_TYPE_MISMATCH] Return type mismatch: expected kotlin/String, actual kotlin/String?">xx</error>
|
return <error descr="[SMARTCAST_IMPOSSIBLE] Smart cast to 'kotlin/String' is impossible, because 'this@R|/Mutable|.R|/Mutable.xx|' is a property that has open or custom getter">xx</error>
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -248,7 +248,7 @@ class Mutable(var x: String?) {
|
|||||||
fun bar(other: Mutable): String {
|
fun bar(other: Mutable): String {
|
||||||
var y = other
|
var y = other
|
||||||
if (y.x is String) {
|
if (y.x is String) {
|
||||||
return <error descr="[RETURN_TYPE_MISMATCH] Return type mismatch: expected kotlin/String, actual kotlin/String?">y.x</error>
|
return <error descr="[SMARTCAST_IMPOSSIBLE] Smart cast to 'kotlin/String' is impossible, because 'R|<local>/y|.R|/Mutable.x|' is a mutable property that could have been changed by this time">y.x</error>
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user