From 45d1129dbbe56c703979c0fb8fbb48c7698164ec Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Oct 2015 19:03:42 +0200 Subject: [PATCH] quickfix to move type constraint to 'where' clause --- .../jetbrains/kotlin/diagnostics/Errors.java | 2 +- .../rendering/DefaultErrorMessages.java | 2 +- .../kotlin/resolve/DeclarationsChecker.java | 2 +- .../typeParameters/misplacedConstraints.kt | 6 +- .../inspections/KotlinCleanupInspection.kt | 3 +- .../MoveTypeParameterConstraintFix.kt | 60 +++++++++++++++++++ .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 2 + idea/testData/checker/MultipleBounds.kt | 8 ++- idea/testData/inspections/cleanup/cleanup.kt | 3 + .../inspections/cleanup/cleanup.kt.after | 3 + .../misplacedClassTypeParameter.kt | 3 + .../misplacedClassTypeParameter.kt.after | 3 + .../misplacedFunctionTypeParameter.kt | 3 + .../misplacedFunctionTypeParameter.kt.after | 3 + .../idea/quickfix/QuickFixTestGenerated.java | 21 +++++++ 15 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/MoveTypeParameterConstraintFix.kt create mode 100644 idea/testData/quickfix/typeParameters/misplacedClassTypeParameter.kt create mode 100644 idea/testData/quickfix/typeParameters/misplacedClassTypeParameter.kt.after create mode 100644 idea/testData/quickfix/typeParameters/misplacedFunctionTypeParameter.kt create mode 100644 idea/testData/quickfix/typeParameters/misplacedFunctionTypeParameter.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index f5a1d2a6491..62845be3092 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -268,7 +268,7 @@ public interface Errors { DiagnosticFactory0 CYCLIC_GENERIC_UPPER_BOUND = DiagnosticFactory0.create(ERROR); - DiagnosticFactory0 MISPLACED_TYPE_PARAMETER_CONSTRAITS = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 MISPLACED_TYPE_PARAMETER_CONSTRAINTS = DiagnosticFactory0.create(WARNING); // Members diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 42ba06dc288..97bd0877675 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -496,7 +496,7 @@ public class DefaultErrorMessages { MAP.put(DEPRECATED_TYPE_PARAMETER_SYNTAX, "Placing function type parameters after the function name is deprecated"); - MAP.put(MISPLACED_TYPE_PARAMETER_CONSTRAITS, "If a type parameter has multiple constraints, they all need to be placed in the 'where' clause"); + MAP.put(MISPLACED_TYPE_PARAMETER_CONSTRAINTS, "If a type parameter has multiple constraints, they all need to be placed in the 'where' clause"); MAP.put(TYPE_VARIANCE_CONFLICT, "Type parameter {0} is declared as ''{1}'' but occurs in ''{2}'' position in type {3}", new MultiRenderer() { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java index d2986fe328e..15861e429c6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java @@ -342,7 +342,7 @@ public class DeclarationsChecker { if (!constraints.isEmpty()) { for (JetTypeParameter typeParameter : typeParameterListOwner.getTypeParameters()) { if (typeParameter.getExtendsBound() != null && hasConstraints(typeParameter, constraints)) { - trace.report(MISPLACED_TYPE_PARAMETER_CONSTRAITS.on(typeParameter)); + trace.report(MISPLACED_TYPE_PARAMETER_CONSTRAINTS.on(typeParameter)); } } } diff --git a/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt b/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt index fb1f7e0d9c8..512f8361b87 100644 --- a/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt +++ b/compiler/testData/diagnostics/tests/typeParameters/misplacedConstraints.kt @@ -1,9 +1,9 @@ -class Foo<T : Cloneable> where T : Comparable { - fun <U : Cloneable> foo(u: U): U where U: Comparable { +class Foo<T : Cloneable> where T : Comparable { + fun <U : Cloneable> foo(u: U): U where U: Comparable { return u } - val <U : Cloneable> U.foo: U? where U: Comparable + val <U : Cloneable> U.foo: U? where U: Comparable get() { return null } } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt index d42a403b999..060d9da5590 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt @@ -93,7 +93,8 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe Errors.BACKING_FIELD_SYNTAX_DEPRECATED, Errors.OPERATOR_MODIFIER_REQUIRED, Errors.CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS, - Errors.DEPRECATED_TYPE_PARAMETER_SYNTAX + Errors.DEPRECATED_TYPE_PARAMETER_SYNTAX, + Errors.MISPLACED_TYPE_PARAMETER_CONSTRAINTS ) private fun Diagnostic.isObsoleteLabel(): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/MoveTypeParameterConstraintFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/MoveTypeParameterConstraintFix.kt new file mode 100644 index 00000000000..e3463f85170 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/MoveTypeParameterConstraintFix.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType + +class MoveTypeParameterConstraintFix(element: JetTypeParameter) : JetIntentionAction(element), CleanupFix { + override fun getText(): String = "Move type parameter constraint to 'where' clause" + override fun getFamilyName(): String = text + + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + val typeParameterName = element.nameAsName ?: return + val psiFactory = JetPsiFactory(file) + val templateClass = psiFactory.buildDeclaration { + appendFixedText("class A<") + appendName(typeParameterName) + appendFixedText("> where ") + appendName(typeParameterName) + appendFixedText(":") + appendTypeReference(element.extendsBound) + } as JetTypeParameterListOwner + val templateConstraintList = templateClass.typeConstraintList!! + + val declaration = element.getStrictParentOfType() ?: return + val constraintList = declaration.typeConstraintList ?: return + constraintList.addAfter(psiFactory.createComma(), null) + constraintList.addAfter(templateConstraintList.constraints[0], null) + + element.extendsBound?.delete() + val colon = element.node.findChildByType(JetTokens.COLON) + colon?.psi?.delete() + } + + companion object : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val typeParameter = diagnostic.psiElement as? JetTypeParameter ?: return null + return MoveTypeParameterConstraintFix(typeParameter) + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 8ec1482dae5..0e4067b2323 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -340,5 +340,7 @@ public class QuickFixRegistrar : QuickFixContributor { DEPRECATED_TYPE_PARAMETER_SYNTAX.registerFactory(MigrateTypeParameterListFix) UNRESOLVED_REFERENCE.registerFactory(KotlinAddOrderEntryActionFactory) + + MISPLACED_TYPE_PARAMETER_CONSTRAINTS.registerFactory(MoveTypeParameterConstraintFix) } } diff --git a/idea/testData/checker/MultipleBounds.kt b/idea/testData/checker/MultipleBounds.kt index 6d018eabaac..6a45c9d19be 100644 --- a/idea/testData/checker/MultipleBounds.kt +++ b/idea/testData/checker/MultipleBounds.kt @@ -14,8 +14,9 @@ class D() { companion object : A(), B {} } -class Test1() +class Test1() where + T : A, T : B, B : T // error { @@ -41,10 +42,11 @@ class BarFoo> class Buzz where T : Bar<Int>, T : nioho class XFoo> -class Y<T : Foo> where T : Bar +class Y<T> where T : Foo, T : Bar -fun test2(t : T) +fun test2(t : T) where + T : A, T : B, B : T { diff --git a/idea/testData/inspections/cleanup/cleanup.kt b/idea/testData/inspections/cleanup/cleanup.kt index cd21ee0722b..52a697a4995 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt +++ b/idea/testData/inspections/cleanup/cleanup.kt @@ -63,3 +63,6 @@ class C { fun typed() { } + +fun withTypeParameters() where T : Comparable { +} diff --git a/idea/testData/inspections/cleanup/cleanup.kt.after b/idea/testData/inspections/cleanup/cleanup.kt.after index 06a012c6886..c2fd2e3d59c 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt.after +++ b/idea/testData/inspections/cleanup/cleanup.kt.after @@ -62,3 +62,6 @@ class C { fun typed() { } + +fun withTypeParameters() where T : Cloneable, T : Comparable { +} diff --git a/idea/testData/quickfix/typeParameters/misplacedClassTypeParameter.kt b/idea/testData/quickfix/typeParameters/misplacedClassTypeParameter.kt new file mode 100644 index 00000000000..8dca3c76230 --- /dev/null +++ b/idea/testData/quickfix/typeParameters/misplacedClassTypeParameter.kt @@ -0,0 +1,3 @@ +// "Move type parameter constraint to 'where' clause" "true" +class A<T : Cloneable> where T : Comparable<*> { +} diff --git a/idea/testData/quickfix/typeParameters/misplacedClassTypeParameter.kt.after b/idea/testData/quickfix/typeParameters/misplacedClassTypeParameter.kt.after new file mode 100644 index 00000000000..4312dbc1149 --- /dev/null +++ b/idea/testData/quickfix/typeParameters/misplacedClassTypeParameter.kt.after @@ -0,0 +1,3 @@ +// "Move type parameter constraint to 'where' clause" "true" +class A where T : Cloneable, T : Comparable<*> { +} diff --git a/idea/testData/quickfix/typeParameters/misplacedFunctionTypeParameter.kt b/idea/testData/quickfix/typeParameters/misplacedFunctionTypeParameter.kt new file mode 100644 index 00000000000..39cb1293911 --- /dev/null +++ b/idea/testData/quickfix/typeParameters/misplacedFunctionTypeParameter.kt @@ -0,0 +1,3 @@ +// "Move type parameter constraint to 'where' clause" "true" +fun <T : Cloneable> foo() where T : Comparable<*> { +} diff --git a/idea/testData/quickfix/typeParameters/misplacedFunctionTypeParameter.kt.after b/idea/testData/quickfix/typeParameters/misplacedFunctionTypeParameter.kt.after new file mode 100644 index 00000000000..1d816377055 --- /dev/null +++ b/idea/testData/quickfix/typeParameters/misplacedFunctionTypeParameter.kt.after @@ -0,0 +1,3 @@ +// "Move type parameter constraint to 'where' clause" "true" +fun foo() where T : Cloneable, T : Comparable<*> { +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index db312750c6f..ea8a4f51c4a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -6544,6 +6544,27 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/typeParameters") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeParameters extends AbstractQuickFixTest { + public void testAllFilesPresentInTypeParameters() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/typeParameters"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("misplacedClassTypeParameter.kt") + public void testMisplacedClassTypeParameter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/typeParameters/misplacedClassTypeParameter.kt"); + doTest(fileName); + } + + @TestMetadata("misplacedFunctionTypeParameter.kt") + public void testMisplacedFunctionTypeParameter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/typeParameters/misplacedFunctionTypeParameter.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/typeProjection") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)