IfThenToSafeAccessInspection: fix it works correctly for variable/operator call
#KT-30513 Fixed #KT-17071 Fixed
This commit is contained in:
committed by
Yan Zhulanow
parent
871ad2b909
commit
5095caee50
+2
@@ -104,6 +104,8 @@ private fun IfThenToSelectData.clausesReplaceableBySafeCall(): Boolean = when {
|
|||||||
context.diagnostics.forElement(condition)
|
context.diagnostics.forElement(condition)
|
||||||
.any { it.factory == Errors.SENSELESS_COMPARISON || it.factory == Errors.USELESS_IS_CHECK } -> false
|
.any { it.factory == Errors.SENSELESS_COMPARISON || it.factory == Errors.USELESS_IS_CHECK } -> false
|
||||||
baseClause.evaluatesTo(receiverExpression) -> true
|
baseClause.evaluatesTo(receiverExpression) -> true
|
||||||
|
(baseClause as? KtCallExpression)?.calleeExpression?.evaluatesTo(receiverExpression) == true
|
||||||
|
&& baseClause.isCallingInvokeFunction(context) -> true
|
||||||
baseClause.hasFirstReceiverOf(receiverExpression) -> withoutResultInCallChain(baseClause, context)
|
baseClause.hasFirstReceiverOf(receiverExpression) -> withoutResultInCallChain(baseClause, context)
|
||||||
baseClause.anyArgumentEvaluatesTo(receiverExpression) -> true
|
baseClause.anyArgumentEvaluatesTo(receiverExpression) -> true
|
||||||
receiverExpression is KtThisExpression -> getImplicitReceiver()?.let { it.type == receiverExpression.getType(context) } == true
|
receiverExpression is KtThisExpression -> getImplicitReceiver()?.let { it.type == receiverExpression.getType(context) } == true
|
||||||
|
|||||||
+29
-2
@@ -12,12 +12,15 @@ import com.intellij.openapi.util.TextRange
|
|||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import org.jetbrains.kotlin.KtNodeTypes
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
|
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||||
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
|
||||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.callExpression
|
||||||
import org.jetbrains.kotlin.idea.intentions.getLeftMostReceiverExpression
|
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.idea.refactoring.inline.KotlinInlineValHandler
|
import org.jetbrains.kotlin.idea.refactoring.inline.KotlinInlineValHandler
|
||||||
@@ -242,12 +245,36 @@ data class IfThenToSelectData(
|
|||||||
receiverExpression,
|
receiverExpression,
|
||||||
baseClause
|
baseClause
|
||||||
).insertSafeCalls(factory)
|
).insertSafeCalls(factory)
|
||||||
baseClause is KtCallExpression -> baseClause.replaceCallWithLet(receiverExpression, factory)
|
baseClause is KtCallExpression -> {
|
||||||
else -> baseClause.insertSafeCalls(factory)
|
val callee = baseClause.calleeExpression
|
||||||
|
if (callee != null && baseClause.isCallingInvokeFunction(context)) {
|
||||||
|
factory.createExpressionByPattern("$0?.invoke()", callee)
|
||||||
|
} else {
|
||||||
|
baseClause.replaceCallWithLet(receiverExpression, factory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
var replaced = baseClause.insertSafeCalls(factory)
|
||||||
|
if (replaced is KtQualifiedExpression) {
|
||||||
|
val call = replaced.callExpression
|
||||||
|
val callee = call?.calleeExpression
|
||||||
|
if (callee != null && call.isCallingInvokeFunction(context)) {
|
||||||
|
replaced = factory.createExpressionByPattern("$0?.${callee.text}?.invoke()", replaced.receiverExpression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
replaced
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun KtExpression.isCallingInvokeFunction(context: BindingContext): Boolean {
|
||||||
|
if (this !is KtCallExpression) return false
|
||||||
|
val resolvedCall = getResolvedCall(context) ?: resolveToCall() ?: return false
|
||||||
|
val descriptor = resolvedCall.resultingDescriptor as? SimpleFunctionDescriptor ?: return false
|
||||||
|
return descriptor is FunctionInvokeDescriptor || descriptor.isOperator && descriptor.name.asString() == "invoke"
|
||||||
|
}
|
||||||
|
|
||||||
internal fun getImplicitReceiver(): ImplicitReceiver? {
|
internal fun getImplicitReceiver(): ImplicitReceiver? {
|
||||||
val resolvedCall = baseClause.getResolvedCall(context) ?: return null
|
val resolvedCall = baseClause.getResolvedCall(context) ?: return null
|
||||||
if (resolvedCall.getExplicitReceiverValue() != null) return null
|
if (resolvedCall.getExplicitReceiverValue() != null) return null
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
fun test(foo: Foo?) {
|
||||||
|
<caret>if (foo != null) foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
operator fun invoke() {}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
fun test(foo: Foo?) {
|
||||||
|
foo?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
operator fun invoke() {}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
class Foo(val bar: Bar)
|
||||||
|
|
||||||
|
class Bar {
|
||||||
|
operator fun invoke() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(foo: Foo?) {
|
||||||
|
<caret>if (foo != null) {
|
||||||
|
foo.bar()
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
class Foo(val bar: Bar)
|
||||||
|
|
||||||
|
class Bar {
|
||||||
|
operator fun invoke() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test(foo: Foo?) {
|
||||||
|
foo?.bar?.invoke()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
fun test(foo: (() -> Unit)?) {
|
||||||
|
<caret>if (foo != null) foo()
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
// HIGHLIGHT: INFORMATION
|
||||||
|
fun test(foo: (() -> Unit)?) {
|
||||||
|
foo?.invoke()
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class Foo(val f: () -> Unit)
|
||||||
|
|
||||||
|
fun test(foo: Foo?) {
|
||||||
|
<caret>if (foo != null) {
|
||||||
|
foo.f()
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
class Foo(val f: () -> Unit)
|
||||||
|
|
||||||
|
fun test(foo: Foo?) {
|
||||||
|
foo?.f?.invoke()
|
||||||
|
}
|
||||||
+20
@@ -522,6 +522,26 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/call4.kt");
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/call4.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callInvokeOperator.kt")
|
||||||
|
public void testCallInvokeOperator() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/callInvokeOperator.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callInvokeOperator2.kt")
|
||||||
|
public void testCallInvokeOperator2() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/callInvokeOperator2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callVariable.kt")
|
||||||
|
public void testCallVariable() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/callVariable.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callVariable2.kt")
|
||||||
|
public void testCallVariable2() throws Exception {
|
||||||
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/callVariable2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("conditionComparesNullWithNull.kt")
|
@TestMetadata("conditionComparesNullWithNull.kt")
|
||||||
public void testConditionComparesNullWithNull() throws Exception {
|
public void testConditionComparesNullWithNull() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/conditionComparesNullWithNull.kt");
|
runTest("idea/testData/inspectionsLocal/branched/ifThenToSafeAccess/conditionComparesNullWithNull.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user