Handle implicit receivers more accurately in if-then to safe access
#KT-26599 Fixed
This commit is contained in:
+7
-6
@@ -215,14 +215,14 @@ data class IfThenToSelectData(
|
|||||||
baseClause is KtDotQualifiedExpression -> baseClause.replaceFirstReceiver(
|
baseClause is KtDotQualifiedExpression -> baseClause.replaceFirstReceiver(
|
||||||
factory, newReceiver!!, safeAccess = true
|
factory, newReceiver!!, safeAccess = true
|
||||||
)
|
)
|
||||||
hasImplicitReceiver() -> factory.createExpressionByPattern("$0?.$1", newReceiver!!, baseClause).insertSafeCalls(
|
hasImplicitReceiverReplaceableBySafeCall() -> factory.createExpressionByPattern("$0?.$1", newReceiver!!, baseClause).insertSafeCalls(
|
||||||
factory
|
factory
|
||||||
)
|
)
|
||||||
baseClause is KtCallExpression -> replacedBaseClauseWithLet(baseClause, newReceiver!!, factory)
|
baseClause is KtCallExpression -> replacedBaseClauseWithLet(baseClause, newReceiver!!, factory)
|
||||||
else -> error("Illegal state")
|
else -> error("Illegal state")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasImplicitReceiver() -> factory.createExpressionByPattern("this?.$0", baseClause).insertSafeCalls(factory)
|
hasImplicitReceiverReplaceableBySafeCall() -> factory.createExpressionByPattern("this?.$0", baseClause).insertSafeCalls(factory)
|
||||||
baseClause is KtCallExpression -> replacedBaseClauseWithLet(baseClause, receiverExpression, factory)
|
baseClause is KtCallExpression -> replacedBaseClauseWithLet(baseClause, receiverExpression, factory)
|
||||||
else -> baseClause.insertSafeCalls(factory)
|
else -> baseClause.insertSafeCalls(factory)
|
||||||
}
|
}
|
||||||
@@ -235,7 +235,8 @@ data class IfThenToSelectData(
|
|||||||
return resolvedCall.getImplicitReceiverValue()
|
return resolvedCall.getImplicitReceiverValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun hasImplicitReceiver(): Boolean = getImplicitReceiver() != null
|
internal fun hasImplicitReceiverReplaceableBySafeCall(): Boolean =
|
||||||
|
receiverExpression is KtThisExpression && getImplicitReceiver() != null
|
||||||
|
|
||||||
private fun replacedBaseClauseWithLet(baseClause: KtCallExpression, receiver: KtExpression, factory: KtPsiFactory): KtExpression {
|
private fun replacedBaseClauseWithLet(baseClause: KtCallExpression, receiver: KtExpression, factory: KtPsiFactory): KtExpression {
|
||||||
val needExplicitParameter = baseClause.valueArguments.any { it.getArgumentExpression()?.text == "it" }
|
val needExplicitParameter = baseClause.valueArguments.any { it.getArgumentExpression()?.text == "it" }
|
||||||
@@ -295,15 +296,15 @@ internal fun KtIfExpression.buildSelectTransformationData(): IfThenToSelectData?
|
|||||||
return IfThenToSelectData(context, condition, receiverExpression, baseClause, negatedClause)
|
return IfThenToSelectData(context, condition, receiverExpression, baseClause, negatedClause)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun KtExpression?.isClauseTransformableToLetOnly() =
|
internal fun KtExpression?.isClauseTransformableToLetOnly(receiver: KtExpression?) =
|
||||||
this is KtCallExpression && resolveToCall()?.getImplicitReceiverValue() == null
|
this is KtCallExpression && (resolveToCall()?.getImplicitReceiverValue() == null || receiver !is KtThisExpression)
|
||||||
|
|
||||||
internal fun KtIfExpression.shouldBeTransformed(): Boolean {
|
internal fun KtIfExpression.shouldBeTransformed(): Boolean {
|
||||||
val condition = condition
|
val condition = condition
|
||||||
return when (condition) {
|
return when (condition) {
|
||||||
is KtBinaryExpression -> {
|
is KtBinaryExpression -> {
|
||||||
val baseClause = (if (condition.operationToken == KtTokens.EQEQ) `else` else then)?.unwrapBlockOrParenthesis()
|
val baseClause = (if (condition.operationToken == KtTokens.EQEQ) `else` else then)?.unwrapBlockOrParenthesis()
|
||||||
!baseClause.isClauseTransformableToLetOnly()
|
!baseClause.isClauseTransformableToLetOnly(checkedExpression())
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@ class IfThenToElvisIntention : SelfTargetingOffsetIndependentIntention<KtIfExpre
|
|||||||
false
|
false
|
||||||
baseClause.evaluatesTo(receiverExpression) ->
|
baseClause.evaluatesTo(receiverExpression) ->
|
||||||
true
|
true
|
||||||
receiverExpression is KtThisExpression && hasImplicitReceiver() || baseClause.hasFirstReceiverOf(receiverExpression) ->
|
hasImplicitReceiverReplaceableBySafeCall() || baseClause.hasFirstReceiverOf(receiverExpression) ->
|
||||||
!baseClause.hasNullableType(context)
|
!baseClause.hasNullableType(context)
|
||||||
else ->
|
else ->
|
||||||
false
|
false
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
fun doAThing(param1: String) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doAThingIfPresent(param1: String?) {
|
||||||
|
<caret>if (param1 != null) {
|
||||||
|
doAThing(param1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
fun doAThing(param1: String) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doAThingIfPresent(param1: String?) {
|
||||||
|
param1?.let { doAThing(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
fun doAThing(param1: String) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doAThingIfPresent(param1: String?) {
|
||||||
|
<caret>if (param1 is String) {
|
||||||
|
doAThing(param1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
fun doAThing(param1: String) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doAThingIfPresent(param1: String?) {
|
||||||
|
(param1 as? String)?.let { doAThing(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
fun doAThing(param1: String): String {
|
||||||
|
return param1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doAThingIfPresent(param1: String?) {
|
||||||
|
// In theory could propose transformation to 'let'
|
||||||
|
<caret>if (param1 != null) {
|
||||||
|
doAThing(param1)
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
fun doAThing(param1: String): String {
|
||||||
|
return param1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doAThingIfPresent(param1: String?) {
|
||||||
|
// In theory could propose transformation to 'let'
|
||||||
|
<caret>if (param1 is String) {
|
||||||
|
doAThing(param1)
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -286,6 +286,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/propertyWithProperty.kt");
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/propertyWithProperty.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("replaceWithLetInMember.kt")
|
||||||
|
public void testReplaceWithLetInMember() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/replaceWithLetInMember.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("replaceWithLetInMemberWithIs.kt")
|
||||||
|
public void testReplaceWithLetInMemberWithIs() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/replaceWithLetInMemberWithIs.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("rhsEqualsNull.kt")
|
@TestMetadata("rhsEqualsNull.kt")
|
||||||
public void testRhsEqualsNull() throws Exception {
|
public void testRhsEqualsNull() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsEqualsNull.kt");
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/rhsEqualsNull.kt");
|
||||||
|
|||||||
@@ -2426,6 +2426,16 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
runTest("idea/testData/intentions/branched/ifThenToElvis/otherBlockHasMoreThanOneStatement.kt");
|
runTest("idea/testData/intentions/branched/ifThenToElvis/otherBlockHasMoreThanOneStatement.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("replaceWithLetInMember.kt")
|
||||||
|
public void testReplaceWithLetInMember() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/branched/ifThenToElvis/replaceWithLetInMember.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("replaceWithLetInMemberWithIs.kt")
|
||||||
|
public void testReplaceWithLetInMemberWithIs() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/branched/ifThenToElvis/replaceWithLetInMemberWithIs.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("rhsEqualsNull.kt")
|
@TestMetadata("rhsEqualsNull.kt")
|
||||||
public void testRhsEqualsNull() throws Exception {
|
public void testRhsEqualsNull() throws Exception {
|
||||||
runTest("idea/testData/intentions/branched/ifThenToElvis/rhsEqualsNull.kt");
|
runTest("idea/testData/intentions/branched/ifThenToElvis/rhsEqualsNull.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user