Support '.toType()' and literal fixes related to unsigned type mismatch

#KT-26836 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-12-26 15:05:31 +03:00
parent 73b6148407
commit 8420fceb8c
14 changed files with 102 additions and 19 deletions
@@ -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
@@ -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<KtExpression>(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'"
@@ -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)
@@ -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()
+1
View File
@@ -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
@@ -0,0 +1,7 @@
// "Convert expression to 'Int'" "true"
// WITH_RUNTIME
fun foo(param: Int) {}
fun test(expr: UInt) {
foo(<caret>expr)
}
@@ -0,0 +1,7 @@
// "Convert expression to 'Int'" "true"
// WITH_RUNTIME
fun foo(param: Int) {}
fun test(expr: UInt) {
foo(expr.toInt())
}
@@ -0,0 +1,7 @@
// "Convert expression to 'UInt'" "true"
// WITH_RUNTIME
fun foo(param: UInt) {}
fun test(expr: Int) {
foo(<caret>expr)
}
@@ -0,0 +1,7 @@
// "Convert expression to 'UInt'" "true"
// WITH_RUNTIME
fun foo(param: UInt) {}
fun test(expr: Int) {
foo(expr.toUInt())
}
@@ -0,0 +1,7 @@
// "Change to '1u'" "true"
// WITH_RUNTIME
fun foo(param: UInt) {}
fun test() {
foo(<caret>1)
}
@@ -0,0 +1,7 @@
// "Change to '1u'" "true"
// WITH_RUNTIME
fun foo(param: UInt) {}
fun test() {
foo(1u)
}
@@ -0,0 +1,7 @@
// "Change to '1'" "true"
// WITH_RUNTIME
fun foo(param: Int) {}
fun test() {
foo(<caret>1u)
}
@@ -0,0 +1,7 @@
// "Change to '1'" "true"
// WITH_RUNTIME
fun foo(param: Int) {}
fun test() {
foo(1)
}
@@ -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");
}
}
}