From cdf7f46ce146d05c7137844871a3738788013f16 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 20 Dec 2019 15:53:44 +0900 Subject: [PATCH] Provide quickfix for specifying type of variable initialized with null #KT-23394 Fixed --- .../idea/quickfix/ChangeVariableTypeFix.kt | 17 ++++++++++++++++ .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 1 + .../variableInitializedWithNull.kt | 7 +++++++ .../variableInitializedWithNull.kt.after | 7 +++++++ .../variableInitializedWithNull2.kt | 12 +++++++++++ .../variableInitializedWithNull3.kt | 11 ++++++++++ .../variableInitializedWithNull4.kt | 11 ++++++++++ .../idea/quickfix/QuickFixTestGenerated.java | 20 +++++++++++++++++++ 8 files changed, 86 insertions(+) create mode 100644 idea/testData/quickfix/typeMismatch/variableInitializedWithNull.kt create mode 100644 idea/testData/quickfix/typeMismatch/variableInitializedWithNull.kt.after create mode 100644 idea/testData/quickfix/typeMismatch/variableInitializedWithNull2.kt create mode 100644 idea/testData/quickfix/typeMismatch/variableInitializedWithNull3.kt create mode 100644 idea/testData/quickfix/typeMismatch/variableInitializedWithNull4.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt index 41c8953b65a..ae2e4dd31d0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt @@ -23,20 +23,26 @@ import com.intellij.openapi.project.Project import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression +import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.checker.KotlinTypeChecker +import org.jetbrains.kotlin.types.typeUtil.makeNullable import java.util.* open class ChangeVariableTypeFix(element: KtCallableDeclaration, type: KotlinType) : KotlinQuickFixAction(element) { @@ -166,4 +172,15 @@ open class ChangeVariableTypeFix(element: KtCallableDeclaration, type: KotlinTyp return actions } } + + object VariableInitializedWithNullFactory : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val binaryExpression = diagnostic.psiElement.getStrictParentOfType() ?: return null + val left = binaryExpression.left ?: return null + if (binaryExpression.operationToken != KtTokens.EQ) return null + val property = left.mainReference?.resolve() as? KtProperty ?: return null + if (!property.isVar || property.typeReference != null || !property.initializer.isNullExpression()) return null + return ChangeVariableTypeFix(property, Errors.TYPE_MISMATCH.cast(diagnostic).b.makeNullable()) + } + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 407da4888ea..caf2ffe79f1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -316,6 +316,7 @@ class QuickFixRegistrar : QuickFixContributor { PROPERTY_TYPE_MISMATCH_ON_OVERRIDE.registerFactory(changeVariableTypeFix) VAR_TYPE_MISMATCH_ON_OVERRIDE.registerFactory(changeVariableTypeFix) COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.registerFactory(ChangeVariableTypeFix.ComponentFunctionReturnTypeMismatchFactory) + TYPE_MISMATCH.registerFactory(ChangeVariableTypeFix.VariableInitializedWithNullFactory) val changeFunctionReturnTypeFix = ChangeCallableReturnTypeFix.ChangingReturnTypeToUnitFactory RETURN_TYPE_MISMATCH.registerFactory(changeFunctionReturnTypeFix) diff --git a/idea/testData/quickfix/typeMismatch/variableInitializedWithNull.kt b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull.kt new file mode 100644 index 00000000000..14e204e747e --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull.kt @@ -0,0 +1,7 @@ +// "Change type of 'x' to 'String?'" "true" +fun foo(condition: Boolean) { + var x = null + if (condition) { + x = "abc" + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/variableInitializedWithNull.kt.after b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull.kt.after new file mode 100644 index 00000000000..9a3c8499cda --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull.kt.after @@ -0,0 +1,7 @@ +// "Change type of 'x' to 'String?'" "true" +fun foo(condition: Boolean) { + var x: String? = null + if (condition) { + x = "abc" + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/variableInitializedWithNull2.kt b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull2.kt new file mode 100644 index 00000000000..7285342bf20 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull2.kt @@ -0,0 +1,12 @@ +// "Change type of 'x' to 'String?'" "false" +// ACTION: Remove braces from 'if' statement +// ACTION: To raw string literal +// ACTION: Convert assignment to assignment expression +// ERROR: Type mismatch: inferred type is String but Nothing? was expected +// ERROR: Val cannot be reassigned +fun foo(condition: Boolean) { + val x = null + if (condition) { + x = "abc" + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/variableInitializedWithNull3.kt b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull3.kt new file mode 100644 index 00000000000..7ce29a77a3f --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull3.kt @@ -0,0 +1,11 @@ +// "Change type of 'x' to 'String?'" "false" +// ACTION: Remove braces from 'if' statement +// ACTION: To raw string literal +// ACTION: Convert assignment to assignment expression +// ERROR: Type mismatch: inferred type is String but Int? was expected +fun foo(condition: Boolean) { + var x: Int? = null + if (condition) { + x = "abc" + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/variableInitializedWithNull4.kt b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull4.kt new file mode 100644 index 00000000000..2d3c5af4d67 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/variableInitializedWithNull4.kt @@ -0,0 +1,11 @@ +// "Change type of 'x' to 'String?'" "false" +// ACTION: Remove braces from 'if' statement +// ACTION: To raw string literal +// ACTION: Convert assignment to assignment expression +// ERROR: Type mismatch: inferred type is String but Int was expected +fun foo(condition: Boolean) { + var x = 1 + if (condition) { + x = "abc" + } +} \ 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 10b02e61085..40c97ff8a39 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -14258,6 +14258,26 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { runTest("idea/testData/quickfix/typeMismatch/unresolvableTypeParams.kt"); } + @TestMetadata("variableInitializedWithNull.kt") + public void testVariableInitializedWithNull() throws Exception { + runTest("idea/testData/quickfix/typeMismatch/variableInitializedWithNull.kt"); + } + + @TestMetadata("variableInitializedWithNull2.kt") + public void testVariableInitializedWithNull2() throws Exception { + runTest("idea/testData/quickfix/typeMismatch/variableInitializedWithNull2.kt"); + } + + @TestMetadata("variableInitializedWithNull3.kt") + public void testVariableInitializedWithNull3() throws Exception { + runTest("idea/testData/quickfix/typeMismatch/variableInitializedWithNull3.kt"); + } + + @TestMetadata("variableInitializedWithNull4.kt") + public void testVariableInitializedWithNull4() throws Exception { + runTest("idea/testData/quickfix/typeMismatch/variableInitializedWithNull4.kt"); + } + @TestMetadata("when.kt") public void testWhen() throws Exception { runTest("idea/testData/quickfix/typeMismatch/when.kt");