Provide quickfix for specifying type of variable initialized with null

#KT-23394 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-12-20 15:53:44 +09:00
committed by Yan Zhulanow
parent 55038e19ec
commit cdf7f46ce1
8 changed files with 86 additions and 0 deletions
@@ -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<KtCallableDeclaration>(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<KtBinaryExpression>() ?: 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())
}
}
}
@@ -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)
@@ -0,0 +1,7 @@
// "Change type of 'x' to 'String?'" "true"
fun foo(condition: Boolean) {
var x = null
if (condition) {
x = "abc"<caret>
}
}
@@ -0,0 +1,7 @@
// "Change type of 'x' to 'String?'" "true"
fun foo(condition: Boolean) {
var x: String? = null
if (condition) {
x = "abc"
}
}
@@ -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"<caret>
}
}
@@ -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"<caret>
}
}
@@ -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"<caret>
}
}
@@ -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");