FIR checkers: report SMARTCAST_IMPOSSIBLE
This commit is contained in:
committed by
Dmitriy Novozhilov
parent
62f7e8f71f
commit
84334b087c
@@ -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
@@ -465,6 +465,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") {
|
||||||
|
|||||||
@@ -303,6 +303,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
@@ -273,6 +273,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
|
||||||
@@ -634,6 +635,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
@@ -46,6 +46,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 })
|
||||||
}
|
}
|
||||||
@@ -134,8 +143,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
|
||||||
@@ -166,9 +175,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)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.ir.util.classId
|
|||||||
import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded
|
import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded
|
||||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||||
|
import org.jetbrains.kotlin.types.SmartcastStability
|
||||||
|
|
||||||
class Fir2IrImplicitCastInserter(
|
class Fir2IrImplicitCastInserter(
|
||||||
private val components: Fir2IrComponents,
|
private val components: Fir2IrComponents,
|
||||||
|
|||||||
@@ -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(smartcastType, 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,
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ 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.AbstractTypeChecker
|
||||||
|
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)
|
||||||
@@ -105,6 +107,19 @@ object CheckDispatchReceiver : ResolutionStage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (explicitReceiverExpression is FirExpressionWithSmartcast && explicitReceiverExpression.smartcastStability != SmartcastStability.STABLE_VALUE) {
|
||||||
|
val expectedDispatchReceiverType = (candidate.symbol.fir as? FirCallableMemberDeclaration)?.dispatchReceiverType
|
||||||
|
if (expectedDispatchReceiverType != null &&
|
||||||
|
!AbstractTypeChecker.isSubtypeOf(
|
||||||
|
context.session.typeContext,
|
||||||
|
explicitReceiverExpression.originalType.coneType,
|
||||||
|
expectedDispatchReceiverType
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
sink.yieldDiagnostic(UnstableSmartCast(explicitReceiverExpression, expectedDispatchReceiverType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val dispatchReceiverValueType = candidate.dispatchReceiverValue?.type ?: return
|
val dispatchReceiverValueType = candidate.dispatchReceiverValue?.type ?: return
|
||||||
|
|
||||||
if (!AbstractNullabilityChecker.isSubtypeOfAny(context.session.typeContext, dispatchReceiverValueType)) {
|
if (!AbstractNullabilityChecker.isSubtypeOfAny(context.session.typeContext, dispatchReceiverValueType)) {
|
||||||
|
|||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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.declarations.FirCallableMemberDeclaration
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||||
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
|
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 smartcastType: ConeKotlinType,
|
||||||
|
private val smartcastScope: FirTypeScope,
|
||||||
|
private val originalScope: FirTypeScope
|
||||||
|
) : FirTypeScope(), FirContainingNamesAwareScope {
|
||||||
|
private val scopes = listOf(smartcastScope, originalScope)
|
||||||
|
override fun processClassifiersByNameWithSubstitution(
|
||||||
|
name: Name,
|
||||||
|
processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit
|
||||||
|
) {
|
||||||
|
for (scope in scopes) {
|
||||||
|
scope.processClassifiersByNameWithSubstitution(name, processor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <T> processComposite(
|
||||||
|
process: FirTypeScope.(Name, (T) -> Unit) -> Unit,
|
||||||
|
name: Name,
|
||||||
|
noinline processor: (T) -> Unit
|
||||||
|
) {
|
||||||
|
val unique = mutableSetOf<T>()
|
||||||
|
for (scope in scopes) {
|
||||||
|
scope.process(name) {
|
||||||
|
if (unique.add(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 {
|
||||||
|
originalScope.process(name) { symbol, firTypeScope ->
|
||||||
|
processor(symbol, firTypeScope)
|
||||||
|
}.let { if (it == ProcessorAction.STOP) return ProcessorAction.STOP }
|
||||||
|
|
||||||
|
smartcastScope.process(name) { symbol, firTypeScope ->
|
||||||
|
// Only process the symbol if the dispatcher type is exactly the smartcast type. This way, we don't add any additional
|
||||||
|
// symbols that already exists in the original scope.
|
||||||
|
if ((symbol.fir as? FirCallableMemberDeclaration)?.dispatchReceiverType == smartcastType) {
|
||||||
|
processor(symbol, firTypeScope)
|
||||||
|
} else {
|
||||||
|
ProcessorAction.NEXT
|
||||||
|
}
|
||||||
|
}.let { if (it == ProcessorAction.STOP) return ProcessorAction.STOP }
|
||||||
|
return ProcessorAction.NEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -15,7 +15,7 @@ enum class CandidateApplicability {
|
|||||||
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 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
@@ -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,
|
||||||
@@ -218,7 +218,7 @@ class ArgumentTypeMismatchDiagnostic(
|
|||||||
val expectedType: UnwrappedType,
|
val expectedType: UnwrappedType,
|
||||||
val actualType: UnwrappedType,
|
val actualType: UnwrappedType,
|
||||||
val expressionArgument: ExpressionKotlinCallArgument
|
val expressionArgument: ExpressionKotlinCallArgument
|
||||||
) : KotlinCallDiagnostic(MAY_THROW_RUNTIME_ERROR) {
|
) : KotlinCallDiagnostic(UNSTABLE_SMARTCAST) {
|
||||||
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> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-37
@@ -1,37 +0,0 @@
|
|||||||
// !CHECK_TYPE
|
|
||||||
|
|
||||||
interface A {
|
|
||||||
fun foo(): CharSequence?
|
|
||||||
fun baz(x: Any) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface B {
|
|
||||||
fun foo(): String
|
|
||||||
fun baz(x: Int): String =""
|
|
||||||
fun baz(x: Int, y: Int) {}
|
|
||||||
|
|
||||||
fun foobar(): CharSequence?
|
|
||||||
}
|
|
||||||
|
|
||||||
interface C {
|
|
||||||
fun foo(): String
|
|
||||||
fun baz(x: Int): String =""
|
|
||||||
fun baz(x: Int, y: Int) {}
|
|
||||||
|
|
||||||
fun foobar(): String
|
|
||||||
}
|
|
||||||
|
|
||||||
var x: A = null!!
|
|
||||||
|
|
||||||
fun test() {
|
|
||||||
x.foo().checkType { _<CharSequence?>() }
|
|
||||||
|
|
||||||
if (x is B && x is C) {
|
|
||||||
x.foo().checkType { _<CharSequence?>() }
|
|
||||||
x.baz("")
|
|
||||||
x.baz(1).checkType { _<Unit>() }
|
|
||||||
x.baz(1, <!TOO_MANY_ARGUMENTS!>2<!>)
|
|
||||||
|
|
||||||
x.<!UNRESOLVED_REFERENCE!>foobar<!>().checkType { <!INAPPLICABLE_CANDIDATE!>_<!><String>() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+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.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.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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+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<!>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -1382,6 +1382,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
@@ -978,6 +978,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
@@ -1579,6 +1579,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