diff --git a/ChangeLog.md b/ChangeLog.md index f4bf25d4199..fe49fd98202 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -261,6 +261,7 @@ Using 'this' as function argument in constructor of non-final class - [`KT-13128`](https://youtrack.jetbrains.com/issue/KT-13128) Introduce Variable: Retain entered name after changing "Specify type explicitly" option - [`KT-13054`](https://youtrack.jetbrains.com/issue/KT-13054) Introduce Variable: Skip leading/trailing comments inside selection - [`KT-13277`](https://youtrack.jetbrains.com/issue/KT-13277) Change Signature: Fix usage processing to prevent interfering with Python support plugin +- [`KT-13254`](https://youtrack.jetbrains.com/issue/KT-13254) Rename: Conflict detection for type parameters ##### New features diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index a2ea5319343..f2e95d85d40 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -440,6 +440,8 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt new file mode 100644 index 00000000000..b6dc2adb45a --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinTypeParameterProcessor.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2016 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.refactoring.rename + +import com.intellij.psi.PsiElement +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.psi.KtTypeParameter + +class RenameKotlinTypeParameterProcessor : RenameKotlinPsiProcessor() { + override fun canProcessElement(element: PsiElement) = element is KtTypeParameter + + override fun findCollisions( + element: PsiElement, + newName: String?, + allRenames: MutableMap, + result: MutableList + ) { + if (newName == null) return + val declaration = element as? KtTypeParameter ?: return + val descriptor = declaration.resolveToDescriptor() + checkRedeclarations(descriptor, newName, result) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameConflictUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameConflictUtils.kt index 0dfae8c7c18..86c632985fb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameConflictUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/renameConflictUtils.kt @@ -30,7 +30,6 @@ import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.refactoring.explicateAsText -import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.refactoring.getThisLabelName import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.search.and @@ -83,11 +82,28 @@ internal fun checkRedeclarations( result: MutableList ) { fun getSiblingWithNewName(): DeclarationDescriptor? { + val containingDescriptor = descriptor.containingDeclaration + if (descriptor is ValueParameterDescriptor) { - return descriptor.containingDeclaration.valueParameters.firstOrNull { it.name.asString() == newName } + return (containingDescriptor as CallableDescriptor).valueParameters.firstOrNull { it.name.asString() == newName } + } + + if (descriptor is TypeParameterDescriptor) { + val typeParameters = when (containingDescriptor) { + is ClassDescriptor -> containingDescriptor.declaredTypeParameters + is CallableDescriptor -> containingDescriptor.typeParameters + else -> emptyList() + } + typeParameters.firstOrNull { it.name.asString() == newName }?.let { return it } + + val containingDeclaration = (containingDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() as? KtDeclaration + ?: return null + val dummyVar = KtPsiFactory(containingDeclaration).createProperty("val foo: $newName") + val outerScope = containingDeclaration.getResolutionScope() + val context = dummyVar.analyzeInContext(outerScope, containingDeclaration) + return context[BindingContext.VARIABLE, dummyVar]?.type?.constructor?.declarationDescriptor } - val containingDescriptor = descriptor.containingDeclaration val containingScope = when (containingDescriptor) { is ClassDescriptor -> containingDescriptor.unsubstitutedMemberScope is PackageFragmentDescriptor -> containingDescriptor.getMemberScope() diff --git a/idea/testData/refactoring/rename/renameClassTypeParameterRedeclaration/before/test.kt b/idea/testData/refactoring/rename/renameClassTypeParameterRedeclaration/before/test.kt new file mode 100644 index 00000000000..22ab0732dfb --- /dev/null +++ b/idea/testData/refactoring/rename/renameClassTypeParameterRedeclaration/before/test.kt @@ -0,0 +1 @@ +class A \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameClassTypeParameterRedeclaration/renameClassTypeParameterRedeclaration.test b/idea/testData/refactoring/rename/renameClassTypeParameterRedeclaration/renameClassTypeParameterRedeclaration.test new file mode 100644 index 00000000000..ba2ae803f6b --- /dev/null +++ b/idea/testData/refactoring/rename/renameClassTypeParameterRedeclaration/renameClassTypeParameterRedeclaration.test @@ -0,0 +1,6 @@ +{ + "type": "MARKED_ELEMENT", + "mainFile": "test.kt", + "newName": "V", + "hint": "Type parameter 'V' is already declared in class 'A'" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameClassTypeParameterRedeclarationWithOuterScope/before/test.kt b/idea/testData/refactoring/rename/renameClassTypeParameterRedeclarationWithOuterScope/before/test.kt new file mode 100644 index 00000000000..7e7a8028448 --- /dev/null +++ b/idea/testData/refactoring/rename/renameClassTypeParameterRedeclarationWithOuterScope/before/test.kt @@ -0,0 +1,3 @@ +class A { + inner class B +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameClassTypeParameterRedeclarationWithOuterScope/renameClassTypeParameterRedeclarationWithOuterScope.test b/idea/testData/refactoring/rename/renameClassTypeParameterRedeclarationWithOuterScope/renameClassTypeParameterRedeclarationWithOuterScope.test new file mode 100644 index 00000000000..88ba60d9af9 --- /dev/null +++ b/idea/testData/refactoring/rename/renameClassTypeParameterRedeclarationWithOuterScope/renameClassTypeParameterRedeclarationWithOuterScope.test @@ -0,0 +1,6 @@ +{ + "type": "MARKED_ELEMENT", + "mainFile": "test.kt", + "newName": "T", + "hint": "Type parameter 'T' is already declared in class 'A'" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclaration/before/test.kt b/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclaration/before/test.kt new file mode 100644 index 00000000000..9c6b297b8b4 --- /dev/null +++ b/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclaration/before/test.kt @@ -0,0 +1,3 @@ +fun foo() { + +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclaration/renameFunctionTypeParameterRedeclaration.test b/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclaration/renameFunctionTypeParameterRedeclaration.test new file mode 100644 index 00000000000..729a01fa960 --- /dev/null +++ b/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclaration/renameFunctionTypeParameterRedeclaration.test @@ -0,0 +1,6 @@ +{ + "type": "MARKED_ELEMENT", + "mainFile": "test.kt", + "newName": "V", + "hint": "Type parameter 'V' is already declared in function 'foo'" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclarationWithOuterScope/before/test.kt b/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclarationWithOuterScope/before/test.kt new file mode 100644 index 00000000000..144673b3d77 --- /dev/null +++ b/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclarationWithOuterScope/before/test.kt @@ -0,0 +1,5 @@ +class A { + fun foo() { + + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclarationWithOuterScope/renameFunctionTypeParameterRedeclarationWithOuterScope.test b/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclarationWithOuterScope/renameFunctionTypeParameterRedeclarationWithOuterScope.test new file mode 100644 index 00000000000..88ba60d9af9 --- /dev/null +++ b/idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclarationWithOuterScope/renameFunctionTypeParameterRedeclarationWithOuterScope.test @@ -0,0 +1,6 @@ +{ + "type": "MARKED_ELEMENT", + "mainFile": "test.kt", + "newName": "T", + "hint": "Type parameter 'T' is already declared in class 'A'" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java index 3255341a48e..a201de344ac 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java @@ -281,6 +281,18 @@ public class RenameTestGenerated extends AbstractRenameTest { doTest(fileName); } + @TestMetadata("renameClassTypeParameterRedeclaration/renameClassTypeParameterRedeclaration.test") + public void testRenameClassTypeParameterRedeclaration_RenameClassTypeParameterRedeclaration() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameClassTypeParameterRedeclaration/renameClassTypeParameterRedeclaration.test"); + doTest(fileName); + } + + @TestMetadata("renameClassTypeParameterRedeclarationWithOuterScope/renameClassTypeParameterRedeclarationWithOuterScope.test") + public void testRenameClassTypeParameterRedeclarationWithOuterScope_RenameClassTypeParameterRedeclarationWithOuterScope() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameClassTypeParameterRedeclarationWithOuterScope/renameClassTypeParameterRedeclarationWithOuterScope.test"); + doTest(fileName); + } + @TestMetadata("renameClassWithAutoVarConventions/renameClassWithAutoVarConventions.test") public void testRenameClassWithAutoVarConventions_RenameClassWithAutoVarConventions() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameClassWithAutoVarConventions/renameClassWithAutoVarConventions.test"); @@ -353,6 +365,18 @@ public class RenameTestGenerated extends AbstractRenameTest { doTest(fileName); } + @TestMetadata("renameFunctionTypeParameterRedeclaration/renameFunctionTypeParameterRedeclaration.test") + public void testRenameFunctionTypeParameterRedeclaration_RenameFunctionTypeParameterRedeclaration() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclaration/renameFunctionTypeParameterRedeclaration.test"); + doTest(fileName); + } + + @TestMetadata("renameFunctionTypeParameterRedeclarationWithOuterScope/renameFunctionTypeParameterRedeclarationWithOuterScope.test") + public void testRenameFunctionTypeParameterRedeclarationWithOuterScope_RenameFunctionTypeParameterRedeclarationWithOuterScope() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameFunctionTypeParameterRedeclarationWithOuterScope/renameFunctionTypeParameterRedeclarationWithOuterScope.test"); + doTest(fileName); + } + @TestMetadata("renameGet/get.test") public void testRenameGet_Get() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameGet/get.test");