Add "Round using roundToInt/roundToLong" quick fix
#KT-30389 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
1455451601
commit
607325c6e5
@@ -126,6 +126,7 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
|
||||
actions.add(wrongPrimitiveLiteralFix)
|
||||
}
|
||||
actions.add(NumberConversionFix(diagnosticElement, expectedType, wrongPrimitiveLiteralFix))
|
||||
actions.add(RoundNumberFix(diagnosticElement, expectedType, wrongPrimitiveLiteralFix))
|
||||
}
|
||||
|
||||
if (KotlinBuiltIns.isCharSequenceOrNullableCharSequence(expectedType) || KotlinBuiltIns.isStringOrNullableString(expectedType)) {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
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.isDouble
|
||||
import org.jetbrains.kotlin.types.typeUtil.isFloat
|
||||
import org.jetbrains.kotlin.types.typeUtil.isInt
|
||||
import org.jetbrains.kotlin.types.typeUtil.isLong
|
||||
|
||||
class RoundNumberFix(
|
||||
element: KtExpression,
|
||||
type: KotlinType,
|
||||
private val disableIfAvailable: IntentionAction? = null
|
||||
) : KotlinQuickFixAction<KtExpression>(element), LowPriorityAction {
|
||||
|
||||
private val isTarget = type.isLongOrInt() && element.analyze(BodyResolveMode.PARTIAL).getType(element)?.isDoubleOrFloat() == true
|
||||
|
||||
private val roundFunction = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderType(type).let { "roundTo$it" }
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: KtFile) =
|
||||
disableIfAvailable?.isAvailable(project, editor, file) != true && isTarget
|
||||
|
||||
override fun getText() = "Round using $roundFunction()"
|
||||
|
||||
override fun getFamilyName() = text
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
val element = element ?: return
|
||||
val replaced = element.replaced(KtPsiFactory(file).createExpressionByPattern("$0.$roundFunction()", element))
|
||||
file.resolveImportReference(FqName("kotlin.math.$roundFunction")).firstOrNull()?.also {
|
||||
ImportInsertHelper.getInstance(project).importDescriptor(file, it)
|
||||
}
|
||||
editor?.caretModel?.moveToOffset(replaced.endOffset)
|
||||
}
|
||||
|
||||
private fun KotlinType.isLongOrInt() = this.isLong() || this.isInt()
|
||||
|
||||
private fun KotlinType.isDoubleOrFloat() = this.isDouble() || this.isFloat()
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// "Create function 'synchronized'" "false"
|
||||
// ACTION: Convert expression to 'Int'
|
||||
// ACTION: Round using roundToInt()
|
||||
// ERROR: Type mismatch: inferred type is Float but Int was expected
|
||||
// WITH_RUNTIME
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Round using roundToInt()" "false"
|
||||
// DISABLE-ERRORS
|
||||
// ACTION: Change parameter 'x' type of function 'foo' to 'Double'
|
||||
// ACTION: Convert expression to 'Float'
|
||||
// ACTION: Create function 'foo'
|
||||
// WITH_RUNTIME
|
||||
fun test(d: Double) {
|
||||
foo(d<caret>)
|
||||
}
|
||||
|
||||
fun foo(x: Float) {}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Round using roundToInt()" "false"
|
||||
// DISABLE-ERRORS
|
||||
// ACTION: Change parameter 'x' type of function 'foo' to 'Long'
|
||||
// ACTION: Convert expression to 'Int'
|
||||
// ACTION: Create function 'foo'
|
||||
// WITH_RUNTIME
|
||||
fun test(l: Long) {
|
||||
foo(l<caret>)
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Round using roundToInt()" "true"
|
||||
// WITH_RUNTIME
|
||||
fun test(d: Double) {
|
||||
foo(d<caret>)
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
@@ -0,0 +1,9 @@
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
// "Round using roundToInt()" "true"
|
||||
// WITH_RUNTIME
|
||||
fun test(d: Double) {
|
||||
foo(d.roundToInt())
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Round using roundToLong()" "true"
|
||||
// WITH_RUNTIME
|
||||
fun test(d: Double) {
|
||||
bar(d<caret>)
|
||||
}
|
||||
|
||||
fun bar(x: Long) {}
|
||||
@@ -0,0 +1,9 @@
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
// "Round using roundToLong()" "true"
|
||||
// WITH_RUNTIME
|
||||
fun test(d: Double) {
|
||||
bar(d.roundToLong())
|
||||
}
|
||||
|
||||
fun bar(x: Long) {}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Round using roundToInt()" "true"
|
||||
// WITH_RUNTIME
|
||||
fun test(f: Float) {
|
||||
foo(f<caret>)
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
@@ -0,0 +1,9 @@
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
// "Round using roundToInt()" "true"
|
||||
// WITH_RUNTIME
|
||||
fun test(f: Float) {
|
||||
foo(f.roundToInt())
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Round using roundToLong()" "true"
|
||||
// WITH_RUNTIME
|
||||
fun test(f: Float) {
|
||||
bar(f<caret>)
|
||||
}
|
||||
|
||||
fun bar(x: Long) {}
|
||||
@@ -0,0 +1,9 @@
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
// "Round using roundToLong()" "true"
|
||||
// WITH_RUNTIME
|
||||
fun test(f: Float) {
|
||||
bar(f.roundToLong())
|
||||
}
|
||||
|
||||
fun bar(x: Long) {}
|
||||
+1
@@ -3,6 +3,7 @@
|
||||
// ACTION: Change type of 'a' to 'Double'
|
||||
// ACTION: Convert expression to 'Int'
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Round using roundToInt()
|
||||
// ERROR: The floating-point literal does not conform to the expected type Int
|
||||
|
||||
val a : Int = 1.12<caret>
|
||||
@@ -4,6 +4,7 @@
|
||||
// ACTION: Convert expression to 'Long'
|
||||
// ACTION: Convert property initializer to getter
|
||||
// ACTION: Add underscores
|
||||
// ACTION: Round using roundToLong()
|
||||
// ERROR: The floating-point literal does not conform to the expected type Long
|
||||
|
||||
val a : Long = 10000000000000000000.0<caret>
|
||||
+13
@@ -4424,6 +4424,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/roundNumber")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class RoundNumber extends AbstractQuickFixMultiFileTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInRoundNumber() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/typeMismatch/roundNumber"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -13294,6 +13294,49 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/roundNumber")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class RoundNumber extends AbstractQuickFixTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInRoundNumber() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/typeMismatch/roundNumber"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicable.kt")
|
||||
public void testNotApplicable() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/roundNumber/notApplicable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notApplicable2.kt")
|
||||
public void testNotApplicable2() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/roundNumber/notApplicable2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("roundDoubleToInt.kt")
|
||||
public void testRoundDoubleToInt() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/roundNumber/roundDoubleToInt.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("roundDoubleToLong.kt")
|
||||
public void testRoundDoubleToLong() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/roundNumber/roundDoubleToLong.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("roundFloatToInt.kt")
|
||||
public void testRoundFloatToInt() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/roundNumber/roundFloatToInt.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("roundFloatToLong.kt")
|
||||
public void testRoundFloatToLong() throws Exception {
|
||||
runTest("idea/testData/quickfix/typeMismatch/roundNumber/roundFloatToLong.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user