diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/OperatorConventions.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/OperatorConventions.java index 898cbb6b73c..448bcea3ae6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/OperatorConventions.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/OperatorConventions.java @@ -117,7 +117,7 @@ public class OperatorConventions { .build(); public static final ImmutableSet CONVENTION_NAMES = ImmutableSet.builder() - .add(GET, SET, INVOKE, CONTAINS, ITERATOR, NEXT, HAS_NEXT, EQUALS, COMPARE_TO) + .add(GET, SET, INVOKE, CONTAINS, ITERATOR, NEXT, HAS_NEXT, EQUALS, COMPARE_TO, GET_VALUE, SET_VALUE) .addAll(UNARY_OPERATION_NAMES.values()) .addAll(BINARY_OPERATION_NAMES.values()) .addAll(ASSIGNMENT_OPERATIONS.values()) diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt index 7dc08117b3d..86f8be936e1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt @@ -92,6 +92,8 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe Errors.NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION, Errors.BACKING_FIELD_OLD_SYNTAX, Errors.OPERATOR_MODIFIER_REQUIRED, + Errors.DEPRECATED_UNARY_PLUS_MINUS, + Errors.DELEGATE_RESOLVED_TO_DEPRECATED_CONVENTION, Errors.INFIX_MODIFIER_REQUIRED, Errors.CALLABLE_REFERENCE_TO_MEMBER_OR_EXTENSION_WITH_EMPTY_LHS, Errors.DEPRECATED_TYPE_PARAMETER_SYNTAX, diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedFunctionConventionFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedFunctionConventionFix.kt new file mode 100644 index 00000000000..edf123c6ce1 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedFunctionConventionFix.kt @@ -0,0 +1,71 @@ +/* + * 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 com.intellij.psi.PsiElement +import com.intellij.refactoring.rename.RenameProcessor +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2 +import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters3 +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde +import org.jetbrains.kotlin.psi.* + +class DeprecatedFunctionConventionFix( + element: JetNamedFunction, + private val newName: String +) : JetIntentionAction(element), CleanupFix { + override fun getText(): String = "Rename to '$newName'" + override fun getFamilyName(): String = text + + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + FilteredRenameProcessor(project, element, newName, false, false).run() + } + + companion object : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val (functionDescriptor, newName) = when (diagnostic) { + is DiagnosticWithParameters3<*, *, *, *> -> Pair(diagnostic.a, diagnostic.c) + is DiagnosticWithParameters2<*, *, *> -> Pair(diagnostic.a, diagnostic.b) + else -> Pair(null, null) + } + if (functionDescriptor !is FunctionDescriptor || newName !is String) return null + + val element = DescriptorToSourceUtilsIde.getAnyDeclaration(diagnostic.psiFile.project, functionDescriptor) + as? JetNamedFunction ?: return null + return DeprecatedFunctionConventionFix(element, newName) + } + } + + private class FilteredRenameProcessor( + project: Project, + element: PsiElement, + newName: String, + isSearchInComments: Boolean, + isSearchTextOccurrences: Boolean + ) : RenameProcessor(project, element, newName, isSearchInComments, isSearchTextOccurrences) { + override fun findUsages(): Array { + return super.findUsages().filter { + it.element !is JetOperationReferenceExpression + }.toTypedArray() + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 4546271ec90..a219de21336 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -341,5 +341,8 @@ public class QuickFixRegistrar : QuickFixContributor { UNRESOLVED_REFERENCE.registerFactory(KotlinAddOrderEntryActionFactory) MISPLACED_TYPE_PARAMETER_CONSTRAINTS.registerFactory(MoveTypeParameterConstraintFix) + + DELEGATE_RESOLVED_TO_DEPRECATED_CONVENTION.registerFactory(DeprecatedFunctionConventionFix) + DEPRECATED_UNARY_PLUS_MINUS.registerFactory(DeprecatedFunctionConventionFix) } } diff --git a/idea/testData/inspections/cleanup/cleanup.kt b/idea/testData/inspections/cleanup/cleanup.kt index 6b26f0061b1..a6273bea671 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt +++ b/idea/testData/inspections/cleanup/cleanup.kt @@ -45,7 +45,14 @@ class Foo { set(value) { $x = value } } +class CustomDelegate { + operator fun get(thisRef: Any?, prop: PropertyMetadata): String = "" + operator fun set(thisRef: Any?, prop: PropertyMetadata, value: String) {} +} + class B { + var a: String by CustomDelegate() + fun plus(a: A): A = A() } diff --git a/idea/testData/inspections/cleanup/cleanup.kt.after b/idea/testData/inspections/cleanup/cleanup.kt.after index e35756025b9..26c99b101ca 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt.after +++ b/idea/testData/inspections/cleanup/cleanup.kt.after @@ -36,7 +36,7 @@ const val i = 1 annotation class Fancy(val param: Int) -@Fancy(i) class D +@Fancy(i) class D class Foo { var x: Int = 0 @@ -44,7 +44,14 @@ class Foo { set(value) { field = value } } +class CustomDelegate { + operator fun getValue(thisRef: Any?, prop: PropertyMetadata): String = "" + operator fun setValue(thisRef: Any?, prop: PropertyMetadata, value: String) {} +} + class B { + var a: String by CustomDelegate() + operator fun plus(a: A): A = A() } diff --git a/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetExtension.kt b/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetExtension.kt new file mode 100644 index 00000000000..9915f8babb4 --- /dev/null +++ b/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetExtension.kt @@ -0,0 +1,8 @@ +// "Rename to 'getValue'" "true" +class CustomDelegate + +operator fun CustomDelegate.get(thisRef: Any?, prop: PropertyMetadata): String = "" + +class Example { + val a: String by CustomDelegate() +} diff --git a/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetExtension.kt.after b/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetExtension.kt.after new file mode 100644 index 00000000000..bc2d89e7442 --- /dev/null +++ b/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetExtension.kt.after @@ -0,0 +1,8 @@ +// "Rename to 'getValue'" "true" +class CustomDelegate + +operator fun CustomDelegate.getValue(thisRef: Any?, prop: PropertyMetadata): String = "" + +class Example { + val a: String by CustomDelegate() +} diff --git a/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetMember.kt b/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetMember.kt new file mode 100644 index 00000000000..7c5f04d074a --- /dev/null +++ b/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetMember.kt @@ -0,0 +1,9 @@ +// "Rename to 'setValue'" "true" +class CustomDelegate { + operator fun get(thisRef: Any?, prop: PropertyMetadata): String = "" + operator fun set(thisRef: Any?, prop: PropertyMetadata, value: String) {} +} + +class Example { + var a: String by CustomDelegate() +} diff --git a/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetMember.kt.after b/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetMember.kt.after new file mode 100644 index 00000000000..a026f1eca94 --- /dev/null +++ b/idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetMember.kt.after @@ -0,0 +1,9 @@ +// "Rename to 'setValue'" "true" +class CustomDelegate { + operator fun get(thisRef: Any?, prop: PropertyMetadata): String = "" + operator fun setValue(thisRef: Any?, prop: PropertyMetadata, value: String) {} +} + +class Example { + var a: String by CustomDelegate() +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 1b606ece00d..d0d8afedcbb 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -4373,6 +4373,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("deprecatedDelegatesGetSetExtension.kt") + public void testDeprecatedDelegatesGetSetExtension() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetExtension.kt"); + doTest(fileName); + } + + @TestMetadata("deprecatedDelegatesGetSetMember.kt") + public void testDeprecatedDelegatesGetSetMember() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/modifiers/deprecatedDelegatesGetSetMember.kt"); + doTest(fileName); + } + @TestMetadata("finalTrait.kt") public void testFinalTrait() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/modifiers/finalTrait.kt");