diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 460fb1c7b85..3b8e8a12ed5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -553,5 +553,7 @@ class QuickFixRegistrar : QuickFixContributor { EXPERIMENTAL_OVERRIDE.registerFactory(ExperimentalFixesFactory) EXPERIMENTAL_OVERRIDE_ERROR.registerFactory(ExperimentalFixesFactory) EXPERIMENTAL_IS_NOT_ENABLED.registerFactory(MakeModuleExperimentalFix) + + TYPE_VARIANCE_CONFLICT.registerFactory(RemoveTypeVarianceFix) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveTypeVarianceFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveTypeVarianceFix.kt new file mode 100644 index 00000000000..269ebdcb3e0 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveTypeVarianceFix.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2018 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.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtTypeParameter +import org.jetbrains.kotlin.psi.KtTypeReference +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.Variance + +class RemoveTypeVarianceFix( + typeParameter: KtTypeParameter, + private val variance: Variance, + private val type: String +) : KotlinQuickFixAction(typeParameter) { + + override fun getText(): String = "Remove '${variance.label}' variance from '$type'" + + override fun getFamilyName(): String = text + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val typeParameter = element ?: return + when (variance) { + Variance.IN_VARIANCE -> KtTokens.IN_KEYWORD + Variance.OUT_VARIANCE -> KtTokens.OUT_KEYWORD + else -> null + }?.let { + typeParameter.removeModifier(it) + } + } + + companion object : KotlinSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { + val typeReference = diagnostic.psiElement.parent as? KtTypeReference ?: return null + val type = typeReference.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference] ?: return null + val descriptor = type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return null + val variance = descriptor.variance + if (variance == Variance.INVARIANT) return null + val typeParameter = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor) as? KtTypeParameter ?: return null + return RemoveTypeVarianceFix(typeParameter, variance, IdeDescriptorRenderers.SOURCE_CODE_TYPES.renderType(type)) + } + } + +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeTypeVariance/in.kt b/idea/testData/quickfix/removeTypeVariance/in.kt new file mode 100644 index 00000000000..60f326b0eab --- /dev/null +++ b/idea/testData/quickfix/removeTypeVariance/in.kt @@ -0,0 +1,7 @@ +// "Remove 'in' variance from 'T'" "true" +// WITH_RUNTIME + +class Test { + fun foo(t: T) {} + fun bar(): T = TODO() +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeTypeVariance/in.kt.after b/idea/testData/quickfix/removeTypeVariance/in.kt.after new file mode 100644 index 00000000000..b0b815fc178 --- /dev/null +++ b/idea/testData/quickfix/removeTypeVariance/in.kt.after @@ -0,0 +1,7 @@ +// "Remove 'in' variance from 'T'" "true" +// WITH_RUNTIME + +class Test { + fun foo(t: T) {} + fun bar(): T = TODO() +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeTypeVariance/out.kt b/idea/testData/quickfix/removeTypeVariance/out.kt new file mode 100644 index 00000000000..f62f3ec92b1 --- /dev/null +++ b/idea/testData/quickfix/removeTypeVariance/out.kt @@ -0,0 +1,7 @@ +// "Remove 'out' variance from 'T'" "true" +// WITH_RUNTIME + +class Test { + fun foo(t: T) {} + fun bar(): T = TODO() +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeTypeVariance/out.kt.after b/idea/testData/quickfix/removeTypeVariance/out.kt.after new file mode 100644 index 00000000000..d329d1ce258 --- /dev/null +++ b/idea/testData/quickfix/removeTypeVariance/out.kt.after @@ -0,0 +1,7 @@ +// "Remove 'out' variance from 'T'" "true" +// WITH_RUNTIME + +class Test { + fun foo(t: T) {} + fun bar(): T = TODO() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index b31ce5d94a7..ad831720739 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -3345,6 +3345,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/removeTypeVariance") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class RemoveTypeVariance extends AbstractQuickFixMultiFileTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInRemoveTypeVariance() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/removeTypeVariance"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + } + @TestMetadata("idea/testData/quickfix/removeUnused") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 1c17eab3649..794b3fb3a41 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -9065,6 +9065,29 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/removeTypeVariance") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class RemoveTypeVariance extends AbstractQuickFixTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInRemoveTypeVariance() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/removeTypeVariance"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("in.kt") + public void testIn() throws Exception { + runTest("idea/testData/quickfix/removeTypeVariance/in.kt"); + } + + @TestMetadata("out.kt") + public void testOut() throws Exception { + runTest("idea/testData/quickfix/removeTypeVariance/out.kt"); + } + } + @TestMetadata("idea/testData/quickfix/removeUnused") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)