diff --git a/idea/resources/intentionDescriptions/ConvertUnsafeCastCallToUnsafeCastIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertUnsafeCastCallToUnsafeCastIntention/after.kt.template new file mode 100644 index 00000000000..09cb3823787 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertUnsafeCastCallToUnsafeCastIntention/after.kt.template @@ -0,0 +1,3 @@ +fun test(foo: dynamic) { + val s = foo as String +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertUnsafeCastCallToUnsafeCastIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertUnsafeCastCallToUnsafeCastIntention/before.kt.template new file mode 100644 index 00000000000..868847ec645 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertUnsafeCastCallToUnsafeCastIntention/before.kt.template @@ -0,0 +1,3 @@ +fun test(foo: dynamic) { + val s = foo.unsafeCast() +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertUnsafeCastCallToUnsafeCastIntention/description.html b/idea/resources/intentionDescriptions/ConvertUnsafeCastCallToUnsafeCastIntention/description.html new file mode 100644 index 00000000000..3ddb36a40a1 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertUnsafeCastCallToUnsafeCastIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts unsafeCast() call to unsafe cast. + + \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertUnsafeCastToUnsafeCastCallIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertUnsafeCastToUnsafeCastCallIntention/after.kt.template new file mode 100644 index 00000000000..868847ec645 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertUnsafeCastToUnsafeCastCallIntention/after.kt.template @@ -0,0 +1,3 @@ +fun test(foo: dynamic) { + val s = foo.unsafeCast() +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertUnsafeCastToUnsafeCastCallIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertUnsafeCastToUnsafeCastCallIntention/before.kt.template new file mode 100644 index 00000000000..09cb3823787 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertUnsafeCastToUnsafeCastCallIntention/before.kt.template @@ -0,0 +1,3 @@ +fun test(foo: dynamic) { + val s = foo as String +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertUnsafeCastToUnsafeCastCallIntention/description.html b/idea/resources/intentionDescriptions/ConvertUnsafeCastToUnsafeCastCallIntention/description.html new file mode 100644 index 00000000000..bfaf49288b6 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertUnsafeCastToUnsafeCastCallIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts unsafe cast to unsafeCast() call. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 816d5bee71e..c12a4526b73 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1539,6 +1539,16 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertUnsafeCastCallToUnsafeCastIntention + Kotlin + + + + org.jetbrains.kotlin.idea.intentions.ConvertUnsafeCastToUnsafeCastCallIntention + Kotlin + + ( + KtDotQualifiedExpression::class.java, "Convert to unsafe cast" +) { + + override fun isApplicableTo(element: KtDotQualifiedExpression, caretOffset: Int): Boolean { + if (element.platform != JsPlatform) return false + if ((element.selectorExpression as? KtCallExpression)?.calleeExpression?.text != "unsafeCast") return false + + val context = element.analyze(BodyResolveMode.PARTIAL) + val fqName = element.getResolvedCall(context)?.resultingDescriptor?.fqNameOrNull()?.asString() ?: return false + if (fqName != "kotlin.js.unsafeCast") return false + + val type = element.callExpression?.typeArguments?.singleOrNull() ?: return false + + text = "Convert to '${element.receiverExpression.text} as ${type.text}'" + return true + } + + override fun applyTo(element: KtDotQualifiedExpression, editor: Editor?) { + val type = element.callExpression?.typeArguments?.singleOrNull() ?: return + val newExpression = KtPsiFactory(element).createExpressionByPattern("$0 as $1", element.receiverExpression, type.text) + element.replace(newExpression) + } + +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertUnsafeCastToUnsafeCastCallIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertUnsafeCastToUnsafeCastCallIntention.kt new file mode 100644 index 00000000000..d72c931fa88 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertUnsafeCastToUnsafeCastCallIntention.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.intentions + +import com.intellij.openapi.editor.Editor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.project.platform +import org.jetbrains.kotlin.js.resolve.JsPlatform +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.createExpressionByPattern +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.TypeUtils + +class ConvertUnsafeCastToUnsafeCastCallIntention : SelfTargetingIntention( + KtBinaryExpressionWithTypeRHS::class.java, "Convert to unsafeCast() call" +) { + + override fun isApplicableTo(element: KtBinaryExpressionWithTypeRHS, caretOffset: Int): Boolean { + if (element.platform != JsPlatform) return false + + if (element.operationReference.getReferencedNameElementType() != KtTokens.AS_KEYWORD) return false + + val right = element.right ?: return false + val context = right.analyze(BodyResolveMode.PARTIAL) + val type = context[BindingContext.TYPE, right] ?: return false + if (TypeUtils.isNullableType(type)) return false + + text = "Convert to '${element.left.text}.unsafeCast<${right.text}>()'" + return true + } + + override fun applyTo(element: KtBinaryExpressionWithTypeRHS, editor: Editor?) { + val right = element.right ?: return + val newExpression = KtPsiFactory(element).createExpressionByPattern("$0.unsafeCast<$1>()", element.left, right) + element.replace(newExpression) + } + +} \ No newline at end of file diff --git a/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/.intention b/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/.intention new file mode 100644 index 00000000000..c0ea104e0e2 --- /dev/null +++ b/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertUnsafeCastCallToUnsafeCastIntention \ No newline at end of file diff --git a/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/call.kt b/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/call.kt new file mode 100644 index 00000000000..dc412102c5f --- /dev/null +++ b/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/call.kt @@ -0,0 +1,4 @@ +// JS +fun test(foo: dynamic) { + val s = foo.unsafeCast() +} \ No newline at end of file diff --git a/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/call.kt.after b/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/call.kt.after new file mode 100644 index 00000000000..e496a5912f6 --- /dev/null +++ b/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/call.kt.after @@ -0,0 +1,4 @@ +// JS +fun test(foo: dynamic) { + val s = foo as String +} \ No newline at end of file diff --git a/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/safeCall.kt b/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/safeCall.kt new file mode 100644 index 00000000000..37068c95cc0 --- /dev/null +++ b/idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/safeCall.kt @@ -0,0 +1,5 @@ +// JS +// IS_APPLICABLE: false +fun test(foo: Any?) { + val s = foo?.unsafeCast() +} \ No newline at end of file diff --git a/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/.intention b/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/.intention new file mode 100644 index 00000000000..3c151521e19 --- /dev/null +++ b/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertUnsafeCastToUnsafeCastCallIntention \ No newline at end of file diff --git a/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/cast.kt b/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/cast.kt new file mode 100644 index 00000000000..e496a5912f6 --- /dev/null +++ b/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/cast.kt @@ -0,0 +1,4 @@ +// JS +fun test(foo: dynamic) { + val s = foo as String +} \ No newline at end of file diff --git a/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/cast.kt.after b/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/cast.kt.after new file mode 100644 index 00000000000..dc412102c5f --- /dev/null +++ b/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/cast.kt.after @@ -0,0 +1,4 @@ +// JS +fun test(foo: dynamic) { + val s = foo.unsafeCast() +} \ No newline at end of file diff --git a/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/safeCast.kt b/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/safeCast.kt new file mode 100644 index 00000000000..9444a2eb9d4 --- /dev/null +++ b/idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/safeCast.kt @@ -0,0 +1,5 @@ +// JS +// IS_APPLICABLE: false +fun test(foo: Any) { + val s = foo as? String +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 1c25acca029..08e4e7d94cc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -7141,6 +7141,48 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/convertUnsafeCastCallToUnsafeCast") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertUnsafeCastCallToUnsafeCast extends AbstractIntentionTest { + public void testAllFilesPresentInConvertUnsafeCastCallToUnsafeCast() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertUnsafeCastCallToUnsafeCast"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("call.kt") + public void testCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/call.kt"); + doTest(fileName); + } + + @TestMetadata("safeCall.kt") + public void testSafeCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertUnsafeCastCallToUnsafeCast/safeCall.kt"); + doTest(fileName); + } + } + + @TestMetadata("idea/testData/intentions/convertUnsafeCastToUnsafeCastCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertUnsafeCastToUnsafeCastCall extends AbstractIntentionTest { + public void testAllFilesPresentInConvertUnsafeCastToUnsafeCastCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertUnsafeCastToUnsafeCastCall"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("cast.kt") + public void testCast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/cast.kt"); + doTest(fileName); + } + + @TestMetadata("safeCast.kt") + public void testSafeCast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertUnsafeCastToUnsafeCastCall/safeCast.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/copyConcatenatedStringToClipboard") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)