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|"];
|
||||
177 [label="Access variable R|<local>/q|"];
|
||||
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|"];
|
||||
181 [label="Access variable R|/Q.data|"];
|
||||
182 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
||||
183 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||
182 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||
183 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||
184 [label="Exit block"];
|
||||
}
|
||||
185 [label="Exit when branch result"];
|
||||
@@ -566,11 +566,11 @@ digraph nullability_kt {
|
||||
208 [label="Access variable R|/Q.data|"];
|
||||
209 [label="Access variable R|<local>/q|"];
|
||||
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|"];
|
||||
213 [label="Access variable R|/Q.data|"];
|
||||
214 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
||||
215 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||
214 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||
215 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||
216 [label="Exit block"];
|
||||
}
|
||||
217 [label="Exit function test_6" style="filled" fillcolor=red];
|
||||
@@ -1280,11 +1280,11 @@ digraph nullability_kt {
|
||||
490 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
||||
491 [label="Access variable R|<local>/q|"];
|
||||
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|"];
|
||||
495 [label="Access variable R|/QImplWithCustomGetter.data|"];
|
||||
496 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
||||
497 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||
496 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||
497 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||
498 [label="Exit block"];
|
||||
}
|
||||
499 [label="Exit when branch result"];
|
||||
@@ -1363,11 +1363,11 @@ digraph nullability_kt {
|
||||
524 [label="Access variable R|/QImplMutable.data|"];
|
||||
525 [label="Access variable R|<local>/q|"];
|
||||
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|"];
|
||||
529 [label="Access variable R|/QImplMutable.data|"];
|
||||
530 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
|
||||
531 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||
530 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
|
||||
531 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
|
||||
532 [label="Exit block"];
|
||||
}
|
||||
533 [label="Exit when branch result"];
|
||||
|
||||
+8
-8
@@ -102,8 +102,8 @@ FILE: nullability.kt
|
||||
when () {
|
||||
!=(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|.<Inapplicable(UNSAFE_CALL): /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|<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| {
|
||||
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|.<Inapplicable(UNSAFE_CALL): /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|<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| {
|
||||
when () {
|
||||
@@ -216,8 +216,8 @@ FILE: nullability.kt
|
||||
when () {
|
||||
!=(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|.<Inapplicable(UNSAFE_CALL): /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|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,8 +226,8 @@ FILE: nullability.kt
|
||||
when () {
|
||||
!=(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|.<Inapplicable(UNSAFE_CALL): /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|<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.
|
||||
if (q?.data?.s?.inc() != null) {
|
||||
q.data // good
|
||||
q.data<!UNSAFE_CALL!>.<!>s // should be bad
|
||||
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
|
||||
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // 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?.s?.inc() ?: return
|
||||
q.data // good
|
||||
q.data<!UNSAFE_CALL!>.<!>s // should be bad
|
||||
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
|
||||
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // should be bad
|
||||
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s.inc() // should be bad
|
||||
}
|
||||
|
||||
fun test_7(q: Q?) {
|
||||
@@ -162,8 +162,8 @@ fun test_12(q: QImplWithCustomGetter?) {
|
||||
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
|
||||
if (q?.data?.s?.inc() != null) {
|
||||
q.data // good
|
||||
q.data<!UNSAFE_CALL!>.<!>s // should be bad
|
||||
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
|
||||
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // 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.
|
||||
if (q?.data?.s?.inc() != null) {
|
||||
q.data // good
|
||||
q.data<!UNSAFE_CALL!>.<!>s // should be bad
|
||||
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
|
||||
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // 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 {
|
||||
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
|
||||
|
||||
+6
@@ -26663,6 +26663,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
||||
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("unneededUnstableSmartcast.kt")
|
||||
public void testUnneededUnstableSmartcast() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+6
@@ -475,6 +475,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
parameter<Variance>("variance")
|
||||
parameter<ConeKotlinType>("containingType")
|
||||
}
|
||||
|
||||
val SMARTCAST_IMPOSSIBLE by error<KtExpression> {
|
||||
parameter<ConeKotlinType>("desiredType")
|
||||
parameter<FirExpression>("subject")
|
||||
parameter<String>("description")
|
||||
}
|
||||
}
|
||||
|
||||
val REFLECTION by object : DiagnosticGroup("Reflection") {
|
||||
|
||||
@@ -309,6 +309,7 @@ object FirErrors {
|
||||
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_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
|
||||
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.FirErrors.NULL_FOR_NONNULL_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
|
||||
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.isExhaustive
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.types.SmartcastStability
|
||||
|
||||
object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
|
||||
override fun check(expression: FirReturnExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
@@ -36,10 +39,23 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
|
||||
if (resultExpression.isNullLiteral && functionReturnType.nullability == ConeNullability.NOT_NULL) {
|
||||
reporter.reportOn(resultExpression.source, NULL_FOR_NONNULL_TYPE, context)
|
||||
} else {
|
||||
reporter.report(
|
||||
RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement),
|
||||
context
|
||||
)
|
||||
if (resultExpression is FirExpressionWithSmartcast && resultExpression.smartcastStability != SmartcastStability.STABLE_VALUE &&
|
||||
isSubtypeForTypeMismatch(typeContext, subtype = resultExpression.smartcastType.coneType, supertype = functionReturnType)
|
||||
) {
|
||||
reporter.reportOn(
|
||||
returnExpressionSource,
|
||||
SMARTCAST_IMPOSSIBLE,
|
||||
functionReturnType,
|
||||
resultExpression,
|
||||
resultExpression.smartcastStability.description,
|
||||
context
|
||||
)
|
||||
} else {
|
||||
reporter.report(
|
||||
RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement),
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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.SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SINGLETON_IN_SUPERTYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPES_FOR_ANNOTATION_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_APPEARS_TWICE
|
||||
@@ -654,6 +655,13 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
map.put(UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, "Extension function type can not be used as an upper bound")
|
||||
map.put(INCOMPATIBLE_TYPES, "Incompatible types: {0} and {1}", RENDER_TYPE, RENDER_TYPE)
|
||||
map.put(INCOMPATIBLE_TYPES_WARNING, "Potentially incompatible types: {0} and {1}", RENDER_TYPE, RENDER_TYPE)
|
||||
map.put(
|
||||
SMARTCAST_IMPOSSIBLE,
|
||||
"Smart cast to ''{0}'' is impossible, because ''{1}'' is a {2}",
|
||||
RENDER_TYPE,
|
||||
FIR,
|
||||
TO_STRING
|
||||
)
|
||||
|
||||
map.put(
|
||||
TYPE_VARIANCE_CONFLICT,
|
||||
|
||||
+25
-4
@@ -47,6 +47,15 @@ private fun ConeDiagnostic.toFirDiagnostic(
|
||||
val candidate = candidates.first { it.currentApplicability == CandidateApplicability.UNSAFE_CALL }
|
||||
val unsafeCall = candidate.diagnostics.firstIsInstance<UnsafeCall>()
|
||||
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 {
|
||||
FirErrors.NONE_APPLICABLE.on(source, this.candidates.map { it.symbol })
|
||||
}
|
||||
@@ -142,8 +151,8 @@ private fun mapInapplicableCandidateError(
|
||||
source: FirSourceElement,
|
||||
qualifiedAccessSource: FirSourceElement?,
|
||||
): List<FirDiagnostic<FirSourceElement>> {
|
||||
// TODO: Need to distinguish SMARTCAST_IMPOSSIBLE
|
||||
return diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause ->
|
||||
val genericDiagnostic = FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)
|
||||
val diagnostics = diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause ->
|
||||
when (rootCause) {
|
||||
is VarargArgumentOutsideParentheses -> FirErrors.VARARG_OUTSIDE_PARENTHESES.on(
|
||||
rootCause.argument.source ?: qualifiedAccessSource
|
||||
@@ -174,9 +183,21 @@ private fun mapInapplicableCandidateError(
|
||||
is InfixCallOfNonInfixFunction -> FirErrors.INFIX_MODIFIER_REQUIRED.on(source, rootCause.function)
|
||||
is OperatorCallOfNonOperatorFunction ->
|
||||
FirErrors.OPERATOR_MODIFIER_REQUIRED.on(source, rootCause.function, rootCause.function.fir.name.asString())
|
||||
else -> null
|
||||
is UnstableSmartCast -> FirErrors.SMARTCAST_IMPOSSIBLE.on(
|
||||
rootCause.argument.source,
|
||||
rootCause.targetType,
|
||||
rootCause.argument,
|
||||
rootCause.argument.smartcastStability.description
|
||||
)
|
||||
else -> genericDiagnostic
|
||||
}
|
||||
}.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) }
|
||||
}.distinct()
|
||||
return if (diagnostics.size > 1) {
|
||||
// If there are more specific diagnostics, filter out the generic diagnostic.
|
||||
diagnostics.filter { it != genericDiagnostic }
|
||||
} else {
|
||||
diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
|
||||
@@ -10,9 +10,11 @@ import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolved
|
||||
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
|
||||
import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope
|
||||
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirScopeWithFakeOverrideTypeCalculator
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker
|
||||
@@ -25,6 +27,25 @@ import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.types.SmartcastStability
|
||||
|
||||
fun FirExpressionWithSmartcast.smartcastScope(
|
||||
useSiteSession: FirSession,
|
||||
scopeSession: ScopeSession
|
||||
): FirTypeScope? {
|
||||
val smartcastType = smartcastType.coneType
|
||||
val smartcastScope = smartcastType.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
|
||||
if (smartcastStability == SmartcastStability.STABLE_VALUE) {
|
||||
return smartcastScope
|
||||
}
|
||||
val originalScope = originalType.coneType.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
|
||||
?: return smartcastScope
|
||||
|
||||
if (smartcastScope == null) {
|
||||
return originalScope
|
||||
}
|
||||
return FirUnstableSmartcastTypeScope(smartcastScope, originalScope)
|
||||
}
|
||||
|
||||
fun ConeKotlinType.scope(
|
||||
useSiteSession: FirSession,
|
||||
|
||||
@@ -30,9 +30,9 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.SmartcastStability
|
||||
import org.jetbrains.kotlin.types.model.CaptureStatus
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
@@ -358,17 +358,7 @@ private fun checkApplicabilityForArgumentType(
|
||||
) {
|
||||
if (expectedType == null) return
|
||||
|
||||
fun unstableSmartCastOrSubtypeError(
|
||||
unstableType: ConeKotlinType?,
|
||||
actualExpectedType: ConeKotlinType,
|
||||
position: ConstraintPosition
|
||||
): ResolutionDiagnostic {
|
||||
if (unstableType != null) {
|
||||
if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, actualExpectedType, position)) {
|
||||
return UnstableSmartCast.ResolutionError(argument, unstableType)
|
||||
}
|
||||
}
|
||||
|
||||
fun subtypeError(actualExpectedType: ConeKotlinType): ResolutionDiagnostic {
|
||||
if (argument.isNullLiteral && actualExpectedType.nullability == ConeNullability.NOT_NULL) {
|
||||
return NullForNotNullType(argument)
|
||||
}
|
||||
@@ -406,14 +396,17 @@ private fun checkApplicabilityForArgumentType(
|
||||
}
|
||||
|
||||
if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) {
|
||||
val smartcastExpression = argument as? FirExpressionWithSmartcast
|
||||
if (smartcastExpression != null && smartcastExpression.smartcastStability != SmartcastStability.STABLE_VALUE) {
|
||||
val unstableType = smartcastExpression.smartcastType.coneType
|
||||
if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, expectedType, position)) {
|
||||
sink.reportDiagnostic(UnstableSmartCast(smartcastExpression, expectedType))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!isReceiver) {
|
||||
sink.reportDiagnosticIfNotNull(
|
||||
unstableSmartCastOrSubtypeError(
|
||||
unstableType = null, // TODO: handle unstable smartcasts
|
||||
expectedType,
|
||||
position
|
||||
)
|
||||
)
|
||||
sink.reportDiagnosticIfNotNull(subtypeError(expectedType))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.calls
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
|
||||
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionWithSmartcast
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression
|
||||
@@ -16,6 +17,7 @@ import org.jetbrains.kotlin.fir.renderWithType
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.constructType
|
||||
import org.jetbrains.kotlin.fir.resolve.scope
|
||||
import org.jetbrains.kotlin.fir.resolve.smartcastScope
|
||||
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
|
||||
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
|
||||
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||
@@ -57,7 +59,11 @@ abstract class AbstractExplicitReceiverValue<E : FirExpression> : AbstractExplic
|
||||
|
||||
class ExpressionReceiverValue(
|
||||
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<*>>(
|
||||
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.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
|
||||
import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
@@ -74,21 +75,7 @@ object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(RES
|
||||
|
||||
object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED)
|
||||
|
||||
sealed class UnstableSmartCast(
|
||||
val argument: FirExpression,
|
||||
val targetType: ConeKotlinType,
|
||||
applicability: CandidateApplicability
|
||||
) : ResolutionDiagnostic(applicability) {
|
||||
class ResolutionError(
|
||||
argument: FirExpression,
|
||||
targetType: ConeKotlinType,
|
||||
) : UnstableSmartCast(argument, targetType, MAY_THROW_RUNTIME_ERROR)
|
||||
|
||||
class DiagnosticError(
|
||||
argument: FirExpression,
|
||||
targetType: ConeKotlinType,
|
||||
) : UnstableSmartCast(argument, targetType, RESOLVED_WITH_ERROR)
|
||||
}
|
||||
class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType) : ResolutionDiagnostic(UNSTABLE_SMARTCAST)
|
||||
|
||||
class ArgumentTypeMismatch(
|
||||
val expectedType: ConeKotlinType,
|
||||
|
||||
+12
-1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.*
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope
|
||||
import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
@@ -26,6 +27,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
|
||||
import org.jetbrains.kotlin.types.AbstractNullabilityChecker
|
||||
import org.jetbrains.kotlin.types.SmartcastStability
|
||||
|
||||
abstract class ResolutionStage {
|
||||
abstract suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext)
|
||||
@@ -106,8 +108,17 @@ object CheckDispatchReceiver : ResolutionStage() {
|
||||
}
|
||||
|
||||
val dispatchReceiverValueType = candidate.dispatchReceiverValue?.type ?: return
|
||||
val isReceiverNullable = !AbstractNullabilityChecker.isSubtypeOfAny(context.session.typeContext, dispatchReceiverValueType)
|
||||
|
||||
if (!AbstractNullabilityChecker.isSubtypeOfAny(context.session.typeContext, dispatchReceiverValueType)) {
|
||||
val isCandidateFromUnstableSmartcast =
|
||||
(candidate.originScope as? FirUnstableSmartcastTypeScope)?.isSymbolFromUnstableSmartcast(candidate.symbol) == true
|
||||
|
||||
if (explicitReceiverExpression is FirExpressionWithSmartcast &&
|
||||
explicitReceiverExpression.smartcastStability != SmartcastStability.STABLE_VALUE &&
|
||||
(isCandidateFromUnstableSmartcast || isReceiverNullable)
|
||||
) {
|
||||
sink.yieldDiagnostic(UnstableSmartCast(explicitReceiverExpression, explicitReceiverExpression.smartcastType.coneType))
|
||||
} else if (isReceiverNullable) {
|
||||
sink.yieldDiagnostic(UnsafeCall(dispatchReceiverValueType))
|
||||
}
|
||||
}
|
||||
|
||||
+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 -> {
|
||||
require(diagnostic is ArgumentTypeMismatchDiagnostic)
|
||||
ArgumentNullabilityMismatchDiagnostic::class.java -> {
|
||||
require(diagnostic is ArgumentNullabilityMismatchDiagnostic)
|
||||
val expression = callArgument.safeAs<PSIKotlinCallArgument>()?.valueArgument?.getArgumentExpression()?.let {
|
||||
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)
|
||||
IMPOSSIBLE_TO_GENERATE, // access to outer class from nested
|
||||
RUNTIME_ERROR, // problems with visibility
|
||||
UNSAFE_CALL, // receiver nullability doesn't match
|
||||
MAY_THROW_RUNTIME_ERROR, // unstable smart cast
|
||||
UNSAFE_CALL, // receiver or argument nullability doesn't match
|
||||
UNSTABLE_SMARTCAST, // unstable smart cast
|
||||
CONVENTION_ERROR, // missing infix, operator etc
|
||||
RESOLVED_LOW_PRIORITY,
|
||||
RESOLVED_NEED_PRESERVE_COMPATIBILITY, // call resolved successfully, but using new features that changes resolve
|
||||
|
||||
+2
-2
@@ -85,7 +85,7 @@ private fun checkExpressionArgument(
|
||||
if (argumentType.isMarkedNullable) {
|
||||
if (csBuilder.addSubtypeConstraintIfCompatible(argumentType, actualExpectedType, position)) return null
|
||||
if (csBuilder.addSubtypeConstraintIfCompatible(argumentType.makeNotNullable(), actualExpectedType, position)) {
|
||||
return ArgumentTypeMismatchDiagnostic(actualExpectedType, argumentType, expressionArgument)
|
||||
return ArgumentNullabilityMismatchDiagnostic(actualExpectedType, argumentType, expressionArgument)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ private fun checkExpressionArgument(
|
||||
|
||||
// Used only for arguments with @NotNull annotation
|
||||
if (expectedType is NotNullTypeVariable && argumentType.isMarkedNullable) {
|
||||
diagnosticsHolder.addDiagnostic(ArgumentTypeMismatchDiagnostic(expectedType, argumentType, expressionArgument))
|
||||
diagnosticsHolder.addDiagnostic(ArgumentNullabilityMismatchDiagnostic(expectedType, argumentType, expressionArgument))
|
||||
}
|
||||
|
||||
if (expressionArgument.isSafeCall) {
|
||||
|
||||
+3
-3
@@ -161,7 +161,7 @@ sealed class UnstableSmartCast(
|
||||
class UnstableSmartCastResolutionError(
|
||||
argument: ExpressionKotlinCallArgument,
|
||||
targetType: UnwrappedType,
|
||||
) : UnstableSmartCast(argument, targetType, MAY_THROW_RUNTIME_ERROR)
|
||||
) : UnstableSmartCast(argument, targetType, UNSTABLE_SMARTCAST)
|
||||
|
||||
class UnstableSmartCastDiagnosticError(
|
||||
argument: ExpressionKotlinCallArgument,
|
||||
@@ -214,11 +214,11 @@ class NonApplicableCallForBuilderInferenceDiagnostic(val kotlinCall: KotlinCall)
|
||||
}
|
||||
}
|
||||
|
||||
class ArgumentTypeMismatchDiagnostic(
|
||||
class ArgumentNullabilityMismatchDiagnostic(
|
||||
val expectedType: UnwrappedType,
|
||||
val actualType: UnwrappedType,
|
||||
val expressionArgument: ExpressionKotlinCallArgument
|
||||
) : KotlinCallDiagnostic(MAY_THROW_RUNTIME_ERROR) {
|
||||
) : KotlinCallDiagnostic(UNSAFE_CALL) {
|
||||
override fun report(reporter: DiagnosticReporter) {
|
||||
reporter.onCallArgument(expressionArgument, this)
|
||||
}
|
||||
|
||||
+1
-1
@@ -120,7 +120,7 @@ object ErrorDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED) // todo discus
|
||||
object LowPriorityDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
|
||||
object DynamicDescriptorDiagnostic : ResolutionDiagnostic(RESOLVED_LOW_PRIORITY)
|
||||
object ResolvedUsingNewFeatures : ResolutionDiagnostic(RESOLVED_NEED_PRESERVE_COMPATIBILITY)
|
||||
object UnstableSmartCastDiagnostic : ResolutionDiagnostic(MAY_THROW_RUNTIME_ERROR)
|
||||
object UnstableSmartCastDiagnostic : ResolutionDiagnostic(UNSTABLE_SMARTCAST)
|
||||
object HiddenExtensionRelatedToDynamicTypes : ResolutionDiagnostic(HIDDEN)
|
||||
object HiddenDescriptor : ResolutionDiagnostic(HIDDEN)
|
||||
|
||||
|
||||
-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
|
||||
|
||||
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
|
||||
|
||||
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> {
|
||||
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> {
|
||||
}
|
||||
|
||||
|
||||
+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
|
||||
|
||||
interface A {
|
||||
|
||||
+1
-1
@@ -10,6 +10,6 @@ fun failsWithClassCastException() {
|
||||
val sometimesNotInt: Any? by AlternatingDelegate()
|
||||
|
||||
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()
|
||||
|
||||
if (sometimesNotInt is Int) {
|
||||
sometimesNotInt.<!UNRESOLVED_REFERENCE!>inc<!>()
|
||||
<!SMARTCAST_IMPOSSIBLE!>sometimesNotInt<!>.inc()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ sealed class My(open val x: Int?) {
|
||||
init {
|
||||
if (x != null) {
|
||||
// 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?) {
|
||||
fun foo(): String {
|
||||
if (y != null) return <!RETURN_TYPE_MISMATCH!>y<!>
|
||||
if (y != null) return <!SMARTCAST_IMPOSSIBLE!>y<!>
|
||||
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 {
|
||||
private val x : String? = null
|
||||
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 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
|
||||
// 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 var x : String? = null
|
||||
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?) {
|
||||
init {
|
||||
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 {
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ class UnstableReceiver {
|
||||
x<!UNSAFE_CALL!>.<!>inc()
|
||||
}
|
||||
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
|
||||
// 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 }
|
||||
// OTHER_ERROR
|
||||
// UNSAFE_CALL_ERROR
|
||||
// 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
|
||||
}
|
||||
@@ -32,7 +32,7 @@ fun <T : Any> valuesTNotNull(map: MutableMap<Int, T>, newValue: T) {
|
||||
|
||||
fun <T : Any> valuesTNullable(map: MutableMap<Int, T?>, newValue: T?) {
|
||||
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
|
||||
// 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 }
|
||||
|
||||
@@ -3,7 +3,7 @@ import java.util.*
|
||||
fun use() {
|
||||
val x: String? = "x"
|
||||
Optional.of(x)
|
||||
// OTHER_ERROR
|
||||
// UNSAFE_CALL_ERROR
|
||||
// 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
|
||||
|
||||
|
||||
Generated
+6
@@ -26753,6 +26753,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("unneededUnstableSmartcast.kt")
|
||||
public void testUnneededUnstableSmartcast() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+2
-2
@@ -9,7 +9,6 @@ import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1
|
||||
import org.jetbrains.kotlin.checkers.utils.TypeOfCall
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
|
||||
@@ -43,6 +42,7 @@ import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.test.utils.AbstractTwoAttributesMetaInfoProcessor
|
||||
import org.jetbrains.kotlin.types.SmartcastStability
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
@@ -176,7 +176,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
|
||||
diagnosedRangesToDiagnosticNames: Map<IntRange, Set<String>>
|
||||
): FirDiagnosticWithParameters1<FirSourceElement, String>? =
|
||||
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 {
|
||||
|
||||
@@ -33,7 +33,7 @@ fun case_3(x: Int) = ""
|
||||
fun case_3(x: Int?) = 10
|
||||
fun case_3() {
|
||||
if (case_3_prop != null) {
|
||||
val z = case_3(<!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<!>
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ fun case_7(x: Int) = ""
|
||||
fun case_7(x: Int?) = 10
|
||||
fun case_7() {
|
||||
if (case_7_prop != null) {
|
||||
val z = case_7(<!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<!>
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -23320,6 +23320,11 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
public void testStabilityOfSmartcastsAgainstGenericFunctions() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/stabilityOfSmartcastsAgainstGenericFunctions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unneededUnstableSmartcast.kt")
|
||||
public void testUnneededUnstableSmartcast() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/smartCasts/inference/unneededUnstableSmartcast.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/smartCasts/intersectionScope")
|
||||
|
||||
+9
@@ -1411,6 +1411,15 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.SMARTCAST_IMPOSSIBLE) { firDiagnostic ->
|
||||
SmartcastImpossibleImpl(
|
||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||
firDiagnostic.b.source!!.psi as KtExpression,
|
||||
firDiagnostic.c,
|
||||
firDiagnostic as FirPsiDiagnostic<*>,
|
||||
token,
|
||||
)
|
||||
}
|
||||
add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic ->
|
||||
ExtensionInClassReferenceNotAllowedImpl(
|
||||
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration),
|
||||
|
||||
+7
@@ -999,6 +999,13 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
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>() {
|
||||
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
|
||||
abstract val referencedDeclaration: KtCallableSymbol
|
||||
|
||||
+10
@@ -1612,6 +1612,16 @@ internal class TypeVarianceConflictInExpandedTypeImpl(
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class SmartcastImpossibleImpl(
|
||||
override val desiredType: KtType,
|
||||
override val subject: KtExpression,
|
||||
override val description: String,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
override val token: ValidityToken,
|
||||
) : KtFirDiagnostic.SmartcastImpossible(), KtAbstractFirDiagnostic<KtExpression> {
|
||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class ExtensionInClassReferenceNotAllowedImpl(
|
||||
override val referencedDeclaration: KtCallableSymbol,
|
||||
firDiagnostic: FirPsiDiagnostic<*>,
|
||||
|
||||
+4
-4
@@ -234,13 +234,13 @@ class Mutable(var x: String?) {
|
||||
|
||||
fun foo(): 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) {
|
||||
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) {
|
||||
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 ""
|
||||
}
|
||||
@@ -248,7 +248,7 @@ class Mutable(var x: String?) {
|
||||
fun bar(other: Mutable): String {
|
||||
var y = other
|
||||
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 ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user