diff --git a/ChangeLog.md b/ChangeLog.md index f717b9503d6..bafd0a652cc 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -223,6 +223,7 @@ These artifacts include extensions for the types available in the latter JDKs, s #### Intention actions, inspections and quickfixes - [`KT-14569`](https://youtrack.jetbrains.com/issue/KT-14569) Convert Property to Function Intention: Search occurrences using progress dialog +- [`KT-14501`](https://youtrack.jetbrains.com/issue/KT-14501) Create from Usage: Support array access expressions/binary expressions with type mismatch errors #### Refactorings diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 195473926c2..c49218d61d3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -296,6 +296,7 @@ class QuickFixRegistrar : QuickFixContributor { NONE_APPLICABLE.registerFactory(CreateBinaryOperationActionFactory) NO_VALUE_FOR_PARAMETER.registerFactory(CreateBinaryOperationActionFactory) TOO_MANY_ARGUMENTS.registerFactory(CreateBinaryOperationActionFactory) + TYPE_MISMATCH_ERRORS.forEach { it.registerFactory(CreateBinaryOperationActionFactory) } UNRESOLVED_REFERENCE_WRONG_RECEIVER.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES) UNRESOLVED_REFERENCE.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES) @@ -340,7 +341,9 @@ class QuickFixRegistrar : QuickFixContributor { MANY_CLASSES_IN_SUPERTYPE_LIST.registerFactory(RemoveSupertypeFix) NO_GET_METHOD.registerFactory(CreateGetFunctionActionFactory) + TYPE_MISMATCH_ERRORS.forEach { it.registerFactory(CreateGetFunctionActionFactory) } NO_SET_METHOD.registerFactory(CreateSetFunctionActionFactory) + TYPE_MISMATCH_ERRORS.forEach { it.registerFactory(CreateSetFunctionActionFactory) } HAS_NEXT_MISSING.registerFactory(CreateHasNextFunctionActionFactory) HAS_NEXT_FUNCTION_NONE_APPLICABLE.registerFactory(CreateHasNextFunctionActionFactory) NEXT_MISSING.registerFactory(CreateNextFunctionActionFactory) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetFunctionActionFactory.kt index 9b66fb63004..93d0f9ee239 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetFunctionActionFactory.kt @@ -17,20 +17,15 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable import org.jetbrains.kotlin.diagnostics.Diagnostic -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.types.Variance -import java.util.Collections - -object CreateGetFunctionActionFactory : CreateCallableMemberFromUsageFactory() { - override fun getElementOfInterest(diagnostic: Diagnostic): KtArrayAccessExpression? { - return QuickFixUtil.getParentElementOfType(diagnostic, KtArrayAccessExpression::class.java) - } +import java.util.* +object CreateGetFunctionActionFactory : CreateGetSetFunctionActionFactory(isGet = true) { override fun createCallableInfo(element: KtArrayAccessExpression, diagnostic: Diagnostic): CallableInfo? { val arrayExpr = element.arrayExpression ?: return null diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetSetFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetSetFunctionActionFactory.kt new file mode 100644 index 00000000000..6b1a8f3d0a6 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetSetFunctionActionFactory.kt @@ -0,0 +1,45 @@ +/* + * 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.quickfix.createFromUsage.createCallable + +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtArrayAccessExpression +import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtContainerNode +import org.jetbrains.kotlin.psi.KtPsiUtil + +abstract class CreateGetSetFunctionActionFactory(private val isGet: Boolean) : CreateCallableMemberFromUsageFactory() { + override fun getElementOfInterest(diagnostic: Diagnostic): KtArrayAccessExpression? { + return when (diagnostic.factory) { + Errors.NO_GET_METHOD -> if (isGet) Errors.NO_GET_METHOD.cast(diagnostic).psiElement else null + Errors.NO_SET_METHOD -> if (!isGet) Errors.NO_SET_METHOD.cast(diagnostic).psiElement else null + in Errors.TYPE_MISMATCH_ERRORS -> { + val indicesNode = diagnostic.psiElement.parent as? KtContainerNode ?: return null + if (indicesNode.node.elementType != KtNodeTypes.INDICES) return null + val arrayAccess = indicesNode.parent as? KtArrayAccessExpression ?: return null + val parent = arrayAccess.parent + val isAssignmentLHS = KtPsiUtil.isAssignment(parent) && (parent as KtBinaryExpression).left == arrayAccess + if (isAssignmentLHS == isGet) return null + + arrayAccess + } + else -> throw IllegalArgumentException("Unexpected diagnostic: ${diagnostic.factory.name}") + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateSetFunctionActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateSetFunctionActionFactory.kt index e7d4e27a01a..80d43b3c4b7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateSetFunctionActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateSetFunctionActionFactory.kt @@ -34,11 +34,7 @@ import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.expressions.OperatorConventions import java.util.* -object CreateSetFunctionActionFactory : CreateCallableMemberFromUsageFactory() { - override fun getElementOfInterest(diagnostic: Diagnostic): KtArrayAccessExpression? { - return QuickFixUtil.getParentElementOfType(diagnostic, KtArrayAccessExpression::class.java) - } - +object CreateSetFunctionActionFactory : CreateGetSetFunctionActionFactory(isGet = false) { override fun createCallableInfo(element: KtArrayAccessExpression, diagnostic: Diagnostic): CallableInfo? { val arrayExpr = element.arrayExpression ?: return null val arrayType = TypeInfo(arrayExpr, Variance.IN_VARIANCE) diff --git a/idea/testData/quickfix/createFromUsage/createFunction/binaryOperations/typeMismatch.kt b/idea/testData/quickfix/createFromUsage/createFunction/binaryOperations/typeMismatch.kt new file mode 100644 index 00000000000..bb2e487a2e0 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/binaryOperations/typeMismatch.kt @@ -0,0 +1,8 @@ +// "Create extension function 'A.times'" "true" +class A + +operator fun A.times(i: Int) = this + +fun test() { + A() * "1" +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/binaryOperations/typeMismatch.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/binaryOperations/typeMismatch.kt.after new file mode 100644 index 00000000000..165cef2e85e --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/binaryOperations/typeMismatch.kt.after @@ -0,0 +1,12 @@ +// "Create extension function 'A.times'" "true" +class A + +operator fun A.times(i: Int) = this + +fun test() { + A() * "1" +} + +private infix operator fun A.times(s: String) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. +} diff --git a/idea/testData/quickfix/createFromUsage/createFunction/get/typeMismatch.kt b/idea/testData/quickfix/createFromUsage/createFunction/get/typeMismatch.kt new file mode 100644 index 00000000000..74b3a1d0b64 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/get/typeMismatch.kt @@ -0,0 +1,8 @@ +// "Create extension function 'A.get'" "true" +class A + +fun A.get(i: Int) = this + +fun test() { + A()["1"] +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/get/typeMismatch.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/get/typeMismatch.kt.after new file mode 100644 index 00000000000..6ce87dc96e9 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/get/typeMismatch.kt.after @@ -0,0 +1,12 @@ +// "Create extension function 'A.get'" "true" +class A + +fun A.get(i: Int) = this + +fun test() { + A()["1"] +} + +private operator fun A.get(s: String) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. +} diff --git a/idea/testData/quickfix/createFromUsage/createFunction/set/typeMismatch.kt b/idea/testData/quickfix/createFromUsage/createFunction/set/typeMismatch.kt new file mode 100644 index 00000000000..fca177442e5 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/set/typeMismatch.kt @@ -0,0 +1,10 @@ +// "Create extension function 'A.set'" "true" +class A + +fun A.set(i: Int, j: Int) { + +} + +fun test() { + A()["1"] = 2 +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/set/typeMismatch.kt.after b/idea/testData/quickfix/createFromUsage/createFunction/set/typeMismatch.kt.after new file mode 100644 index 00000000000..50fbdb4630e --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/set/typeMismatch.kt.after @@ -0,0 +1,14 @@ +// "Create extension function 'A.set'" "true" +class A + +fun A.set(i: Int, j: Int) { + +} + +fun test() { + A()["1"] = 2 +} + +private operator fun A.set(s: String, value: Int) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 566d03f5406..0dbec2d9cd8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -2074,6 +2074,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("typeMismatch.kt") + public void testTypeMismatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/binaryOperations/typeMismatch.kt"); + doTest(fileName); + } + @TestMetadata("whenInOnUserType.kt") public void testWhenInOnUserType() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/binaryOperations/whenInOnUserType.kt"); @@ -2769,6 +2775,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/get/createGetFromUsage9.kt"); doTest(fileName); } + + @TestMetadata("typeMismatch.kt") + public void testTypeMismatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/get/typeMismatch.kt"); + doTest(fileName); + } } @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/hasNext") @@ -2898,6 +2910,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/set/setterForIncrement.kt"); doTest(fileName); } + + @TestMetadata("typeMismatch.kt") + public void testTypeMismatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/set/typeMismatch.kt"); + doTest(fileName); + } } @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/unaryOperations")