FIR checkers: report SMARTCAST_IMPOSSIBLE

This commit is contained in:
Tianyu Geng
2021-05-08 20:51:02 -07:00
committed by Dmitriy Novozhilov
parent 62f7e8f71f
commit 84334b087c
51 changed files with 308 additions and 297 deletions
@@ -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"];
@@ -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
}
}
@@ -465,6 +465,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") {
@@ -303,6 +303,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)
@@ -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
)
}
}
}
}
@@ -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.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
@@ -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(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,
@@ -46,6 +46,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 })
}
@@ -134,8 +143,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
@@ -166,9 +175,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)
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.ir.util.classId
import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.SmartcastStability
class Fir2IrImplicitCastInserter(
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.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(smartcastType, 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,
@@ -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,
@@ -26,6 +26,8 @@ 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.AbstractTypeChecker
import org.jetbrains.kotlin.types.SmartcastStability
abstract class ResolutionStage {
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
if (!AbstractNullabilityChecker.isSubtypeOfAny(context.session.typeContext, dispatchReceiverValueType)) {
@@ -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 }
}
}
@@ -15,7 +15,7 @@ enum class CandidateApplicability {
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
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
@@ -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,
@@ -218,7 +218,7 @@ class ArgumentTypeMismatchDiagnostic(
val expectedType: UnwrappedType,
val actualType: UnwrappedType,
val expressionArgument: ExpressionKotlinCallArgument
) : KotlinCallDiagnostic(MAY_THROW_RUNTIME_ERROR) {
) : KotlinCallDiagnostic(UNSTABLE_SMARTCAST) {
override fun report(reporter: DiagnosticReporter) {
reporter.onCallArgument(expressionArgument, this)
}
@@ -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)
@@ -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,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
interface Ctx
@@ -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,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> {
}
@@ -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,3 +1,4 @@
// FIR_IDENTICAL
// !CHECK_TYPE
interface A {
@@ -10,6 +10,6 @@ fun failsWithClassCastException() {
val sometimesNotInt: Any? by AlternatingDelegate()
if (sometimesNotInt is Int) {
sometimesNotInt.inc()
<!SMARTCAST_IMPOSSIBLE!>sometimesNotInt<!>.inc()
}
}
}
@@ -10,6 +10,6 @@ fun failsWithClassCastException() {
val sometimesNotInt: Any? by AlternatingDelegate()
if (sometimesNotInt is Int) {
sometimesNotInt.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 ""
}
}
@@ -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() {}
}
@@ -28,7 +28,7 @@ class UnstableReceiver {
x<!UNSAFE_CALL!>.<!>inc()
}
else {
x<!UNSAFE_CALL!>.<!>dec()
<!SMARTCAST_IMPOSSIBLE!>x<!>.dec()
}
}
}
@@ -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<!>
}
}
@@ -1382,6 +1382,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),
@@ -978,6 +978,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
@@ -1579,6 +1579,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
View File
@@ -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 ""
}