From 2ac8e5b4a797a20e6dfc0910fa8b60b0c6622104 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 5 Dec 2016 11:51:55 +0300 Subject: [PATCH] "Change return type fix" now handles data class properties correctly #KT-15028 Fixed --- ...eFix.kt => ChangeCallableReturnTypeFix.kt} | 49 ++++++++++++------- .../ChangeFunctionLiteralReturnTypeFix.kt | 2 +- .../idea/quickfix/ChangeVariableTypeFix.kt | 2 +- .../QuickFixFactoryForTypeMismatchError.kt | 6 +-- .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 12 ++--- ...antChangeReturnTypeOfOverriddenFunction.kt | 2 +- .../dataClass.kt | 8 +++ .../dataClass.kt.after | 8 +++ .../notApplicableToConstructor.kt | 2 +- .../idea/quickfix/QuickFixTestGenerated.java | 6 +++ 10 files changed, 65 insertions(+), 32 deletions(-) rename idea/src/org/jetbrains/kotlin/idea/quickfix/{ChangeFunctionReturnTypeFix.kt => ChangeCallableReturnTypeFix.kt} (85%) create mode 100644 idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt create mode 100644 idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionReturnTypeFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCallableReturnTypeFix.kt similarity index 85% rename from idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionReturnTypeFix.kt rename to idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCallableReturnTypeFix.kt index 2fa645054f1..eeac30ecf32 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionReturnTypeFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCallableReturnTypeFix.kt @@ -48,7 +48,10 @@ import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.utils.addToStdlib.check import java.util.* -abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType) : KotlinQuickFixAction(element) { +abstract class ChangeCallableReturnTypeFix( + element: KtCallableDeclaration, + type: KotlinType +) : KotlinQuickFixAction(element) { private val changeFunctionLiteralReturnTypeFix: ChangeFunctionLiteralReturnTypeFix? @@ -73,32 +76,36 @@ abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType if (name != null) { val container = element.resolveToDescriptor().containingDeclaration as? ClassDescriptor val containerName = container?.name?.check { !it.isSpecial }?.asString() - return "function " + (if (containerName != null) "'$containerName.$name'" else "'$name'") + val fullName = if (containerName != null) "'$containerName.$name'" else "'$name'" + if (element is KtParameter) { + return "property $fullName" + } + return "function $fullName" } else { return null } } - class OnType(element: KtFunction, type: KotlinType) : ChangeFunctionReturnTypeFix(element, type), HighPriorityAction { + class OnType(element: KtFunction, type: KotlinType) : ChangeCallableReturnTypeFix(element, type), HighPriorityAction { override fun functionPresentation() = null } - class ForEnclosing(element: KtFunction, type: KotlinType) : ChangeFunctionReturnTypeFix(element, type), HighPriorityAction { + class ForEnclosing(element: KtFunction, type: KotlinType) : ChangeCallableReturnTypeFix(element, type), HighPriorityAction { override fun functionPresentation(): String? { val presentation = super.functionPresentation() ?: return "enclosing function" return "enclosing $presentation" } } - class ForCalled(element: KtFunction, type: KotlinType) : ChangeFunctionReturnTypeFix(element, type) { + class ForCalled(element: KtCallableDeclaration, type: KotlinType) : ChangeCallableReturnTypeFix(element, type) { override fun functionPresentation(): String? { val presentation = super.functionPresentation() ?: return "called function" - return "called $presentation" + return if (element is KtParameter) "accessed $presentation" else "called $presentation" } } - class ForOverridden(element: KtFunction, type: KotlinType) : ChangeFunctionReturnTypeFix(element, type) { + class ForOverridden(element: KtFunction, type: KotlinType) : ChangeCallableReturnTypeFix(element, type) { override fun functionPresentation(): String? { val presentation = super.functionPresentation() ?: return null return "base $presentation" @@ -114,17 +121,19 @@ abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType val functionPresentation = functionPresentation() - if (isUnitType && element.hasBlockBody()) { + if (isUnitType && element is KtFunction && element.hasBlockBody()) { return if (functionPresentation == null) "Remove explicitly specified return type" else "Remove explicitly specified return type of $functionPresentation" } + val typeName = if (element is KtFunction) "return type" else "type" + return if (functionPresentation == null) - "Change return type to '$typePresentation'" + "Change $typeName to '$typePresentation'" else - "Change return type of $functionPresentation to '$typePresentation'" + "Change $typeName of $functionPresentation to '$typePresentation'" } override fun getFamilyName() = KotlinBundle.message("change.type.family") @@ -142,7 +151,7 @@ abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType changeFunctionLiteralReturnTypeFix.invoke(project, editor!!, file) } else { - if (!(isUnitType && element.hasBlockBody())) { + if (!(isUnitType && element is KtFunction && element.hasBlockBody())) { var newTypeRef = KtPsiFactory(project).createType(typeSourceCode) newTypeRef = element.setTypeReference(newTypeRef)!! ShortenReferences.DEFAULT.process(newTypeRef) @@ -158,9 +167,11 @@ abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType val entry = getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(diagnostic) val context = entry.analyze(BodyResolveMode.PARTIAL) val resolvedCall = context.get(BindingContext.COMPONENT_RESOLVED_CALL, entry) ?: return null - val componentFunction = DescriptorToSourceUtils.descriptorToDeclaration(resolvedCall.candidateDescriptor) as KtFunction? ?: return null + val componentFunction = + DescriptorToSourceUtils.descriptorToDeclaration(resolvedCall.candidateDescriptor) as? KtCallableDeclaration + ?: return null val expectedType = context[BindingContext.TYPE, entry.typeReference!!] ?: return null - return ChangeFunctionReturnTypeFix.ForCalled(componentFunction, expectedType) + return ChangeCallableReturnTypeFix.ForCalled(componentFunction, expectedType) } } @@ -172,7 +183,7 @@ abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType val resolvedCall = context[BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, expression] ?: return null val hasNextDescriptor = resolvedCall.candidateDescriptor val hasNextFunction = DescriptorToSourceUtils.descriptorToDeclaration(hasNextDescriptor) as KtFunction? ?: return null - return ChangeFunctionReturnTypeFix.ForCalled(hasNextFunction, hasNextDescriptor.builtIns.booleanType) + return ChangeCallableReturnTypeFix.ForCalled(hasNextFunction, hasNextDescriptor.builtIns.booleanType) } } @@ -183,7 +194,7 @@ abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType val resolvedCall = expression.getResolvedCall(context) ?: return null val compareToDescriptor = resolvedCall.candidateDescriptor val compareTo = DescriptorToSourceUtils.descriptorToDeclaration(compareToDescriptor) as? KtFunction ?: return null - return ChangeFunctionReturnTypeFix.ForCalled(compareTo, compareToDescriptor.builtIns.intType) + return ChangeCallableReturnTypeFix.ForCalled(compareTo, compareToDescriptor.builtIns.intType) } } @@ -197,7 +208,7 @@ abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType val matchingReturnType = QuickFixUtil.findLowerBoundOfOverriddenCallablesReturnTypes(descriptor) if (matchingReturnType != null) { - actions.add(ChangeFunctionReturnTypeFix.OnType(function, matchingReturnType)) + actions.add(ChangeCallableReturnTypeFix.OnType(function, matchingReturnType)) } val functionType = descriptor.returnType ?: return actions @@ -213,7 +224,7 @@ abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType if (overriddenMismatchingFunctions.size == 1) { val overriddenFunction = DescriptorToSourceUtils.descriptorToDeclaration(overriddenMismatchingFunctions[0]) if (overriddenFunction is KtFunction) { - actions.add(ChangeFunctionReturnTypeFix.ForOverridden(overriddenFunction, functionType)) + actions.add(ChangeCallableReturnTypeFix.ForOverridden(overriddenFunction, functionType)) } } @@ -224,14 +235,14 @@ abstract class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType object ChangingReturnTypeToUnitFactory : KotlinSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): IntentionAction? { val function = QuickFixUtil.getParentElementOfType(diagnostic, KtFunction::class.java) ?: return null - return ChangeFunctionReturnTypeFix.ForEnclosing(function, function.builtIns.unitType) + return ChangeCallableReturnTypeFix.ForEnclosing(function, function.builtIns.unitType) } } object ChangingReturnTypeToNothingFactory : KotlinSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): IntentionAction? { val function = QuickFixUtil.getParentElementOfType(diagnostic, KtFunction::class.java) ?: return null - return ChangeFunctionReturnTypeFix.ForEnclosing(function, function.builtIns.nothingType) + return ChangeCallableReturnTypeFix.ForEnclosing(function, function.builtIns.nothingType) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralReturnTypeFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralReturnTypeFix.kt index 185787c21dd..4f39072902b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralReturnTypeFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeFunctionLiteralReturnTypeFix.kt @@ -96,7 +96,7 @@ class ChangeFunctionLiteralReturnTypeFix( val parentFunctionReturnTypeRef = parentFunction.typeReference val parentFunctionReturnType = context.get(BindingContext.TYPE, parentFunctionReturnTypeRef) return if (parentFunctionReturnType != null && !KotlinTypeChecker.DEFAULT.isSubtypeOf(eventualFunctionLiteralType, parentFunctionReturnType)) - ChangeFunctionReturnTypeFix.ForEnclosing(parentFunction, eventualFunctionLiteralType) + ChangeCallableReturnTypeFix.ForEnclosing(parentFunction, eventualFunctionLiteralType) else null } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt index 614fa9ff972..d9b4f35840d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt @@ -113,7 +113,7 @@ open class ChangeVariableTypeFix(element: KtVariableDeclaration, type: KotlinTyp object ComponentFunctionReturnTypeMismatchFactory : KotlinSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val entry = ChangeFunctionReturnTypeFix.getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(diagnostic) + val entry = ChangeCallableReturnTypeFix.getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(diagnostic) val context = entry.analyze() val resolvedCall = context.get(BindingContext.COMPONENT_RESOLVED_CALL, entry) ?: return null if (DescriptorToSourceUtils.descriptorToDeclaration(resolvedCall.candidateDescriptor) == null) return null diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt index 650646fccd8..9d4cb7dc6f6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt @@ -141,7 +141,7 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() { if (function is KtFunction && QuickFixUtil.canFunctionOrGetterReturnExpression(function, diagnosticElement)) { val scope = function.getResolutionScope(context, function.getResolutionFacade()) val typeToInsert = expressionType.approximateWithResolvableType(scope, false) - actions.add(ChangeFunctionReturnTypeFix.ForEnclosing(function, typeToInsert)) + actions.add(ChangeCallableReturnTypeFix.ForEnclosing(function, typeToInsert)) } // Fixing overloaded operators: @@ -150,7 +150,7 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() { if (resolvedCall != null) { val declaration = getFunctionDeclaration(resolvedCall) if (declaration != null) { - actions.add(ChangeFunctionReturnTypeFix.ForCalled(declaration, expectedType)) + actions.add(ChangeCallableReturnTypeFix.ForCalled(declaration, expectedType)) } } } @@ -161,7 +161,7 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() { if (resolvedCall != null) { val declaration = getFunctionDeclaration(resolvedCall) if (declaration != null) { - actions.add(ChangeFunctionReturnTypeFix.ForCalled(declaration, expectedType)) + actions.add(ChangeCallableReturnTypeFix.ForCalled(declaration, expectedType)) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index f782a800a09..e3563f338a5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -264,14 +264,14 @@ class QuickFixRegistrar : QuickFixContributor { VAR_TYPE_MISMATCH_ON_OVERRIDE.registerFactory(changeVariableTypeFix) COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.registerFactory(ChangeVariableTypeFix.ComponentFunctionReturnTypeMismatchFactory) - val changeFunctionReturnTypeFix = ChangeFunctionReturnTypeFix.ChangingReturnTypeToUnitFactory + val changeFunctionReturnTypeFix = ChangeCallableReturnTypeFix.ChangingReturnTypeToUnitFactory RETURN_TYPE_MISMATCH.registerFactory(changeFunctionReturnTypeFix) NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY.registerFactory(changeFunctionReturnTypeFix) - RETURN_TYPE_MISMATCH_ON_OVERRIDE.registerFactory(ChangeFunctionReturnTypeFix.ReturnTypeMismatchOnOverrideFactory) - COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.registerFactory(ChangeFunctionReturnTypeFix.ComponentFunctionReturnTypeMismatchFactory) - HAS_NEXT_FUNCTION_TYPE_MISMATCH.registerFactory(ChangeFunctionReturnTypeFix.HasNextFunctionTypeMismatchFactory) - COMPARE_TO_TYPE_MISMATCH.registerFactory(ChangeFunctionReturnTypeFix.CompareToTypeMismatchFactory) - IMPLICIT_NOTHING_RETURN_TYPE.registerFactory(ChangeFunctionReturnTypeFix.ChangingReturnTypeToNothingFactory) + RETURN_TYPE_MISMATCH_ON_OVERRIDE.registerFactory(ChangeCallableReturnTypeFix.ReturnTypeMismatchOnOverrideFactory) + COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.registerFactory(ChangeCallableReturnTypeFix.ComponentFunctionReturnTypeMismatchFactory) + HAS_NEXT_FUNCTION_TYPE_MISMATCH.registerFactory(ChangeCallableReturnTypeFix.HasNextFunctionTypeMismatchFactory) + COMPARE_TO_TYPE_MISMATCH.registerFactory(ChangeCallableReturnTypeFix.CompareToTypeMismatchFactory) + IMPLICIT_NOTHING_RETURN_TYPE.registerFactory(ChangeCallableReturnTypeFix.ChangingReturnTypeToNothingFactory) TOO_MANY_ARGUMENTS.registerFactory(ChangeFunctionSignatureFix) NO_VALUE_FOR_PARAMETER.registerFactory(ChangeFunctionSignatureFix) diff --git a/idea/testData/quickfix/override/typeMismatchOnOverride/cantChangeReturnTypeOfOverriddenFunction.kt b/idea/testData/quickfix/override/typeMismatchOnOverride/cantChangeReturnTypeOfOverriddenFunction.kt index a2fd8ca03c2..c556a11f027 100644 --- a/idea/testData/quickfix/override/typeMismatchOnOverride/cantChangeReturnTypeOfOverriddenFunction.kt +++ b/idea/testData/quickfix/override/typeMismatchOnOverride/cantChangeReturnTypeOfOverriddenFunction.kt @@ -1,4 +1,4 @@ -// "class org.jetbrains.kotlin.idea.quickfix.ChangeFunctionReturnTypeFix" "false" +// "class org.jetbrains.kotlin.idea.quickfix.ChangeCallableReturnTypeFix" "false" // ERROR: Return type of 'foo' is not a subtype of the return type of the overridden member 'public abstract fun foo(): Int defined in A' interface A { fun foo(): Int diff --git a/idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt b/idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt new file mode 100644 index 00000000000..4b90f97abcb --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt @@ -0,0 +1,8 @@ +// "Change type of accessed property 'A.x' to 'String'" "true" + +// See KT-15028 (CCE) +data class A(val x: Int, val y: String) + +fun f1(a: A) { + val (x: String, y) = a +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt.after b/idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt.after new file mode 100644 index 00000000000..715aebb7dd3 --- /dev/null +++ b/idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt.after @@ -0,0 +1,8 @@ +// "Change type of accessed property 'A.x' to 'String'" "true" + +// See KT-15028 (CCE) +data class A(val x: String, val y: String) + +fun f1(a: A) { + val (x: String, y) = a +} \ No newline at end of file diff --git a/idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/notApplicableToConstructor.kt b/idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/notApplicableToConstructor.kt index 8cfe9e7924c..9c7e04bc87c 100644 --- a/idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/notApplicableToConstructor.kt +++ b/idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/notApplicableToConstructor.kt @@ -1,4 +1,4 @@ -// "class org.jetbrains.kotlin.idea.quickfix.ChangeFunctionReturnTypeFix" "false" +// "class org.jetbrains.kotlin.idea.quickfix.ChangeCallableReturnTypeFix" "false" // ACTION: Change type of 'b' to 'A' // ACTION: Convert property initializer to getter // ACTION: Let 'A' implement interface 'B' diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index e47ba46b216..7fea0a4d1ca 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -9346,6 +9346,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch5.kt"); doTest(fileName); } + + @TestMetadata("dataClass.kt") + public void testDataClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt"); + doTest(fileName); + } } @TestMetadata("idea/testData/quickfix/typeMismatch/fixOverloadedOperator")