FIR IDE: add quickfix to change function return type

This commit is contained in:
Tianyu Geng
2021-05-06 09:43:04 -07:00
committed by teamcityserver
parent b5994fa3a9
commit 71c5c9f6c5
22 changed files with 340 additions and 93 deletions
@@ -418,6 +418,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val RETURN_TYPE_MISMATCH by error<KtExpression>(PositioningStrategy.WHOLE_ELEMENT) {
parameter<ConeKotlinType>("expectedType")
parameter<ConeKotlinType>("actualType")
parameter<FirSimpleFunction>("targetFunction")
}
val CYCLIC_GENERIC_UPPER_BOUND by error<PsiElement>()
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
@@ -288,7 +289,7 @@ object FirErrors {
val REIFIED_TYPE_PARAMETER_NO_INLINE by error0<KtTypeParameter>(SourceElementPositioningStrategies.REIFIED_MODIFIER)
val TYPE_PARAMETERS_NOT_ALLOWED by error0<KtDeclaration>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
val TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER by error0<KtTypeParameter>()
val RETURN_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>(SourceElementPositioningStrategies.WHOLE_ELEMENT)
val RETURN_TYPE_MISMATCH by error3<KtExpression, ConeKotlinType, ConeKotlinType, FirSimpleFunction>(SourceElementPositioningStrategies.WHOLE_ELEMENT)
val CYCLIC_GENERIC_UPPER_BOUND by error0<PsiElement>()
val DEPRECATED_TYPE_PARAMETER_SYNTAX by error0<KtDeclaration>(SourceElementPositioningStrategies.TYPE_PARAMETERS_LIST)
val MISPLACED_TYPE_PARAMETER_CONSTRAINTS by warning0<KtTypeParameter>()
@@ -36,7 +36,10 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
if (resultExpression.isNullLiteral && functionReturnType.nullability == ConeNullability.NOT_NULL) {
reporter.reportOn(resultExpression.source, NULL_FOR_NONNULL_TYPE, context)
} else {
reporter.report(RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType), context)
reporter.report(
RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement),
context
)
}
}
}
@@ -667,7 +667,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
map.put(TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER, "Type parameter of a property must be used in its receiver type")
map.put(RETURN_TYPE_MISMATCH, "Return type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE)
map.put(RETURN_TYPE_MISMATCH, "Return type mismatch: expected {0}, actual {1}", RENDER_TYPE, RENDER_TYPE, NOT_RENDERED)
map.put(CYCLIC_GENERIC_UPPER_BOUND, "Type parameter has cyclic upper bounds")
@@ -1148,6 +1148,8 @@ fun main(args: Array<String>) {
model("quickfix/variables/changeMutability", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/when", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/wrapWithSafeLetCall", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/typeMismatch/componentFunctionReturnTypeMismatch", pattern = pattern, filenameStartsLowerCase = true)
model("quickfix/typeMismatch/typeMismatchOnReturnedExpression", pattern = pattern, filenameStartsLowerCase = true)
}
testClass<AbstractHighLevelQuickFixMultiFileTest> {
@@ -12,8 +12,6 @@ import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesListBuilder
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
import org.jetbrains.kotlin.idea.quickfix.fixes.*
import org.jetbrains.kotlin.idea.quickfix.fixes.InitializePropertyQuickFixFactory
import org.jetbrains.kotlin.idea.quickfix.fixes.ChangeTypeQuickFix
import org.jetbrains.kotlin.idea.quickfix.fixes.ReplaceCallFixFactories
class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
private val modifiers = KtQuickFixesListBuilder.registerPsiQuickFix {
@@ -78,9 +76,9 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
}
private val overrides = KtQuickFixesListBuilder.registerPsiQuickFix {
registerApplicator(ChangeTypeQuickFix.changeFunctionReturnTypeOnOverride)
registerApplicator(ChangeTypeQuickFix.changePropertyReturnTypeOnOverride)
registerApplicator(ChangeTypeQuickFix.changeVariableReturnTypeOnOverride)
registerApplicator(ChangeTypeQuickFixFactories.changeFunctionReturnTypeOnOverride)
registerApplicator(ChangeTypeQuickFixFactories.changePropertyReturnTypeOnOverride)
registerApplicator(ChangeTypeQuickFixFactories.changeVariableReturnTypeOnOverride)
registerApplicator(MemberNotImplementedQuickfixFactories.abstractMemberNotImplemented)
registerApplicator(MemberNotImplementedQuickfixFactories.abstractClassMemberNotImplemented)
registerApplicator(MemberNotImplementedQuickfixFactories.manyInterfacesMemberNotImplemented)
@@ -120,6 +118,11 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
registerApplicator(WrapWithSafeLetCallFixFactories.forArgumentTypeMismatch)
}
private val returnTypes = KtQuickFixesListBuilder.registerPsiQuickFix {
registerApplicator(ChangeTypeQuickFixFactories.componentFunctionReturnTypeMismatch)
registerApplicator(ChangeTypeQuickFixFactories.returnTypeMismatch)
}
override val list: KtQuickFixesList = KtQuickFixesList.createCombined(
modifiers,
propertyInitialization,
@@ -127,5 +130,6 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
imports,
mutability,
expressions,
returnTypes
)
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.quickfix.fixes
import com.intellij.psi.PsiElement
import com.intellij.psi.util.parentOfType
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.api.applicator.HLApplicatorInput
import org.jetbrains.kotlin.idea.api.applicator.applicator
import org.jetbrains.kotlin.idea.fir.api.fixes.HLApplicatorTargetWithInput
@@ -16,17 +17,24 @@ import org.jetbrains.kotlin.idea.fir.applicators.CallableReturnTypeUpdaterApplic
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPropertySymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithMembers
import org.jetbrains.kotlin.idea.frontend.api.symbols.psiSafe
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.idea.quickfix.ChangeCallableReturnTypeFix
import org.jetbrains.kotlin.idea.quickfix.ChangeTypeFixUtils
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
object ChangeTypeQuickFix {
object ChangeTypeQuickFixFactories {
val applicator = applicator<KtCallableDeclaration, Input> {
familyName(CallableReturnTypeUpdaterApplicator.applicator.getFamilyName())
actionName { declaration, (updateBaseFunction, type) ->
val presentation = getPresentation(updateBaseFunction, declaration)
actionName { declaration, (targetType, type) ->
val presentation = getPresentation(targetType, declaration)
getActionName(declaration, presentation, type)
}
@@ -39,7 +47,7 @@ object ChangeTypeQuickFix {
declaration: KtCallableDeclaration,
presentation: String?,
type: CallableReturnTypeUpdaterApplicator.Type
) = ChangeCallableReturnTypeFix.StringPresentation.getTextForQuickFix(
) = ChangeTypeFixUtils.getTextForQuickFix(
declaration,
presentation,
type.isUnit,
@@ -47,21 +55,48 @@ object ChangeTypeQuickFix {
)
private fun getPresentation(
updateBaseFunction: Boolean,
targetType: TargetType,
declaration: KtCallableDeclaration
) = when {
updateBaseFunction -> {
val containerName = declaration.parentOfType<KtNamedDeclaration>()?.nameAsName?.takeUnless { it.isSpecial }
ChangeCallableReturnTypeFix.StringPresentation.baseFunctionOrConstructorParameterPresentation(
declaration,
containerName
): String? {
return when (targetType) {
TargetType.CURRENT_DECLARATION -> null
TargetType.BASE_DECLARATION -> KotlinBundle.message(
"fix.change.return.type.presentation.base",
declaration.presentationForQuickfix ?: return null
)
TargetType.ENCLOSING_DECLARATION -> KotlinBundle.message(
"fix.change.return.type.presentation.enclosing",
declaration.presentationForQuickfix ?: return KotlinBundle.message("fix.change.return.type.presentation.enclosing.function")
)
TargetType.CALLED_FUNCTION -> {
val presentation =
declaration.presentationForQuickfix
?: return KotlinBundle.message("fix.change.return.type.presentation.called.function")
when (declaration) {
is KtParameter -> KotlinBundle.message("fix.change.return.type.presentation.accessed", presentation)
else -> KotlinBundle.message("fix.change.return.type.presentation.called", presentation)
}
}
TargetType.VARIABLE -> return "'${declaration.name}'"
}
else -> null
}
private val KtCallableDeclaration.presentationForQuickfix: String?
get() {
val containerName = parentOfType<KtNamedDeclaration>()?.nameAsName?.takeUnless { it.isSpecial }
return ChangeTypeFixUtils.functionOrConstructorParameterPresentation(this, containerName?.asString())
}
enum class TargetType {
CURRENT_DECLARATION,
BASE_DECLARATION,
ENCLOSING_DECLARATION,
CALLED_FUNCTION,
VARIABLE,
}
data class Input(
val updateBaseFunction: Boolean,
val targetType: TargetType,
val type: CallableReturnTypeUpdaterApplicator.Type
) : HLApplicatorInput {
override fun isValidFor(psi: PsiElement): Boolean = type.isValidFor(psi)
@@ -82,6 +117,31 @@ object ChangeTypeQuickFix {
it.variable as? KtPropertySymbol
}
val returnTypeMismatch =
diagnosticFixFactory<KtFirDiagnostic.ReturnTypeMismatch, KtCallableDeclaration, Input>(applicator) { diagnostic ->
val function = diagnostic.targetFunction.psi as? KtCallableDeclaration ?: return@diagnosticFixFactory emptyList()
listOf(function withInput Input(TargetType.ENCLOSING_DECLARATION, createTypeInfo(diagnostic.actualType)))
}
@OptIn(ExperimentalStdlibApi::class)
val componentFunctionReturnTypeMismatch =
diagnosticFixFactory<KtFirDiagnostic.ComponentFunctionReturnTypeMismatch, KtCallableDeclaration, Input>(applicator) { diagnostic ->
val entryWithWrongType =
getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(
diagnostic.componentFunctionName,
diagnostic.psi
)
?: return@diagnosticFixFactory emptyList()
buildList<HLApplicatorTargetWithInput<KtCallableDeclaration, Input>> {
add(entryWithWrongType withInput Input(TargetType.VARIABLE, createTypeInfo(diagnostic.destructingType)))
val classSymbol = (diagnostic.psi.getKtType() as? KtClassType)?.classSymbol as? KtSymbolWithMembers ?: return@buildList
val componentFunction = classSymbol.getMemberScope()
.getCallableSymbols { it == diagnostic.componentFunctionName }
.firstOrNull()?.psi as? KtCallableDeclaration
?: return@buildList
add(componentFunction withInput Input(TargetType.CALLED_FUNCTION, createTypeInfo(diagnostic.expectedType)))
}
}
private inline fun <DIAGNOSTIC : KtDiagnosticWithPsi<KtNamedDeclaration>> changeReturnTypeOnOverride(
crossinline getCallableSymbol: (DIAGNOSTIC) -> KtCallableSymbol?
@@ -100,7 +160,7 @@ object ChangeTypeQuickFix {
): HLApplicatorTargetWithInput<PSI, Input>? {
val lowerSuperType = findLowerBoundOfOverriddenCallablesReturnTypes(callable) ?: return null
val changeToTypeInfo = createTypeInfo(lowerSuperType)
return declaration withInput Input(updateBaseFunction = false, changeToTypeInfo)
return declaration withInput Input(TargetType.CURRENT_DECLARATION, changeToTypeInfo)
}
private fun KtAnalysisSession.createChangeOverriddenFunctionQuickFix(
@@ -110,7 +170,8 @@ object ChangeTypeQuickFix {
val singleNonMatchingOverriddenFunction = findSingleNonMatchingOverriddenFunction(callable, type) ?: return null
val singleMatchingOverriddenFunctionPsi = singleNonMatchingOverriddenFunction.psiSafe<KtCallableDeclaration>() ?: return null
val changeToTypeInfo = createTypeInfo(type)
return singleMatchingOverriddenFunctionPsi withInput Input(updateBaseFunction = true, changeToTypeInfo)
if (!singleMatchingOverriddenFunctionPsi.isWritable) return null
return singleMatchingOverriddenFunctionPsi withInput Input(TargetType.BASE_DECLARATION, changeToTypeInfo)
}
private fun KtAnalysisSession.findSingleNonMatchingOverriddenFunction(
@@ -143,4 +204,13 @@ object ChangeTypeQuickFix {
}
return lowestType
}
private fun getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(
componentName: Name,
rhsExpression: KtExpression
): KtDestructuringDeclarationEntry? {
val componentIndex = componentName.asString().removePrefix("component").toIntOrNull() ?: return null
val destructuringDeclaration = rhsExpression.getParentOfType<KtDestructuringDeclaration>(strict = true) ?: return null
return destructuringDeclaration.entries[componentIndex - 1]
}
}
@@ -1813,4 +1813,140 @@ public class HighLevelQuickFixTestGenerated extends AbstractHighLevelQuickFixTes
runTest("idea/testData/quickfix/wrapWithSafeLetCall/wrapAllNonNullablePositions6.kt");
}
}
@TestMetadata("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ComponentFunctionReturnTypeMismatch extends AbstractHighLevelQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInComponentFunctionReturnTypeMismatch() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
}
@TestMetadata("componentFunctionReturnTypeMismatch1.kt")
public void testComponentFunctionReturnTypeMismatch1() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch1.kt");
}
@TestMetadata("componentFunctionReturnTypeMismatch2.kt")
public void testComponentFunctionReturnTypeMismatch2() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch2.kt");
}
@TestMetadata("componentFunctionReturnTypeMismatch3.kt")
public void testComponentFunctionReturnTypeMismatch3() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch3.kt");
}
@TestMetadata("componentFunctionReturnTypeMismatch4.kt")
public void testComponentFunctionReturnTypeMismatch4() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch4.kt");
}
@TestMetadata("componentFunctionReturnTypeMismatch5.kt")
public void testComponentFunctionReturnTypeMismatch5() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/componentFunctionReturnTypeMismatch5.kt");
}
@TestMetadata("dataClass.kt")
public void testDataClass() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/componentFunctionReturnTypeMismatch/dataClass.kt");
}
}
@TestMetadata("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class TypeMismatchOnReturnedExpression extends AbstractHighLevelQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInTypeMismatchOnReturnedExpression() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true);
}
@TestMetadata("assignmentTypeMismatch.kt")
public void testAssignmentTypeMismatch() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/assignmentTypeMismatch.kt");
}
@TestMetadata("changeFunctionReturnTypeToFunctionType.kt")
public void testChangeFunctionReturnTypeToFunctionType() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/changeFunctionReturnTypeToFunctionType.kt");
}
@TestMetadata("changeFunctionReturnTypeToMatchReturnTypeOfReturnedLiteral.kt")
public void testChangeFunctionReturnTypeToMatchReturnTypeOfReturnedLiteral() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/changeFunctionReturnTypeToMatchReturnTypeOfReturnedLiteral.kt");
}
@TestMetadata("dontChangeFunctionReturnTypeToErrorType.kt")
public void testDontChangeFunctionReturnTypeToErrorType() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/dontChangeFunctionReturnTypeToErrorType.kt");
}
@TestMetadata("literalPropertyWithGetter.kt")
public void testLiteralPropertyWithGetter() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/literalPropertyWithGetter.kt");
}
@TestMetadata("multiFakeOverride.kt")
public void testMultiFakeOverride() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/multiFakeOverride.kt");
}
@TestMetadata("multiFakeOverrideForOperatorConvention.kt")
public void testMultiFakeOverrideForOperatorConvention() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/multiFakeOverrideForOperatorConvention.kt");
}
@TestMetadata("nonLocalReturnRuntime.kt")
public void testNonLocalReturnRuntime() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/nonLocalReturnRuntime.kt");
}
@TestMetadata("nonLocalReturnWithLabelRuntime.kt")
public void testNonLocalReturnWithLabelRuntime() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/nonLocalReturnWithLabelRuntime.kt");
}
@TestMetadata("notApplicableToConstructor.kt")
public void testNotApplicableToConstructor() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/notApplicableToConstructor.kt");
}
@TestMetadata("propertyGetterInitializerTypeMismatch.kt")
public void testPropertyGetterInitializerTypeMismatch() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/propertyGetterInitializerTypeMismatch.kt");
}
@TestMetadata("returnedExpressionTypeMismatchFunctionParameterType.kt")
public void testReturnedExpressionTypeMismatchFunctionParameterType() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/returnedExpressionTypeMismatchFunctionParameterType.kt");
}
@TestMetadata("typeMismatchInIfStatementReturnedByFunction.kt")
public void testTypeMismatchInIfStatementReturnedByFunction() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInIfStatementReturnedByFunction.kt");
}
@TestMetadata("typeMismatchInIfStatementReturnedByLiteral.kt")
public void testTypeMismatchInIfStatementReturnedByLiteral() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInIfStatementReturnedByLiteral.kt");
}
@TestMetadata("typeMismatchInInitializer.kt")
public void testTypeMismatchInInitializer() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInInitializer.kt");
}
@TestMetadata("typeMismatchInReturnStatement.kt")
public void testTypeMismatchInReturnStatement() throws Exception {
runTest("idea/testData/quickfix/typeMismatch/typeMismatchOnReturnedExpression/typeMismatchInReturnStatement.kt");
}
}
}
@@ -217,6 +217,11 @@ private object FirToKtConversionCreator {
KtSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirDeclaration")
),
FirSimpleFunction::class to HLFunctionCallConversion(
"firSymbolBuilder.buildSymbol({0})",
KtSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirSimpleFunction")
),
FirNamedFunctionSymbol::class to HLFunctionCallConversion(
"firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol({0}.fir)",
KtFunctionLikeSymbol::class.createType(),
@@ -137,7 +137,11 @@ object FirIdeDeserializedDeclarationSourceProvider {
private fun KtElement.isCompiled(): Boolean = containingKtFile.isCompiled
private val allowedFakeElementKinds = setOf(FirFakeSourceElementKind.PropertyFromParameter, FirFakeSourceElementKind.ItLambdaParameter)
private val allowedFakeElementKinds = setOf(
FirFakeSourceElementKind.PropertyFromParameter,
FirFakeSourceElementKind.ItLambdaParameter,
FirFakeSourceElementKind.DataClassGeneratedMembers
)
private fun FirElement.getAllowedPsi() = when (val source = source) {
null -> null
@@ -1289,6 +1289,7 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
ReturnTypeMismatchImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
firSymbolBuilder.buildSymbol(firDiagnostic.c),
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
@@ -910,6 +910,7 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ReturnTypeMismatch::class
abstract val expectedType: KtType
abstract val actualType: KtType
abstract val targetFunction: KtSymbol
}
abstract class CyclicGenericUpperBound : KtFirDiagnostic<PsiElement>() {
@@ -1471,6 +1471,7 @@ internal class TypeParameterOfPropertyNotUsedInReceiverImpl(
internal class ReturnTypeMismatchImpl(
override val expectedType: KtType,
override val actualType: KtType,
override val targetFunction: KtSymbol,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.ReturnTypeMismatch(), KtAbstractFirDiagnostic<KtExpression> {
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.quickfix
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
object ChangeTypeFixUtils {
fun familyName(): String = KotlinBundle.message("fix.change.return.type.family")
fun functionOrConstructorParameterPresentation(element: KtCallableDeclaration, containerName: String?): String? {
val name = element.name
return if (name != null) {
val fullName = if (containerName != null) "'${containerName}.$name'" else "'$name'"
when (element) {
is KtParameter -> KotlinBundle.message("fix.change.return.type.presentation.property", fullName)
is KtProperty -> KotlinBundle.message("fix.change.return.type.presentation.property", fullName)
else -> KotlinBundle.message("fix.change.return.type.presentation.function", fullName)
}
} else null
}
fun baseFunctionOrConstructorParameterPresentation(presentation: String): String =
KotlinBundle.message("fix.change.return.type.presentation.base", presentation)
fun baseFunctionOrConstructorParameterPresentation(element: KtCallableDeclaration, containerName: String?): String? {
val presentation = functionOrConstructorParameterPresentation(element, containerName) ?: return null
return baseFunctionOrConstructorParameterPresentation(presentation)
}
fun getTextForQuickFix(
element: KtCallableDeclaration,
presentation: String?,
isUnitType: Boolean,
typePresentation: String
): String {
if (isUnitType && element is KtFunction && element.hasBlockBody()) {
return if (presentation == null)
KotlinBundle.message("fix.change.return.type.remove.explicit.return.type")
else
KotlinBundle.message("fix.change.return.type.remove.explicit.return.type.of", presentation)
}
return when (element) {
is KtFunction -> {
if (presentation != null)
KotlinBundle.message("fix.change.return.type.return.type.text.of", presentation, typePresentation)
else
KotlinBundle.message("fix.change.return.type.return.type.text", typePresentation)
}
else -> {
if (presentation != null)
KotlinBundle.message("fix.change.return.type.type.text.of", presentation, typePresentation)
else
KotlinBundle.message("fix.change.return.type.type.text", typePresentation)
}
}
}
}
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
import org.jetbrains.kotlin.idea.project.builtIns
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.BindingContext
@@ -74,8 +73,8 @@ abstract class ChangeCallableReturnTypeFix(
val element = element!!
if (element.name == null) return null
val container = element.unsafeResolveToDescriptor().containingDeclaration as? ClassDescriptor
val containerName = container?.name?.takeUnless { it.isSpecial }
return StringPresentation.functionOrConstructorParameterPresentation(element, containerName)
val containerName = container?.name?.takeUnless { it.isSpecial }?.asString()
return ChangeTypeFixUtils.functionOrConstructorParameterPresentation(element, containerName)
}
class OnType(element: KtFunction, type: KotlinType) : ChangeCallableReturnTypeFix(element, type), HighPriorityAction {
@@ -104,7 +103,7 @@ abstract class ChangeCallableReturnTypeFix(
class ForOverridden(element: KtFunction, type: KotlinType) : ChangeCallableReturnTypeFix(element, type) {
override fun functionPresentation(): String? {
val presentation = super.functionPresentation() ?: return null
return StringPresentation.baseFunctionOrConstructorParameterPresentation(presentation)
return ChangeTypeFixUtils.baseFunctionOrConstructorParameterPresentation(presentation)
}
}
@@ -115,10 +114,10 @@ abstract class ChangeCallableReturnTypeFix(
return changeFunctionLiteralReturnTypeFix.text
}
return StringPresentation.getTextForQuickFix(element, functionPresentation(), isUnitType, typePresentation)
return ChangeTypeFixUtils.getTextForQuickFix(element, functionPresentation(), isUnitType, typePresentation)
}
override fun getFamilyName(): String = StringPresentation.familyName()
override fun getFamilyName(): String = ChangeTypeFixUtils.familyName()
override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
return !typeContainsError &&
@@ -234,59 +233,5 @@ abstract class ChangeCallableReturnTypeFix(
return multiDeclaration.entries[componentIndex - 1]
}
}
object StringPresentation {
fun familyName(): String = KotlinBundle.message("fix.change.return.type.family")
fun functionOrConstructorParameterPresentation(element: KtCallableDeclaration, containerName: Name?): String? {
val name = element.name
return if (name != null) {
val fullName = if (containerName != null) "'${containerName.asString()}.$name'" else "'$name'"
when (element) {
is KtParameter -> KotlinBundle.message("fix.change.return.type.presentation.property", fullName)
is KtProperty -> KotlinBundle.message("fix.change.return.type.presentation.property", fullName)
else -> KotlinBundle.message("fix.change.return.type.presentation.function", fullName)
}
} else null
}
fun baseFunctionOrConstructorParameterPresentation(presentation: String): String =
KotlinBundle.message("fix.change.return.type.presentation.base", presentation)
fun baseFunctionOrConstructorParameterPresentation(element: KtCallableDeclaration, containerName: Name?): String? {
val presentation = functionOrConstructorParameterPresentation(element, containerName) ?: return null
return baseFunctionOrConstructorParameterPresentation(presentation)
}
fun getTextForQuickFix(
element: KtCallableDeclaration,
presentation: String?,
isUnitType: Boolean,
typePresentation: String
): String {
if (isUnitType && element is KtFunction && element.hasBlockBody()) {
return if (presentation == null)
KotlinBundle.message("fix.change.return.type.remove.explicit.return.type")
else
KotlinBundle.message("fix.change.return.type.remove.explicit.return.type.of", presentation)
}
return when (element) {
is KtFunction -> {
if (presentation != null)
KotlinBundle.message("fix.change.return.type.return.type.text.of", presentation, typePresentation)
else
KotlinBundle.message("fix.change.return.type.return.type.text", typePresentation)
}
else -> {
if (presentation != null)
KotlinBundle.message("fix.change.return.type.type.text.of", presentation, typePresentation)
else
KotlinBundle.message("fix.change.return.type.type.text", typePresentation)
}
}
}
}
}
@@ -4,4 +4,5 @@ fun foo() {
var x = 1
x += 21<caret>
}
}
}
/* IGNORE_FIR */
@@ -4,4 +4,5 @@ fun foo() {
var x = 1
x += 21<caret>
}
}
}
/* IGNORE_FIR */
@@ -1,4 +1,5 @@
// "Change type of 'complex' to '(Int) -> Long'" "true"
val complex: (Int) -> String
get() = { it.toLong()<caret> }
get() = { it.toLong()<caret> }
/* IGNORE_FIR */
@@ -1,4 +1,5 @@
// "Change type of 'complex' to '(Int) -> Long'" "true"
val complex: (Int) -> Long
get() = { it.toLong() }
get() = { it.toLong() }
/* IGNORE_FIR */
@@ -4,4 +4,5 @@ class A {
var x: Int
get(): Int = if (true) { {42}<caret> } else { {24} }
set(i: Int) {}
}
}
/* IGNORE_FIR */
@@ -3,4 +3,5 @@ fun foo(f: () -> Int) {
foo {
""<caret>
}
}
}
/* IGNORE_FIR */
@@ -9,4 +9,5 @@ fun foo() {
if (true) x<caret> else x
}
}
}
}
/* IGNORE_FIR */