FIR IDE: AddToString quickfix
This commit is contained in:
committed by
Ilya Kirillov
parent
1b8b5b9a79
commit
f6078b24df
+1
@@ -1183,6 +1183,7 @@ fun main(args: Array<String>) {
|
|||||||
model("quickfix/wrapWithSafeLetCall", pattern = pattern, filenameStartsLowerCase = true)
|
model("quickfix/wrapWithSafeLetCall", pattern = pattern, filenameStartsLowerCase = true)
|
||||||
model("quickfix/typeMismatch/componentFunctionReturnTypeMismatch", pattern = pattern, filenameStartsLowerCase = true)
|
model("quickfix/typeMismatch/componentFunctionReturnTypeMismatch", pattern = pattern, filenameStartsLowerCase = true)
|
||||||
model("quickfix/typeMismatch/typeMismatchOnReturnedExpression", pattern = pattern, filenameStartsLowerCase = true)
|
model("quickfix/typeMismatch/typeMismatchOnReturnedExpression", pattern = pattern, filenameStartsLowerCase = true)
|
||||||
|
model("quickfix/toString", pattern = pattern, filenameStartsLowerCase = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
testClass<AbstractHighLevelQuickFixMultiFileTest> {
|
testClass<AbstractHighLevelQuickFixMultiFileTest> {
|
||||||
|
|||||||
@@ -137,11 +137,18 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
|
|||||||
registerPsiQuickFixes(KtFirDiagnostic.InapplicableLateinitModifier::class, RemoveNullableFix.removeForLateInitProperty)
|
registerPsiQuickFixes(KtFirDiagnostic.InapplicableLateinitModifier::class, RemoveNullableFix.removeForLateInitProperty)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val returnTypes = KtQuickFixesListBuilder.registerPsiQuickFix {
|
private val typeMismatch = KtQuickFixesListBuilder.registerPsiQuickFix {
|
||||||
registerApplicator(ChangeTypeQuickFixFactories.componentFunctionReturnTypeMismatch)
|
registerApplicator(ChangeTypeQuickFixFactories.componentFunctionReturnTypeMismatch)
|
||||||
registerApplicator(ChangeTypeQuickFixFactories.returnTypeMismatch)
|
registerApplicator(ChangeTypeQuickFixFactories.returnTypeMismatch)
|
||||||
|
|
||||||
|
registerApplicator(AddToStringFixFactories.typeMismatch)
|
||||||
|
registerApplicator(AddToStringFixFactories.argumentTypeMismatch)
|
||||||
|
registerApplicator(AddToStringFixFactories.assignmentTypeMismatch)
|
||||||
|
registerApplicator(AddToStringFixFactories.returnTypeMismatch)
|
||||||
|
registerApplicator(AddToStringFixFactories.initializerTypeMismatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override val list: KtQuickFixesList = KtQuickFixesList.createCombined(
|
override val list: KtQuickFixesList = KtQuickFixesList.createCombined(
|
||||||
keywords,
|
keywords,
|
||||||
propertyInitialization,
|
propertyInitialization,
|
||||||
@@ -149,6 +156,6 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
|
|||||||
imports,
|
imports,
|
||||||
mutability,
|
mutability,
|
||||||
expressions,
|
expressions,
|
||||||
returnTypes
|
typeMismatch
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* 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.fixes
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.AddToStringFix
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
|
||||||
|
object AddToStringFixFactories {
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
private fun KtAnalysisSession.getFixes(element: PsiElement?, expectedType: KtType, actualType: KtType): List<AddToStringFix> {
|
||||||
|
if (element !is KtExpression) return emptyList()
|
||||||
|
return buildList {
|
||||||
|
if (expectedType.isString || expectedType.isCharSequence) {
|
||||||
|
add(AddToStringFix(element, false))
|
||||||
|
if (expectedType.isMarkedNullable && actualType.isMarkedNullable) {
|
||||||
|
add(AddToStringFix(element, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val typeMismatch = diagnosticFixFactory(KtFirDiagnostic.TypeMismatch::class) { diagnostic ->
|
||||||
|
getFixes(diagnostic.psi, diagnostic.expectedType, diagnostic.actualType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val argumentTypeMismatch = diagnosticFixFactory(KtFirDiagnostic.ArgumentTypeMismatch::class) { diagnostic ->
|
||||||
|
getFixes(diagnostic.psi, diagnostic.expectedType, diagnostic.actualType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val assignmentTypeMismatch = diagnosticFixFactory(KtFirDiagnostic.AssignmentTypeMismatch::class) { diagnostic ->
|
||||||
|
getFixes(diagnostic.psi, diagnostic.expectedType, diagnostic.actualType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val returnTypeMismatch = diagnosticFixFactory(KtFirDiagnostic.ReturnTypeMismatch::class) { diagnostic ->
|
||||||
|
getFixes(diagnostic.psi, diagnostic.expectedType, diagnostic.actualType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val initializerTypeMismatch = diagnosticFixFactory(KtFirDiagnostic.InitializerTypeMismatch::class) { diagnostic ->
|
||||||
|
getFixes(diagnostic.psi.initializer, diagnostic.expectedType, diagnostic.actualType)
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+48
@@ -2334,4 +2334,52 @@ public class HighLevelQuickFixTestGenerated extends AbstractHighLevelQuickFixTes
|
|||||||
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInReturnStatement.kt");
|
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInReturnStatement.kt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/toString")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ToString extends AbstractHighLevelQuickFixTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInToString() throws Exception {
|
||||||
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/toString"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callArgument.kt")
|
||||||
|
public void testCallArgument() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/callArgument.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notNullableExpectedNullable.kt")
|
||||||
|
public void testNotNullableExpectedNullable() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/notNullableExpectedNullable.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullableExpectedNotNullable.kt")
|
||||||
|
public void testNullableExpectedNotNullable() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/nullableExpectedNotNullable.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nullableExpectedNullable.kt")
|
||||||
|
public void testNullableExpectedNullable() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/nullableExpectedNullable.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyInitializer.kt")
|
||||||
|
public void testPropertyInitializer() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/propertyInitializer.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("returnStatement.kt")
|
||||||
|
public void testReturnStatement() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/returnStatement.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("variableAssignment.kt")
|
||||||
|
public void testVariableAssignment() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/variableAssignment.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -38,6 +38,7 @@ interface KtTypeInfoProviderMixIn : KtAnalysisSessionMixIn {
|
|||||||
val KtType.isChar: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.CHAR)
|
val KtType.isChar: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.CHAR)
|
||||||
val KtType.isBoolean: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.BOOLEAN)
|
val KtType.isBoolean: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.BOOLEAN)
|
||||||
val KtType.isString: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.STRING)
|
val KtType.isString: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.STRING)
|
||||||
|
val KtType.isCharSequence: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.CHAR_SEQUENCE)
|
||||||
val KtType.isAny: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.ANY)
|
val KtType.isAny: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.ANY)
|
||||||
|
|
||||||
val KtType.isUInt: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uInt)
|
val KtType.isUInt: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uInt)
|
||||||
@@ -85,6 +86,7 @@ object DefaultTypeClassIds {
|
|||||||
val CHAR = ClassId.topLevel(StandardNames.FqNames._char.toSafe())
|
val CHAR = ClassId.topLevel(StandardNames.FqNames._char.toSafe())
|
||||||
val BOOLEAN = ClassId.topLevel(StandardNames.FqNames._boolean.toSafe())
|
val BOOLEAN = ClassId.topLevel(StandardNames.FqNames._boolean.toSafe())
|
||||||
val STRING = ClassId.topLevel(StandardNames.FqNames.string.toSafe())
|
val STRING = ClassId.topLevel(StandardNames.FqNames.string.toSafe())
|
||||||
|
val CHAR_SEQUENCE = ClassId.topLevel(StandardNames.FqNames.charSequence.toSafe())
|
||||||
val ANY = ClassId.topLevel(StandardNames.FqNames.any.toSafe())
|
val ANY = ClassId.topLevel(StandardNames.FqNames.any.toSafe())
|
||||||
val PRIMITIVES = setOf(INT, LONG, SHORT, BYTE, FLOAT, DOUBLE, CHAR, BOOLEAN)
|
val PRIMITIVES = setOf(INT, LONG, SHORT, BYTE, FLOAT, DOUBLE, CHAR, BOOLEAN)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -17,12 +17,12 @@ import org.jetbrains.kotlin.psi.KtPsiFactory
|
|||||||
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
import org.jetbrains.kotlin.psi.createExpressionByPattern
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
|
||||||
class AddToStringFix(element: KtExpression, private val nullable: Boolean) :
|
class AddToStringFix(element: KtExpression, private val useSafeCallOperator: Boolean) :
|
||||||
KotlinPsiOnlyQuickFixAction<KtExpression>(element), KotlinUniversalQuickFix, LowPriorityAction {
|
KotlinPsiOnlyQuickFixAction<KtExpression>(element), KotlinUniversalQuickFix, LowPriorityAction {
|
||||||
override fun getFamilyName() = KotlinBundle.message("fix.add.tostring.call.family")
|
override fun getFamilyName() = KotlinBundle.message("fix.add.tostring.call.family")
|
||||||
|
|
||||||
override fun getText(): String {
|
override fun getText(): String {
|
||||||
return when (nullable) {
|
return when (useSafeCallOperator) {
|
||||||
true -> KotlinBundle.message("fix.add.tostring.call.text.safe")
|
true -> KotlinBundle.message("fix.add.tostring.call.text.safe")
|
||||||
false -> KotlinBundle.message("fix.add.tostring.call.text")
|
false -> KotlinBundle.message("fix.add.tostring.call.text")
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ class AddToStringFix(element: KtExpression, private val nullable: Boolean) :
|
|||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
val element = element ?: return
|
val element = element ?: return
|
||||||
val pattern = if (nullable) "$0?.toString()" else "$0.toString()"
|
val pattern = if (useSafeCallOperator) "$0?.toString()" else "$0.toString()"
|
||||||
val expressionToInsert = KtPsiFactory(file).createExpressionByPattern(pattern, element)
|
val expressionToInsert = KtPsiFactory(file).createExpressionByPattern(pattern, element)
|
||||||
val newExpression = element.replaced(expressionToInsert)
|
val newExpression = element.replaced(expressionToInsert)
|
||||||
editor?.caretModel?.moveToOffset(newExpression.endOffset)
|
editor?.caretModel?.moveToOffset(newExpression.endOffset)
|
||||||
|
|||||||
Vendored
@@ -0,0 +1,3 @@
|
|||||||
|
// "Add 'toString()' call" "true"
|
||||||
|
|
||||||
|
val s: String = 1 <caret>+ 2 * 3
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// "Add 'toString()' call" "true"
|
||||||
|
|
||||||
|
val s: String = (1 + 2 * 3).toString()<caret>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Add 'toString()' call" "true"
|
||||||
|
|
||||||
|
fun test(): String {
|
||||||
|
return 1 <caret>+ 2 * 3
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Add 'toString()' call" "true"
|
||||||
|
|
||||||
|
fun test(): String {
|
||||||
|
return (1 + 2 * 3).toString()<caret>
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add 'toString()' call" "true"
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
var s: String = ""
|
||||||
|
s = 1 <caret>+ 2 * 3
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add 'toString()' call" "true"
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
var s: String = ""
|
||||||
|
s = (1 + 2 * 3).toString()<caret>
|
||||||
|
}
|
||||||
@@ -13668,6 +13668,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/toString"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/toString"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callArgument.kt")
|
||||||
|
public void testCallArgument() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/callArgument.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("notNullableExpectedNullable.kt")
|
@TestMetadata("notNullableExpectedNullable.kt")
|
||||||
public void testNotNullableExpectedNullable() throws Exception {
|
public void testNotNullableExpectedNullable() throws Exception {
|
||||||
runTest("idea/testData/quickfix/toString/notNullableExpectedNullable.kt");
|
runTest("idea/testData/quickfix/toString/notNullableExpectedNullable.kt");
|
||||||
@@ -13683,9 +13688,19 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
runTest("idea/testData/quickfix/toString/nullableExpectedNullable.kt");
|
runTest("idea/testData/quickfix/toString/nullableExpectedNullable.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("simple.kt")
|
@TestMetadata("propertyInitializer.kt")
|
||||||
public void testSimple() throws Exception {
|
public void testPropertyInitializer() throws Exception {
|
||||||
runTest("idea/testData/quickfix/toString/simple.kt");
|
runTest("idea/testData/quickfix/toString/propertyInitializer.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("returnStatement.kt")
|
||||||
|
public void testReturnStatement() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/returnStatement.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("variableAssignment.kt")
|
||||||
|
public void testVariableAssignment() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/toString/variableAssignment.kt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user