From 48310d66dacb3f6db9d1a964f3157038d3d2f79a Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Fri, 11 Mar 2016 00:30:18 +0100 Subject: [PATCH] Implement quick fix for converting primitive types #KT-10476 Fixed --- .../org/jetbrains/kotlin/types/TypeUtils.kt | 2 + .../idea/quickfix/NumberConversionFix.kt | 52 +++++++++++++++++++ .../QuickFixFactoryForTypeMismatchError.java | 4 ++ .../convertBinaryExpression.kt | 7 +++ .../convertBinaryExpression.kt.after | 7 +++ .../numberConversion/convertLiteral.kt | 7 +++ .../numberConversion/convertLiteral.kt.after | 7 +++ .../idea/quickfix/QuickFixTestGenerated.java | 21 ++++++++ 8 files changed, 107 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/NumberConversionFix.kt create mode 100644 idea/testData/quickfix/typeMismatch/numberConversion/convertBinaryExpression.kt create mode 100644 idea/testData/quickfix/typeMismatch/numberConversion/convertBinaryExpression.kt.after create mode 100644 idea/testData/quickfix/typeMismatch/numberConversion/convertLiteral.kt create mode 100644 idea/testData/quickfix/typeMismatch/numberConversion/convertLiteral.kt.after diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index 1254c806ffd..ac31397cc90 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -55,6 +55,7 @@ fun KotlinType.isUnit(): Boolean = KotlinBuiltIns.isUnit(this) fun KotlinType.isAnyOrNullableAny(): Boolean = KotlinBuiltIns.isAnyOrNullableAny(this) fun KotlinType.isNullableAny(): Boolean = KotlinBuiltIns.isNullableAny(this) fun KotlinType.isBoolean(): Boolean = KotlinBuiltIns.isBoolean(this) +fun KotlinType.isPrimitiveNumberType(): Boolean = KotlinBuiltIns.isPrimitiveType(this) && !isBoolean() fun KotlinType.isBooleanOrNullableBoolean(): Boolean = KotlinBuiltIns.isBooleanOrNullableBoolean(this) fun KotlinType.isTypeParameter(): Boolean = TypeUtils.isTypeParameter(this) @@ -71,6 +72,7 @@ fun KotlinType.isSubtypeOf(superType: KotlinType): Boolean = KotlinTypeChecker.D fun KotlinType.cannotBeReified(): Boolean = KotlinBuiltIns.isNothingOrNullableNothing(this) || this.isDynamic() || this.isCaptured() + fun KotlinType.unsafeAsReifiedArgument(): Boolean = arguments.any { !it.isStarProjection } fun TypeProjection.substitute(doSubstitute: (KotlinType) -> KotlinType): TypeProjection { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/NumberConversionFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/NumberConversionFix.kt new file mode 100644 index 00000000000..a682adbf4ed --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/NumberConversionFix.kt @@ -0,0 +1,52 @@ +/* + * 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 com.intellij.psi.PsiFile +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +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 +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType + +class NumberConversionFix(element: KtExpression, private val type: KotlinType) : KotlinQuickFixAction(element) { + + override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { + if (!super.isAvailable(project, editor, file)) return false + + val expressionType = element.analyze(BodyResolveMode.PARTIAL).getType(element) ?: return false + return expressionType != type && expressionType.isPrimitiveNumberType() && type.isPrimitiveNumberType() + } + + override fun getFamilyName() = "Insert number conversion" + override fun getText() = "Convert expression to '${IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type)}'" + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val renderedType = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type) + val expressionToInsert = KtPsiFactory(file).createExpressionByPattern("$0.to$1()", element, renderedType) + 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.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.java index 1169682c2a5..92cbd0d1d56 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.java @@ -93,6 +93,10 @@ public class QuickFixFactoryForTypeMismatchError extends KotlinIntentionActionsF return Collections.emptyList(); } + if (TypeUtilsKt.isPrimitiveNumberType(expressionType) && TypeUtilsKt.isPrimitiveNumberType(expectedType)) { + actions.add(new NumberConversionFix(expression, expectedType)); + } + // We don't want to cast a cast or type-asserted expression: if (!(expression instanceof KtBinaryExpressionWithTypeRHS) && !(expression.getParent() instanceof KtBinaryExpressionWithTypeRHS)) { actions.add(new CastExpressionFix(expression, expectedType)); diff --git a/idea/testData/quickfix/typeMismatch/numberConversion/convertBinaryExpression.kt b/idea/testData/quickfix/typeMismatch/numberConversion/convertBinaryExpression.kt new file mode 100644 index 00000000000..02c668370fe --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/numberConversion/convertBinaryExpression.kt @@ -0,0 +1,7 @@ +// "Convert expression to 'Float'" "true" +fun foo() { + bar(1 + 3L) +} + +fun bar(l: Float) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/numberConversion/convertBinaryExpression.kt.after b/idea/testData/quickfix/typeMismatch/numberConversion/convertBinaryExpression.kt.after new file mode 100644 index 00000000000..0dae383fcf5 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/numberConversion/convertBinaryExpression.kt.after @@ -0,0 +1,7 @@ +// "Convert expression to 'Float'" "true" +fun foo() { + bar((1 + 3L).toFloat()) +} + +fun bar(l: Float) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/numberConversion/convertLiteral.kt b/idea/testData/quickfix/typeMismatch/numberConversion/convertLiteral.kt new file mode 100644 index 00000000000..e1f4f5c63b6 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/numberConversion/convertLiteral.kt @@ -0,0 +1,7 @@ +// "Convert expression to 'Int'" "true" +fun foo() { + bar(1L) +} + +fun bar(l: Int) { +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/numberConversion/convertLiteral.kt.after b/idea/testData/quickfix/typeMismatch/numberConversion/convertLiteral.kt.after new file mode 100644 index 00000000000..8cc6903238b --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/numberConversion/convertLiteral.kt.after @@ -0,0 +1,7 @@ +// "Convert expression to 'Int'" "true" +fun foo() { + bar(1L.toInt()) +} + +fun bar(l: Int) { +} \ 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 c9e4605df3e..ac6e832c2be 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -7444,6 +7444,27 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/typeMismatch/numberConversion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NumberConversion extends AbstractQuickFixTest { + public void testAllFilesPresentInNumberConversion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/typeMismatch/numberConversion"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("convertBinaryExpression.kt") + public void testConvertBinaryExpression() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/numberConversion/convertBinaryExpression.kt"); + doTest(fileName); + } + + @TestMetadata("convertLiteral.kt") + public void testConvertLiteral() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/numberConversion/convertLiteral.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/typeMismatch/parameterTypeMismatch") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)