"Change return type fix" now handles data class properties correctly #KT-15028 Fixed
This commit is contained in:
+30
-19
@@ -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<KtFunction>(element) {
|
||||
abstract class ChangeCallableReturnTypeFix(
|
||||
element: KtCallableDeclaration,
|
||||
type: KotlinType
|
||||
) : KotlinQuickFixAction<KtCallableDeclaration>(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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
+8
@@ -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<caret>
|
||||
}
|
||||
+8
@@ -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<caret>
|
||||
}
|
||||
Vendored
+1
-1
@@ -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'
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user