From 2629d235762695b43a1a270aa8b9c688e59a6dbf Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Thu, 28 Apr 2016 11:15:39 +0200 Subject: [PATCH] Add quickfix to add 'toString' (#866) Fixes #KT-12121 --- .../kotlin/builtins/KotlinBuiltIns.java | 8 ++++ .../kotlin/idea/quickfix/AddToStringFix.kt | 38 +++++++++++++++++++ .../QuickFixFactoryForTypeMismatchError.kt | 8 ++++ .../toString/notNullableExpectedNullable.kt | 9 +++++ .../notNullableExpectedNullable.kt.after | 9 +++++ .../toString/nullableExpectedNotNullable.kt | 9 +++++ .../nullableExpectedNotNullable.kt.after | 9 +++++ .../toString/nullableExpectedNullable.kt | 10 +++++ .../nullableExpectedNullable.kt.after | 10 +++++ idea/testData/quickfix/toString/simple.kt | 8 ++++ .../quickfix/toString/simple.kt.after | 8 ++++ .../idea/quickfix/QuickFixTestGenerated.java | 33 ++++++++++++++++ 12 files changed, 159 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/AddToStringFix.kt create mode 100644 idea/testData/quickfix/toString/notNullableExpectedNullable.kt create mode 100644 idea/testData/quickfix/toString/notNullableExpectedNullable.kt.after create mode 100644 idea/testData/quickfix/toString/nullableExpectedNotNullable.kt create mode 100644 idea/testData/quickfix/toString/nullableExpectedNotNullable.kt.after create mode 100644 idea/testData/quickfix/toString/nullableExpectedNullable.kt create mode 100644 idea/testData/quickfix/toString/nullableExpectedNullable.kt.after create mode 100644 idea/testData/quickfix/toString/simple.kt create mode 100644 idea/testData/quickfix/toString/simple.kt.after diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index ae0672e32db..894bb03538f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -879,6 +879,14 @@ public abstract class KotlinBuiltIns { return type != null && isNotNullConstructedFromGivenClass(type, FQ_NAMES.string); } + public static boolean isCharSequenceOrNullableCharSequence(@Nullable KotlinType type) { + return type != null && isConstructedFromGivenClass(type, FQ_NAMES.charSequence); + } + + public static boolean isStringOrNullableString(@Nullable KotlinType type) { + return type != null && isConstructedFromGivenClass(type, FQ_NAMES.string); + } + public static boolean isCollectionOrNullableCollection(@NotNull KotlinType type) { return isConstructedFromGivenClass(type, FQ_NAMES._collection); } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddToStringFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddToStringFix.kt new file mode 100644 index 00000000000..6cc8b4e29c3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddToStringFix.kt @@ -0,0 +1,38 @@ +/* + * 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.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.createExpressionByPattern +import org.jetbrains.kotlin.psi.psiUtil.endOffset + +class AddToStringFix(element: KtExpression, private val nullable: Boolean) : KotlinQuickFixAction(element) { + override fun getFamilyName() = "Add 'toString()' call" + override fun getText() = if (nullable) "Add safe '?.toString()' call" else "Add 'toString()' call" + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val pattern = if (nullable) "$0?.toString()" else "$0.toString()" + val expressionToInsert = KtPsiFactory(file).createExpressionByPattern(pattern, element) + val newExpression = element.replaced(expressionToInsert) + editor?.caretModel?.moveToOffset(newExpression.endOffset) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt index b87089142d5..4129fa8fcb8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt @@ -86,6 +86,14 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() { actions.add(NumberConversionFix(diagnosticElement, expectedType)) } + if (KotlinBuiltIns.isCharSequenceOrNullableCharSequence(expectedType) + || KotlinBuiltIns.isStringOrNullableString(expectedType)) { + actions.add(AddToStringFix(diagnosticElement, false)) + if (expectedType.isMarkedNullable && expressionType.isMarkedNullable) { + actions.add(AddToStringFix(diagnosticElement, true)) + } + } + if (expectedType.isInterface()) { val expressionTypeDeclaration = expressionType.constructor.declarationDescriptor?.let { DescriptorToSourceUtils.descriptorToDeclaration(it) diff --git a/idea/testData/quickfix/toString/notNullableExpectedNullable.kt b/idea/testData/quickfix/toString/notNullableExpectedNullable.kt new file mode 100644 index 00000000000..41207da0cf5 --- /dev/null +++ b/idea/testData/quickfix/toString/notNullableExpectedNullable.kt @@ -0,0 +1,9 @@ +// "Add 'toString()' call" "true" +// ACTION: Add 'toString()' call + +fun foo() { + bar(Any()) +} + +fun bar(a: String?) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/toString/notNullableExpectedNullable.kt.after b/idea/testData/quickfix/toString/notNullableExpectedNullable.kt.after new file mode 100644 index 00000000000..c78c5b5ea7a --- /dev/null +++ b/idea/testData/quickfix/toString/notNullableExpectedNullable.kt.after @@ -0,0 +1,9 @@ +// "Add 'toString()' call" "true" +// ACTION: Add 'toString()' call + +fun foo() { + bar(Any().toString()) +} + +fun bar(a: String?) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/toString/nullableExpectedNotNullable.kt b/idea/testData/quickfix/toString/nullableExpectedNotNullable.kt new file mode 100644 index 00000000000..f665a335147 --- /dev/null +++ b/idea/testData/quickfix/toString/nullableExpectedNotNullable.kt @@ -0,0 +1,9 @@ +// "Add 'toString()' call" "true" +// ACTION: Add 'toString()' call + +fun foo() { + bar(null as Any?) +} + +fun bar(a: String) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/toString/nullableExpectedNotNullable.kt.after b/idea/testData/quickfix/toString/nullableExpectedNotNullable.kt.after new file mode 100644 index 00000000000..13b3dd658c7 --- /dev/null +++ b/idea/testData/quickfix/toString/nullableExpectedNotNullable.kt.after @@ -0,0 +1,9 @@ +// "Add 'toString()' call" "true" +// ACTION: Add 'toString()' call + +fun foo() { + bar((null as Any?).toString()) +} + +fun bar(a: String) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/toString/nullableExpectedNullable.kt b/idea/testData/quickfix/toString/nullableExpectedNullable.kt new file mode 100644 index 00000000000..4088bb3a058 --- /dev/null +++ b/idea/testData/quickfix/toString/nullableExpectedNullable.kt @@ -0,0 +1,10 @@ +// "Add safe '?.toString()' call" "true" +// ACTION: Add 'toString()' call +// ACTION: Add safe '?.toString()' call + +fun foo() { + bar(null as Any?) +} + +fun bar(a: String?) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/toString/nullableExpectedNullable.kt.after b/idea/testData/quickfix/toString/nullableExpectedNullable.kt.after new file mode 100644 index 00000000000..119fad4509b --- /dev/null +++ b/idea/testData/quickfix/toString/nullableExpectedNullable.kt.after @@ -0,0 +1,10 @@ +// "Add safe '?.toString()' call" "true" +// ACTION: Add 'toString()' call +// ACTION: Add safe '?.toString()' call + +fun foo() { + bar((null as Any?)?.toString()) +} + +fun bar(a: String?) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/toString/simple.kt b/idea/testData/quickfix/toString/simple.kt new file mode 100644 index 00000000000..8d6dfb95779 --- /dev/null +++ b/idea/testData/quickfix/toString/simple.kt @@ -0,0 +1,8 @@ +// "Add 'toString()' call" "true" + +fun foo() { + bar(Any()) +} + +fun bar(a: String) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/toString/simple.kt.after b/idea/testData/quickfix/toString/simple.kt.after new file mode 100644 index 00000000000..0a990ef641c --- /dev/null +++ b/idea/testData/quickfix/toString/simple.kt.after @@ -0,0 +1,8 @@ +// "Add 'toString()' call" "true" + +fun foo() { + bar(Any().toString()) +} + +fun bar(a: String) { +} \ 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 01a163a8fc9..44f284ee5dd 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -7265,6 +7265,39 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/toString") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ToString extends AbstractQuickFixTest { + public void testAllFilesPresentInToString() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/toString"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("notNullableExpectedNullable.kt") + public void testNotNullableExpectedNullable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/toString/notNullableExpectedNullable.kt"); + doTest(fileName); + } + + @TestMetadata("nullableExpectedNotNullable.kt") + public void testNullableExpectedNotNullable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/toString/nullableExpectedNotNullable.kt"); + doTest(fileName); + } + + @TestMetadata("nullableExpectedNullable.kt") + public void testNullableExpectedNullable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/toString/nullableExpectedNullable.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/toString/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/typeAddition") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)