From 6b2c22aff18ed1386963058fc028fe257ed39ff3 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Thu, 17 Aug 2017 09:45:36 +0900 Subject: [PATCH] Add intention to specify all types explicitly in destructuring assignment #KT-16260 Fixed --- .../after.kt.template | 3 ++ .../before.kt.template | 3 ++ .../description.html | 5 ++ idea/src/META-INF/plugin.xml | 5 ++ ...citlyInDestructuringAssignmentIntention.kt | 43 ++++++++++++++++ .../SpecifyTypeExplicitlyIntention.kt | 25 +++++++-- .../.intention | 1 + .../in.kt | 6 +++ .../in.kt.after | 6 +++ .../lambda.kt | 6 +++ .../lambda.kt.after | 6 +++ .../lambdaHasSignature.kt | 6 +++ .../lambdaHasSignature.kt.after | 6 +++ .../variableHasAllTypes.kt | 5 ++ .../variableHasNoTypes.kt | 4 ++ .../variableHasNoTypes.kt.after | 4 ++ .../variableHasTypes.kt | 4 ++ .../variableHasTypes.kt.after | 4 ++ .../variableHasUnderscore.kt | 4 ++ .../variableHasUnderscore.kt.after | 4 ++ .../intentions/IntentionTestGenerated.java | 51 +++++++++++++++++++ 21 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention.kt create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/.intention create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt.after create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt.after create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt.after create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasAllTypes.kt create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt.after create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt.after create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt create mode 100644 idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt.after diff --git a/idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/after.kt.template b/idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/after.kt.template new file mode 100644 index 00000000000..ffef59be997 --- /dev/null +++ b/idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/after.kt.template @@ -0,0 +1,3 @@ +fun main(args: Array) { + val (i: Int, s: String) = Pair(1, "s") +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/before.kt.template b/idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/before.kt.template new file mode 100644 index 00000000000..28a57c0550e --- /dev/null +++ b/idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/before.kt.template @@ -0,0 +1,3 @@ +fun main(args: Array) { + val (i, s) = Pair(1, "s") +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/description.html b/idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/description.html new file mode 100644 index 00000000000..264e5dba2a1 --- /dev/null +++ b/idea/resources/intentionDescriptions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention/description.html @@ -0,0 +1,5 @@ + + +Specifies all types explicitly in a destructuring declaration. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 7d3ee5a2907..20711bf3b8a 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -799,6 +799,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyInDestructuringAssignmentIntention + Kotlin + + org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeIntention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention.kt new file mode 100644 index 00000000000..c6c4f418e3f --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyInDestructuringAssignmentIntention.kt @@ -0,0 +1,43 @@ +/* + * 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.codeInsight.intention.LowPriorityAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.util.TextRange +import org.jetbrains.kotlin.idea.core.setType +import org.jetbrains.kotlin.psi.KtCodeFragment +import org.jetbrains.kotlin.psi.KtDestructuringDeclaration +import org.jetbrains.kotlin.psi.KtParameterList +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.types.isError + +class SpecifyTypeExplicitlyInDestructuringAssignmentIntention : SelfTargetingRangeIntention( + KtDestructuringDeclaration::class.java, "Specify all types explicitly in destructuring declaration" +), LowPriorityAction { + + override fun applicabilityRange(element: KtDestructuringDeclaration): TextRange? { + if (element.containingFile is KtCodeFragment) return null + val entries = element.noTypeReferenceEntries() + if (entries.isEmpty()) return null + if (entries.any { SpecifyTypeExplicitlyIntention.getTypeForDeclaration(it).isError }) return null + return TextRange(element.startOffset, element.initializer?.let { it.startOffset - 1 } ?: element.endOffset) + } + + override fun applyTo(element: KtDestructuringDeclaration, editor: Editor?) { + val entries = element.noTypeReferenceEntries() + if (editor != null && element.getParentOfType(false) == null) + SpecifyTypeExplicitlyIntention.addTypeAnnotationWithTemplate(editor, entries.iterator()) + else + entries.forEach { + it.setType(SpecifyTypeExplicitlyIntention.getTypeForDeclaration(it)) + } + } +} + +private fun KtDestructuringDeclaration.noTypeReferenceEntries() = entries.filter { it.typeReference == null } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt index ab3e1fda2f3..453f99d05c2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt @@ -166,18 +166,32 @@ class SpecifyTypeExplicitlyIntention : } } - fun createTypeReferencePostprocessor(declaration: KtCallableDeclaration): TemplateEditingAdapter { + @JvmOverloads + fun createTypeReferencePostprocessor(declaration: KtCallableDeclaration, + iterator: Iterator? = null, + editor: Editor? = null): TemplateEditingAdapter { return object : TemplateEditingAdapter() { override fun templateFinished(template: Template?, brokenOff: Boolean) { val typeRef = declaration.typeReference if (typeRef != null && typeRef.isValid) { - runWriteAction { ShortenReferences.DEFAULT.process(typeRef) } + runWriteAction { + ShortenReferences.DEFAULT.process(typeRef) + if (iterator != null && editor != null) addTypeAnnotationWithTemplate(editor, iterator) + } } } } } - private fun addTypeAnnotationWithTemplate(editor: Editor, declaration: KtCallableDeclaration, exprType: KotlinType) { + fun addTypeAnnotationWithTemplate(editor: Editor, iterator: Iterator?) { + if (iterator == null || !iterator.hasNext()) return + val declaration = iterator.next() + val exprType = getTypeForDeclaration(declaration) + addTypeAnnotationWithTemplate(editor, declaration, exprType, iterator) + } + + private fun addTypeAnnotationWithTemplate(editor: Editor, declaration: KtCallableDeclaration, exprType: KotlinType, + iterator: Iterator? = null) { assert(!exprType.isError) { "Unexpected error type, should have been checked before: " + declaration.getElementTextWithContext() + ", type = " + exprType } val project = declaration.project @@ -194,7 +208,10 @@ class SpecifyTypeExplicitlyIntention : editor.caretModel.moveToOffset(newTypeRef.node.startOffset) - TemplateManager.getInstance(project).startTemplate(editor, builder.buildInlineTemplate(), createTypeReferencePostprocessor(declaration)) + TemplateManager.getInstance(project).startTemplate( + editor, + builder.buildInlineTemplate(), + createTypeReferencePostprocessor(declaration, iterator, editor)) } } } diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/.intention b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/.intention new file mode 100644 index 00000000000..d3625b22476 --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyInDestructuringAssignmentIntention diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt new file mode 100644 index 00000000000..447666dc2fa --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt @@ -0,0 +1,6 @@ +// WITH_RUNTIME +fun test() { + val map = mapOf(1 to "two") + for ((key, value) in map) { + } +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt.after b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt.after new file mode 100644 index 00000000000..3771fddde9b --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt.after @@ -0,0 +1,6 @@ +// WITH_RUNTIME +fun test() { + val map = mapOf(1 to "two") + for ((key: Int, value: String) in map) { + } +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt new file mode 100644 index 00000000000..12b9a59236a --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt @@ -0,0 +1,6 @@ +// WITH_RUNTIME +fun test() { + val list = emptyList>() + list.forEach { (i, s) -> + } +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt.after b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt.after new file mode 100644 index 00000000000..df8798570c4 --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt.after @@ -0,0 +1,6 @@ +// WITH_RUNTIME +fun test() { + val list = emptyList>() + list.forEach { (i: Int, s: String) -> + } +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt new file mode 100644 index 00000000000..db8e52edd73 --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt @@ -0,0 +1,6 @@ +// WITH_RUNTIME +fun test() { + val list = emptyList>() + list.forEach { (i, s): Pair -> + } +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt.after b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt.after new file mode 100644 index 00000000000..ffc1b4dbd91 --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt.after @@ -0,0 +1,6 @@ +// WITH_RUNTIME +fun test() { + val list = emptyList>() + list.forEach { (i: Int, s: String): Pair -> + } +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasAllTypes.kt b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasAllTypes.kt new file mode 100644 index 00000000000..b7748eec1fb --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasAllTypes.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME +fun test() { + val (i: Int, s: String) = Pair(1, "s") +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt new file mode 100644 index 00000000000..38b189ae390 --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun test() { + val (i, s) = Pair(1, "s") +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt.after b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt.after new file mode 100644 index 00000000000..5d5fc25acac --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun test() { + val (i: Int, s: String) = Pair(1, "s") +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt new file mode 100644 index 00000000000..fe8af6115ab --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun test() { + val (i: Int, s) = Pair(1, "s") +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt.after b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt.after new file mode 100644 index 00000000000..5d5fc25acac --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun test() { + val (i: Int, s: String) = Pair(1, "s") +} \ No newline at end of file diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt new file mode 100644 index 00000000000..d4eebafcd65 --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun test() { + val (_, s) = Pair(1, "s") +} diff --git a/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt.after b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt.after new file mode 100644 index 00000000000..a5b4e6de0f5 --- /dev/null +++ b/idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun test() { + val (_: Int, s: String) = Pair(1, "s") +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index c9b57820833..7921a0de073 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -14724,6 +14724,57 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SpecifyTypeExplicitlyInDestructuringAssignment extends AbstractIntentionTest { + public void testAllFilesPresentInSpecifyTypeExplicitlyInDestructuringAssignment() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("in.kt") + public void testIn() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/in.kt"); + doTest(fileName); + } + + @TestMetadata("lambda.kt") + public void testLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambda.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaHasSignature.kt") + public void testLambdaHasSignature() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/lambdaHasSignature.kt"); + doTest(fileName); + } + + @TestMetadata("variableHasAllTypes.kt") + public void testVariableHasAllTypes() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasAllTypes.kt"); + doTest(fileName); + } + + @TestMetadata("variableHasNoTypes.kt") + public void testVariableHasNoTypes() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasNoTypes.kt"); + doTest(fileName); + } + + @TestMetadata("variableHasTypes.kt") + public void testVariableHasTypes() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasTypes.kt"); + doTest(fileName); + } + + @TestMetadata("variableHasUnderscore.kt") + public void testVariableHasUnderscore() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/specifyTypeExplicitlyInDestructuringAssignment/variableHasUnderscore.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/splitIf") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)