New J2K: fix NPEs in nullability analysis
#KT-31817 fixed
This commit is contained in:
+12
-8
@@ -30,7 +30,6 @@ internal class BoundTypeStorage(private val analysisAnalysisContext: AnalysisCon
|
|||||||
|
|
||||||
fun boundTypeFor(expression: KtExpression): BoundType =
|
fun boundTypeFor(expression: KtExpression): BoundType =
|
||||||
cache.getOrPut(expression) {
|
cache.getOrPut(expression) {
|
||||||
if (expression is KtParenthesizedExpression) return@getOrPut boundTypeFor(expression.expression!!)
|
|
||||||
val boundType =
|
val boundType =
|
||||||
when (expression) {
|
when (expression) {
|
||||||
is KtParenthesizedExpression -> expression.expression?.let { boundTypeFor(it) }
|
is KtParenthesizedExpression -> expression.expression?.let { boundTypeFor(it) }
|
||||||
@@ -131,13 +130,18 @@ internal class BoundTypeStorage(private val analysisAnalysisContext: AnalysisCon
|
|||||||
val descriptor =
|
val descriptor =
|
||||||
getResolvedCall(bindingContext)?.candidateDescriptor?.original?.safeAs<CallableDescriptor>() ?: return null
|
getResolvedCall(bindingContext)?.candidateDescriptor?.original?.safeAs<CallableDescriptor>() ?: return null
|
||||||
val typeParameters =
|
val typeParameters =
|
||||||
if (this is KtCallElement) {
|
run {
|
||||||
typeArguments.mapIndexed { index, typeArgument ->
|
if (this is KtCallElement) {
|
||||||
//TODO better check
|
typeArguments.mapIndexed { index, typeArgument ->
|
||||||
descriptor.typeParameters[index] to
|
//TODO better check
|
||||||
analysisAnalysisContext.typeElementToTypeVariable.getValue(typeArgument.typeReference?.typeElement!!)
|
val typeParameter = descriptor.typeParameters.getOrNull(index) ?: return@run null
|
||||||
}.toMap()
|
val typeVariable =
|
||||||
} else emptyMap()
|
analysisAnalysisContext.typeElementToTypeVariable[typeArgument.typeReference?.typeElement ?: return@run null]
|
||||||
|
?: return@run null
|
||||||
|
typeParameter to typeVariable
|
||||||
|
}.toMap()
|
||||||
|
} else emptyMap()
|
||||||
|
} ?: emptyMap()
|
||||||
return descriptor.returnType?.toBoundType(contextBoundType, typeParameters)
|
return descriptor.returnType?.toBoundType(contextBoundType, typeParameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-7
@@ -80,7 +80,7 @@ internal class ConstraintsCollector(
|
|||||||
}
|
}
|
||||||
|
|
||||||
expression is KtBinaryExpression && expression.asAssignment() != null -> {
|
expression is KtBinaryExpression && expression.asAssignment() != null -> {
|
||||||
expression.right?.addSubtypeNullabilityConstraint(expression.left!!, ConstraintCameFrom.ASSIGNMENT_TARGET)
|
expression.right?.addSubtypeNullabilityConstraint(expression.left ?: return, ConstraintCameFrom.ASSIGNMENT_TARGET)
|
||||||
}
|
}
|
||||||
|
|
||||||
expression is KtBinaryExpression && expression.isComaprationWithNull() -> {
|
expression is KtBinaryExpression && expression.isComaprationWithNull() -> {
|
||||||
@@ -152,7 +152,7 @@ internal class ConstraintsCollector(
|
|||||||
analysisContext.typeElementToTypeVariable[typeElement]
|
analysisContext.typeElementToTypeVariable[typeElement]
|
||||||
}
|
}
|
||||||
if (loopParameterTypeVariable != null) {
|
if (loopParameterTypeVariable != null) {
|
||||||
val loopRangeBoundType = boundTypeStorage.boundTypeFor(expression.loopRange!!)
|
val loopRangeBoundType = boundTypeStorage.boundTypeFor(expression.loopRange ?: return)
|
||||||
val loopRangeType = expression.loopRange?.getType(expression.analyze()) ?: return
|
val loopRangeType = expression.loopRange?.getType(expression.analyze()) ?: return
|
||||||
val loopRangeItemType = loopRangeType
|
val loopRangeItemType = loopRangeType
|
||||||
.constructor
|
.constructor
|
||||||
@@ -280,7 +280,7 @@ internal class ConstraintsCollector(
|
|||||||
}
|
}
|
||||||
}?.let { type ->
|
}?.let { type ->
|
||||||
boundTypeStorage
|
boundTypeStorage
|
||||||
.boundTypeForType(type, receiverBoundType, callExpression.typeArgumentsDescriptors(descriptor))
|
.boundTypeForType(type, receiverBoundType, callExpression.typeArgumentsDescriptors(descriptor).orEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -299,9 +299,12 @@ internal class ConstraintsCollector(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtCallExpression.typeArgumentsDescriptors(descriptor: CallableDescriptor): Map<TypeParameterDescriptor, TypeVariable> =
|
private fun KtCallExpression.typeArgumentsDescriptors(descriptor: CallableDescriptor): Map<TypeParameterDescriptor, TypeVariable>? {
|
||||||
descriptor.typeParameters.zip(typeArguments) { typeParameter, typeArgument ->
|
return descriptor.typeParameters.zip(typeArguments) { typeParameter, typeArgument ->
|
||||||
typeParameter to
|
val typeVariable =
|
||||||
this@ConstraintsCollector.analysisContext.typeElementToTypeVariable.getValue(typeArgument.typeReference?.typeElement!!)
|
analysisContext.typeElementToTypeVariable[typeArgument.typeReference?.typeElement ?: return null]
|
||||||
|
?: return null
|
||||||
|
typeParameter to typeVariable
|
||||||
}.toMap()
|
}.toMap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ internal class ContextCreator(
|
|||||||
if (typeArguments.isNotEmpty())
|
if (typeArguments.isNotEmpty())
|
||||||
FunctionCallTypeArgumentTarget(
|
FunctionCallTypeArgumentTarget(
|
||||||
this,
|
this,
|
||||||
typeArguments.map { it.typeReference?.typeElement?.asTypeVariable()!! }
|
typeArguments.map { it.typeReference?.typeElement?.asTypeVariable() ?: return null }
|
||||||
)
|
)
|
||||||
else null
|
else null
|
||||||
else -> null
|
else -> null
|
||||||
|
|||||||
+5
-6
@@ -11,7 +11,6 @@ import com.intellij.psi.PsiClass
|
|||||||
import com.intellij.psi.PsiComment
|
import com.intellij.psi.PsiComment
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiElementVisitor
|
import com.intellij.psi.PsiElementVisitor
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
|
||||||
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
|
||||||
import org.jetbrains.kotlin.nj2k.asLabel
|
import org.jetbrains.kotlin.nj2k.asLabel
|
||||||
import org.jetbrains.kotlin.nj2k.parentOfType
|
import org.jetbrains.kotlin.nj2k.parentOfType
|
||||||
@@ -29,10 +28,10 @@ internal fun TypeVariable.changeNullability(toNullable: Boolean) {
|
|||||||
internal fun KtTypeElement.changeNullability(toNullable: Boolean) {
|
internal fun KtTypeElement.changeNullability(toNullable: Boolean) {
|
||||||
val factory = KtPsiFactory(this)
|
val factory = KtPsiFactory(this)
|
||||||
if (this is KtNullableType && !toNullable) {
|
if (this is KtNullableType && !toNullable) {
|
||||||
replace(factory.createType(innerType!!.text).typeElement!!)
|
replace(factory.createType(innerType?.text ?: return).typeElement ?: return)
|
||||||
}
|
}
|
||||||
if (this !is KtNullableType && toNullable) {
|
if (this !is KtNullableType && toNullable) {
|
||||||
replace(factory.createType("$text?").typeElement!!)
|
replace(factory.createType("$text?").typeElement ?: return)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,10 +69,10 @@ internal fun KtTypeElement.classReference(): ClassReference {
|
|||||||
return when (target) {
|
return when (target) {
|
||||||
is KtClassOrObject -> KtClassReference(target)
|
is KtClassOrObject -> KtClassReference(target)
|
||||||
is PsiClass -> JavaClassReference(target)
|
is PsiClass -> JavaClassReference(target)
|
||||||
is KtTypeAlias -> target.getTypeReference()?.typeElement?.classReference()!!
|
is KtTypeAlias -> target.getTypeReference()?.typeElement?.classReference()
|
||||||
is KtTypeParameter -> TypeParameterClassReference(target)
|
is KtTypeParameter -> TypeParameterClassReference(target)
|
||||||
else -> UnknownClassReference(text)
|
else -> null
|
||||||
}
|
} ?: UnknownClassReference(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NullabilityAnalysisFacade(
|
class NullabilityAnalysisFacade(
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
|||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
|
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
|
||||||
import org.jetbrains.kotlin.resolve.jvm.checkers.mustNotBeNull
|
import org.jetbrains.kotlin.resolve.jvm.checkers.mustNotBeNull
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.isNullable
|
||||||
import org.jetbrains.kotlin.util.javaslang.getOrNull
|
import org.jetbrains.kotlin.util.javaslang.getOrNull
|
||||||
|
|
||||||
internal fun KtExpression.deepestReceiver(): KtExpression =
|
internal fun KtExpression.deepestReceiver(): KtExpression =
|
||||||
@@ -52,7 +53,7 @@ internal fun KtExpression.getForcedNullability(): Nullability? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun KotlinType.isExternallyAnnotatedNotNull(dataFlowInfo: DataFlowInfo, dataFlowValue: DataFlowValue): Boolean=
|
private fun KotlinType.isExternallyAnnotatedNotNull(dataFlowInfo: DataFlowInfo, dataFlowValue: DataFlowValue): Boolean =
|
||||||
mustNotBeNull()?.isFromJava == true && dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()
|
mustNotBeNull()?.isFromJava == true && dataFlowInfo.getStableNullability(dataFlowValue).canBeNull()
|
||||||
|
|
||||||
private fun IElementType.isEqualsToken() =
|
private fun IElementType.isEqualsToken() =
|
||||||
|
|||||||
Reference in New Issue
Block a user