From 8420fceb8cd7b30b49383666e681353b82246d32 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 26 Dec 2018 15:05:31 +0300 Subject: [PATCH] Support '.toType()' and literal fixes related to unsigned type mismatch #KT-26836 Fixed --- .../org/jetbrains/kotlin/types/TypeUtils.kt | 3 +++ .../idea/quickfix/NumberConversionFix.kt | 16 ++++++------- .../QuickFixFactoryForTypeMismatchError.kt | 2 +- .../idea/quickfix/WrongPrimitiveLiteralFix.kt | 23 +++++++++++-------- .../quickfix/typeMismatch/casts/unsigned.kt | 1 + .../numberConversion/convertToSigned.kt | 7 ++++++ .../numberConversion/convertToSigned.kt.after | 7 ++++++ .../numberConversion/convertToUnsigned.kt | 7 ++++++ .../convertToUnsigned.kt.after | 7 ++++++ .../wrongPrimitive/intToUnsigned.kt | 7 ++++++ .../wrongPrimitive/intToUnsigned.kt.after | 7 ++++++ .../wrongPrimitive/unsignedToInt.kt | 7 ++++++ .../wrongPrimitive/unsignedToInt.kt.after | 7 ++++++ .../idea/quickfix/QuickFixTestGenerated.java | 20 ++++++++++++++++ 14 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 idea/testData/quickfix/typeMismatch/numberConversion/convertToSigned.kt create mode 100644 idea/testData/quickfix/typeMismatch/numberConversion/convertToSigned.kt.after create mode 100644 idea/testData/quickfix/typeMismatch/numberConversion/convertToUnsigned.kt create mode 100644 idea/testData/quickfix/typeMismatch/numberConversion/convertToUnsigned.kt.after create mode 100644 idea/testData/quickfix/typeMismatch/wrongPrimitive/intToUnsigned.kt create mode 100644 idea/testData/quickfix/typeMismatch/wrongPrimitive/intToUnsigned.kt.after create mode 100644 idea/testData/quickfix/typeMismatch/wrongPrimitive/unsignedToInt.kt create mode 100644 idea/testData/quickfix/typeMismatch/wrongPrimitive/unsignedToInt.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 1a8bffe936a..b056f3d287a 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.types.typeUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.UnsignedTypes import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -58,6 +59,8 @@ fun KotlinType.isAnyOrNullableAny(): Boolean = KotlinBuiltIns.isAnyOrNullableAny fun KotlinType.isNullableAny(): Boolean = KotlinBuiltIns.isNullableAny(this) fun KotlinType.isBoolean(): Boolean = KotlinBuiltIns.isBoolean(this) fun KotlinType.isPrimitiveNumberType(): Boolean = KotlinBuiltIns.isPrimitiveType(this) && !isBoolean() +fun KotlinType.isUnsignedNumberType(): Boolean = UnsignedTypes.isUnsignedType(this) +fun KotlinType.isSignedOrUnsignedNumberType(): Boolean = isPrimitiveNumberType() || isUnsignedNumberType() fun KotlinType.isBooleanOrNullableBoolean(): Boolean = KotlinBuiltIns.isBooleanOrNullableBoolean(this) fun KotlinType.isNotNullThrowable(): Boolean = KotlinBuiltIns.isThrowableOrNullableThrowable(this) && !isMarkedNullable diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/NumberConversionFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/NumberConversionFix.kt index 124f6a65a8c..5f6c3a61050 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/NumberConversionFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/NumberConversionFix.kt @@ -29,22 +29,22 @@ 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 +import org.jetbrains.kotlin.types.typeUtil.isSignedOrUnsignedNumberType class NumberConversionFix( - element: KtExpression, - type: KotlinType, - private val disableIfAvailable: IntentionAction? = null + element: KtExpression, + type: KotlinType, + private val disableIfAvailable: IntentionAction? = null ) : KotlinQuickFixAction(element) { private val isConversionAvailable: Boolean = run { val expressionType = element.analyze(BodyResolveMode.PARTIAL).getType(element) - expressionType != null && expressionType != type && expressionType.isPrimitiveNumberType() && type.isPrimitiveNumberType() + expressionType != null && expressionType != type && + expressionType.isSignedOrUnsignedNumberType() && type.isSignedOrUnsignedNumberType() } private val typePresentation = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderType(type) - override fun isAvailable(project: Project, editor: Editor?, file: KtFile) - = disableIfAvailable?.isAvailable(project, editor, file) != true - && isConversionAvailable + override fun isAvailable(project: Project, editor: Editor?, file: KtFile) = + disableIfAvailable?.isAvailable(project, editor, file) != true && isConversionAvailable override fun getFamilyName() = "Insert number conversion" override fun getText() = "Convert expression to '$typePresentation'" diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt index dd4c3212edf..18f3d7e6ea1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt @@ -117,7 +117,7 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() { return emptyList() } - if (expressionType.isPrimitiveNumberType() && expectedType.isPrimitiveNumberType()) { + if (expressionType.isSignedOrUnsignedNumberType() && expectedType.isSignedOrUnsignedNumberType()) { var wrongPrimitiveLiteralFix: WrongPrimitiveLiteralFix? = null if (diagnosticElement is KtConstantExpression && !KotlinBuiltIns.isChar(expectedType)) { wrongPrimitiveLiteralFix = WrongPrimitiveLiteralFix(diagnosticElement, expectedType) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/WrongPrimitiveLiteralFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/WrongPrimitiveLiteralFix.kt index 3212868daf2..8dd613dc666 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/WrongPrimitiveLiteralFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/WrongPrimitiveLiteralFix.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isUnsignedNumberType private val valueRanges = mapOf( KotlinBuiltIns.FQ_NAMES._byte to Byte.MIN_VALUE.toLong()..Byte.MAX_VALUE.toLong(), @@ -45,11 +46,13 @@ class WrongPrimitiveLiteralFix(element: KtConstantExpression, type: KotlinType) private val typeName = DescriptorUtils.getFqName(type.constructor.declarationDescriptor!!) private val expectedTypeIsFloat = KotlinBuiltIns.isFloat(type) private val expectedTypeIsDouble = KotlinBuiltIns.isDouble(type) + private val expectedTypeIsUnsigned = type.isUnsignedNumberType() private val constValue = run { - val shouldInlineCosntVals = element.languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals) + val shouldInlineConstVals = element.languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals) ExpressionCodegen.getPrimitiveOrStringCompileTimeConstant( - element, element.analyze(BodyResolveMode.PARTIAL), shouldInlineCosntVals)?.value as? Number + element, element.analyze(BodyResolveMode.PARTIAL), shouldInlineConstVals + )?.value as? Number } private val fixedExpression = buildString { @@ -57,17 +60,17 @@ class WrongPrimitiveLiteralFix(element: KtConstantExpression, type: KotlinType) append(constValue) if (expectedTypeIsFloat) { append('F') - } - else if ('.' !in this) { + } else if ('.' !in this) { append(".0") } - } - else { + } else if (expectedTypeIsUnsigned) { + append(constValue) + append('u') + } else { if (constValue is Float || constValue is Double) { append(constValue.toLong()) - } - else { - append(element.text.trimEnd('l', 'L')) + } else { + append(element.text.trimEnd('l', 'L', 'u')) } if (KotlinBuiltIns.isLong(type)) { @@ -78,7 +81,7 @@ class WrongPrimitiveLiteralFix(element: KtConstantExpression, type: KotlinType) override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean { if (constValue == null) return false - if (expectedTypeIsFloat || expectedTypeIsDouble) return true + if (expectedTypeIsFloat || expectedTypeIsDouble || expectedTypeIsUnsigned) return true if (constValue is Float || constValue is Double) { val value = constValue.toDouble() diff --git a/idea/testData/quickfix/typeMismatch/casts/unsigned.kt b/idea/testData/quickfix/typeMismatch/casts/unsigned.kt index 40cd5b5bff6..e09e71f468c 100644 --- a/idea/testData/quickfix/typeMismatch/casts/unsigned.kt +++ b/idea/testData/quickfix/typeMismatch/casts/unsigned.kt @@ -3,6 +3,7 @@ // ERROR: Conversion of signed constants to unsigned ones is prohibited // ACTION: Change parameter 'u' type of function 'takeUInt' to 'Int' // ACTION: Convert property initializer to getter +// ACTION: Change to '1u' // ACTION: Do not show hints for current method // ACTION: Add 'u =' to argument diff --git a/idea/testData/quickfix/typeMismatch/numberConversion/convertToSigned.kt b/idea/testData/quickfix/typeMismatch/numberConversion/convertToSigned.kt new file mode 100644 index 00000000000..57b39bb6971 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/numberConversion/convertToSigned.kt @@ -0,0 +1,7 @@ +// "Convert expression to 'Int'" "true" +// WITH_RUNTIME +fun foo(param: Int) {} + +fun test(expr: UInt) { + foo(expr) +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/numberConversion/convertToSigned.kt.after b/idea/testData/quickfix/typeMismatch/numberConversion/convertToSigned.kt.after new file mode 100644 index 00000000000..7331e64b5d0 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/numberConversion/convertToSigned.kt.after @@ -0,0 +1,7 @@ +// "Convert expression to 'Int'" "true" +// WITH_RUNTIME +fun foo(param: Int) {} + +fun test(expr: UInt) { + foo(expr.toInt()) +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/numberConversion/convertToUnsigned.kt b/idea/testData/quickfix/typeMismatch/numberConversion/convertToUnsigned.kt new file mode 100644 index 00000000000..b2058642bbd --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/numberConversion/convertToUnsigned.kt @@ -0,0 +1,7 @@ +// "Convert expression to 'UInt'" "true" +// WITH_RUNTIME +fun foo(param: UInt) {} + +fun test(expr: Int) { + foo(expr) +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/numberConversion/convertToUnsigned.kt.after b/idea/testData/quickfix/typeMismatch/numberConversion/convertToUnsigned.kt.after new file mode 100644 index 00000000000..1c0c11486f5 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/numberConversion/convertToUnsigned.kt.after @@ -0,0 +1,7 @@ +// "Convert expression to 'UInt'" "true" +// WITH_RUNTIME +fun foo(param: UInt) {} + +fun test(expr: Int) { + foo(expr.toUInt()) +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/wrongPrimitive/intToUnsigned.kt b/idea/testData/quickfix/typeMismatch/wrongPrimitive/intToUnsigned.kt new file mode 100644 index 00000000000..dfff080a6ba --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/wrongPrimitive/intToUnsigned.kt @@ -0,0 +1,7 @@ +// "Change to '1u'" "true" +// WITH_RUNTIME +fun foo(param: UInt) {} + +fun test() { + foo(1) +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/wrongPrimitive/intToUnsigned.kt.after b/idea/testData/quickfix/typeMismatch/wrongPrimitive/intToUnsigned.kt.after new file mode 100644 index 00000000000..a1bb4599299 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/wrongPrimitive/intToUnsigned.kt.after @@ -0,0 +1,7 @@ +// "Change to '1u'" "true" +// WITH_RUNTIME +fun foo(param: UInt) {} + +fun test() { + foo(1u) +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/wrongPrimitive/unsignedToInt.kt b/idea/testData/quickfix/typeMismatch/wrongPrimitive/unsignedToInt.kt new file mode 100644 index 00000000000..bcf4715d4bf --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/wrongPrimitive/unsignedToInt.kt @@ -0,0 +1,7 @@ +// "Change to '1'" "true" +// WITH_RUNTIME +fun foo(param: Int) {} + +fun test() { + foo(1u) +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/wrongPrimitive/unsignedToInt.kt.after b/idea/testData/quickfix/typeMismatch/wrongPrimitive/unsignedToInt.kt.after new file mode 100644 index 00000000000..542f7d594f3 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/wrongPrimitive/unsignedToInt.kt.after @@ -0,0 +1,7 @@ +// "Change to '1'" "true" +// WITH_RUNTIME +fun foo(param: Int) {} + +fun test() { + foo(1) +} \ 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 c2f07abc93c..4ffb9e61f6d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -12488,6 +12488,16 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { public void testConvertExpression() throws Exception { runTest("idea/testData/quickfix/typeMismatch/numberConversion/convertExpression.kt"); } + + @TestMetadata("convertToSigned.kt") + public void testConvertToSigned() throws Exception { + runTest("idea/testData/quickfix/typeMismatch/numberConversion/convertToSigned.kt"); + } + + @TestMetadata("convertToUnsigned.kt") + public void testConvertToUnsigned() throws Exception { + runTest("idea/testData/quickfix/typeMismatch/numberConversion/convertToUnsigned.kt"); + } } @TestMetadata("idea/testData/quickfix/typeMismatch/parameterTypeMismatch") @@ -12771,6 +12781,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { runTest("idea/testData/quickfix/typeMismatch/wrongPrimitive/intToFloat.kt"); } + @TestMetadata("intToUnsigned.kt") + public void testIntToUnsigned() throws Exception { + runTest("idea/testData/quickfix/typeMismatch/wrongPrimitive/intToUnsigned.kt"); + } + @TestMetadata("longToDouble.kt") public void testLongToDouble() throws Exception { runTest("idea/testData/quickfix/typeMismatch/wrongPrimitive/longToDouble.kt"); @@ -12785,6 +12800,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { public void testLongToIntBinary() throws Exception { runTest("idea/testData/quickfix/typeMismatch/wrongPrimitive/longToIntBinary.kt"); } + + @TestMetadata("unsignedToInt.kt") + public void testUnsignedToInt() throws Exception { + runTest("idea/testData/quickfix/typeMismatch/wrongPrimitive/unsignedToInt.kt"); + } } }