If-then to safe access: more correct receiver calculation
So #KT-18928 Fixed
This commit is contained in:
+11
-11
@@ -88,9 +88,8 @@ fun KtThrowExpression.throwsNullPointerExceptionWithNoArguments(): Boolean {
|
|||||||
&& thrownExpression.valueArguments.isEmpty()
|
&& thrownExpression.valueArguments.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtExpression.evaluatesTo(other: KtExpression): Boolean {
|
fun KtExpression.evaluatesTo(other: KtExpression): Boolean =
|
||||||
return this.unwrapBlockOrParenthesis().text == other.text
|
this.unwrapBlockOrParenthesis().text == other.text
|
||||||
}
|
|
||||||
|
|
||||||
fun KtExpression.convertToIfNotNullExpression(conditionLhs: KtExpression, thenClause: KtExpression, elseClause: KtExpression?): KtIfExpression {
|
fun KtExpression.convertToIfNotNullExpression(conditionLhs: KtExpression, thenClause: KtExpression, elseClause: KtExpression?): KtIfExpression {
|
||||||
val condition = KtPsiFactory(this).createExpressionByPattern("$0 != null", conditionLhs)
|
val condition = KtPsiFactory(this).createExpressionByPattern("$0 != null", conditionLhs)
|
||||||
@@ -102,9 +101,8 @@ fun KtExpression.convertToIfNullExpression(conditionLhs: KtExpression, thenClaus
|
|||||||
return this.convertToIfStatement(condition, thenClause)
|
return this.convertToIfStatement(condition, thenClause)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtExpression.convertToIfStatement(condition: KtExpression, thenClause: KtExpression, elseClause: KtExpression? = null): KtIfExpression {
|
fun KtExpression.convertToIfStatement(condition: KtExpression, thenClause: KtExpression, elseClause: KtExpression? = null): KtIfExpression =
|
||||||
return replaced(KtPsiFactory(this).createIf(condition, thenClause, elseClause))
|
replaced(KtPsiFactory(this).createIf(condition, thenClause, elseClause))
|
||||||
}
|
|
||||||
|
|
||||||
fun KtIfExpression.introduceValueForCondition(occurrenceInThenClause: KtExpression, editor: Editor?) {
|
fun KtIfExpression.introduceValueForCondition(occurrenceInThenClause: KtExpression, editor: Editor?) {
|
||||||
val project = this.project
|
val project = this.project
|
||||||
@@ -157,14 +155,17 @@ data class IfThenToSelectData(
|
|||||||
val baseClause: KtExpression?,
|
val baseClause: KtExpression?,
|
||||||
val negatedClause: KtExpression?
|
val negatedClause: KtExpression?
|
||||||
) {
|
) {
|
||||||
|
internal fun baseClauseEvaluatesToReceiver() =
|
||||||
|
baseClause?.evaluatesTo(receiverExpression) == true
|
||||||
|
|
||||||
internal fun replacedBaseClause(factory: KtPsiFactory): KtExpression {
|
internal fun replacedBaseClause(factory: KtPsiFactory): KtExpression {
|
||||||
baseClause ?: error("Base clause must be not-null here")
|
baseClause ?: error("Base clause must be not-null here")
|
||||||
val newReceiver = (condition as? KtIsExpression)?.let {
|
val newReceiver = (condition as? KtIsExpression)?.let {
|
||||||
factory.createExpressionByPattern("$0 as? $1",
|
factory.createExpressionByPattern("$0 as? $1",
|
||||||
(baseClause as? KtDotQualifiedExpression)?.getLeftMostReceiverExpression() ?: baseClause,
|
it.leftHandSide,
|
||||||
it.typeReference!!)
|
it.typeReference!!)
|
||||||
}
|
}
|
||||||
return if (baseClause.evaluatesTo(receiverExpression)) {
|
return if (baseClauseEvaluatesToReceiver()) {
|
||||||
if (condition is KtIsExpression) newReceiver!! else baseClause
|
if (condition is KtIsExpression) newReceiver!! else baseClause
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -177,9 +178,8 @@ data class IfThenToSelectData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun hasImplicitReceiver(): Boolean {
|
internal fun hasImplicitReceiver(): Boolean =
|
||||||
return baseClause.getResolvedCall(context)?.getImplicitReceiverValue() != null
|
baseClause.getResolvedCall(context)?.getImplicitReceiverValue() != null
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun KtIfExpression.buildSelectTransformationData(): IfThenToSelectData? {
|
internal fun KtIfExpression.buildSelectTransformationData(): IfThenToSelectData? {
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ class IfThenToSafeAccessIntention : SelfTargetingOffsetIndependentIntention<KtIf
|
|||||||
override fun isApplicableTo(element: KtIfExpression): Boolean {
|
override fun isApplicableTo(element: KtIfExpression): Boolean {
|
||||||
val ifThenToSelectData = element.buildSelectTransformationData() ?: return false
|
val ifThenToSelectData = element.buildSelectTransformationData() ?: return false
|
||||||
if (!ifThenToSelectData.receiverExpression.isStable(ifThenToSelectData.context)) return false
|
if (!ifThenToSelectData.receiverExpression.isStable(ifThenToSelectData.context)) return false
|
||||||
if (ifThenToSelectData.baseClause !is KtDotQualifiedExpression) {
|
if (ifThenToSelectData.baseClauseEvaluatesToReceiver()) {
|
||||||
text = if (ifThenToSelectData.condition is KtIsExpression) {
|
text = if (ifThenToSelectData.condition is KtIsExpression) {
|
||||||
"Replace 'if' expression with safe cast expression"
|
"Replace 'if' expression with safe cast expression"
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -149,6 +149,22 @@
|
|||||||
<module>light_idea_test_case</module>
|
<module>light_idea_test_case</module>
|
||||||
<entry_point TYPE="file" FQNAME="temp:///src/implicitReceiver.kt" />
|
<entry_point TYPE="file" FQNAME="temp:///src/implicitReceiver.kt" />
|
||||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||||
|
<description>Replace 'if' expression with safe access expression</description>
|
||||||
|
</problem>
|
||||||
|
<problem>
|
||||||
|
<file>property.kt</file>
|
||||||
|
<line>10</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="temp:///src/property.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||||
|
<description>Replace 'if' expression with safe cast expression</description>
|
||||||
|
</problem>
|
||||||
|
<problem>
|
||||||
|
<file>propertyNotNull.kt</file>
|
||||||
|
<line>9</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="temp:///src/propertyNotNull.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">If-Then foldable to '?.'</problem_class>
|
||||||
<description>Remove redundant 'if' expression</description>
|
<description>Remove redundant 'if' expression</description>
|
||||||
</problem>
|
</problem>
|
||||||
</problems>
|
</problems>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// IS_APPLICABLE: true
|
||||||
|
// INTENTION_TEXT: Replace 'if' expression with safe cast expression
|
||||||
|
|
||||||
|
interface Foo
|
||||||
|
interface Bar : Foo
|
||||||
|
|
||||||
|
data class Data(val foo: Foo)
|
||||||
|
|
||||||
|
fun handle(data: Data) {
|
||||||
|
val bar = <caret>if (data.foo is Bar) data.foo else null
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// IS_APPLICABLE: true
|
||||||
|
// INTENTION_TEXT: Replace 'if' expression with safe cast expression
|
||||||
|
|
||||||
|
interface Foo
|
||||||
|
interface Bar : Foo
|
||||||
|
|
||||||
|
data class Data(val foo: Foo)
|
||||||
|
|
||||||
|
fun handle(data: Data) {
|
||||||
|
val bar = data.foo as? Bar
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// IS_APPLICABLE: true
|
||||||
|
// INTENTION_TEXT: Remove redundant 'if' expression
|
||||||
|
|
||||||
|
interface Bar
|
||||||
|
|
||||||
|
data class Data(val bar: Bar?)
|
||||||
|
|
||||||
|
fun handle(data: Data) {
|
||||||
|
val bar = <caret>if (data.bar != null) data.bar else null
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// IS_APPLICABLE: true
|
||||||
|
// INTENTION_TEXT: Remove redundant 'if' expression
|
||||||
|
|
||||||
|
interface Bar
|
||||||
|
|
||||||
|
data class Data(val bar: Bar?)
|
||||||
|
|
||||||
|
fun handle(data: Data) {
|
||||||
|
val bar = data.bar
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
|
||||||
|
interface Foo
|
||||||
|
interface Bar : Foo {
|
||||||
|
val x: String
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Data(val foo: Foo)
|
||||||
|
|
||||||
|
fun handle(data: Data) {
|
||||||
|
// Not available yet (possible in principle)
|
||||||
|
val bar = <caret>if (data.foo is Bar) data.foo.x else null
|
||||||
|
}
|
||||||
@@ -1774,6 +1774,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("property.kt")
|
||||||
|
public void testProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToSafeAccess/property.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyNotNull.kt")
|
||||||
|
public void testPropertyNotNull() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToSafeAccess/propertyNotNull.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyWithProperty.kt")
|
||||||
|
public void testPropertyWithProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToSafeAccess/propertyWithProperty.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("rhsEqualsNull.kt")
|
@TestMetadata("rhsEqualsNull.kt")
|
||||||
public void testRhsEqualsNull() throws Exception {
|
public void testRhsEqualsNull() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToSafeAccess/rhsEqualsNull.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifThenToSafeAccess/rhsEqualsNull.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user