If then to elvis: now not applicable for is with nullable type #KT-14545 Fixed
This commit is contained in:
+1
-2
@@ -130,8 +130,7 @@ fun KtPostfixExpression.inlineBaseExpressionIfApplicableWithPrompt(editor: Edito
|
|||||||
(this.baseExpression as? KtNameReferenceExpression)?.inlineIfDeclaredLocallyAndOnlyUsedOnceWithPrompt(editor)
|
(this.baseExpression as? KtNameReferenceExpression)?.inlineIfDeclaredLocallyAndOnlyUsedOnceWithPrompt(editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtExpression.isStableVariable(): Boolean {
|
fun KtExpression.isStableVariable(context: BindingContext = this.analyze()): Boolean {
|
||||||
val context = this.analyze()
|
|
||||||
val descriptor = BindingContextUtils.extractVariableDescriptorFromReference(context, this)
|
val descriptor = BindingContextUtils.extractVariableDescriptorFromReference(context, this)
|
||||||
return descriptor is VariableDescriptor &&
|
return descriptor is VariableDescriptor &&
|
||||||
DataFlowValueFactory.isStableValue(descriptor, DescriptorUtils.getContainingModule(descriptor))
|
DataFlowValueFactory.isStableValue(descriptor, DescriptorUtils.getContainingModule(descriptor))
|
||||||
|
|||||||
+19
-9
@@ -27,8 +27,10 @@ import org.jetbrains.kotlin.idea.intentions.getLeftMostReceiverExpression
|
|||||||
import org.jetbrains.kotlin.idea.intentions.replaceFirstReceiver
|
import org.jetbrains.kotlin.idea.intentions.replaceFirstReceiver
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode.Companion.PARTIAL
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode.Companion.PARTIAL
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||||
import org.jetbrains.kotlin.types.typeUtil.nullability
|
import org.jetbrains.kotlin.types.typeUtil.nullability
|
||||||
|
|
||||||
@@ -39,9 +41,9 @@ class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpre
|
|||||||
"Replace 'if' expression with elvis expression"
|
"Replace 'if' expression with elvis expression"
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private fun KtExpression.clausesReplaceableByElvis(firstClause: KtExpression, secondClause: KtExpression) =
|
private fun KtExpression.clausesReplaceableByElvis(firstClause: KtExpression, secondClause: KtExpression, context: BindingContext) =
|
||||||
!firstClause.isNullOrBlockExpression() &&
|
!firstClause.isNullOrBlockExpression() &&
|
||||||
(secondClause.evaluatesTo(this) || secondClause.hasFirstReceiverOf(this) && !secondClause.isNullableExpression()) &&
|
(secondClause.evaluatesTo(this) || secondClause.hasFirstReceiverOf(this) && !secondClause.isNullableExpression(context)) &&
|
||||||
!(firstClause is KtThrowExpression && firstClause.throwsNullPointerExceptionWithNoArguments())
|
!(firstClause is KtThrowExpression && firstClause.throwsNullPointerExceptionWithNoArguments())
|
||||||
|
|
||||||
private fun KtExpression.checkedExpression() = when (this) {
|
private fun KtExpression.checkedExpression() = when (this) {
|
||||||
@@ -55,18 +57,24 @@ class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpre
|
|||||||
val thenClause = element.then ?: return false
|
val thenClause = element.then ?: return false
|
||||||
val elseClause = element.`else` ?: return false
|
val elseClause = element.`else` ?: return false
|
||||||
|
|
||||||
|
val context = condition.analyze(PARTIAL)
|
||||||
|
|
||||||
val checkedExpression = condition.checkedExpression() ?: return false
|
val checkedExpression = condition.checkedExpression() ?: return false
|
||||||
if (!checkedExpression.isStableVariable()) return false
|
if (!checkedExpression.isStableVariable(context)) return false
|
||||||
|
|
||||||
return when (condition) {
|
return when (condition) {
|
||||||
is KtBinaryExpression -> when (condition.operationToken) {
|
is KtBinaryExpression -> when (condition.operationToken) {
|
||||||
KtTokens.EQEQ -> checkedExpression.clausesReplaceableByElvis(thenClause, elseClause)
|
KtTokens.EQEQ -> checkedExpression.clausesReplaceableByElvis(thenClause, elseClause, context)
|
||||||
KtTokens.EXCLEQ -> checkedExpression.clausesReplaceableByElvis(elseClause, thenClause)
|
KtTokens.EXCLEQ -> checkedExpression.clausesReplaceableByElvis(elseClause, thenClause, context)
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
is KtIsExpression -> when (condition.isNegated) {
|
is KtIsExpression -> {
|
||||||
true -> checkedExpression.clausesReplaceableByElvis(thenClause, elseClause)
|
if (!context[BindingContext.TYPE, condition.typeReference].isNotNull()) return false
|
||||||
false -> checkedExpression.clausesReplaceableByElvis(elseClause, thenClause)
|
|
||||||
|
when (condition.isNegated) {
|
||||||
|
true -> checkedExpression.clausesReplaceableByElvis(thenClause, elseClause, context)
|
||||||
|
false -> checkedExpression.clausesReplaceableByElvis(elseClause, thenClause, context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
@@ -77,7 +85,9 @@ class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpre
|
|||||||
return innerExpression is KtBlockExpression || innerExpression.node.elementType == KtNodeTypes.NULL
|
return innerExpression is KtBlockExpression || innerExpression.node.elementType == KtNodeTypes.NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtExpression.isNullableExpression() = getType(analyze(PARTIAL))?.nullability() != TypeNullability.NOT_NULL
|
private fun KtExpression.isNullableExpression(context: BindingContext) = !getType(context).isNotNull()
|
||||||
|
|
||||||
|
private fun KotlinType?.isNotNull() = this?.nullability() == TypeNullability.NOT_NULL
|
||||||
|
|
||||||
private fun KtExpression.hasFirstReceiverOf(receiver: KtExpression): Boolean {
|
private fun KtExpression.hasFirstReceiverOf(receiver: KtExpression): Boolean {
|
||||||
val actualReceiver = (unwrapBlockOrParenthesis() as? KtDotQualifiedExpression)?.getLeftMostReceiverExpression() ?: return false
|
val actualReceiver = (unwrapBlockOrParenthesis() as? KtDotQualifiedExpression)?.getLeftMostReceiverExpression() ?: return false
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
|
||||||
|
fun foo(x: CharSequence?) {
|
||||||
|
val y = if (x is String?) {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(x as CharSequence).toString()
|
||||||
|
}<caret>
|
||||||
|
}
|
||||||
@@ -1302,6 +1302,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("isCheckForNullableType.kt")
|
||||||
|
public void testIsCheckForNullableType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/isCheckForNullableType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("isCheckWithSelector.kt")
|
@TestMetadata("isCheckWithSelector.kt")
|
||||||
public void testIsCheckWithSelector() throws Exception {
|
public void testIsCheckWithSelector() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/isCheckWithSelector.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToElvis/isCheckWithSelector.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user