FIR IDE: Add isDefinitelyNull() and isDefinitelyNotNull() to
KtExpressionTypeProvider.
This commit is contained in:
committed by
teamcityserver
parent
c6427d57f1
commit
97ea901507
+33
-1
@@ -13,8 +13,9 @@ import org.jetbrains.kotlin.psi.KtExpression
|
||||
abstract class KtExpressionTypeProvider : KtAnalysisSessionComponent() {
|
||||
abstract fun getReturnTypeForKtDeclaration(declaration: KtDeclaration): KtType
|
||||
abstract fun getKtExpressionType(expression: KtExpression): KtType
|
||||
|
||||
abstract fun getExpectedType(expression: PsiElement): KtType?
|
||||
abstract fun isDefinitelyNull(expression: KtExpression): Boolean
|
||||
abstract fun isDefinitelyNotNull(expression: KtExpression): Boolean
|
||||
}
|
||||
|
||||
interface KtExpressionTypeProviderMixIn : KtAnalysisSessionMixIn {
|
||||
@@ -30,4 +31,35 @@ interface KtExpressionTypeProviderMixIn : KtAnalysisSessionMixIn {
|
||||
*/
|
||||
fun PsiElement.getExpectedType(): KtType? =
|
||||
analysisSession.expressionTypeProvider.getExpectedType(this)
|
||||
|
||||
/**
|
||||
* Returns `true` if this expression is definitely null, based on declared nullability and smart cast types derived from
|
||||
* data-flow analysis facts. Examples:
|
||||
* ```
|
||||
* fun <T : Any> foo(t: T, nt: T?, s: String, ns: String?) {
|
||||
* t // t.isDefinitelyNull() == false && t.isDefinitelyNotNull() == true
|
||||
* nt // nt.isDefinitelyNull() == false && nt.isDefinitelyNotNull() == false
|
||||
* s // s.isDefinitelyNull() == false && s.isDefinitelyNotNull() == true
|
||||
* ns // ns.isDefinitelyNull() == false && ns.isDefinitelyNotNull() == false
|
||||
*
|
||||
* if (ns != null) {
|
||||
* ns // ns.isDefinitelyNull() == false && ns.isDefinitelyNotNull() == true
|
||||
* } else {
|
||||
* ns // ns.isDefinitelyNull() == true && ns.isDefinitelyNotNull() == false
|
||||
* }
|
||||
*
|
||||
* ns!! // From this point on: ns.isDefinitelyNull() == false && ns.isDefinitelyNotNull() == true
|
||||
* }
|
||||
* ```
|
||||
* Note that only nullability from "stable" smart cast types is considered. The
|
||||
* [spec](https://kotlinlang.org/spec/type-inference.html#smart-cast-sink-stability) provides an explanation on smart cast stability.
|
||||
*/
|
||||
fun KtExpression.isDefinitelyNull(): Boolean =
|
||||
analysisSession.expressionTypeProvider.isDefinitelyNull(this)
|
||||
|
||||
/**
|
||||
* Returns `true` if this expression is definitely not null. See [isDefinitelyNull] for examples.
|
||||
*/
|
||||
fun KtExpression.isDefinitelyNotNull(): Boolean =
|
||||
analysisSession.expressionTypeProvider.isDefinitelyNotNull(this)
|
||||
}
|
||||
|
||||
+30
-6
@@ -7,24 +7,22 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.components
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||
import org.jetbrains.kotlin.fir.expressions.argumentMapping
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassErrorType
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFir
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirOfType
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirSafe
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtExpressionTypeProvider
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtErrorType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.withValidityAssertion
|
||||
@@ -142,6 +140,30 @@ internal class KtFirExpressionTypeProvider(
|
||||
|
||||
private fun PsiElement.isIfCondition() =
|
||||
unwrapQualified<KtIfExpression> { ifExpr, cond -> ifExpr.condition == cond } != null
|
||||
|
||||
override fun isDefinitelyNull(expression: KtExpression): Boolean =
|
||||
getDefiniteNullability(expression) == DefiniteNullability.DEFINITELY_NULL
|
||||
|
||||
override fun isDefinitelyNotNull(expression: KtExpression): Boolean =
|
||||
getDefiniteNullability(expression) == DefiniteNullability.DEFINITELY_NOT_NULL
|
||||
|
||||
private fun getDefiniteNullability(expression: KtExpression): DefiniteNullability = withValidityAssertion {
|
||||
// TODO: Check stability of smartcast (pending PR 4382)
|
||||
return when (val fir = expression.getOrBuildFir(analysisSession.firResolveState)) {
|
||||
is FirExpressionWithSmartcastToNull -> DefiniteNullability.DEFINITELY_NULL
|
||||
is FirExpression -> {
|
||||
// Note: This includes FirExpressionWithSmartcast
|
||||
with(analysisSession.rootModuleSession.typeContext) {
|
||||
if (!fir.typeRef.coneType.isNullableType()) {
|
||||
DefiniteNullability.DEFINITELY_NOT_NULL
|
||||
} else {
|
||||
DefiniteNullability.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> DefiniteNullability.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class KtCallWithArgument(val call: KtCallExpression, val argument: KtExpression)
|
||||
@@ -165,4 +187,6 @@ private val PsiElement.nonContainerParent: PsiElement?
|
||||
get() = when (val parent = parent) {
|
||||
is KtContainerNode -> parent.parent
|
||||
else -> parent
|
||||
}
|
||||
}
|
||||
|
||||
private enum class DefiniteNullability { DEFINITELY_NULL, DEFINITELY_NOT_NULL, UNKNOWN }
|
||||
|
||||
Reference in New Issue
Block a user