From 47c1106d5df8012a7954161fdaed0231448c5882 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 6 Jun 2016 15:53:07 +0300 Subject: [PATCH] Quick-fix "wrap with safe let call" introduced #KT-11104 Fixed --- .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 4 ++ .../idea/quickfix/WrapWithSafeLetCallFix.kt | 54 +++++++++++++++++++ .../wrapWithSafeLetCall/chainedUnsafeCall.kt | 10 ++++ .../expressionUnsafeCall.kt | 9 ++++ .../wrapWithSafeLetCall/invokeFuncUnsafe.kt | 6 +++ .../invokeFuncUnsafe.kt.after | 6 +++ .../wrapWithSafeLetCall/invokeUnsafe.kt | 8 +++ .../wrapWithSafeLetCall/invokeUnsafe.kt.after | 8 +++ .../wrapWithSafeLetCall/unstableValue.kt | 13 +++++ .../idea/quickfix/QuickFixTestGenerated.java | 39 ++++++++++++++ 10 files changed, 157 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/WrapWithSafeLetCallFix.kt create mode 100644 idea/testData/quickfix/wrapWithSafeLetCall/chainedUnsafeCall.kt create mode 100644 idea/testData/quickfix/wrapWithSafeLetCall/expressionUnsafeCall.kt create mode 100644 idea/testData/quickfix/wrapWithSafeLetCall/invokeFuncUnsafe.kt create mode 100644 idea/testData/quickfix/wrapWithSafeLetCall/invokeFuncUnsafe.kt.after create mode 100644 idea/testData/quickfix/wrapWithSafeLetCall/invokeUnsafe.kt create mode 100644 idea/testData/quickfix/wrapWithSafeLetCall/invokeUnsafe.kt.after create mode 100644 idea/testData/quickfix/wrapWithSafeLetCall/unstableValue.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index f128fb9f498..600a6b7dfb2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -194,6 +194,10 @@ class QuickFixRegistrar : QuickFixContributor { UNSAFE_IMPLICIT_INVOKE_CALL.registerFactory(SurroundWithNullCheckFix) UNSAFE_INFIX_CALL.registerFactory(SurroundWithNullCheckFix) + UNSAFE_CALL.registerFactory(WrapWithSafeLetCallFix) + UNSAFE_IMPLICIT_INVOKE_CALL.registerFactory(WrapWithSafeLetCallFix) + UNSAFE_INFIX_CALL.registerFactory(WrapWithSafeLetCallFix) + UNSAFE_CALL.registerFactory(AddExclExclCallFix) UNNECESSARY_NOT_NULL_ASSERTION.registerFactory(RemoveExclExclCallFix) UNSAFE_INFIX_CALL.registerFactory(ReplaceInfixCallFix) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/WrapWithSafeLetCallFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/WrapWithSafeLetCallFix.kt new file mode 100644 index 00000000000..8487595b607 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/WrapWithSafeLetCallFix.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType + +class WrapWithSafeLetCallFix( + expression: KtExpression, + val nullableExpression: KtExpression +) : KotlinQuickFixAction(expression) { + + override fun getFamilyName() = text + + override fun getText() = "Wrap with '?.let { ... }' call" + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val factory = KtPsiFactory(element) + val nullableText = nullableExpression.text + nullableExpression.replace(factory.createExpression("it")) + val wrapped = factory.createExpressionByPattern("$0?.let { $1 }", nullableText, element.text) + element.replace(wrapped) + } + + companion object : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val element = diagnostic.psiElement + val expression = element.getParentOfType(true) ?: return null + + val parent = element.parent + val nullableExpression = (parent as? KtCallExpression)?.calleeExpression ?: return null + + return WrapWithSafeLetCallFix(expression, nullableExpression) + } + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/wrapWithSafeLetCall/chainedUnsafeCall.kt b/idea/testData/quickfix/wrapWithSafeLetCall/chainedUnsafeCall.kt new file mode 100644 index 00000000000..e2751de2138 --- /dev/null +++ b/idea/testData/quickfix/wrapWithSafeLetCall/chainedUnsafeCall.kt @@ -0,0 +1,10 @@ +// "Wrap with '?.let { ... }' call" "false" +// WITH_RUNTIME +// ACTION: Add non-null asserted (!!) call +// ACTION: Introduce local variable +// ACTION: Replace with safe (?.) call +// ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Int? + +fun foo(arg: Int?) { + arg?.hashCode().toLong() +} \ No newline at end of file diff --git a/idea/testData/quickfix/wrapWithSafeLetCall/expressionUnsafeCall.kt b/idea/testData/quickfix/wrapWithSafeLetCall/expressionUnsafeCall.kt new file mode 100644 index 00000000000..f7266b0f6f8 --- /dev/null +++ b/idea/testData/quickfix/wrapWithSafeLetCall/expressionUnsafeCall.kt @@ -0,0 +1,9 @@ +// "Wrap with '?.let { ... }' call" "false" +// WITH_RUNTIME +// ACTION: Add non-null asserted (!!) call +// ACTION: Convert to block body +// ACTION: Introduce local variable +// ACTION: Replace with safe (?.) call +// ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Int? + +fun foo(arg: Int?) = arg.hashCode() \ No newline at end of file diff --git a/idea/testData/quickfix/wrapWithSafeLetCall/invokeFuncUnsafe.kt b/idea/testData/quickfix/wrapWithSafeLetCall/invokeFuncUnsafe.kt new file mode 100644 index 00000000000..4bd1371edb4 --- /dev/null +++ b/idea/testData/quickfix/wrapWithSafeLetCall/invokeFuncUnsafe.kt @@ -0,0 +1,6 @@ +// "Wrap with '?.let { ... }' call" "true" +// WITH_RUNTIME + +fun foo(exec: (() -> Unit)?) { + exec() +} \ No newline at end of file diff --git a/idea/testData/quickfix/wrapWithSafeLetCall/invokeFuncUnsafe.kt.after b/idea/testData/quickfix/wrapWithSafeLetCall/invokeFuncUnsafe.kt.after new file mode 100644 index 00000000000..cde4011712c --- /dev/null +++ b/idea/testData/quickfix/wrapWithSafeLetCall/invokeFuncUnsafe.kt.after @@ -0,0 +1,6 @@ +// "Wrap with '?.let { ... }' call" "true" +// WITH_RUNTIME + +fun foo(exec: (() -> Unit)?) { + exec?.let { it() } +} \ No newline at end of file diff --git a/idea/testData/quickfix/wrapWithSafeLetCall/invokeUnsafe.kt b/idea/testData/quickfix/wrapWithSafeLetCall/invokeUnsafe.kt new file mode 100644 index 00000000000..fdaef56783e --- /dev/null +++ b/idea/testData/quickfix/wrapWithSafeLetCall/invokeUnsafe.kt @@ -0,0 +1,8 @@ +// "Wrap with '?.let { ... }' call" "true" +// WITH_RUNTIME + +operator fun Int.invoke() = this + +fun foo(arg: Int?) { + arg() +} \ No newline at end of file diff --git a/idea/testData/quickfix/wrapWithSafeLetCall/invokeUnsafe.kt.after b/idea/testData/quickfix/wrapWithSafeLetCall/invokeUnsafe.kt.after new file mode 100644 index 00000000000..bba4c445cf6 --- /dev/null +++ b/idea/testData/quickfix/wrapWithSafeLetCall/invokeUnsafe.kt.after @@ -0,0 +1,8 @@ +// "Wrap with '?.let { ... }' call" "true" +// WITH_RUNTIME + +operator fun Int.invoke() = this + +fun foo(arg: Int?) { + arg?.let { it() } +} \ No newline at end of file diff --git a/idea/testData/quickfix/wrapWithSafeLetCall/unstableValue.kt b/idea/testData/quickfix/wrapWithSafeLetCall/unstableValue.kt new file mode 100644 index 00000000000..370b14fa998 --- /dev/null +++ b/idea/testData/quickfix/wrapWithSafeLetCall/unstableValue.kt @@ -0,0 +1,13 @@ +// "Wrap with '?.let { ... }' call" "false" +// WITH_RUNTIME +// ACTION: Add non-null asserted (!!) call +// ACTION: Introduce local variable +// ACTION: Replace with safe (?.) call +// ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Int? + +class My(var x: Int?) { + + fun foo() { + x.hashCode() + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index c3094dcaea2..f0e4c65a695 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -8761,4 +8761,43 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } } + + @TestMetadata("idea/testData/quickfix/wrapWithSafeLetCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class WrapWithSafeLetCall extends AbstractQuickFixTest { + public void testAllFilesPresentInWrapWithSafeLetCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/wrapWithSafeLetCall"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("chainedUnsafeCall.kt") + public void testChainedUnsafeCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/wrapWithSafeLetCall/chainedUnsafeCall.kt"); + doTest(fileName); + } + + @TestMetadata("expressionUnsafeCall.kt") + public void testExpressionUnsafeCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/wrapWithSafeLetCall/expressionUnsafeCall.kt"); + doTest(fileName); + } + + @TestMetadata("invokeFuncUnsafe.kt") + public void testInvokeFuncUnsafe() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/wrapWithSafeLetCall/invokeFuncUnsafe.kt"); + doTest(fileName); + } + + @TestMetadata("invokeUnsafe.kt") + public void testInvokeUnsafe() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/wrapWithSafeLetCall/invokeUnsafe.kt"); + doTest(fileName); + } + + @TestMetadata("unstableValue.kt") + public void testUnstableValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/wrapWithSafeLetCall/unstableValue.kt"); + doTest(fileName); + } + } }