From d67c793a9bccd8786a8bc51150757e4da230bd08 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 22 Feb 2019 12:07:38 +0900 Subject: [PATCH] map.get() with not-null assertion: add quick-fixes #KT-30010 Fixed --- .../MapGetWithNotNullAssertionOperator.html | 2 +- ...tWithNotNullAssertionOperatorInspection.kt | 37 ++++++++++++++++++- .../mapGetWithNotNullAssertionOperator/get.kt | 1 + .../get.kt.after | 1 + .../indexedAccess.kt | 1 + .../indexedAccess.kt.after | 1 + .../replaceWithElvisError.kt | 5 +++ .../replaceWithElvisError.kt.after | 5 +++ .../replaceWithGetOrElse.kt | 6 +++ .../replaceWithGetOrElse.kt.after | 6 +++ .../LocalInspectionTestGenerated.java | 10 +++++ 11 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithElvisError.kt create mode 100644 idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithElvisError.kt.after create mode 100644 idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithGetOrElse.kt create mode 100644 idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithGetOrElse.kt.after diff --git a/idea/resources/inspectionDescriptions/MapGetWithNotNullAssertionOperator.html b/idea/resources/inspectionDescriptions/MapGetWithNotNullAssertionOperator.html index a5884cc186a..81f6877429b 100644 --- a/idea/resources/inspectionDescriptions/MapGetWithNotNullAssertionOperator.html +++ b/idea/resources/inspectionDescriptions/MapGetWithNotNullAssertionOperator.html @@ -1,5 +1,5 @@ -This inspection reports map.get()!! that can be replaced with map.getValue(). +This inspection reports map.get()!! that can be replaced with map.getValue(), map.getOrElse(), etc. \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/MapGetWithNotNullAssertionOperatorInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/MapGetWithNotNullAssertionOperatorInspection.kt index 06fd623191a..96d7d20d465 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/MapGetWithNotNullAssertionOperatorInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/MapGetWithNotNullAssertionOperatorInspection.kt @@ -10,6 +10,7 @@ import com.intellij.codeInspection.ProblemDescriptor import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDocumentManager import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.intentions.callExpression @@ -17,6 +18,7 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe class MapGetWithNotNullAssertionOperatorInspection : AbstractKotlinInspection() { @@ -29,7 +31,9 @@ class MapGetWithNotNullAssertionOperatorInspection : AbstractKotlinInspection() expression.operationReference, "map.get() with not-null assertion operator (!!)", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - ReplaceWithGetValueCallFix() + ReplaceWithGetValueCallFix(), + ReplaceWithGetOrElseFix(), + ReplaceWithElvisErrorFix() ) }) @@ -43,6 +47,37 @@ class MapGetWithNotNullAssertionOperatorInspection : AbstractKotlinInspection() replaced.findExistingEditor()?.caretModel?.moveToOffset(replaced.endOffset) } } + + private class ReplaceWithGetOrElseFix : LocalQuickFix { + override fun getName() = "Replace with 'getOrElse' call" + override fun getFamilyName() = name + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val expression = descriptor.psiElement.parent as? KtPostfixExpression ?: return + val (reference, index) = expression.getReplacementData() ?: return + val replaced = expression.replaced(KtPsiFactory(expression).createExpressionByPattern("$0.getOrElse($1){}", reference, index)) + + val editor = replaced.findExistingEditor() ?: return + val offset = (replaced as KtQualifiedExpression).callExpression?.lambdaArguments?.firstOrNull()?.startOffset ?: return + val document = editor.document + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document) + document.insertString(offset + 1, " ") + editor.caretModel.moveToOffset(offset + 2) + } + } + + private class ReplaceWithElvisErrorFix : LocalQuickFix { + override fun getName() = "Replace with '?: error(\"\")'" + override fun getFamilyName() = name + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val expression = descriptor.psiElement.parent as? KtPostfixExpression ?: return + val (reference, index) = expression.getReplacementData() ?: return + val replaced = expression.replace(KtPsiFactory(expression).createExpressionByPattern("$0[$1] ?: error(\"\")", reference, index)) + + val editor = replaced.findExistingEditor() ?: return + val offset = (replaced as? KtBinaryExpression)?.right?.endOffset ?: return + editor.caretModel.moveToOffset(offset - 2) + } + } } private fun KtPostfixExpression.getReplacementData(): Pair? { diff --git a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/get.kt b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/get.kt index 26e1fe89a2b..19b06741146 100644 --- a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/get.kt +++ b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/get.kt @@ -1,3 +1,4 @@ +// FIX: Replace with 'getValue' call // WITH_RUNTIME fun test(map: Map) { val s = map.get(1)!! diff --git a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/get.kt.after b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/get.kt.after index 43f6104cd3c..6b558d2b016 100644 --- a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/get.kt.after +++ b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/get.kt.after @@ -1,3 +1,4 @@ +// FIX: Replace with 'getValue' call // WITH_RUNTIME fun test(map: Map) { val s = map.getValue(1) diff --git a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/indexedAccess.kt b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/indexedAccess.kt index 1aaa64a7e05..576c2e93190 100644 --- a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/indexedAccess.kt +++ b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/indexedAccess.kt @@ -1,3 +1,4 @@ +// FIX: Replace with 'getValue' call // WITH_RUNTIME fun test(map: Map) { val s = map[1]!! diff --git a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/indexedAccess.kt.after b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/indexedAccess.kt.after index 43f6104cd3c..6b558d2b016 100644 --- a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/indexedAccess.kt.after +++ b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/indexedAccess.kt.after @@ -1,3 +1,4 @@ +// FIX: Replace with 'getValue' call // WITH_RUNTIME fun test(map: Map) { val s = map.getValue(1) diff --git a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithElvisError.kt b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithElvisError.kt new file mode 100644 index 00000000000..4302535abcc --- /dev/null +++ b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithElvisError.kt @@ -0,0 +1,5 @@ +// FIX: Replace with '?: error("")' +// WITH_RUNTIME +fun test(map: Map) { + val s = map[1]!! +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithElvisError.kt.after b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithElvisError.kt.after new file mode 100644 index 00000000000..f1a93dabc10 --- /dev/null +++ b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithElvisError.kt.after @@ -0,0 +1,5 @@ +// FIX: Replace with '?: error("")' +// WITH_RUNTIME +fun test(map: Map) { + val s = map[1] ?: error("") +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithGetOrElse.kt b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithGetOrElse.kt new file mode 100644 index 00000000000..dcc744fa7de --- /dev/null +++ b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithGetOrElse.kt @@ -0,0 +1,6 @@ +// FIX: Replace with 'getOrElse' call +// DISABLE-ERRORS +// WITH_RUNTIME +fun test(map: Map) { + val s = map[1]!! +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithGetOrElse.kt.after b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithGetOrElse.kt.after new file mode 100644 index 00000000000..c6a4c2241a8 --- /dev/null +++ b/idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithGetOrElse.kt.after @@ -0,0 +1,6 @@ +// FIX: Replace with 'getOrElse' call +// DISABLE-ERRORS +// WITH_RUNTIME +fun test(map: Map) { + val s = map.getOrElse(1) { } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 37a3bbc22e2..e70ba38eca1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -3840,6 +3840,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { public void testNoNotNullAssersion() throws Exception { runTest("idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/noNotNullAssersion.kt"); } + + @TestMetadata("replaceWithElvisError.kt") + public void testReplaceWithElvisError() throws Exception { + runTest("idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithElvisError.kt"); + } + + @TestMetadata("replaceWithGetOrElse.kt") + public void testReplaceWithGetOrElse() throws Exception { + runTest("idea/testData/inspectionsLocal/mapGetWithNotNullAssertionOperator/replaceWithGetOrElse.kt"); + } } @TestMetadata("idea/testData/inspectionsLocal/mayBeConstant")