Refactor getKotlinTypeWithPossibleSmartCastToFP and related parts

This commit is contained in:
Mikhail Glukhikh
2018-10-10 15:25:33 +03:00
parent bbac1d802f
commit 01091ba1aa
3 changed files with 72 additions and 84 deletions
@@ -10,13 +10,12 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
import org.jetbrains.kotlin.resolve.calls.smartcasts.getKotlinTypeForComparison
import org.jetbrains.kotlin.resolve.calls.smartcasts.getKotlinTypeWithPossibleSmartCastToFP
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
class TypeAndNullability(@JvmField val type: Type, @JvmField val isNullable: Boolean) class TypeAndNullability(@JvmField val type: Type, @JvmField val isNullable: Boolean)
@@ -35,60 +34,31 @@ fun calcProperTypeForIeee754ArithmeticIfNeeded(
return TypeAndNullability(asmType, isNullable) return TypeAndNullability(asmType, isNullable)
} }
private fun KotlinType.isFloatingPointOrNullable() =
KotlinBuiltIns.isDoubleOrNullableDouble(this) || KotlinBuiltIns.isFloatOrNullableFloat(this)
fun KtExpression.getKotlinTypeForComparison(bindingContext: BindingContext): KotlinType? =
when {
this is KtProperty -> bindingContext[BindingContext.VARIABLE, this]?.type
else -> kotlinType(bindingContext)
}
fun legacyCalcTypeForIeee754ArithmeticIfNeeded( fun legacyCalcTypeForIeee754ArithmeticIfNeeded(
expression: KtExpression?, expression: KtExpression?,
bindingContext: BindingContext, bindingContext: BindingContext,
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): TypeAndNullability? { ): TypeAndNullability? {
val ktType = expression?.getKotlinTypeForComparison(bindingContext) ?: return null val ktType = expression.getKotlinTypeWithPossibleSmartCastToFP(
// NB. Using DataFlowValueFactoryImpl is a hack, but it is ok for 'legacy'
if (KotlinBuiltIns.isDoubleOrNullableDouble(ktType)) { bindingContext, descriptor, languageVersionSettings, DataFlowValueFactoryImpl(languageVersionSettings)
return TypeAndNullability(
Type.DOUBLE_TYPE,
TypeUtils.isNullableType(ktType)
)
}
if (KotlinBuiltIns.isFloatOrNullableFloat(ktType)) {
return TypeAndNullability(
Type.FLOAT_TYPE,
TypeUtils.isNullableType(ktType)
)
}
// NB. Using DataFlowValueFactoryImpl is a hack, but it is ok for 'legacy'
val dataFlow = DataFlowValueFactoryImpl(languageVersionSettings).createDataFlowValue(
expression,
ktType,
bindingContext,
descriptor
) )
val stableTypes = bindingContext.getDataFlowInfoBefore(expression).getStableTypes(dataFlow, languageVersionSettings)
return stableTypes.firstNotNullResult { if (ktType != null) {
when { if (KotlinBuiltIns.isDoubleOrNullableDouble(ktType)) {
KotlinBuiltIns.isDoubleOrNullableDouble(it) -> TypeAndNullability( return TypeAndNullability(
Type.DOUBLE_TYPE, Type.DOUBLE_TYPE,
TypeUtils.isNullableType( TypeUtils.isNullableType(ktType)
it
)
) )
KotlinBuiltIns.isFloatOrNullableFloat(it) -> TypeAndNullability( }
if (KotlinBuiltIns.isFloatOrNullableFloat(ktType)) {
return TypeAndNullability(
Type.FLOAT_TYPE, Type.FLOAT_TYPE,
TypeUtils.isNullableType( TypeUtils.isNullableType(ktType)
it
)
) )
else -> null
} }
} }
return null
} }
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.resolve.calls.smartcasts
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
fun KtExpression.getKotlinTypeForComparison(bindingContext: BindingContext): KotlinType? =
when {
this is KtProperty -> bindingContext[BindingContext.VARIABLE, this]?.type
else -> bindingContext.getType(this)
}
fun KtExpression?.getKotlinTypeWithPossibleSmartCastToFP(
bindingContext: BindingContext,
descriptor: DeclarationDescriptor?,
languageVersionSettings: LanguageVersionSettings,
dataFlowValueFactory: DataFlowValueFactory
): KotlinType? {
val givenType = this?.getKotlinTypeForComparison(bindingContext) ?: return null
if (KotlinBuiltIns.isDoubleOrNullableDouble(givenType)) {
return givenType
}
if (KotlinBuiltIns.isFloatOrNullableFloat(givenType)) {
return givenType
}
if (descriptor != null) {
val dataFlow = dataFlowValueFactory.createDataFlowValue(this, givenType, bindingContext, descriptor)
val stableTypes = bindingContext.getDataFlowInfoBefore(this).getStableTypes(dataFlow, languageVersionSettings)
stableTypes.firstNotNullResult {
when {
KotlinBuiltIns.isDoubleOrNullableDouble(it) -> it
KotlinBuiltIns.isFloatOrNullableFloat(it) -> it
else -> null
}
}?.let { return it }
}
return givenType
}
@@ -21,10 +21,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
import org.jetbrains.kotlin.codegen.getKotlinTypeForComparison
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
@@ -38,19 +35,17 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
import org.jetbrains.kotlin.resolve.calls.callUtil.getFirstArgumentExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getFirstArgumentExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getReceiverExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.smartcasts.getKotlinTypeWithPossibleSmartCastToFP
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>( class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>(
KtDotQualifiedExpression::class.java KtDotQualifiedExpression::class.java
@@ -172,46 +167,17 @@ class ReplaceCallWithBinaryOperatorInspection : AbstractApplicabilityBasedInspec
private fun KtDotQualifiedExpression.isFloatingPointNumberEquals(): Boolean { private fun KtDotQualifiedExpression.isFloatingPointNumberEquals(): Boolean {
val resolvedCall = resolveToCall() ?: return false val resolvedCall = resolveToCall() ?: return false
val context = analyze(BodyResolveMode.PARTIAL) val context = analyze(BodyResolveMode.PARTIAL)
val calcDescriptor = { val dataFlowValueFactory = getResolutionFacade().getFrontendService(DataFlowValueFactory::class.java)
this.containingDeclarationForPseudocode?.resolveToDescriptorIfAny()
}
val receiverType = resolvedCall.getReceiverExpression()?.getKotlinTypeWithPossibleSmartCastToFP( val receiverType = resolvedCall.getReceiverExpression()?.getKotlinTypeWithPossibleSmartCastToFP(
context, calcDescriptor context, containingDeclarationForPseudocode?.resolveToDescriptorIfAny(), languageVersionSettings, dataFlowValueFactory
) ?: return false ) ?: return false
val argumentType = resolvedCall.getFirstArgumentExpression()?.getKotlinTypeWithPossibleSmartCastToFP( val argumentType = resolvedCall.getFirstArgumentExpression()?.getKotlinTypeWithPossibleSmartCastToFP(
context, calcDescriptor context, containingDeclarationForPseudocode?.resolveToDescriptorIfAny(), languageVersionSettings, dataFlowValueFactory
) ?: return false ) ?: return false
return receiverType.isFpType() && argumentType.isNumericType() || return receiverType.isFpType() && argumentType.isNumericType() ||
argumentType.isFpType() && receiverType.isNumericType() argumentType.isFpType() && receiverType.isNumericType()
} }
private fun KtExpression.getKotlinTypeWithPossibleSmartCastToFP(
context: BindingContext,
calcDescriptor: () -> DeclarationDescriptor?
): KotlinType? {
val givenType = getKotlinTypeForComparison(context) ?: return null
if (KotlinBuiltIns.isDoubleOrNullableDouble(givenType) || KotlinBuiltIns.isFloatOrNullableFloat(givenType)) {
return givenType
}
val dataFlowValueFactory = getResolutionFacade().getFrontendService(DataFlowValueFactory::class.java)
val dataFlow = dataFlowValueFactory.createDataFlowValue(
this,
givenType,
context,
calcDescriptor() ?: return givenType
)
val stableTypes = context.getDataFlowInfoBefore(this).getStableTypes(dataFlow, languageVersionSettings)
return stableTypes.firstNotNullResult {
when {
KotlinBuiltIns.isDoubleOrNullableDouble(it) -> it
KotlinBuiltIns.isFloatOrNullableFloat(it) -> it
else -> null
}
} ?: givenType
}
private fun KtSimpleNameExpression.isFloatingPointNumberEquals(): Boolean { private fun KtSimpleNameExpression.isFloatingPointNumberEquals(): Boolean {
val dotQualified = parent.parent as? KtDotQualifiedExpression ?: return false val dotQualified = parent.parent as? KtDotQualifiedExpression ?: return false
return dotQualified.isFloatingPointNumberEquals() return dotQualified.isFloatingPointNumberEquals()