[kotlin] Quickfix update: change T to T & Any (CR fixes)

* Removed the check that prevented unsafe cast action for DNN types:
  this behavior is more consistent as unsafe casts are allowed for
  other types.
* Removed unnecessary DNN feature check.
* Added the action to change the type of a variable.
* Refactored and cleaned up the code.
* Updated tests.

^KTIJ-20425 Fixed

(cherry picked from commit 6f685e1f4dda27282fd9ec37caef5785e3b27838)

GitOrigin-RevId: 0f5811fae3442c3246ec91e1a52ebfc046f8e214
This commit is contained in:
Dmitry Timofeev
2022-02-08 08:52:55 +00:00
committed by intellij-monorepo-bot
parent c2afb0763b
commit 5e91e7fb01
18 changed files with 153 additions and 70 deletions
@@ -2529,16 +2529,6 @@ public abstract class HighLevelQuickFixTestGenerated extends AbstractHighLevelQu
runTest("../idea/tests/testData/quickfix/typeMismatch/casts/typeMismatch4.kt");
}
@TestMetadata("typeMismatchDefNonNullable1.kt")
public void testTypeMismatchDefNonNullable1() throws Exception {
runTest("../idea/tests/testData/quickfix/typeMismatch/casts/typeMismatchDefNonNullable1.kt");
}
@TestMetadata("typeMismatchDefNonNullable2.kt")
public void testTypeMismatchDefNonNullable2() throws Exception {
runTest("../idea/tests/testData/quickfix/typeMismatch/casts/typeMismatchDefNonNullable2.kt");
}
@TestMetadata("typeMismatchLongNameRuntime.kt")
public void testTypeMismatchLongNameRuntime() throws Exception {
runTest("../idea/tests/testData/quickfix/typeMismatch/casts/typeMismatchLongNameRuntime.kt");
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isDefinitelyNotNullType
class ChangeParameterTypeFix(element: KtParameter, type: KotlinType) : KotlinQuickFixAction<KtParameter>(element) {
private val typePresentation = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderType(type)
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.jvm.ir.psiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isKFunctionType
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
@@ -22,7 +23,6 @@ import org.jetbrains.kotlin.idea.intentions.reflectToRegularFunctionType
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.util.approximateWithResolvableType
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
@@ -153,33 +153,10 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
return qualifiedOrThis
}
// Check if the type is a definitely non-nullable type (`T & Any`)
// or contains a definitely non-nullable type as a type argument (e.g., `Collection<T & Any>`)
fun KotlinType.containsDefinitelyNotNullComponent(): Boolean {
if (isDefinitelyNotNullType) return true
return arguments.any { it.type.containsDefinitelyNotNullComponent() }
}
// Suggest replacing type argument `T` with its expected definitely non-nullable subtype `T & Any`.
// Types that contain DNN types as arguments (like `(Mutable)Collection<T & Any>`) are currently not supported.
if (diagnosticElement is KtReferenceExpression &&
expectedType.isDefinitelyNotNullType &&
diagnosticElement.module?.languageVersionSettings?.supportsFeature(LanguageFeature.DefinitelyNonNullableTypes) == true
) {
val descriptor = context[BindingContext.REFERENCE_TARGET, diagnosticElement]?.safeAs<DeclarationDescriptorWithSource>()
val parameter = descriptor?.psiElement?.safeAs<KtParameter>()
val parentParameterList = parameter?.parent as? KtParameterList
if (parameter != null && parentParameterList != null) {
actions.add(ChangeParameterTypeFix(parameter, expectedType))
}
}
// We don't want to cast a cast or type-asserted expression.
// We also don't want to cast a potentially nullable type `T` to its definitely non-nullable subtype `T & Any` as it is unsafe.
if (diagnostic.factory != Errors.SIGNED_CONSTANT_CONVERTED_TO_UNSIGNED &&
diagnosticElement !is KtBinaryExpressionWithTypeRHS &&
diagnosticElement.parent !is KtBinaryExpressionWithTypeRHS &&
!expectedType.containsDefinitelyNotNullComponent()
diagnosticElement.parent !is KtBinaryExpressionWithTypeRHS
) {
actions.add(CastExpressionFix(diagnosticElement.getTopMostQualifiedForSelectorIfAny(), expectedType))
}
@@ -208,6 +185,23 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
actions.add(createFix(callable, typeToInsert))
}
// Suggest replacing the parameter type `T` with its expected definitely non-nullable subtype `T & Any`.
// Types that contain DNN types as arguments (like `(Mutable)Collection<T & Any>`) are currently not supported.
if (diagnosticElement is KtReferenceExpression && expectedType.isDefinitelyNotNullType) {
val descriptor = context[BindingContext.REFERENCE_TARGET, diagnosticElement]?.safeAs<CallableDescriptor>()
when (val declaration = QuickFixUtil.safeGetDeclaration(descriptor)) {
is KtParameter -> {
// Check the parent parameter list to avoid creating actions for loop iterators
if (declaration.parent is KtParameterList) {
actions.add(ChangeParameterTypeFix(declaration, expectedType))
}
}
is KtProperty -> {
addChangeTypeFix(declaration, expectedType, ::ChangeVariableTypeFix)
}
}
}
// Property initializer type mismatch property type:
val property = PsiTreeUtil.getParentOfType(diagnosticElement, KtProperty::class.java)
if (property != null) {
@@ -13988,16 +13988,6 @@ public abstract class QuickFixTestGenerated extends AbstractQuickFixTest {
runTest("testData/quickfix/typeMismatch/casts/typeMismatch4.kt");
}
@TestMetadata("typeMismatchDefNonNullable1.kt")
public void testTypeMismatchDefNonNullable1() throws Exception {
runTest("testData/quickfix/typeMismatch/casts/typeMismatchDefNonNullable1.kt");
}
@TestMetadata("typeMismatchDefNonNullable2.kt")
public void testTypeMismatchDefNonNullable2() throws Exception {
runTest("testData/quickfix/typeMismatch/casts/typeMismatchDefNonNullable2.kt");
}
@TestMetadata("typeMismatchLongNameRuntime.kt")
public void testTypeMismatchLongNameRuntime() throws Exception {
runTest("testData/quickfix/typeMismatch/casts/typeMismatchLongNameRuntime.kt");
@@ -14110,6 +14100,49 @@ public abstract class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/typeMismatch/definitelyNonNullableTypes")
public static class DefinitelyNonNullableTypes extends AbstractQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("changeFunctionParameterType1.kt")
public void testChangeFunctionParameterType1() throws Exception {
runTest("testData/quickfix/typeMismatch/definitelyNonNullableTypes/changeFunctionParameterType1.kt");
}
@TestMetadata("changeFunctionParameterType2.kt")
public void testChangeFunctionParameterType2() throws Exception {
runTest("testData/quickfix/typeMismatch/definitelyNonNullableTypes/changeFunctionParameterType2.kt");
}
@TestMetadata("changePrimaryConstructorParameterType.kt")
public void testChangePrimaryConstructorParameterType() throws Exception {
runTest("testData/quickfix/typeMismatch/definitelyNonNullableTypes/changePrimaryConstructorParameterType.kt");
}
@TestMetadata("changeVariableType1.kt")
public void testChangeVariableType1() throws Exception {
runTest("testData/quickfix/typeMismatch/definitelyNonNullableTypes/changeVariableType1.kt");
}
@TestMetadata("changeVariableType2.kt")
public void testChangeVariableType2() throws Exception {
runTest("testData/quickfix/typeMismatch/definitelyNonNullableTypes/changeVariableType2.kt");
}
@TestMetadata("explicitCast1.kt")
public void testExplicitCast1() throws Exception {
runTest("testData/quickfix/typeMismatch/definitelyNonNullableTypes/explicitCast1.kt");
}
@TestMetadata("explicitCast2.kt")
public void testExplicitCast2() throws Exception {
runTest("testData/quickfix/typeMismatch/definitelyNonNullableTypes/explicitCast2.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/typeMismatch/fixOverloadedOperator")
public static class FixOverloadedOperator extends AbstractQuickFixTest {
@@ -14188,16 +14221,6 @@ public abstract class QuickFixTestGenerated extends AbstractQuickFixTest {
runTest("testData/quickfix/typeMismatch/parameterTypeMismatch/changeFunctionParameterType4.kt");
}
@TestMetadata("changeFunctionParameterTypeDefNonNullable1.kt")
public void testChangeFunctionParameterTypeDefNonNullable1() throws Exception {
runTest("testData/quickfix/typeMismatch/parameterTypeMismatch/changeFunctionParameterTypeDefNonNullable1.kt");
}
@TestMetadata("changeFunctionParameterTypeDefNonNullable2.kt")
public void testChangeFunctionParameterTypeDefNonNullable2() throws Exception {
runTest("testData/quickfix/typeMismatch/parameterTypeMismatch/changeFunctionParameterTypeDefNonNullable2.kt");
}
@TestMetadata("changeFunctionParameterTypeWithComment.kt")
public void testChangeFunctionParameterTypeWithComment() throws Exception {
runTest("testData/quickfix/typeMismatch/parameterTypeMismatch/changeFunctionParameterTypeWithComment.kt");
@@ -1,8 +0,0 @@
// "class org.jetbrains.kotlin.idea.quickfix.CastExpressionFix" "false"
// LANGUAGE_VERSION: 1.7
// ERROR: Type mismatch: inferred type is Collection<T> but Collection<T & Any> was expected
fun <T> foo(x: Collection<T & Any>) {}
fun <T> bar(x: Collection<T>) {
foo(x<caret>)
}
@@ -1,5 +1,5 @@
// "Change parameter 'x' type of function 'bar' to 'T & Any'" "true"
// LANGUAGE_VERSION: 1.7
// LANGUAGE_VERSION: 1.8
package a
fun <T> foo(x: T & Any) {}
@@ -1,5 +1,5 @@
// "Change parameter 'x' type of function 'bar' to 'T & Any'" "true"
// LANGUAGE_VERSION: 1.7
// LANGUAGE_VERSION: 1.8
package a
fun <T> foo(x: T & Any) {}
@@ -2,9 +2,10 @@
// ERROR: Type mismatch: inferred type is List<T> but List<T & Any> was expected
// ACTION: Add 'Any' as upper bound for T to make it non-nullable
// ACTION: Add 'x =' to argument
// ACTION: Cast expression 'x' to 'List<T & Any>'
// ACTION: Change parameter 'x' type of function 'foo' to 'List<T>'
// ACTION: Create function 'foo'
// LANGUAGE_VERSION: 1.7
// LANGUAGE_VERSION: 1.8
package a
fun <T> foo(x: List<T & Any>) {}
@@ -0,0 +1,11 @@
// "Change parameter 'x' type of primary constructor of class 'Foo' to 'T & Any'" "true"
// LANGUAGE_VERSION: 1.8
package a
class Foo<T>(val x: T) {
fun foo(y: T & Any) {}
}
fun <T> Foo<T>.bar(z: T) {
foo(x<caret>)
}
@@ -0,0 +1,11 @@
// "Change parameter 'x' type of primary constructor of class 'Foo' to 'T & Any'" "true"
// LANGUAGE_VERSION: 1.8
package a
class Foo<T>(val x: T & Any) {
fun foo(y: T & Any) {}
}
fun <T> Foo<T>.bar(z: T) {
foo(x)
}
@@ -0,0 +1,10 @@
// "Change type of 'z' to 'T & Any'" "true"
// LANGUAGE_VERSION: 1.8
package a
fun <T> foo(x: T & Any) {}
fun <T> bar(x: T & Any) {
val z: T = x
foo(z<caret>)
}
@@ -0,0 +1,10 @@
// "Change type of 'z' to 'T & Any'" "true"
// LANGUAGE_VERSION: 1.8
package a
fun <T> foo(x: T & Any) {}
fun <T> bar(x: T & Any) {
val z: T & Any = x
foo(z)
}
@@ -1,8 +1,11 @@
// "class org.jetbrains.kotlin.idea.quickfix.CastExpressionFix" "false"
// LANGUAGE_VERSION: 1.7
// "Change type of 'z' to 'T & Any'" "true"
// ERROR: Type mismatch: inferred type is T but T & Any was expected
// LANGUAGE_VERSION: 1.8
package a
fun <T> foo(x: T & Any) {}
fun <T> bar(x: T) {
foo(x<caret>)
val z: T = x
foo(z<caret>)
}
@@ -0,0 +1,11 @@
// "Change type of 'z' to 'T & Any'" "true"
// ERROR: Type mismatch: inferred type is T but T & Any was expected
// LANGUAGE_VERSION: 1.8
package a
fun <T> foo(x: T & Any) {}
fun <T> bar(x: T) {
val z: T & Any = x
foo(z)
}
@@ -0,0 +1,7 @@
// "Cast expression 'x' to 'T & Any'" "true"
// LANGUAGE_VERSION: 1.8
fun <T> foo(x: T & Any) {}
fun <T> bar(x: T) {
foo(x<caret>)
}
@@ -0,0 +1,7 @@
// "Cast expression 'x' to 'T & Any'" "true"
// LANGUAGE_VERSION: 1.8
fun <T> foo(x: T & Any) {}
fun <T> bar(x: T) {
foo(x as (T & Any))
}
@@ -0,0 +1,7 @@
// "Cast expression 'x' to 'Collection<T & Any>'" "true"
// LANGUAGE_VERSION: 1.8
fun <T> foo(x: Collection<T & Any>) {}
fun <T> bar(x: Collection<T>) {
foo(x<caret>)
}
@@ -0,0 +1,7 @@
// "Cast expression 'x' to 'Collection<T & Any>'" "true"
// LANGUAGE_VERSION: 1.8
fun <T> foo(x: Collection<T & Any>) {}
fun <T> bar(x: Collection<T>) {
foo(x as Collection<T & Any>)
}